1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qrhigles2_p_p.h"
38#include <QWindow>
39#include <QOffscreenSurface>
40#include <QOpenGLContext>
41#include <QtGui/private/qopenglextensions_p.h>
42#include <QtGui/private/qopenglprogrambinarycache_p.h>
43#include <qmath.h>
44
45QT_BEGIN_NAMESPACE
46
47/*
48 OpenGL backend. Binding vertex attribute locations and decomposing uniform
49 buffers into uniforms are handled transparently to the application via the
50 reflection data (QShaderDescription). Real uniform buffers are never used,
51 regardless of the GLSL version. Textures and buffers feature no special
52 logic, it's all just glTexSubImage2D and glBufferSubData (with "dynamic"
53 buffers set to GL_DYNAMIC_DRAW). The swapchain and the associated
54 renderbuffer for depth-stencil will be dummies since we have no control over
55 the underlying buffers here. While the baseline here is plain GLES 2.0, some
56 modern GL(ES) features like multisample renderbuffers, blits, and compute are
57 used when available. Also functional with core profile contexts.
58*/
59
60/*!
61 \class QRhiGles2InitParams
62 \internal
63 \inmodule QtGui
64 \brief OpenGL specific initialization parameters.
65
66 An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
67 Additionally, while optional, it is recommended that the QWindow the first
68 QRhiSwapChain will target is passed in as well.
69
70 \badcode
71 QOffscreenSurface *fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
72 QRhiGles2InitParams params;
73 params.fallbackSurface = fallbackSurface;
74 params.window = window;
75 rhi = QRhi::create(QRhi::OpenGLES2, &params);
76 \endcode
77
78 By default QRhi creates a QOpenGLContext on its own. This approach works
79 well in most cases, included threaded scenarios, where there is a dedicated
80 QRhi for each rendering thread. As there will be a QOpenGLContext for each
81 QRhi, the OpenGL context requirements (a context can only be current on one
82 thread) are satisfied. The implicitly created context is destroyed
83 automatically together with the QRhi.
84
85 The QSurfaceFormat for the context is specified in \l format. The
86 constructor sets this to QSurfaceFormat::defaultFormat() so applications
87 that use QSurfaceFormat::setDefaultFormat() do not need to set the format
88 again.
89
90 \note The depth and stencil buffer sizes are set automatically to 24 and 8
91 when no size was explicitly set for these buffers in \l format. As there
92 are possible adjustments to \l format, applications can use
93 adjustedFormat() to query the effective format that is passed to
94 QOpenGLContext::setFormat() internally.
95
96 A QOffscreenSurface has to be specified in \l fallbackSurface. In order to
97 prevent mistakes in threaded situations, this is never created
98 automatically by the QRhi since, like QWindow, QOffscreenSurface can only
99 be created on the gui/main thread.
100
101 As a convenience, applications can use newFallbackSurface() which creates
102 and returns a QOffscreenSurface that is compatible with the QOpenGLContext
103 that is going to be created by the QRhi afterwards. Note that the ownership
104 of the returned QOffscreenSurface is transferred to the caller and the QRhi
105 will not destroy it.
106
107 \note QRhiSwapChain can only target QWindow instances that have their
108 surface type set to QSurface::OpenGLSurface.
109
110 \note \l window is optional. It is recommended to specify it whenever
111 possible, in order to avoid problems on multi-adapter and multi-screen
112 systems. When \l window is not set, the very first
113 QOpenGLContext::makeCurrent() happens with \l fallbackSurface which may be
114 an invisible window on some platforms (for example, Windows) and that may
115 trigger unexpected problems in some cases.
116
117 \section2 Working with existing OpenGL contexts
118
119 When interoperating with another graphics engine, it may be necessary to
120 get a QRhi instance that uses the same OpenGL context. This can be achieved
121 by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The
122 \l{QRhiGles2NativeHandles::context}{context} must be set to a non-null
123 value.
124
125 An alternative approach is to create a QOpenGLContext that
126 \l{QOpenGLContext::setShareContext()}{shares resources} with the other
127 engine's context and passing in that context via QRhiGles2NativeHandles.
128
129 The QRhi does not take ownership of the QOpenGLContext passed in via
130 QRhiGles2NativeHandles.
131 */
132
133/*!
134 \class QRhiGles2NativeHandles
135 \internal
136 \inmodule QtGui
137 \brief Holds the OpenGL context used by the QRhi.
138 */
139
140#ifndef GL_BGRA
141#define GL_BGRA 0x80E1
142#endif
143
144#ifndef GL_R8
145#define GL_R8 0x8229
146#endif
147
148#ifndef GL_R16
149#define GL_R16 0x822A
150#endif
151
152#ifndef GL_RED
153#define GL_RED 0x1903
154#endif
155
156#ifndef GL_RGBA8
157#define GL_RGBA8 0x8058
158#endif
159
160#ifndef GL_RGBA32F
161#define GL_RGBA32F 0x8814
162#endif
163
164#ifndef GL_RGBA16F
165#define GL_RGBA16F 0x881A
166#endif
167
168#ifndef GL_R16F
169#define GL_R16F 0x822D
170#endif
171
172#ifndef GL_R32F
173#define GL_R32F 0x822E
174#endif
175
176#ifndef GL_HALF_FLOAT
177#define GL_HALF_FLOAT 0x140B
178#endif
179
180#ifndef GL_DEPTH_COMPONENT16
181#define GL_DEPTH_COMPONENT16 0x81A5
182#endif
183
184#ifndef GL_DEPTH_COMPONENT24
185#define GL_DEPTH_COMPONENT24 0x81A6
186#endif
187
188#ifndef GL_DEPTH_COMPONENT32F
189#define GL_DEPTH_COMPONENT32F 0x8CAC
190#endif
191
192#ifndef GL_STENCIL_INDEX
193#define GL_STENCIL_INDEX 0x1901
194#endif
195
196#ifndef GL_STENCIL_INDEX8
197#define GL_STENCIL_INDEX8 0x8D48
198#endif
199
200#ifndef GL_DEPTH24_STENCIL8
201#define GL_DEPTH24_STENCIL8 0x88F0
202#endif
203
204#ifndef GL_DEPTH_STENCIL_ATTACHMENT
205#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
206#endif
207
208#ifndef GL_DEPTH_STENCIL
209#define GL_DEPTH_STENCIL 0x84F9
210#endif
211
212#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
213#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
214#endif
215
216#ifndef GL_FRAMEBUFFER_SRGB
217#define GL_FRAMEBUFFER_SRGB 0x8DB9
218#endif
219
220#ifndef GL_READ_FRAMEBUFFER
221#define GL_READ_FRAMEBUFFER 0x8CA8
222#endif
223
224#ifndef GL_DRAW_FRAMEBUFFER
225#define GL_DRAW_FRAMEBUFFER 0x8CA9
226#endif
227
228#ifndef GL_MAX_DRAW_BUFFERS
229#define GL_MAX_DRAW_BUFFERS 0x8824
230#endif
231
232#ifndef GL_TEXTURE_COMPARE_MODE
233#define GL_TEXTURE_COMPARE_MODE 0x884C
234#endif
235
236#ifndef GL_COMPARE_REF_TO_TEXTURE
237#define GL_COMPARE_REF_TO_TEXTURE 0x884E
238#endif
239
240#ifndef GL_TEXTURE_COMPARE_FUNC
241#define GL_TEXTURE_COMPARE_FUNC 0x884D
242#endif
243
244#ifndef GL_MAX_SAMPLES
245#define GL_MAX_SAMPLES 0x8D57
246#endif
247
248#ifndef GL_SHADER_STORAGE_BUFFER
249#define GL_SHADER_STORAGE_BUFFER 0x90D2
250#endif
251
252#ifndef GL_READ_ONLY
253#define GL_READ_ONLY 0x88B8
254#endif
255
256#ifndef GL_WRITE_ONLY
257#define GL_WRITE_ONLY 0x88B9
258#endif
259
260#ifndef GL_READ_WRITE
261#define GL_READ_WRITE 0x88BA
262#endif
263
264#ifndef GL_COMPUTE_SHADER
265#define GL_COMPUTE_SHADER 0x91B9
266#endif
267
268#ifndef GL_ALL_BARRIER_BITS
269#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
270#endif
271
272#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
273#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
274#endif
275
276#ifndef GL_SHADER_STORAGE_BARRIER_BIT
277#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
278#endif
279
280#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
281#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
282#endif
283
284#ifndef GL_POINT_SPRITE
285#define GL_POINT_SPRITE 0x8861
286#endif
287
288#ifndef GL_MAP_READ_BIT
289#define GL_MAP_READ_BIT 0x0001
290#endif
291
292Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)
293
294/*!
295 Constructs a new QRhiGles2InitParams.
296
297 \l format is set to QSurfaceFormat::defaultFormat().
298 */
299QRhiGles2InitParams::QRhiGles2InitParams()
300{
301 format = QSurfaceFormat::defaultFormat();
302}
303
304/*!
305 \return the QSurfaceFormat that will be set on the QOpenGLContext before
306 calling QOpenGLContext::create(). This format is based on \a format, but
307 may be adjusted. Applicable only when QRhi creates the context.
308 Applications are advised to set this format on their QWindow in order to
309 avoid potential BAD_MATCH failures.
310 */
311QSurfaceFormat QRhiGles2InitParams::adjustedFormat(const QSurfaceFormat &format)
312{
313 QSurfaceFormat fmt = format;
314
315 if (fmt.depthBufferSize() == -1)
316 fmt.setDepthBufferSize(24);
317 if (fmt.stencilBufferSize() == -1)
318 fmt.setStencilBufferSize(8);
319
320 return fmt;
321}
322
323/*!
324 \return a new QOffscreenSurface that can be used with a QRhi by passing it
325 via a QRhiGles2InitParams.
326
327 \a format is adjusted as appropriate in order to avoid having problems
328 afterwards due to an incompatible context and surface.
329
330 \note This function must only be called on the gui/main thread.
331
332 \note It is the application's responsibility to destroy the returned
333 QOffscreenSurface on the gui/main thread once the associated QRhi has been
334 destroyed. The QRhi will not destroy the QOffscreenSurface.
335 */
336QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
337{
338 QSurfaceFormat fmt = adjustedFormat(format);
339
340 // To resolve all fields in the format as much as possible, create a context.
341 // This may be heavy, but allows avoiding BAD_MATCH on some systems.
342 QOpenGLContext tempContext;
343 tempContext.setFormat(fmt);
344 if (tempContext.create())
345 fmt = tempContext.format();
346 else
347 qWarning(msg: "QRhiGles2: Failed to create temporary context");
348
349 QOffscreenSurface *s = new QOffscreenSurface;
350 s->setFormat(fmt);
351 s->create();
352
353 return s;
354}
355
356QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
357 : ofr(this)
358{
359 requestedFormat = QRhiGles2InitParams::adjustedFormat(format: params->format);
360 fallbackSurface = params->fallbackSurface;
361 maybeWindow = params->window; // may be null
362
363 importedContext = importDevice != nullptr;
364 if (importedContext) {
365 ctx = importDevice->context;
366 if (!ctx) {
367 qWarning(msg: "No OpenGL context given, cannot import");
368 importedContext = false;
369 }
370 }
371}
372
373bool QRhiGles2::ensureContext(QSurface *surface) const
374{
375 bool nativeWindowGone = false;
376 if (surface && surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
377 surface = fallbackSurface;
378 nativeWindowGone = true;
379 }
380
381 if (!surface)
382 surface = fallbackSurface;
383
384 if (needsMakeCurrent)
385 needsMakeCurrent = false;
386 else if (!nativeWindowGone && QOpenGLContext::currentContext() == ctx && (surface == fallbackSurface || ctx->surface() == surface))
387 return true;
388
389 if (!ctx->makeCurrent(surface)) {
390 if (ctx->isValid()) {
391 qWarning(msg: "QRhiGles2: Failed to make context current. Expect bad things to happen.");
392 } else {
393 qWarning(msg: "QRhiGles2: Context is lost.");
394 contextLost = true;
395 }
396 return false;
397 }
398
399 return true;
400}
401
402bool QRhiGles2::create(QRhi::Flags flags)
403{
404 Q_UNUSED(flags);
405 Q_ASSERT(fallbackSurface);
406
407 if (!importedContext) {
408 ctx = new QOpenGLContext;
409 ctx->setFormat(requestedFormat);
410 if (!ctx->create()) {
411 qWarning(msg: "QRhiGles2: Failed to create context");
412 delete ctx;
413 ctx = nullptr;
414 return false;
415 }
416 qCDebug(QRHI_LOG_INFO) << "Created OpenGL context" << ctx->format();
417 }
418
419 if (!ensureContext(surface: maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
420 return false;
421
422 f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
423
424 const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
425 const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
426 const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
427 if (vendor && renderer && version)
428 qCDebug(QRHI_LOG_INFO, "OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
429
430 const QSurfaceFormat actualFormat = ctx->format();
431
432 caps.ctxMajor = actualFormat.majorVersion();
433 caps.ctxMinor = actualFormat.minorVersion();
434
435 GLint n = 0;
436 f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, params: &n);
437 supportedCompressedFormats.resize(asize: n);
438 if (n > 0)
439 f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, params: supportedCompressedFormats.data());
440
441 f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, params: &caps.maxTextureSize);
442
443 if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) {
444 f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, params: &caps.maxDrawBuffers);
445 f->glGetIntegerv(GL_MAX_SAMPLES, params: &caps.maxSamples);
446 caps.maxSamples = qMax(a: 1, b: caps.maxSamples);
447 } else {
448 caps.maxDrawBuffers = 1;
449 caps.maxSamples = 1;
450 }
451
452 caps.msaaRenderBuffer = f->hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferMultisample)
453 && f->hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferBlit);
454
455 caps.npotTextureFull = f->hasOpenGLFeature(feature: QOpenGLFunctions::NPOTTextures)
456 && f->hasOpenGLFeature(feature: QOpenGLFunctions::NPOTTextureRepeat);
457
458 caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
459 if (caps.gles)
460 caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
461 else
462 caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
463
464 if (caps.fixedIndexPrimitiveRestart)
465 f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
466
467 caps.bgraExternalFormat = f->hasOpenGLExtension(extension: QOpenGLExtensions::BGRATextureFormat);
468 caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
469 caps.r8Format = f->hasOpenGLFeature(feature: QOpenGLFunctions::TextureRGFormats);
470 caps.r16Format = f->hasOpenGLExtension(extension: QOpenGLExtensions::Sized16Formats);
471 caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
472 caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
473 caps.packedDepthStencil = f->hasOpenGLExtension(extension: QOpenGLExtensions::PackedDepthStencil);
474#ifdef Q_OS_WASM
475 caps.needsDepthStencilCombinedAttach = true;
476#else
477 caps.needsDepthStencilCombinedAttach = false;
478#endif
479 caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(extension: QOpenGLExtensions::SRGBFrameBuffer);
480 caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
481
482 if (caps.gles)
483 caps.uniformBuffers = caps.ctxMajor >= 3; // ES 3.0
484 else
485 caps.uniformBuffers = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // 3.1
486
487 caps.elementIndexUint = f->hasOpenGLExtension(extension: QOpenGLExtensions::ElementIndexUint);
488 caps.depth24 = f->hasOpenGLExtension(extension: QOpenGLExtensions::Depth24);
489 caps.rgba8Format = f->hasOpenGLExtension(extension: QOpenGLExtensions::Sized8Formats);
490
491 if (caps.gles)
492 caps.instancing = caps.ctxMajor >= 3; // ES 3.0
493 else
494 caps.instancing = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3); // 3.3
495
496 caps.baseVertex = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2 or ES 3.2
497
498 if (caps.gles)
499 caps.compute = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
500 else
501 caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
502
503 if (caps.gles)
504 caps.textureCompareMode = caps.ctxMajor >= 3; // ES 3.0
505 else
506 caps.textureCompareMode = true;
507
508 // proper as in ES 3.0 (glMapBufferRange), not the old glMapBuffer
509 // extension(s) (which is not in ES 3.0...messy)
510 caps.properMapBuffer = f->hasOpenGLExtension(extension: QOpenGLExtensions::MapBufferRange);
511
512 if (caps.gles)
513 caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0
514 else
515 caps.nonBaseLevelFramebufferTexture = true;
516
517 caps.texelFetch = caps.ctxMajor >= 3; // 3.0 or ES 3.0
518
519 if (!caps.gles) {
520 f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
521 f->glEnable(GL_POINT_SPRITE);
522 } // else (with gles) these are always on
523
524 nativeHandlesStruct.context = ctx;
525
526 contextLost = false;
527
528 return true;
529}
530
531void QRhiGles2::destroy()
532{
533 if (!f)
534 return;
535
536 ensureContext();
537 executeDeferredReleases();
538
539 if (vao) {
540 f->glDeleteVertexArrays(n: 1, arrays: &vao);
541 vao = 0;
542 }
543
544 for (uint shader : m_shaderCache)
545 f->glDeleteShader(shader);
546 m_shaderCache.clear();
547
548 if (!importedContext) {
549 delete ctx;
550 ctx = nullptr;
551 }
552
553 f = nullptr;
554}
555
556void QRhiGles2::executeDeferredReleases()
557{
558 for (int i = releaseQueue.count() - 1; i >= 0; --i) {
559 const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
560 switch (e.type) {
561 case QRhiGles2::DeferredReleaseEntry::Buffer:
562 f->glDeleteBuffers(n: 1, buffers: &e.buffer.buffer);
563 break;
564 case QRhiGles2::DeferredReleaseEntry::Pipeline:
565 f->glDeleteProgram(program: e.pipeline.program);
566 break;
567 case QRhiGles2::DeferredReleaseEntry::Texture:
568 f->glDeleteTextures(n: 1, textures: &e.texture.texture);
569 break;
570 case QRhiGles2::DeferredReleaseEntry::RenderBuffer:
571 f->glDeleteRenderbuffers(n: 1, renderbuffers: &e.renderbuffer.renderbuffer);
572 f->glDeleteRenderbuffers(n: 1, renderbuffers: &e.renderbuffer.renderbuffer2);
573 break;
574 case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget:
575 f->glDeleteFramebuffers(n: 1, framebuffers: &e.textureRenderTarget.framebuffer);
576 break;
577 default:
578 Q_UNREACHABLE();
579 break;
580 }
581 releaseQueue.removeAt(i);
582 }
583}
584
585QVector<int> QRhiGles2::supportedSampleCounts() const
586{
587 if (supportedSampleCountList.isEmpty()) {
588 // 1, 2, 4, 8, ...
589 for (int i = 1; i <= caps.maxSamples; i *= 2)
590 supportedSampleCountList.append(t: i);
591 }
592 return supportedSampleCountList;
593}
594
595int QRhiGles2::effectiveSampleCount(int sampleCount) const
596{
597 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
598 const int s = qBound(min: 1, val: sampleCount, max: 64);
599 if (!supportedSampleCounts().contains(t: s)) {
600 qWarning(msg: "Attempted to set unsupported sample count %d", sampleCount);
601 return 1;
602 }
603 return s;
604}
605
606QRhiSwapChain *QRhiGles2::createSwapChain()
607{
608 return new QGles2SwapChain(this);
609}
610
611QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
612{
613 return new QGles2Buffer(this, type, usage, size);
614}
615
616int QRhiGles2::ubufAlignment() const
617{
618 // No real uniform buffers are used so no need to pretend there is any
619 // alignment requirement.
620 return 1;
621}
622
623bool QRhiGles2::isYUpInFramebuffer() const
624{
625 return true;
626}
627
628bool QRhiGles2::isYUpInNDC() const
629{
630 return true;
631}
632
633bool QRhiGles2::isClipDepthZeroToOne() const
634{
635 return false;
636}
637
638QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const
639{
640 return QMatrix4x4(); // identity
641}
642
643static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
644{
645 const bool srgb = flags.testFlag(flag: QRhiTexture::sRGB);
646 switch (format) {
647 case QRhiTexture::BC1:
648 return srgb ? 0x8C4C : 0x83F0;
649 case QRhiTexture::BC2:
650 return srgb ? 0x8C4E : 0x83F2;
651 case QRhiTexture::BC3:
652 return srgb ? 0x8C4F : 0x83F3;
653
654 case QRhiTexture::ETC2_RGB8:
655 return srgb ? 0x9275 : 0x9274;
656 case QRhiTexture::ETC2_RGB8A1:
657 return srgb ? 0x9277 : 0x9276;
658 case QRhiTexture::ETC2_RGBA8:
659 return srgb ? 0x9279 : 0x9278;
660
661 case QRhiTexture::ASTC_4x4:
662 return srgb ? 0x93D0 : 0x93B0;
663 case QRhiTexture::ASTC_5x4:
664 return srgb ? 0x93D1 : 0x93B1;
665 case QRhiTexture::ASTC_5x5:
666 return srgb ? 0x93D2 : 0x93B2;
667 case QRhiTexture::ASTC_6x5:
668 return srgb ? 0x93D3 : 0x93B3;
669 case QRhiTexture::ASTC_6x6:
670 return srgb ? 0x93D4 : 0x93B4;
671 case QRhiTexture::ASTC_8x5:
672 return srgb ? 0x93D5 : 0x93B5;
673 case QRhiTexture::ASTC_8x6:
674 return srgb ? 0x93D6 : 0x93B6;
675 case QRhiTexture::ASTC_8x8:
676 return srgb ? 0x93D7 : 0x93B7;
677 case QRhiTexture::ASTC_10x5:
678 return srgb ? 0x93D8 : 0x93B8;
679 case QRhiTexture::ASTC_10x6:
680 return srgb ? 0x93D9 : 0x93B9;
681 case QRhiTexture::ASTC_10x8:
682 return srgb ? 0x93DA : 0x93BA;
683 case QRhiTexture::ASTC_10x10:
684 return srgb ? 0x93DB : 0x93BB;
685 case QRhiTexture::ASTC_12x10:
686 return srgb ? 0x93DC : 0x93BC;
687 case QRhiTexture::ASTC_12x12:
688 return srgb ? 0x93DD : 0x93BD;
689
690 default:
691 return 0; // this is reachable, just return an invalid format
692 }
693}
694
695bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
696{
697 if (isCompressedFormat(format))
698 return supportedCompressedFormats.contains(t: GLint(toGlCompressedTextureFormat(format, flags)));
699
700 switch (format) {
701 case QRhiTexture::D16:
702 case QRhiTexture::D32F:
703 return caps.depthTexture;
704
705 case QRhiTexture::BGRA8:
706 return caps.bgraExternalFormat;
707
708 case QRhiTexture::R8:
709 return caps.r8Format;
710
711 case QRhiTexture::R16:
712 return caps.r16Format;
713
714 case QRhiTexture::RGBA16F:
715 case QRhiTexture::RGBA32F:
716 return caps.floatFormats;
717
718 case QRhiTexture::R16F:
719 case QRhiTexture::R32F:
720 return caps.floatFormats;
721
722 default:
723 break;
724 }
725
726 return true;
727}
728
729bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
730{
731 switch (feature) {
732 case QRhi::MultisampleTexture:
733 return false;
734 case QRhi::MultisampleRenderBuffer:
735 return caps.msaaRenderBuffer;
736 case QRhi::DebugMarkers:
737 return false;
738 case QRhi::Timestamps:
739 return false;
740 case QRhi::Instancing:
741 return caps.instancing;
742 case QRhi::CustomInstanceStepRate:
743 return false;
744 case QRhi::PrimitiveRestart:
745 return caps.fixedIndexPrimitiveRestart;
746 case QRhi::NonDynamicUniformBuffers:
747 return true;
748 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
749 return true;
750 case QRhi::NPOTTextureRepeat:
751 return caps.npotTextureFull;
752 case QRhi::RedOrAlpha8IsRed:
753 return caps.coreProfile;
754 case QRhi::ElementIndexUint:
755 return caps.elementIndexUint;
756 case QRhi::Compute:
757 return caps.compute;
758 case QRhi::WideLines:
759 return true;
760 case QRhi::VertexShaderPointSize:
761 return true;
762 case QRhi::BaseVertex:
763 return caps.baseVertex;
764 case QRhi::BaseInstance:
765 return false; // not in ES 3.2, so won't bother
766 case QRhi::TriangleFanTopology:
767 return true;
768 case QRhi::ReadBackNonUniformBuffer:
769 return !caps.gles || caps.properMapBuffer;
770 case QRhi::ReadBackNonBaseMipLevel:
771 return caps.nonBaseLevelFramebufferTexture;
772 case QRhi::TexelFetch:
773 return caps.texelFetch;
774 default:
775 Q_UNREACHABLE();
776 return false;
777 }
778}
779
780int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
781{
782 switch (limit) {
783 case QRhi::TextureSizeMin:
784 return 1;
785 case QRhi::TextureSizeMax:
786 return caps.maxTextureSize;
787 case QRhi::MaxColorAttachments:
788 return caps.maxDrawBuffers;
789 case QRhi::FramesInFlight:
790 // From our perspective. What the GL impl does internally is another
791 // question, but that's out of our hands and does not concern us here.
792 return 1;
793 case QRhi::MaxAsyncReadbackFrames:
794 return 1;
795 default:
796 Q_UNREACHABLE();
797 return 0;
798 }
799}
800
801const QRhiNativeHandles *QRhiGles2::nativeHandles()
802{
803 return &nativeHandlesStruct;
804}
805
806void QRhiGles2::sendVMemStatsToProfiler()
807{
808 // nothing to do here
809}
810
811bool QRhiGles2::makeThreadLocalNativeContextCurrent()
812{
813 if (inFrame && !ofr.active)
814 return ensureContext(surface: currentSwapChain->surface);
815 else
816 return ensureContext();
817}
818
819void QRhiGles2::releaseCachedResources()
820{
821 if (!ensureContext())
822 return;
823
824 for (uint shader : m_shaderCache)
825 f->glDeleteShader(shader);
826
827 m_shaderCache.clear();
828}
829
830bool QRhiGles2::isDeviceLost() const
831{
832 return contextLost;
833}
834
835QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
836 int sampleCount, QRhiRenderBuffer::Flags flags)
837{
838 return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags);
839}
840
841QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
842 int sampleCount, QRhiTexture::Flags flags)
843{
844 return new QGles2Texture(this, format, pixelSize, sampleCount, flags);
845}
846
847QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
848 QRhiSampler::Filter mipmapMode,
849 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
850{
851 return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
852}
853
854QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
855 QRhiTextureRenderTarget::Flags flags)
856{
857 return new QGles2TextureRenderTarget(this, desc, flags);
858}
859
860QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline()
861{
862 return new QGles2GraphicsPipeline(this);
863}
864
865QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings()
866{
867 return new QGles2ShaderResourceBindings(this);
868}
869
870QRhiComputePipeline *QRhiGles2::createComputePipeline()
871{
872 return new QGles2ComputePipeline(this);
873}
874
875void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
876{
877 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
878 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
879 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
880 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
881
882 if (pipelineChanged) {
883 cbD->currentGraphicsPipeline = ps;
884 cbD->currentComputePipeline = nullptr;
885 cbD->currentPipelineGeneration = psD->generation;
886
887 QGles2CommandBuffer::Command cmd;
888 cmd.cmd = QGles2CommandBuffer::Command::BindGraphicsPipeline;
889 cmd.args.bindGraphicsPipeline.ps = ps;
890 cbD->commands.append(t: cmd);
891 }
892}
893
894void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
895 int dynamicOffsetCount,
896 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
897{
898 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
899 Q_ASSERT(cbD->recordingPass != QGles2CommandBuffer::NoPass);
900 QGles2GraphicsPipeline *gfxPsD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
901 QGles2ComputePipeline *compPsD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
902
903 if (!srb) {
904 if (gfxPsD)
905 srb = gfxPsD->m_shaderResourceBindings;
906 else
907 srb = compPsD->m_shaderResourceBindings;
908 }
909
910 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
911 QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
912 bool hasDynamicOffsetInSrb = false;
913 for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
914 const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(idx: i).data();
915 switch (b->type) {
916 case QRhiShaderResourceBinding::UniformBuffer:
917 // no BufUniformRead / AccessUniform because no real uniform buffers are used
918 if (b->u.ubuf.hasDynamicOffset)
919 hasDynamicOffsetInSrb = true;
920 break;
921 case QRhiShaderResourceBinding::SampledTexture:
922 for (int elem = 0; elem < b->u.stex.count; ++elem) {
923 trackedRegisterTexture(passResTracker: &passResTracker,
924 QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
925 access: QRhiPassResourceTracker::TexSample,
926 stage: QRhiPassResourceTracker::toPassTrackerTextureStage(stages: b->stage));
927 }
928 break;
929 case QRhiShaderResourceBinding::ImageLoad:
930 case QRhiShaderResourceBinding::ImageStore:
931 case QRhiShaderResourceBinding::ImageLoadStore:
932 {
933 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
934 QRhiPassResourceTracker::TextureAccess access;
935 if (b->type == QRhiShaderResourceBinding::ImageLoad)
936 access = QRhiPassResourceTracker::TexStorageLoad;
937 else if (b->type == QRhiShaderResourceBinding::ImageStore)
938 access = QRhiPassResourceTracker::TexStorageStore;
939 else
940 access = QRhiPassResourceTracker::TexStorageLoadStore;
941 trackedRegisterTexture(passResTracker: &passResTracker, texD, access,
942 stage: QRhiPassResourceTracker::toPassTrackerTextureStage(stages: b->stage));
943 }
944 break;
945 case QRhiShaderResourceBinding::BufferLoad:
946 case QRhiShaderResourceBinding::BufferStore:
947 case QRhiShaderResourceBinding::BufferLoadStore:
948 {
949 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
950 QRhiPassResourceTracker::BufferAccess access;
951 if (b->type == QRhiShaderResourceBinding::BufferLoad)
952 access = QRhiPassResourceTracker::BufStorageLoad;
953 else if (b->type == QRhiShaderResourceBinding::BufferStore)
954 access = QRhiPassResourceTracker::BufStorageStore;
955 else
956 access = QRhiPassResourceTracker::BufStorageLoadStore;
957 trackedRegisterBuffer(passResTracker: &passResTracker, bufD, access,
958 stage: QRhiPassResourceTracker::toPassTrackerBufferStage(stages: b->stage));
959 }
960 break;
961 default:
962 break;
963 }
964 }
965
966 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
967 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
968
969 if (srbChanged || srbRebuilt || hasDynamicOffsetInSrb) {
970 if (gfxPsD) {
971 cbD->currentGraphicsSrb = srb;
972 cbD->currentComputeSrb = nullptr;
973 } else {
974 cbD->currentGraphicsSrb = nullptr;
975 cbD->currentComputeSrb = srb;
976 }
977 cbD->currentSrbGeneration = srbD->generation;
978
979 QGles2CommandBuffer::Command cmd;
980 cmd.cmd = QGles2CommandBuffer::Command::BindShaderResources;
981 cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
982 cmd.args.bindShaderResources.maybeComputePs = compPsD;
983 cmd.args.bindShaderResources.srb = srb;
984 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
985 if (hasDynamicOffsetInSrb) {
986 if (dynamicOffsetCount < QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS) {
987 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
988 uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
989 for (int i = 0; i < dynamicOffsetCount; ++i) {
990 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
991 *p++ = uint(dynOfs.first);
992 *p++ = dynOfs.second;
993 }
994 } else {
995 qWarning(msg: "Too many dynamic offsets (%d, max is %d)",
996 dynamicOffsetCount, QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS);
997 }
998 }
999 cbD->commands.append(t: cmd);
1000 }
1001}
1002
1003void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
1004 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1005 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1006{
1007 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1008 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1009 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
1010
1011 for (int i = 0; i < bindingCount; ++i) {
1012 QRhiBuffer *buf = bindings[i].first;
1013 quint32 ofs = bindings[i].second;
1014 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
1015 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
1016
1017 QGles2CommandBuffer::Command cmd;
1018 cmd.cmd = QGles2CommandBuffer::Command::BindVertexBuffer;
1019 cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
1020 cmd.args.bindVertexBuffer.buffer = bufD->buffer;
1021 cmd.args.bindVertexBuffer.offset = ofs;
1022 cmd.args.bindVertexBuffer.binding = startBinding + i;
1023 cbD->commands.append(t: cmd);
1024
1025 trackedRegisterBuffer(passResTracker: &passResTracker, bufD, access: QRhiPassResourceTracker::BufVertexInput,
1026 stage: QRhiPassResourceTracker::BufVertexInputStage);
1027 }
1028
1029 if (indexBuf) {
1030 QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
1031 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
1032
1033 QGles2CommandBuffer::Command cmd;
1034 cmd.cmd = QGles2CommandBuffer::Command::BindIndexBuffer;
1035 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
1036 cmd.args.bindIndexBuffer.offset = indexOffset;
1037 cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
1038 cbD->commands.append(t: cmd);
1039
1040 trackedRegisterBuffer(passResTracker: &passResTracker, bufD: ibufD, access: QRhiPassResourceTracker::BufIndexRead,
1041 stage: QRhiPassResourceTracker::BufVertexInputStage);
1042 }
1043}
1044
1045void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
1046{
1047 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1048 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1049
1050 QGles2CommandBuffer::Command cmd;
1051 cmd.cmd = QGles2CommandBuffer::Command::Viewport;
1052 const std::array<float, 4> r = viewport.viewport();
1053 // A negative width or height is an error. A negative x or y is not.
1054 if (r[2] < 0.0f || r[3] < 0.0f)
1055 return;
1056
1057 cmd.args.viewport.x = r[0];
1058 cmd.args.viewport.y = r[1];
1059 cmd.args.viewport.w = r[2];
1060 cmd.args.viewport.h = r[3];
1061 cmd.args.viewport.d0 = viewport.minDepth();
1062 cmd.args.viewport.d1 = viewport.maxDepth();
1063 cbD->commands.append(t: cmd);
1064}
1065
1066void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
1067{
1068 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1069 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1070
1071 QGles2CommandBuffer::Command cmd;
1072 cmd.cmd = QGles2CommandBuffer::Command::Scissor;
1073 const std::array<int, 4> r = scissor.scissor();
1074 // A negative width or height is an error. A negative x or y is not.
1075 if (r[2] < 0 || r[3] < 0)
1076 return;
1077
1078 cmd.args.scissor.x = r[0];
1079 cmd.args.scissor.y = r[1];
1080 cmd.args.scissor.w = r[2];
1081 cmd.args.scissor.h = r[3];
1082 cbD->commands.append(t: cmd);
1083}
1084
1085void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
1086{
1087 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1088 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1089
1090 QGles2CommandBuffer::Command cmd;
1091 cmd.cmd = QGles2CommandBuffer::Command::BlendConstants;
1092 cmd.args.blendConstants.r = float(c.redF());
1093 cmd.args.blendConstants.g = float(c.greenF());
1094 cmd.args.blendConstants.b = float(c.blueF());
1095 cmd.args.blendConstants.a = float(c.alphaF());
1096 cbD->commands.append(t: cmd);
1097}
1098
1099void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
1100{
1101 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1102 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1103
1104 QGles2CommandBuffer::Command cmd;
1105 cmd.cmd = QGles2CommandBuffer::Command::StencilRef;
1106 cmd.args.stencilRef.ref = refValue;
1107 cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
1108 cbD->commands.append(t: cmd);
1109}
1110
1111void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
1112 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
1113{
1114 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1115 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1116
1117 QGles2CommandBuffer::Command cmd;
1118 cmd.cmd = QGles2CommandBuffer::Command::Draw;
1119 cmd.args.draw.ps = cbD->currentGraphicsPipeline;
1120 cmd.args.draw.vertexCount = vertexCount;
1121 cmd.args.draw.firstVertex = firstVertex;
1122 cmd.args.draw.instanceCount = instanceCount;
1123 cmd.args.draw.baseInstance = firstInstance;
1124 cbD->commands.append(t: cmd);
1125}
1126
1127void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
1128 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
1129{
1130 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1131 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1132
1133 QGles2CommandBuffer::Command cmd;
1134 cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed;
1135 cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
1136 cmd.args.drawIndexed.indexCount = indexCount;
1137 cmd.args.drawIndexed.firstIndex = firstIndex;
1138 cmd.args.drawIndexed.instanceCount = instanceCount;
1139 cmd.args.drawIndexed.baseInstance = firstInstance;
1140 cmd.args.drawIndexed.baseVertex = vertexOffset;
1141 cbD->commands.append(t: cmd);
1142}
1143
1144void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
1145{
1146 if (!debugMarkers)
1147 return;
1148
1149 Q_UNUSED(cb);
1150 Q_UNUSED(name);
1151}
1152
1153void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
1154{
1155 if (!debugMarkers)
1156 return;
1157
1158 Q_UNUSED(cb);
1159}
1160
1161void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
1162{
1163 if (!debugMarkers)
1164 return;
1165
1166 Q_UNUSED(cb);
1167 Q_UNUSED(msg);
1168}
1169
1170const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
1171{
1172 Q_UNUSED(cb);
1173 return nullptr;
1174}
1175
1176static void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type)
1177{
1178 QGles2CommandBuffer::Command cmd;
1179 cmd.cmd = type;
1180 cbD->commands.append(t: cmd);
1181}
1182
1183void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
1184{
1185 if (ofr.active) {
1186 Q_ASSERT(!currentSwapChain);
1187 if (!ensureContext())
1188 return;
1189 } else {
1190 Q_ASSERT(currentSwapChain);
1191 if (!ensureContext(surface: currentSwapChain->surface))
1192 return;
1193 }
1194
1195 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1196
1197 if (cbD->recordingPass == QGles2CommandBuffer::ComputePass
1198 && !cbD->computePassState.writtenResources.isEmpty())
1199 {
1200 QGles2CommandBuffer::Command cmd;
1201 cmd.cmd = QGles2CommandBuffer::Command::Barrier;
1202 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
1203 cbD->commands.append(t: cmd);
1204 }
1205
1206 executeCommandBuffer(cb: cbD);
1207
1208 cbD->resetCommands();
1209
1210 if (vao)
1211 f->glBindVertexArray(array: 0);
1212}
1213
1214void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
1215{
1216 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1217 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
1218
1219 cbD->resetCachedState();
1220
1221 if (cbD->recordingPass != QGles2CommandBuffer::NoPass) {
1222 // Commands that come after this point need a resource tracker and also
1223 // a BarriersForPass command enqueued. (the ones we had from
1224 // beginPass() are now gone since beginExternal() processed all that
1225 // due to calling executeCommandBuffer()).
1226 enqueueBarriersForPass(cbD);
1227 }
1228
1229 addBoundaryCommand(cbD, type: QGles2CommandBuffer::Command::ResetFrame);
1230
1231 if (cbD->currentTarget)
1232 enqueueBindFramebuffer(rt: cbD->currentTarget, cbD);
1233}
1234
1235QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
1236{
1237 Q_UNUSED(flags);
1238
1239 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
1240 if (!ensureContext(surface: swapChainD->surface))
1241 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1242
1243 currentSwapChain = swapChainD;
1244
1245 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1246 QRHI_PROF_F(beginSwapChainFrame(swapChain));
1247
1248 executeDeferredReleases();
1249 swapChainD->cb.resetState();
1250
1251 addBoundaryCommand(cbD: &swapChainD->cb, type: QGles2CommandBuffer::Command::BeginFrame);
1252
1253 return QRhi::FrameOpSuccess;
1254}
1255
1256QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1257{
1258 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
1259 Q_ASSERT(currentSwapChain == swapChainD);
1260
1261 addBoundaryCommand(cbD: &swapChainD->cb, type: QGles2CommandBuffer::Command::EndFrame);
1262
1263 if (!ensureContext(surface: swapChainD->surface))
1264 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1265
1266 executeCommandBuffer(cb: &swapChainD->cb);
1267
1268 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1269 // this must be done before the swap
1270 QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
1271
1272 if (swapChainD->surface && !flags.testFlag(flag: QRhi::SkipPresent)) {
1273 ctx->swapBuffers(surface: swapChainD->surface);
1274 needsMakeCurrent = true;
1275 } else {
1276 f->glFlush();
1277 }
1278
1279 swapChainD->frameCount += 1;
1280 currentSwapChain = nullptr;
1281 return QRhi::FrameOpSuccess;
1282}
1283
1284QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
1285{
1286 Q_UNUSED(flags);
1287 if (!ensureContext())
1288 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1289
1290 ofr.active = true;
1291
1292 executeDeferredReleases();
1293 ofr.cbWrapper.resetState();
1294
1295 addBoundaryCommand(cbD: &ofr.cbWrapper, type: QGles2CommandBuffer::Command::BeginFrame);
1296 *cb = &ofr.cbWrapper;
1297
1298 return QRhi::FrameOpSuccess;
1299}
1300
1301QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
1302{
1303 Q_UNUSED(flags);
1304 Q_ASSERT(ofr.active);
1305 ofr.active = false;
1306
1307 addBoundaryCommand(cbD: &ofr.cbWrapper, type: QGles2CommandBuffer::Command::EndFrame);
1308
1309 if (!ensureContext())
1310 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1311
1312 executeCommandBuffer(cb: &ofr.cbWrapper);
1313
1314 return QRhi::FrameOpSuccess;
1315}
1316
1317QRhi::FrameOpResult QRhiGles2::finish()
1318{
1319 if (inFrame) {
1320 if (ofr.active) {
1321 Q_ASSERT(!currentSwapChain);
1322 Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
1323 if (!ensureContext())
1324 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1325 executeCommandBuffer(cb: &ofr.cbWrapper);
1326 ofr.cbWrapper.resetCommands();
1327 } else {
1328 Q_ASSERT(currentSwapChain);
1329 Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
1330 if (!ensureContext(surface: currentSwapChain->surface))
1331 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1332 executeCommandBuffer(cb: &currentSwapChain->cb);
1333 currentSwapChain->cb.resetCommands();
1334 }
1335 }
1336 return QRhi::FrameOpSuccess;
1337}
1338
1339static bool bufferAccessIsWrite(QGles2Buffer::Access access)
1340{
1341 return access == QGles2Buffer::AccessStorageWrite
1342 || access == QGles2Buffer::AccessStorageReadWrite
1343 || access == QGles2Buffer::AccessUpdate;
1344}
1345
1346static bool textureAccessIsWrite(QGles2Texture::Access access)
1347{
1348 return access == QGles2Texture::AccessStorageWrite
1349 || access == QGles2Texture::AccessStorageReadWrite
1350 || access == QGles2Texture::AccessUpdate
1351 || access == QGles2Texture::AccessFramebuffer;
1352}
1353
1354void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
1355{
1356 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
1357 const QGles2Buffer::Access prevAccess = bufD->usageState.access;
1358 if (access == prevAccess)
1359 return;
1360
1361 if (bufferAccessIsWrite(access: prevAccess)) {
1362 // Generating the minimal barrier set is way too complicated to do
1363 // correctly (prevAccess is overwritten so we won't have proper
1364 // tracking across multiple passes) so setting all barrier bits will do
1365 // for now.
1366 QGles2CommandBuffer::Command cmd;
1367 cmd.cmd = QGles2CommandBuffer::Command::Barrier;
1368 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
1369 cbD->commands.append(t: cmd);
1370 }
1371
1372 bufD->usageState.access = access;
1373}
1374
1375void QRhiGles2::trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access)
1376{
1377 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
1378 const QGles2Texture::Access prevAccess = texD->usageState.access;
1379 if (access == prevAccess)
1380 return;
1381
1382 if (textureAccessIsWrite(access: prevAccess)) {
1383 QGles2CommandBuffer::Command cmd;
1384 cmd.cmd = QGles2CommandBuffer::Command::Barrier;
1385 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
1386 cbD->commands.append(t: cmd);
1387 }
1388
1389 texD->usageState.access = access;
1390}
1391
1392void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
1393 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
1394{
1395 trackedImageBarrier(cbD, texD, access: QGles2Texture::AccessUpdate);
1396 const bool isCompressed = isCompressedFormat(format: texD->m_format);
1397 const bool isCubeMap = texD->m_flags.testFlag(flag: QRhiTexture::CubeMap);
1398 const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
1399 const QPoint dp = subresDesc.destinationTopLeft();
1400 const QByteArray rawData = subresDesc.data();
1401 if (!subresDesc.image().isNull()) {
1402 QImage img = subresDesc.image();
1403 QSize size = img.size();
1404 QGles2CommandBuffer::Command cmd;
1405 cmd.cmd = QGles2CommandBuffer::Command::SubImage;
1406 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
1407 const QPoint sp = subresDesc.sourceTopLeft();
1408 if (!subresDesc.sourceSize().isEmpty())
1409 size = subresDesc.sourceSize();
1410 img = img.copy(x: sp.x(), y: sp.y(), w: size.width(), h: size.height());
1411 }
1412 cmd.args.subImage.target = texD->target;
1413 cmd.args.subImage.texture = texD->texture;
1414 cmd.args.subImage.faceTarget = faceTargetBase + uint(layer);
1415 cmd.args.subImage.level = level;
1416 cmd.args.subImage.dx = dp.x();
1417 cmd.args.subImage.dy = dp.y();
1418 cmd.args.subImage.w = size.width();
1419 cmd.args.subImage.h = size.height();
1420 cmd.args.subImage.glformat = texD->glformat;
1421 cmd.args.subImage.gltype = texD->gltype;
1422 cmd.args.subImage.rowStartAlign = 4;
1423 cmd.args.subImage.data = cbD->retainImage(image: img);
1424 cbD->commands.append(t: cmd);
1425 } else if (!rawData.isEmpty() && isCompressed) {
1426 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(mipLevel: level, baseLevelSize: texD->m_pixelSize)
1427 : subresDesc.sourceSize();
1428 if (texD->specified) {
1429 QGles2CommandBuffer::Command cmd;
1430 cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
1431 cmd.args.compressedSubImage.target = texD->target;
1432 cmd.args.compressedSubImage.texture = texD->texture;
1433 cmd.args.compressedSubImage.faceTarget = faceTargetBase + uint(layer);
1434 cmd.args.compressedSubImage.level = level;
1435 cmd.args.compressedSubImage.dx = dp.x();
1436 cmd.args.compressedSubImage.dy = dp.y();
1437 cmd.args.compressedSubImage.w = size.width();
1438 cmd.args.compressedSubImage.h = size.height();
1439 cmd.args.compressedSubImage.glintformat = texD->glintformat;
1440 cmd.args.compressedSubImage.size = rawData.size();
1441 cmd.args.compressedSubImage.data = cbD->retainData(data: rawData);
1442 cbD->commands.append(t: cmd);
1443 } else {
1444 QGles2CommandBuffer::Command cmd;
1445 cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
1446 cmd.args.compressedImage.target = texD->target;
1447 cmd.args.compressedImage.texture = texD->texture;
1448 cmd.args.compressedImage.faceTarget = faceTargetBase + uint(layer);
1449 cmd.args.compressedImage.level = level;
1450 cmd.args.compressedImage.glintformat = texD->glintformat;
1451 cmd.args.compressedImage.w = size.width();
1452 cmd.args.compressedImage.h = size.height();
1453 cmd.args.compressedImage.size = rawData.size();
1454 cmd.args.compressedImage.data = cbD->retainData(data: rawData);
1455 cbD->commands.append(t: cmd);
1456 }
1457 } else if (!rawData.isEmpty()) {
1458 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(mipLevel: level, baseLevelSize: texD->m_pixelSize)
1459 : subresDesc.sourceSize();
1460 quint32 bpl = 0;
1461 textureFormatInfo(format: texD->m_format, size, bpl: &bpl, byteSize: nullptr);
1462 QGles2CommandBuffer::Command cmd;
1463 cmd.cmd = QGles2CommandBuffer::Command::SubImage;
1464 cmd.args.subImage.target = texD->target;
1465 cmd.args.subImage.texture = texD->texture;
1466 cmd.args.subImage.faceTarget = faceTargetBase + uint(layer);
1467 cmd.args.subImage.level = level;
1468 cmd.args.subImage.dx = dp.x();
1469 cmd.args.subImage.dy = dp.y();
1470 cmd.args.subImage.w = size.width();
1471 cmd.args.subImage.h = size.height();
1472 cmd.args.subImage.glformat = texD->glformat;
1473 cmd.args.subImage.gltype = texD->gltype;
1474 // Default unpack alignment (row start aligment
1475 // requirement) is 4. QImage guarantees 4 byte aligned
1476 // row starts, but our raw data here does not.
1477 cmd.args.subImage.rowStartAlign = (bpl & 3) ? 1 : 4;
1478 cmd.args.subImage.data = cbD->retainData(data: rawData);
1479 cbD->commands.append(t: cmd);
1480 } else {
1481 qWarning(msg: "Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
1482 }
1483}
1484
1485void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1486{
1487 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1488 QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(b: resourceUpdates);
1489
1490 for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
1491 if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
1492 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
1493 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
1494 if (bufD->m_usage.testFlag(flag: QRhiBuffer::UniformBuffer)) {
1495 memcpy(dest: bufD->ubuf.data() + u.offset, src: u.data.constData(), n: size_t(u.data.size()));
1496 } else {
1497 trackedBufferBarrier(cbD, bufD, access: QGles2Buffer::AccessUpdate);
1498 QGles2CommandBuffer::Command cmd;
1499 cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
1500 cmd.args.bufferSubData.target = bufD->targetForDataOps;
1501 cmd.args.bufferSubData.buffer = bufD->buffer;
1502 cmd.args.bufferSubData.offset = u.offset;
1503 cmd.args.bufferSubData.size = u.data.size();
1504 cmd.args.bufferSubData.data = cbD->retainData(data: u.data);
1505 cbD->commands.append(t: cmd);
1506 }
1507 } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
1508 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
1509 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
1510 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
1511 if (bufD->m_usage.testFlag(flag: QRhiBuffer::UniformBuffer)) {
1512 memcpy(dest: bufD->ubuf.data() + u.offset, src: u.data.constData(), n: size_t(u.data.size()));
1513 } else {
1514 trackedBufferBarrier(cbD, bufD, access: QGles2Buffer::AccessUpdate);
1515 QGles2CommandBuffer::Command cmd;
1516 cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
1517 cmd.args.bufferSubData.target = bufD->targetForDataOps;
1518 cmd.args.bufferSubData.buffer = bufD->buffer;
1519 cmd.args.bufferSubData.offset = u.offset;
1520 cmd.args.bufferSubData.size = u.data.size();
1521 cmd.args.bufferSubData.data = cbD->retainData(data: u.data);
1522 cbD->commands.append(t: cmd);
1523 }
1524 } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
1525 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
1526 if (bufD->m_usage.testFlag(flag: QRhiBuffer::UniformBuffer)) {
1527 u.result->data.resize(size: u.readSize);
1528 memcpy(dest: u.result->data.data(), src: bufD->ubuf.constData() + u.offset, n: size_t(u.readSize));
1529 if (u.result->completed)
1530 u.result->completed();
1531 } else {
1532 QGles2CommandBuffer::Command cmd;
1533 cmd.cmd = QGles2CommandBuffer::Command::GetBufferSubData;
1534 cmd.args.getBufferSubData.result = u.result;
1535 cmd.args.getBufferSubData.target = bufD->targetForDataOps;
1536 cmd.args.getBufferSubData.buffer = bufD->buffer;
1537 cmd.args.getBufferSubData.offset = u.offset;
1538 cmd.args.getBufferSubData.size = u.readSize;
1539 cbD->commands.append(t: cmd);
1540 }
1541 }
1542 }
1543
1544 for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
1545 if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
1546 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
1547 for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
1548 for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
1549 for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(t: u.subresDesc[layer][level]))
1550 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
1551 }
1552 }
1553 texD->specified = true;
1554 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
1555 Q_ASSERT(u.src && u.dst);
1556 QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.src);
1557 QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.dst);
1558
1559 trackedImageBarrier(cbD, texD: srcD, access: QGles2Texture::AccessRead);
1560 trackedImageBarrier(cbD, texD: dstD, access: QGles2Texture::AccessUpdate);
1561
1562 const QSize mipSize = q->sizeForMipLevel(mipLevel: u.desc.sourceLevel(), baseLevelSize: srcD->m_pixelSize);
1563 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
1564 // do not translate coordinates, even if sp is bottom-left from gl's pov
1565 const QPoint sp = u.desc.sourceTopLeft();
1566 const QPoint dp = u.desc.destinationTopLeft();
1567
1568 const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(flag: QRhiTexture::CubeMap)
1569 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
1570 const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(flag: QRhiTexture::CubeMap)
1571 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
1572
1573 QGles2CommandBuffer::Command cmd;
1574 cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
1575
1576 cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer());
1577 cmd.args.copyTex.srcTexture = srcD->texture;
1578 cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
1579 cmd.args.copyTex.srcX = sp.x();
1580 cmd.args.copyTex.srcY = sp.y();
1581
1582 cmd.args.copyTex.dstTarget = dstD->target;
1583 cmd.args.copyTex.dstTexture = dstD->texture;
1584 cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer());
1585 cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
1586 cmd.args.copyTex.dstX = dp.x();
1587 cmd.args.copyTex.dstY = dp.y();
1588
1589 cmd.args.copyTex.w = copySize.width();
1590 cmd.args.copyTex.h = copySize.height();
1591
1592 cbD->commands.append(t: cmd);
1593 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
1594 QGles2CommandBuffer::Command cmd;
1595 cmd.cmd = QGles2CommandBuffer::Command::ReadPixels;
1596 cmd.args.readPixels.result = u.result;
1597 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.rb.texture());
1598 if (texD)
1599 trackedImageBarrier(cbD, texD, access: QGles2Texture::AccessRead);
1600 cmd.args.readPixels.texture = texD ? texD->texture : 0;
1601 if (texD) {
1602 const QSize readImageSize = q->sizeForMipLevel(mipLevel: u.rb.level(), baseLevelSize: texD->m_pixelSize);
1603 cmd.args.readPixels.w = readImageSize.width();
1604 cmd.args.readPixels.h = readImageSize.height();
1605 cmd.args.readPixels.format = texD->m_format;
1606 const GLenum faceTargetBase = texD->m_flags.testFlag(flag: QRhiTexture::CubeMap)
1607 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
1608 cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer());
1609 cmd.args.readPixels.level = u.rb.level();
1610 }
1611 cbD->commands.append(t: cmd);
1612 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
1613 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
1614 trackedImageBarrier(cbD, texD, access: QGles2Texture::AccessFramebuffer);
1615 QGles2CommandBuffer::Command cmd;
1616 cmd.cmd = QGles2CommandBuffer::Command::GenMip;
1617 cmd.args.genMip.target = texD->target;
1618 cmd.args.genMip.texture = texD->texture;
1619 cbD->commands.append(t: cmd);
1620 }
1621 }
1622
1623 ud->free();
1624}
1625
1626static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
1627{
1628 switch (t) {
1629 case QRhiGraphicsPipeline::Triangles:
1630 return GL_TRIANGLES;
1631 case QRhiGraphicsPipeline::TriangleStrip:
1632 return GL_TRIANGLE_STRIP;
1633 case QRhiGraphicsPipeline::TriangleFan:
1634 return GL_TRIANGLE_FAN;
1635 case QRhiGraphicsPipeline::Lines:
1636 return GL_LINES;
1637 case QRhiGraphicsPipeline::LineStrip:
1638 return GL_LINE_STRIP;
1639 case QRhiGraphicsPipeline::Points:
1640 return GL_POINTS;
1641 default:
1642 Q_UNREACHABLE();
1643 return GL_TRIANGLES;
1644 }
1645}
1646
1647static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
1648{
1649 switch (c) {
1650 case QRhiGraphicsPipeline::Front:
1651 return GL_FRONT;
1652 case QRhiGraphicsPipeline::Back:
1653 return GL_BACK;
1654 default:
1655 Q_UNREACHABLE();
1656 return GL_BACK;
1657 }
1658}
1659
1660static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
1661{
1662 switch (f) {
1663 case QRhiGraphicsPipeline::CCW:
1664 return GL_CCW;
1665 case QRhiGraphicsPipeline::CW:
1666 return GL_CW;
1667 default:
1668 Q_UNREACHABLE();
1669 return GL_CCW;
1670 }
1671}
1672
1673static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
1674{
1675 switch (f) {
1676 case QRhiGraphicsPipeline::Zero:
1677 return GL_ZERO;
1678 case QRhiGraphicsPipeline::One:
1679 return GL_ONE;
1680 case QRhiGraphicsPipeline::SrcColor:
1681 return GL_SRC_COLOR;
1682 case QRhiGraphicsPipeline::OneMinusSrcColor:
1683 return GL_ONE_MINUS_SRC_COLOR;
1684 case QRhiGraphicsPipeline::DstColor:
1685 return GL_DST_COLOR;
1686 case QRhiGraphicsPipeline::OneMinusDstColor:
1687 return GL_ONE_MINUS_DST_COLOR;
1688 case QRhiGraphicsPipeline::SrcAlpha:
1689 return GL_SRC_ALPHA;
1690 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
1691 return GL_ONE_MINUS_SRC_ALPHA;
1692 case QRhiGraphicsPipeline::DstAlpha:
1693 return GL_DST_ALPHA;
1694 case QRhiGraphicsPipeline::OneMinusDstAlpha:
1695 return GL_ONE_MINUS_DST_ALPHA;
1696 case QRhiGraphicsPipeline::ConstantColor:
1697 return GL_CONSTANT_COLOR;
1698 case QRhiGraphicsPipeline::OneMinusConstantColor:
1699 return GL_ONE_MINUS_CONSTANT_COLOR;
1700 case QRhiGraphicsPipeline::ConstantAlpha:
1701 return GL_CONSTANT_ALPHA;
1702 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
1703 return GL_ONE_MINUS_CONSTANT_ALPHA;
1704 case QRhiGraphicsPipeline::SrcAlphaSaturate:
1705 return GL_SRC_ALPHA_SATURATE;
1706 case QRhiGraphicsPipeline::Src1Color:
1707 case QRhiGraphicsPipeline::OneMinusSrc1Color:
1708 case QRhiGraphicsPipeline::Src1Alpha:
1709 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
1710 qWarning(msg: "Unsupported blend factor %d", f);
1711 return GL_ZERO;
1712 default:
1713 Q_UNREACHABLE();
1714 return GL_ZERO;
1715 }
1716}
1717
1718static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
1719{
1720 switch (op) {
1721 case QRhiGraphicsPipeline::Add:
1722 return GL_FUNC_ADD;
1723 case QRhiGraphicsPipeline::Subtract:
1724 return GL_FUNC_SUBTRACT;
1725 case QRhiGraphicsPipeline::ReverseSubtract:
1726 return GL_FUNC_REVERSE_SUBTRACT;
1727 case QRhiGraphicsPipeline::Min:
1728 return GL_MIN;
1729 case QRhiGraphicsPipeline::Max:
1730 return GL_MAX;
1731 default:
1732 Q_UNREACHABLE();
1733 return GL_FUNC_ADD;
1734 }
1735}
1736
1737static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
1738{
1739 switch (op) {
1740 case QRhiGraphicsPipeline::Never:
1741 return GL_NEVER;
1742 case QRhiGraphicsPipeline::Less:
1743 return GL_LESS;
1744 case QRhiGraphicsPipeline::Equal:
1745 return GL_EQUAL;
1746 case QRhiGraphicsPipeline::LessOrEqual:
1747 return GL_LEQUAL;
1748 case QRhiGraphicsPipeline::Greater:
1749 return GL_GREATER;
1750 case QRhiGraphicsPipeline::NotEqual:
1751 return GL_NOTEQUAL;
1752 case QRhiGraphicsPipeline::GreaterOrEqual:
1753 return GL_GEQUAL;
1754 case QRhiGraphicsPipeline::Always:
1755 return GL_ALWAYS;
1756 default:
1757 Q_UNREACHABLE();
1758 return GL_ALWAYS;
1759 }
1760}
1761
1762static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
1763{
1764 switch (op) {
1765 case QRhiGraphicsPipeline::StencilZero:
1766 return GL_ZERO;
1767 case QRhiGraphicsPipeline::Keep:
1768 return GL_KEEP;
1769 case QRhiGraphicsPipeline::Replace:
1770 return GL_REPLACE;
1771 case QRhiGraphicsPipeline::IncrementAndClamp:
1772 return GL_INCR;
1773 case QRhiGraphicsPipeline::DecrementAndClamp:
1774 return GL_DECR;
1775 case QRhiGraphicsPipeline::Invert:
1776 return GL_INVERT;
1777 case QRhiGraphicsPipeline::IncrementAndWrap:
1778 return GL_INCR_WRAP;
1779 case QRhiGraphicsPipeline::DecrementAndWrap:
1780 return GL_DECR_WRAP;
1781 default:
1782 Q_UNREACHABLE();
1783 return GL_KEEP;
1784 }
1785}
1786
1787static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
1788{
1789 switch (f) {
1790 case QRhiSampler::Nearest:
1791 if (m == QRhiSampler::None)
1792 return GL_NEAREST;
1793 else
1794 return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
1795 case QRhiSampler::Linear:
1796 if (m == QRhiSampler::None)
1797 return GL_LINEAR;
1798 else
1799 return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
1800 default:
1801 Q_UNREACHABLE();
1802 return GL_LINEAR;
1803 }
1804}
1805
1806static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
1807{
1808 switch (f) {
1809 case QRhiSampler::Nearest:
1810 return GL_NEAREST;
1811 case QRhiSampler::Linear:
1812 return GL_LINEAR;
1813 default:
1814 Q_UNREACHABLE();
1815 return GL_LINEAR;
1816 }
1817}
1818
1819static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
1820{
1821 switch (m) {
1822 case QRhiSampler::Repeat:
1823 return GL_REPEAT;
1824 case QRhiSampler::ClampToEdge:
1825 return GL_CLAMP_TO_EDGE;
1826 case QRhiSampler::Mirror:
1827 return GL_MIRRORED_REPEAT;
1828 default:
1829 Q_UNREACHABLE();
1830 return GL_CLAMP_TO_EDGE;
1831 }
1832}
1833
1834static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
1835{
1836 switch (op) {
1837 case QRhiSampler::Never:
1838 return GL_NEVER;
1839 case QRhiSampler::Less:
1840 return GL_LESS;
1841 case QRhiSampler::Equal:
1842 return GL_EQUAL;
1843 case QRhiSampler::LessOrEqual:
1844 return GL_LEQUAL;
1845 case QRhiSampler::Greater:
1846 return GL_GREATER;
1847 case QRhiSampler::NotEqual:
1848 return GL_NOTEQUAL;
1849 case QRhiSampler::GreaterOrEqual:
1850 return GL_GEQUAL;
1851 case QRhiSampler::Always:
1852 return GL_ALWAYS;
1853 default:
1854 Q_UNREACHABLE();
1855 return GL_NEVER;
1856 }
1857}
1858
1859static inline QGles2Buffer::Access toGlAccess(QRhiPassResourceTracker::BufferAccess access)
1860{
1861 switch (access) {
1862 case QRhiPassResourceTracker::BufVertexInput:
1863 return QGles2Buffer::AccessVertex;
1864 case QRhiPassResourceTracker::BufIndexRead:
1865 return QGles2Buffer::AccessIndex;
1866 case QRhiPassResourceTracker::BufUniformRead:
1867 return QGles2Buffer::AccessUniform;
1868 case QRhiPassResourceTracker::BufStorageLoad:
1869 return QGles2Buffer::AccessStorageRead;
1870 case QRhiPassResourceTracker::BufStorageStore:
1871 return QGles2Buffer::AccessStorageWrite;
1872 case QRhiPassResourceTracker::BufStorageLoadStore:
1873 return QGles2Buffer::AccessStorageReadWrite;
1874 default:
1875 Q_UNREACHABLE();
1876 break;
1877 }
1878 return QGles2Buffer::AccessNone;
1879}
1880
1881static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
1882{
1883 QRhiPassResourceTracker::UsageState u;
1884 u.layout = 0; // N/A
1885 u.access = bufUsage.access;
1886 u.stage = 0; // N/A
1887 return u;
1888}
1889
1890static inline QGles2Texture::Access toGlAccess(QRhiPassResourceTracker::TextureAccess access)
1891{
1892 switch (access) {
1893 case QRhiPassResourceTracker::TexSample:
1894 return QGles2Texture::AccessSample;
1895 case QRhiPassResourceTracker::TexColorOutput:
1896 return QGles2Texture::AccessFramebuffer;
1897 case QRhiPassResourceTracker::TexDepthOutput:
1898 return QGles2Texture::AccessFramebuffer;
1899 case QRhiPassResourceTracker::TexStorageLoad:
1900 return QGles2Texture::AccessStorageRead;
1901 case QRhiPassResourceTracker::TexStorageStore:
1902 return QGles2Texture::AccessStorageWrite;
1903 case QRhiPassResourceTracker::TexStorageLoadStore:
1904 return QGles2Texture::AccessStorageReadWrite;
1905 default:
1906 Q_UNREACHABLE();
1907 break;
1908 }
1909 return QGles2Texture::AccessNone;
1910}
1911
1912static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Texture::UsageState &texUsage)
1913{
1914 QRhiPassResourceTracker::UsageState u;
1915 u.layout = 0; // N/A
1916 u.access = texUsage.access;
1917 u.stage = 0; // N/A
1918 return u;
1919}
1920
1921void QRhiGles2::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
1922 QGles2Buffer *bufD,
1923 QRhiPassResourceTracker::BufferAccess access,
1924 QRhiPassResourceTracker::BufferStage stage)
1925{
1926 QGles2Buffer::UsageState &u(bufD->usageState);
1927 passResTracker->registerBuffer(buf: bufD, slot: 0, access: &access, stage: &stage, state: toPassTrackerUsageState(bufUsage: u));
1928 u.access = toGlAccess(access);
1929}
1930
1931void QRhiGles2::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
1932 QGles2Texture *texD,
1933 QRhiPassResourceTracker::TextureAccess access,
1934 QRhiPassResourceTracker::TextureStage stage)
1935{
1936 QGles2Texture::UsageState &u(texD->usageState);
1937 passResTracker->registerTexture(tex: texD, access: &access, stage: &stage, state: toPassTrackerUsageState(texUsage: u));
1938 u.access = toGlAccess(access);
1939}
1940
1941void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
1942{
1943 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1944 GLenum indexType = GL_UNSIGNED_SHORT;
1945 quint32 indexStride = sizeof(quint16);
1946 quint32 indexOffset = 0;
1947 GLuint currentArrayBuffer = 0;
1948 static const int TRACKED_ATTRIB_COUNT = 16;
1949 bool enabledAttribArrays[TRACKED_ATTRIB_COUNT];
1950 memset(s: enabledAttribArrays, c: 0, n: sizeof(enabledAttribArrays));
1951
1952 for (const QGles2CommandBuffer::Command &cmd : qAsConst(t&: cbD->commands)) {
1953 switch (cmd.cmd) {
1954 case QGles2CommandBuffer::Command::BeginFrame:
1955 if (caps.coreProfile) {
1956 if (!vao)
1957 f->glGenVertexArrays(n: 1, arrays: &vao);
1958 f->glBindVertexArray(array: vao);
1959 }
1960 break;
1961 case QGles2CommandBuffer::Command::EndFrame:
1962 if (vao)
1963 f->glBindVertexArray(array: 0);
1964 break;
1965 case QGles2CommandBuffer::Command::ResetFrame:
1966 if (vao)
1967 f->glBindVertexArray(array: vao);
1968 break;
1969 case QGles2CommandBuffer::Command::Viewport:
1970 f->glViewport(x: GLint(cmd.args.viewport.x), y: GLint(cmd.args.viewport.y), width: GLsizei(cmd.args.viewport.w), height: GLsizei(cmd.args.viewport.h));
1971 f->glDepthRangef(zNear: cmd.args.viewport.d0, zFar: cmd.args.viewport.d1);
1972 break;
1973 case QGles2CommandBuffer::Command::Scissor:
1974 f->glScissor(x: cmd.args.scissor.x, y: cmd.args.scissor.y, width: cmd.args.scissor.w, height: cmd.args.scissor.h);
1975 break;
1976 case QGles2CommandBuffer::Command::BlendConstants:
1977 f->glBlendColor(red: cmd.args.blendConstants.r, green: cmd.args.blendConstants.g, blue: cmd.args.blendConstants.b, alpha: cmd.args.blendConstants.a);
1978 break;
1979 case QGles2CommandBuffer::Command::StencilRef:
1980 {
1981 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps);
1982 if (psD) {
1983 const GLint ref = GLint(cmd.args.stencilRef.ref);
1984 f->glStencilFuncSeparate(GL_FRONT, func: toGlCompareOp(op: psD->m_stencilFront.compareOp), ref, mask: psD->m_stencilReadMask);
1985 f->glStencilFuncSeparate(GL_BACK, func: toGlCompareOp(op: psD->m_stencilBack.compareOp), ref, mask: psD->m_stencilReadMask);
1986 cbD->graphicsPassState.dynamic.stencilRef = ref;
1987 } else {
1988 qWarning(msg: "No graphics pipeline active for setStencilRef; ignored");
1989 }
1990 }
1991 break;
1992 case QGles2CommandBuffer::Command::BindVertexBuffer:
1993 {
1994 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
1995 if (psD) {
1996 for (auto it = psD->m_vertexInputLayout.cbeginAttributes(), itEnd = psD->m_vertexInputLayout.cendAttributes();
1997 it != itEnd; ++it)
1998 {
1999 const int bindingIdx = it->binding();
2000 if (bindingIdx != cmd.args.bindVertexBuffer.binding)
2001 continue;
2002
2003 if (cmd.args.bindVertexBuffer.buffer != currentArrayBuffer) {
2004 currentArrayBuffer = cmd.args.bindVertexBuffer.buffer;
2005 // we do not support more than one vertex buffer
2006 f->glBindBuffer(GL_ARRAY_BUFFER, buffer: currentArrayBuffer);
2007 }
2008
2009 const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(index: bindingIdx);
2010 const int stride = int(inputBinding->stride());
2011 int size = 1;
2012 GLenum type = GL_FLOAT;
2013 bool normalize = false;
2014 switch (it->format()) {
2015 case QRhiVertexInputAttribute::Float4:
2016 type = GL_FLOAT;
2017 size = 4;
2018 break;
2019 case QRhiVertexInputAttribute::Float3:
2020 type = GL_FLOAT;
2021 size = 3;
2022 break;
2023 case QRhiVertexInputAttribute::Float2:
2024 type = GL_FLOAT;
2025 size = 2;
2026 break;
2027 case QRhiVertexInputAttribute::Float:
2028 type = GL_FLOAT;
2029 size = 1;
2030 break;
2031 case QRhiVertexInputAttribute::UNormByte4:
2032 type = GL_UNSIGNED_BYTE;
2033 normalize = true;
2034 size = 4;
2035 break;
2036 case QRhiVertexInputAttribute::UNormByte2:
2037 type = GL_UNSIGNED_BYTE;
2038 normalize = true;
2039 size = 2;
2040 break;
2041 case QRhiVertexInputAttribute::UNormByte:
2042 type = GL_UNSIGNED_BYTE;
2043 normalize = true;
2044 size = 1;
2045 break;
2046 default:
2047 break;
2048 }
2049
2050 const int locationIdx = it->location();
2051 quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset;
2052 f->glVertexAttribPointer(indx: GLuint(locationIdx), size, type, normalized: normalize, stride,
2053 ptr: reinterpret_cast<const GLvoid *>(quintptr(ofs)));
2054 if (locationIdx >= TRACKED_ATTRIB_COUNT || !enabledAttribArrays[locationIdx]) {
2055 if (locationIdx < TRACKED_ATTRIB_COUNT)
2056 enabledAttribArrays[locationIdx] = true;
2057 f->glEnableVertexAttribArray(index: GLuint(locationIdx));
2058 }
2059 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing)
2060 f->glVertexAttribDivisor(index: GLuint(locationIdx), divisor: GLuint(inputBinding->instanceStepRate()));
2061 }
2062 } else {
2063 qWarning(msg: "No graphics pipeline active for setVertexInput; ignored");
2064 }
2065 }
2066 break;
2067 case QGles2CommandBuffer::Command::BindIndexBuffer:
2068 indexType = cmd.args.bindIndexBuffer.type;
2069 indexStride = indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
2070 indexOffset = cmd.args.bindIndexBuffer.offset;
2071 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: cmd.args.bindIndexBuffer.buffer);
2072 break;
2073 case QGles2CommandBuffer::Command::Draw:
2074 {
2075 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps);
2076 if (psD) {
2077 if (cmd.args.draw.instanceCount == 1 || !caps.instancing) {
2078 f->glDrawArrays(mode: psD->drawMode, first: GLint(cmd.args.draw.firstVertex), count: GLsizei(cmd.args.draw.vertexCount));
2079 } else {
2080 f->glDrawArraysInstanced(mode: psD->drawMode, first: GLint(cmd.args.draw.firstVertex), count: GLsizei(cmd.args.draw.vertexCount),
2081 instancecount: GLsizei(cmd.args.draw.instanceCount));
2082 }
2083 } else {
2084 qWarning(msg: "No graphics pipeline active for draw; ignored");
2085 }
2086 }
2087 break;
2088 case QGles2CommandBuffer::Command::DrawIndexed:
2089 {
2090 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
2091 if (psD) {
2092 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
2093 quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset));
2094 if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) {
2095 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
2096 f->glDrawElementsBaseVertex(mode: psD->drawMode,
2097 count: GLsizei(cmd.args.drawIndexed.indexCount),
2098 type: indexType,
2099 indices: ofs,
2100 basevertex: cmd.args.drawIndexed.baseVertex);
2101 } else {
2102 f->glDrawElements(mode: psD->drawMode,
2103 count: GLsizei(cmd.args.drawIndexed.indexCount),
2104 type: indexType,
2105 indices: ofs);
2106 }
2107 } else {
2108 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
2109 f->glDrawElementsInstancedBaseVertex(mode: psD->drawMode,
2110 count: GLsizei(cmd.args.drawIndexed.indexCount),
2111 type: indexType,
2112 indices: ofs,
2113 instancecount: GLsizei(cmd.args.drawIndexed.instanceCount),
2114 basevertex: cmd.args.drawIndexed.baseVertex);
2115 } else {
2116 f->glDrawElementsInstanced(mode: psD->drawMode,
2117 count: GLsizei(cmd.args.drawIndexed.indexCount),
2118 type: indexType,
2119 indices: ofs,
2120 instancecount: GLsizei(cmd.args.drawIndexed.instanceCount));
2121 }
2122 }
2123 } else {
2124 qWarning(msg: "No graphics pipeline active for drawIndexed; ignored");
2125 }
2126 }
2127 break;
2128 case QGles2CommandBuffer::Command::BindGraphicsPipeline:
2129 executeBindGraphicsPipeline(cbD, QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindGraphicsPipeline.ps));
2130 break;
2131 case QGles2CommandBuffer::Command::BindShaderResources:
2132 bindShaderResources(maybeGraphicsPs: cmd.args.bindShaderResources.maybeGraphicsPs,
2133 maybeComputePs: cmd.args.bindShaderResources.maybeComputePs,
2134 srb: cmd.args.bindShaderResources.srb,
2135 dynOfsPairs: cmd.args.bindShaderResources.dynamicOffsetPairs,
2136 dynOfsCount: cmd.args.bindShaderResources.dynamicOffsetCount);
2137 break;
2138 case QGles2CommandBuffer::Command::BindFramebuffer:
2139 if (cmd.args.bindFramebuffer.fbo) {
2140 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: cmd.args.bindFramebuffer.fbo);
2141 if (caps.maxDrawBuffers > 1) {
2142 const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
2143 QVarLengthArray<GLenum, 8> bufs;
2144 for (int i = 0; i < colorAttCount; ++i)
2145 bufs.append(GL_COLOR_ATTACHMENT0 + uint(i));
2146 f->glDrawBuffers(n: colorAttCount, bufs: bufs.constData());
2147 }
2148 } else {
2149 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->defaultFramebufferObject());
2150 if (caps.maxDrawBuffers > 1) {
2151 GLenum bufs = GL_BACK;
2152 f->glDrawBuffers(n: 1, bufs: &bufs);
2153 }
2154 }
2155 if (caps.srgbCapableDefaultFramebuffer) {
2156 if (cmd.args.bindFramebuffer.srgb)
2157 f->glEnable(GL_FRAMEBUFFER_SRGB);
2158 else
2159 f->glDisable(GL_FRAMEBUFFER_SRGB);
2160 }
2161 break;
2162 case QGles2CommandBuffer::Command::Clear:
2163 f->glDisable(GL_SCISSOR_TEST);
2164 if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
2165 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2166 f->glClearColor(red: cmd.args.clear.c[0], green: cmd.args.clear.c[1], blue: cmd.args.clear.c[2], alpha: cmd.args.clear.c[3]);
2167 }
2168 if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
2169 f->glDepthMask(GL_TRUE);
2170 f->glClearDepthf(depth: cmd.args.clear.d);
2171 }
2172 if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT)
2173 f->glClearStencil(s: GLint(cmd.args.clear.s));
2174 f->glClear(mask: cmd.args.clear.mask);
2175 cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking
2176 break;
2177 case QGles2CommandBuffer::Command::BufferSubData:
2178 f->glBindBuffer(target: cmd.args.bufferSubData.target, buffer: cmd.args.bufferSubData.buffer);
2179 f->glBufferSubData(target: cmd.args.bufferSubData.target, offset: cmd.args.bufferSubData.offset, size: cmd.args.bufferSubData.size,
2180 data: cmd.args.bufferSubData.data);
2181 break;
2182 case QGles2CommandBuffer::Command::GetBufferSubData:
2183 {
2184 QRhiBufferReadbackResult *result = cmd.args.getBufferSubData.result;
2185 f->glBindBuffer(target: cmd.args.getBufferSubData.target, buffer: cmd.args.getBufferSubData.buffer);
2186 if (caps.gles) {
2187 if (caps.properMapBuffer) {
2188 void *p = f->glMapBufferRange(target: cmd.args.getBufferSubData.target,
2189 offset: cmd.args.getBufferSubData.offset,
2190 length: cmd.args.getBufferSubData.size,
2191 GL_MAP_READ_BIT);
2192 if (p) {
2193 result->data.resize(size: cmd.args.getBufferSubData.size);
2194 memcpy(dest: result->data.data(), src: p, n: size_t(cmd.args.getBufferSubData.size));
2195 f->glUnmapBuffer(target: cmd.args.getBufferSubData.target);
2196 }
2197 }
2198 } else {
2199 result->data.resize(size: cmd.args.getBufferSubData.size);
2200 f->glGetBufferSubData(target: cmd.args.getBufferSubData.target,
2201 offset: cmd.args.getBufferSubData.offset,
2202 size: cmd.args.getBufferSubData.size,
2203 data: result->data.data());
2204 }
2205 if (result->completed)
2206 result->completed();
2207 }
2208 break;
2209 case QGles2CommandBuffer::Command::CopyTex:
2210 {
2211 GLuint fbo;
2212 f->glGenFramebuffers(n: 1, framebuffers: &fbo);
2213 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: fbo);
2214 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2215 textarget: cmd.args.copyTex.srcFaceTarget, texture: cmd.args.copyTex.srcTexture, level: cmd.args.copyTex.srcLevel);
2216 f->glBindTexture(target: cmd.args.copyTex.dstTarget, texture: cmd.args.copyTex.dstTexture);
2217 f->glCopyTexSubImage2D(target: cmd.args.copyTex.dstFaceTarget, level: cmd.args.copyTex.dstLevel,
2218 xoffset: cmd.args.copyTex.dstX, yoffset: cmd.args.copyTex.dstY,
2219 x: cmd.args.copyTex.srcX, y: cmd.args.copyTex.srcY,
2220 width: cmd.args.copyTex.w, height: cmd.args.copyTex.h);
2221 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->defaultFramebufferObject());
2222 f->glDeleteFramebuffers(n: 1, framebuffers: &fbo);
2223 }
2224 break;
2225 case QGles2CommandBuffer::Command::ReadPixels:
2226 {
2227 QRhiReadbackResult *result = cmd.args.readPixels.result;
2228 GLuint tex = cmd.args.readPixels.texture;
2229 GLuint fbo = 0;
2230 int mipLevel = 0;
2231 if (tex) {
2232 result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
2233 result->format = cmd.args.readPixels.format;
2234 mipLevel = cmd.args.readPixels.level;
2235 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
2236 f->glGenFramebuffers(n: 1, framebuffers: &fbo);
2237 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: fbo);
2238 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2239 textarget: cmd.args.readPixels.readTarget, texture: cmd.args.readPixels.texture, level: mipLevel);
2240 }
2241 } else {
2242 result->pixelSize = currentSwapChain->pixelSize;
2243 result->format = QRhiTexture::RGBA8;
2244 // readPixels handles multisample resolving implicitly
2245 }
2246 result->data.resize(size: result->pixelSize.width() * result->pixelSize.height() * 4);
2247 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
2248 // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
2249 f->glReadPixels(x: 0, y: 0, width: result->pixelSize.width(), height: result->pixelSize.height(),
2250 GL_RGBA, GL_UNSIGNED_BYTE,
2251 pixels: result->data.data());
2252 } else {
2253 result->data.fill(c: '\0');
2254 }
2255 if (fbo) {
2256 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->defaultFramebufferObject());
2257 f->glDeleteFramebuffers(n: 1, framebuffers: &fbo);
2258 }
2259 if (result->completed)
2260 result->completed();
2261 }
2262 break;
2263 case QGles2CommandBuffer::Command::SubImage:
2264 f->glBindTexture(target: cmd.args.subImage.target, texture: cmd.args.subImage.texture);
2265 if (cmd.args.subImage.rowStartAlign != 4)
2266 f->glPixelStorei(GL_UNPACK_ALIGNMENT, param: cmd.args.subImage.rowStartAlign);
2267 f->glTexSubImage2D(target: cmd.args.subImage.faceTarget, level: cmd.args.subImage.level,
2268 xoffset: cmd.args.subImage.dx, yoffset: cmd.args.subImage.dy,
2269 width: cmd.args.subImage.w, height: cmd.args.subImage.h,
2270 format: cmd.args.subImage.glformat, type: cmd.args.subImage.gltype,
2271 pixels: cmd.args.subImage.data);
2272 if (cmd.args.subImage.rowStartAlign != 4)
2273 f->glPixelStorei(GL_UNPACK_ALIGNMENT, param: 4);
2274 break;
2275 case QGles2CommandBuffer::Command::CompressedImage:
2276 f->glBindTexture(target: cmd.args.compressedImage.target, texture: cmd.args.compressedImage.texture);
2277 f->glCompressedTexImage2D(target: cmd.args.compressedImage.faceTarget, level: cmd.args.compressedImage.level,
2278 internalformat: cmd.args.compressedImage.glintformat,
2279 width: cmd.args.compressedImage.w, height: cmd.args.compressedImage.h, border: 0,
2280 imageSize: cmd.args.compressedImage.size, data: cmd.args.compressedImage.data);
2281 break;
2282 case QGles2CommandBuffer::Command::CompressedSubImage:
2283 f->glBindTexture(target: cmd.args.compressedSubImage.target, texture: cmd.args.compressedSubImage.texture);
2284 f->glCompressedTexSubImage2D(target: cmd.args.compressedSubImage.faceTarget, level: cmd.args.compressedSubImage.level,
2285 xoffset: cmd.args.compressedSubImage.dx, yoffset: cmd.args.compressedSubImage.dy,
2286 width: cmd.args.compressedSubImage.w, height: cmd.args.compressedSubImage.h,
2287 format: cmd.args.compressedSubImage.glintformat,
2288 imageSize: cmd.args.compressedSubImage.size, data: cmd.args.compressedSubImage.data);
2289 break;
2290 case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
2291 {
2292 GLuint fbo[2];
2293 f->glGenFramebuffers(n: 2, framebuffers: fbo);
2294 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer: fbo[0]);
2295 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2296 GL_RENDERBUFFER, renderbuffer: cmd.args.blitFromRb.renderbuffer);
2297 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer: fbo[1]);
2298
2299 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textarget: cmd.args.blitFromRb.target,
2300 texture: cmd.args.blitFromRb.texture, level: cmd.args.blitFromRb.dstLevel);
2301 f->glBlitFramebuffer(srcX0: 0, srcY0: 0, srcX1: cmd.args.blitFromRb.w, srcY1: cmd.args.blitFromRb.h,
2302 dstX0: 0, dstY0: 0, dstX1: cmd.args.blitFromRb.w, dstY1: cmd.args.blitFromRb.h,
2303 GL_COLOR_BUFFER_BIT,
2304 GL_LINEAR);
2305 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->defaultFramebufferObject());
2306 }
2307 break;
2308 case QGles2CommandBuffer::Command::GenMip:
2309 f->glBindTexture(target: cmd.args.genMip.target, texture: cmd.args.genMip.texture);
2310 f->glGenerateMipmap(target: cmd.args.genMip.target);
2311 break;
2312 case QGles2CommandBuffer::Command::BindComputePipeline:
2313 {
2314 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cmd.args.bindComputePipeline.ps);
2315 f->glUseProgram(program: psD->program);
2316 }
2317 break;
2318 case QGles2CommandBuffer::Command::Dispatch:
2319 f->glDispatchCompute(num_groups_x: cmd.args.dispatch.x, num_groups_y: cmd.args.dispatch.y, num_groups_z: cmd.args.dispatch.z);
2320 break;
2321 case QGles2CommandBuffer::Command::BarriersForPass:
2322 {
2323 GLbitfield barriers = 0;
2324 QRhiPassResourceTracker &tracker(cbD->passResTrackers[cmd.args.barriersForPass.trackerIndex]);
2325 // we only care about after-write, not any other accesses, and
2326 // cannot tell if something was written in a shader several passes
2327 // ago: now the previously written resource may be used with an
2328 // access that was not in the previous passes, result in a missing
2329 // barrier in theory. Hence setting all barrier bits whenever
2330 // something previously written is used for the first time in a
2331 // subsequent pass.
2332 for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
2333 QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(it->stateAtPassBegin.access);
2334 if (bufferAccessIsWrite(access: accessBeforePass))
2335 barriers |= GL_ALL_BARRIER_BITS;
2336 }
2337 for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
2338 QGles2Texture::Access accessBeforePass = QGles2Texture::Access(it->stateAtPassBegin.access);
2339 if (textureAccessIsWrite(access: accessBeforePass))
2340 barriers |= GL_ALL_BARRIER_BITS;
2341 }
2342 if (barriers && caps.compute)
2343 f->glMemoryBarrier(barriers);
2344 }
2345 break;
2346 case QGles2CommandBuffer::Command::Barrier:
2347 if (caps.compute)
2348 f->glMemoryBarrier(barriers: cmd.args.barrier.barriers);
2349 break;
2350 default:
2351 break;
2352 }
2353 }
2354}
2355
2356void QRhiGles2::executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2GraphicsPipeline *psD)
2357{
2358 QGles2CommandBuffer::GraphicsPassState &state(cbD->graphicsPassState);
2359 const bool forceUpdate = !state.valid;
2360 state.valid = true;
2361
2362 const bool scissor = psD->m_flags.testFlag(flag: QRhiGraphicsPipeline::UsesScissor);
2363 if (forceUpdate || scissor != state.scissor) {
2364 state.scissor = scissor;
2365 if (scissor)
2366 f->glEnable(GL_SCISSOR_TEST);
2367 else
2368 f->glDisable(GL_SCISSOR_TEST);
2369 }
2370
2371 const bool cullFace = psD->m_cullMode != QRhiGraphicsPipeline::None;
2372 const GLenum cullMode = cullFace ? toGlCullMode(c: psD->m_cullMode) : GL_NONE;
2373 if (forceUpdate || cullFace != state.cullFace || cullMode != state.cullMode) {
2374 state.cullFace = cullFace;
2375 state.cullMode = cullMode;
2376 if (cullFace) {
2377 f->glEnable(GL_CULL_FACE);
2378 f->glCullFace(mode: cullMode);
2379 } else {
2380 f->glDisable(GL_CULL_FACE);
2381 }
2382 }
2383
2384 const GLenum frontFace = toGlFrontFace(f: psD->m_frontFace);
2385 if (forceUpdate || frontFace != state.frontFace) {
2386 state.frontFace = frontFace;
2387 f->glFrontFace(mode: frontFace);
2388 }
2389
2390 if (!psD->m_targetBlends.isEmpty()) {
2391 // We do not have MRT support here, meaning all targets use the blend
2392 // params from the first one. This is technically incorrect, even if
2393 // nothing in Qt relies on it. However, considering that
2394 // glBlendFuncSeparatei is only available in GL 4.0+ and GLES 3.2+, we
2395 // may just live with this for now because no point in bothering if it
2396 // won't be usable on many GLES (3.1 or 3.0) systems.
2397 const QRhiGraphicsPipeline::TargetBlend &targetBlend(psD->m_targetBlends.first());
2398
2399 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = {
2400 .r: targetBlend.colorWrite.testFlag(flag: QRhiGraphicsPipeline::R),
2401 .g: targetBlend.colorWrite.testFlag(flag: QRhiGraphicsPipeline::G),
2402 .b: targetBlend.colorWrite.testFlag(flag: QRhiGraphicsPipeline::B),
2403 .a: targetBlend.colorWrite.testFlag(flag: QRhiGraphicsPipeline::A)
2404 };
2405 if (forceUpdate || colorMask != state.colorMask) {
2406 state.colorMask = colorMask;
2407 f->glColorMask(red: colorMask.r, green: colorMask.g, blue: colorMask.b, alpha: colorMask.a);
2408 }
2409
2410 const bool blendEnabled = targetBlend.enable;
2411 const QGles2CommandBuffer::GraphicsPassState::Blend blend = {
2412 .srcColor: toGlBlendFactor(f: targetBlend.srcColor),
2413 .dstColor: toGlBlendFactor(f: targetBlend.dstColor),
2414 .srcAlpha: toGlBlendFactor(f: targetBlend.srcAlpha),
2415 .dstAlpha: toGlBlendFactor(f: targetBlend.dstAlpha),
2416 .opColor: toGlBlendOp(op: targetBlend.opColor),
2417 .opAlpha: toGlBlendOp(op: targetBlend.opAlpha)
2418 };
2419 if (forceUpdate || blendEnabled != state.blendEnabled || (blendEnabled && blend != state.blend)) {
2420 state.blendEnabled = blendEnabled;
2421 if (blendEnabled) {
2422 state.blend = blend;
2423 f->glEnable(GL_BLEND);
2424 f->glBlendFuncSeparate(srcRGB: blend.srcColor, dstRGB: blend.dstColor, srcAlpha: blend.srcAlpha, dstAlpha: blend.dstAlpha);
2425 f->glBlendEquationSeparate(modeRGB: blend.opColor, modeAlpha: blend.opAlpha);
2426 } else {
2427 f->glDisable(GL_BLEND);
2428 }
2429 }
2430 } else {
2431 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { .r: true, .g: true, .b: true, .a: true };
2432 if (forceUpdate || colorMask != state.colorMask) {
2433 state.colorMask = colorMask;
2434 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2435 }
2436 const bool blendEnabled = false;
2437 if (forceUpdate || blendEnabled != state.blendEnabled) {
2438 state.blendEnabled = blendEnabled;
2439 f->glDisable(GL_BLEND);
2440 }
2441 }
2442
2443 const bool depthTest = psD->m_depthTest;
2444 if (forceUpdate || depthTest != state.depthTest) {
2445 state.depthTest = depthTest;
2446 if (depthTest)
2447 f->glEnable(GL_DEPTH_TEST);
2448 else
2449 f->glDisable(GL_DEPTH_TEST);
2450 }
2451
2452 const bool depthWrite = psD->m_depthWrite;
2453 if (forceUpdate || depthWrite != state.depthWrite) {
2454 state.depthWrite = depthWrite;
2455 f->glDepthMask(flag: depthWrite);
2456 }
2457
2458 const GLenum depthFunc = toGlCompareOp(op: psD->m_depthOp);
2459 if (forceUpdate || depthFunc != state.depthFunc) {
2460 state.depthFunc = depthFunc;
2461 f->glDepthFunc(func: depthFunc);
2462 }
2463
2464 const bool stencilTest = psD->m_stencilTest;
2465 const GLuint stencilReadMask = psD->m_stencilReadMask;
2466 const GLuint stencilWriteMask = psD->m_stencilWriteMask;
2467 const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilFront = {
2468 .func: toGlCompareOp(op: psD->m_stencilFront.compareOp),
2469 .failOp: toGlStencilOp(op: psD->m_stencilFront.failOp),
2470 .zfailOp: toGlStencilOp(op: psD->m_stencilFront.depthFailOp),
2471 .zpassOp: toGlStencilOp(op: psD->m_stencilFront.passOp)
2472 };
2473 const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilBack = {
2474 .func: toGlCompareOp(op: psD->m_stencilBack.compareOp),
2475 .failOp: toGlStencilOp(op: psD->m_stencilBack.failOp),
2476 .zfailOp: toGlStencilOp(op: psD->m_stencilBack.depthFailOp),
2477 .zpassOp: toGlStencilOp(op: psD->m_stencilBack.passOp)
2478 };
2479 if (forceUpdate || stencilTest != state.stencilTest
2480 || (stencilTest
2481 && (stencilReadMask != state.stencilReadMask || stencilWriteMask != state.stencilWriteMask
2482 || stencilFront != state.stencil[0] || stencilBack != state.stencil[1])))
2483 {
2484 state.stencilTest = stencilTest;
2485 if (stencilTest) {
2486 state.stencilReadMask = stencilReadMask;
2487 state.stencilWriteMask = stencilWriteMask;
2488 state.stencil[0] = stencilFront;
2489 state.stencil[1] = stencilBack;
2490
2491 f->glEnable(GL_STENCIL_TEST);
2492
2493 f->glStencilFuncSeparate(GL_FRONT, func: stencilFront.func, ref: state.dynamic.stencilRef, mask: stencilReadMask);
2494 f->glStencilOpSeparate(GL_FRONT, fail: stencilFront.failOp, zfail: stencilFront.zfailOp, zpass: stencilFront.zpassOp);
2495 f->glStencilMaskSeparate(GL_FRONT, mask: stencilWriteMask);
2496
2497 f->glStencilFuncSeparate(GL_BACK, func: stencilBack.func, ref: state.dynamic.stencilRef, mask: stencilReadMask);
2498 f->glStencilOpSeparate(GL_BACK, fail: stencilBack.failOp, zfail: stencilBack.zfailOp, zpass: stencilBack.zpassOp);
2499 f->glStencilMaskSeparate(GL_BACK, mask: stencilWriteMask);
2500 } else {
2501 f->glDisable(GL_STENCIL_TEST);
2502 }
2503 }
2504
2505 const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(f: psD->m_slopeScaledDepthBias);
2506 const float polyOffsetFactor = psD->m_slopeScaledDepthBias;
2507 const float polyOffsetUnits = psD->m_depthBias;
2508 if (forceUpdate || state.polyOffsetFill != polyOffsetFill
2509 || polyOffsetFactor != state.polyOffsetFactor || polyOffsetUnits != state.polyOffsetUnits)
2510 {
2511 state.polyOffsetFill = polyOffsetFill;
2512 state.polyOffsetFactor = polyOffsetFactor;
2513 state.polyOffsetUnits = polyOffsetUnits;
2514 if (polyOffsetFill) {
2515 f->glPolygonOffset(factor: polyOffsetFactor, units: polyOffsetUnits);
2516 f->glEnable(GL_POLYGON_OFFSET_FILL);
2517 } else {
2518 f->glDisable(GL_POLYGON_OFFSET_FILL);
2519 }
2520 }
2521
2522 if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) {
2523 const float lineWidth = psD->m_lineWidth;
2524 if (forceUpdate || lineWidth != state.lineWidth) {
2525 state.lineWidth = lineWidth;
2526 f->glLineWidth(width: lineWidth);
2527 }
2528 }
2529
2530 f->glUseProgram(program: psD->program);
2531}
2532
2533static inline void qrhi_std140_to_packed(float *dst, int vecSize, int elemCount, const void *src)
2534{
2535 const float *p = reinterpret_cast<const float *>(src);
2536 for (int i = 0; i < elemCount; ++i) {
2537 for (int j = 0; j < vecSize; ++j)
2538 dst[vecSize * i + j] = *p++;
2539 p += 4 - vecSize;
2540 }
2541}
2542
2543void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
2544 QRhiShaderResourceBindings *srb,
2545 const uint *dynOfsPairs, int dynOfsCount)
2546{
2547 QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
2548 int texUnit = 0;
2549 QVarLengthArray<float, 256> packedFloatArray;
2550
2551 for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
2552 const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(idx: i).data();
2553
2554 switch (b->type) {
2555 case QRhiShaderResourceBinding::UniformBuffer:
2556 {
2557 int viewOffset = b->u.ubuf.offset;
2558 if (dynOfsCount) {
2559 for (int j = 0; j < dynOfsCount; ++j) {
2560 if (dynOfsPairs[2 * j] == uint(b->binding)) {
2561 viewOffset = int(dynOfsPairs[2 * j + 1]);
2562 break;
2563 }
2564 }
2565 }
2566 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
2567 const QByteArray bufView = QByteArray::fromRawData(bufD->ubuf.constData() + viewOffset,
2568 size: b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size);
2569 QVector<QGles2UniformDescription> &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
2570 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
2571 for (QGles2UniformDescription &uniform : uniforms) {
2572 if (uniform.binding == b->binding) {
2573 // in a uniform buffer everything is at least 4 byte aligned
2574 // so this should not cause unaligned reads
2575 const void *src = bufView.constData() + uniform.offset;
2576
2577 if (uniform.arrayDim > 0
2578 && uniform.type != QShaderDescription::Float
2579 && uniform.type != QShaderDescription::Vec2
2580 && uniform.type != QShaderDescription::Vec3
2581 && uniform.type != QShaderDescription::Vec4)
2582 {
2583 qWarning(msg: "Uniform with buffer binding %d, buffer offset %d, type %d is an array, "
2584 "but arrays are only supported for float, vec2, vec3, and vec4. "
2585 "Only the first element will be set.",
2586 uniform.binding, uniform.offset, uniform.type);
2587 }
2588
2589 // Our input is an std140 layout uniform block. See
2590 // "Standard Uniform Block Layout" in section 7.6.2.2 of
2591 // the OpenGL spec. This has some peculiar alignment
2592 // requirements, which is not what glUniform* wants. Hence
2593 // the unpacking/repacking for arrays and certain types.
2594
2595 switch (uniform.type) {
2596 case QShaderDescription::Float:
2597 {
2598 const int elemCount = uniform.arrayDim;
2599 if (elemCount < 1) {
2600 f->glUniform1f(location: uniform.glslLocation, x: *reinterpret_cast<const float *>(src));
2601 } else {
2602 // input is 16 bytes per element as per std140, have to convert to packed
2603 packedFloatArray.resize(asize: elemCount);
2604 qrhi_std140_to_packed(dst: packedFloatArray.data(), vecSize: 1, elemCount, src);
2605 f->glUniform1fv(location: uniform.glslLocation, count: elemCount, v: packedFloatArray.constData());
2606 }
2607 }
2608 break;
2609 case QShaderDescription::Vec2:
2610 {
2611 const int elemCount = uniform.arrayDim;
2612 if (elemCount < 1) {
2613 f->glUniform2fv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const float *>(src));
2614 } else {
2615 packedFloatArray.resize(asize: elemCount * 2);
2616 qrhi_std140_to_packed(dst: packedFloatArray.data(), vecSize: 2, elemCount, src);
2617 f->glUniform2fv(location: uniform.glslLocation, count: elemCount, v: packedFloatArray.constData());
2618 }
2619 }
2620 break;
2621 case QShaderDescription::Vec3:
2622 {
2623 const int elemCount = uniform.arrayDim;
2624 if (elemCount < 1) {
2625 f->glUniform3fv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const float *>(src));
2626 } else {
2627 packedFloatArray.resize(asize: elemCount * 3);
2628 qrhi_std140_to_packed(dst: packedFloatArray.data(), vecSize: 3, elemCount, src);
2629 f->glUniform3fv(location: uniform.glslLocation, count: elemCount, v: packedFloatArray.constData());
2630 }
2631 }
2632 break;
2633 case QShaderDescription::Vec4:
2634 f->glUniform4fv(location: uniform.glslLocation, count: qMax(a: 1, b: uniform.arrayDim), v: reinterpret_cast<const float *>(src));
2635 break;
2636 case QShaderDescription::Mat2:
2637 f->glUniformMatrix2fv(location: uniform.glslLocation, count: 1, GL_FALSE, value: reinterpret_cast<const float *>(src));
2638 break;
2639 case QShaderDescription::Mat3:
2640 {
2641 // 4 floats per column (or row, if row-major)
2642 float mat[9];
2643 const float *srcMat = reinterpret_cast<const float *>(src);
2644 memcpy(dest: mat, src: srcMat, n: 3 * sizeof(float));
2645 memcpy(dest: mat + 3, src: srcMat + 4, n: 3 * sizeof(float));
2646 memcpy(dest: mat + 6, src: srcMat + 8, n: 3 * sizeof(float));
2647 f->glUniformMatrix3fv(location: uniform.glslLocation, count: 1, GL_FALSE, value: mat);
2648 }
2649 break;
2650 case QShaderDescription::Mat4:
2651 f->glUniformMatrix4fv(location: uniform.glslLocation, count: 1, GL_FALSE, value: reinterpret_cast<const float *>(src));
2652 break;
2653 case QShaderDescription::Int:
2654 f->glUniform1i(location: uniform.glslLocation, x: *reinterpret_cast<const qint32 *>(src));
2655 break;
2656 case QShaderDescription::Int2:
2657 f->glUniform2iv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const qint32 *>(src));
2658 break;
2659 case QShaderDescription::Int3:
2660 f->glUniform3iv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const qint32 *>(src));
2661 break;
2662 case QShaderDescription::Int4:
2663 f->glUniform4iv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const qint32 *>(src));
2664 break;
2665 case QShaderDescription::Uint:
2666 f->glUniform1ui(location: uniform.glslLocation, v0: *reinterpret_cast<const quint32 *>(src));
2667 break;
2668 case QShaderDescription::Uint2:
2669 f->glUniform2uiv(location: uniform.glslLocation, count: 1, value: reinterpret_cast<const quint32 *>(src));
2670 break;
2671 case QShaderDescription::Uint3:
2672 f->glUniform3uiv(location: uniform.glslLocation, count: 1, value: reinterpret_cast<const quint32 *>(src));
2673 break;
2674 case QShaderDescription::Uint4:
2675 f->glUniform4uiv(location: uniform.glslLocation, count: 1, value: reinterpret_cast<const quint32 *>(src));
2676 break;
2677 case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
2678 f->glUniform1i(location: uniform.glslLocation, x: *reinterpret_cast<const qint32 *>(src));
2679 break;
2680 case QShaderDescription::Bool2:
2681 f->glUniform2iv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const qint32 *>(src));
2682 break;
2683 case QShaderDescription::Bool3:
2684 f->glUniform3iv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const qint32 *>(src));
2685 break;
2686 case QShaderDescription::Bool4:
2687 f->glUniform4iv(location: uniform.glslLocation, count: 1, v: reinterpret_cast<const qint32 *>(src));
2688 break;
2689 default:
2690 qWarning(msg: "Uniform with buffer binding %d, buffer offset %d has unsupported type %d",
2691 uniform.binding, uniform.offset, uniform.type);
2692 break;
2693 }
2694 }
2695 }
2696 }
2697 break;
2698 case QRhiShaderResourceBinding::SampledTexture:
2699 {
2700 QVector<QGles2SamplerDescription> &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
2701 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
2702 for (int elem = 0; elem < b->u.stex.count; ++elem) {
2703 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex);
2704 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[elem].sampler);
2705 for (QGles2SamplerDescription &sampler : samplers) {
2706 if (sampler.binding == b->binding) {
2707 f->glActiveTexture(GL_TEXTURE0 + uint(texUnit));
2708 f->glBindTexture(target: texD->target, texture: texD->texture);
2709
2710 if (texD->samplerState != samplerD->d) {
2711 f->glTexParameteri(target: texD->target, GL_TEXTURE_MIN_FILTER, param: GLint(samplerD->d.glminfilter));
2712 f->glTexParameteri(target: texD->target, GL_TEXTURE_MAG_FILTER, param: GLint(samplerD->d.glmagfilter));
2713 f->glTexParameteri(target: texD->target, GL_TEXTURE_WRAP_S, param: GLint(samplerD->d.glwraps));
2714 f->glTexParameteri(target: texD->target, GL_TEXTURE_WRAP_T, param: GLint(samplerD->d.glwrapt));
2715 // 3D textures not supported by GLES 2.0 or by us atm...
2716 //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
2717 if (caps.textureCompareMode) {
2718 if (samplerD->d.gltexcomparefunc != GL_NEVER) {
2719 f->glTexParameteri(target: texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
2720 f->glTexParameteri(target: texD->target, GL_TEXTURE_COMPARE_FUNC, param: GLint(samplerD->d.gltexcomparefunc));
2721 } else {
2722 f->glTexParameteri(target: texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
2723 }
2724 }
2725 texD->samplerState = samplerD->d;
2726 }
2727
2728 f->glUniform1i(location: sampler.glslLocation + elem, x: texUnit);
2729 ++texUnit;
2730 }
2731 }
2732 }
2733 }
2734 break;
2735 case QRhiShaderResourceBinding::ImageLoad:
2736 case QRhiShaderResourceBinding::ImageStore:
2737 case QRhiShaderResourceBinding::ImageLoadStore:
2738 {
2739 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
2740 const bool layered = texD->m_flags.testFlag(flag: QRhiTexture::CubeMap);
2741 GLenum access = GL_READ_WRITE;
2742 if (b->type == QRhiShaderResourceBinding::ImageLoad)
2743 access = GL_READ_ONLY;
2744 else if (b->type == QRhiShaderResourceBinding::ImageStore)
2745 access = GL_WRITE_ONLY;
2746 f->glBindImageTexture(unit: GLuint(b->binding), texture: texD->texture,
2747 level: b->u.simage.level, layered, layer: 0,
2748 access, format: texD->glsizedintformat);
2749 }
2750 break;
2751 case QRhiShaderResourceBinding::BufferLoad:
2752 case QRhiShaderResourceBinding::BufferStore:
2753 case QRhiShaderResourceBinding::BufferLoadStore:
2754 {
2755 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
2756 if (b->u.sbuf.offset == 0 && b->u.sbuf.maybeSize == 0)
2757 f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index: GLuint(b->binding), buffer: bufD->buffer);
2758 else
2759 f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index: GLuint(b->binding), buffer: bufD->buffer,
2760 offset: b->u.sbuf.offset, size: b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->m_size);
2761 }
2762 break;
2763 default:
2764 Q_UNREACHABLE();
2765 break;
2766 }
2767 }
2768
2769 if (texUnit > 1)
2770 f->glActiveTexture(GL_TEXTURE0);
2771}
2772
2773void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2774{
2775 Q_ASSERT(QRHI_RES(QGles2CommandBuffer, cb)->recordingPass == QGles2CommandBuffer::NoPass);
2776
2777 enqueueResourceUpdates(cb, resourceUpdates);
2778}
2779
2780QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
2781 bool *wantsColorClear, bool *wantsDsClear)
2782{
2783 QGles2RenderTargetData *rtD = nullptr;
2784 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2785
2786 QGles2CommandBuffer::Command fbCmd;
2787 fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer;
2788
2789 switch (rt->resourceType()) {
2790 case QRhiResource::RenderTarget:
2791 rtD = &QRHI_RES(QGles2ReferenceRenderTarget, rt)->d;
2792 if (wantsColorClear)
2793 *wantsColorClear = true;
2794 if (wantsDsClear)
2795 *wantsDsClear = true;
2796 fbCmd.args.bindFramebuffer.fbo = 0;
2797 fbCmd.args.bindFramebuffer.colorAttCount = 1;
2798 break;
2799 case QRhiResource::TextureRenderTarget:
2800 {
2801 QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, rt);
2802 rtD = &rtTex->d;
2803 if (wantsColorClear)
2804 *wantsColorClear = !rtTex->m_flags.testFlag(flag: QRhiTextureRenderTarget::PreserveColorContents);
2805 if (wantsDsClear)
2806 *wantsDsClear = !rtTex->m_flags.testFlag(flag: QRhiTextureRenderTarget::PreserveDepthStencilContents);
2807 fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
2808 fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
2809
2810 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
2811 it != itEnd; ++it)
2812 {
2813 const QRhiColorAttachment &colorAtt(*it);
2814 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
2815 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
2816 if (texD) {
2817 trackedRegisterTexture(passResTracker: &passResTracker, texD,
2818 access: QRhiPassResourceTracker::TexColorOutput,
2819 stage: QRhiPassResourceTracker::TexColorOutputStage);
2820 }
2821 if (resolveTexD) {
2822 trackedRegisterTexture(passResTracker: &passResTracker, texD: resolveTexD,
2823 access: QRhiPassResourceTracker::TexColorOutput,
2824 stage: QRhiPassResourceTracker::TexColorOutputStage);
2825 }
2826 // renderbuffers cannot be written in shaders (no image store) so
2827 // they do not matter here
2828 }
2829 if (rtTex->m_desc.depthTexture()) {
2830 trackedRegisterTexture(passResTracker: &passResTracker, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
2831 access: QRhiPassResourceTracker::TexDepthOutput,
2832 stage: QRhiPassResourceTracker::TexDepthOutputStage);
2833 }
2834 }
2835 break;
2836 default:
2837 Q_UNREACHABLE();
2838 break;
2839 }
2840
2841 fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
2842 cbD->commands.append(t: fbCmd);
2843
2844 return rtD;
2845}
2846
2847void QRhiGles2::enqueueBarriersForPass(QGles2CommandBuffer *cbD)
2848{
2849 cbD->passResTrackers.append(t: QRhiPassResourceTracker());
2850 cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
2851 QGles2CommandBuffer::Command cmd;
2852 cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
2853 cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
2854 cbD->commands.append(t: cmd);
2855}
2856
2857void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
2858 QRhiRenderTarget *rt,
2859 const QColor &colorClearValue,
2860 const QRhiDepthStencilClearValue &depthStencilClearValue,
2861 QRhiResourceUpdateBatch *resourceUpdates)
2862{
2863 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2864 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass);
2865
2866 if (resourceUpdates)
2867 enqueueResourceUpdates(cb, resourceUpdates);
2868
2869 // Get a new resource tracker. Then add a command that will generate
2870 // glMemoryBarrier() calls based on that tracker when submitted.
2871 enqueueBarriersForPass(cbD);
2872
2873 bool wantsColorClear, wantsDsClear;
2874 QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, wantsColorClear: &wantsColorClear, wantsDsClear: &wantsDsClear);
2875
2876 QGles2CommandBuffer::Command clearCmd;
2877 clearCmd.cmd = QGles2CommandBuffer::Command::Clear;
2878 clearCmd.args.clear.mask = 0;
2879 if (rtD->colorAttCount && wantsColorClear)
2880 clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
2881 if (rtD->dsAttCount && wantsDsClear)
2882 clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
2883 clearCmd.args.clear.c[0] = float(colorClearValue.redF());
2884 clearCmd.args.clear.c[1] = float(colorClearValue.greenF());
2885 clearCmd.args.clear.c[2] = float(colorClearValue.blueF());
2886 clearCmd.args.clear.c[3] = float(colorClearValue.alphaF());
2887 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
2888 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
2889 cbD->commands.append(t: clearCmd);
2890
2891 cbD->recordingPass = QGles2CommandBuffer::RenderPass;
2892 cbD->currentTarget = rt;
2893
2894 cbD->resetCachedState();
2895}
2896
2897void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2898{
2899 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2900 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
2901
2902 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
2903 QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
2904 if (rtTex->m_desc.cbeginColorAttachments() != rtTex->m_desc.cendColorAttachments()) {
2905 // handle only 1 color attachment and only (msaa) renderbuffer
2906 const QRhiColorAttachment &colorAtt(*rtTex->m_desc.cbeginColorAttachments());
2907 if (colorAtt.resolveTexture()) {
2908 Q_ASSERT(colorAtt.renderBuffer());
2909 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer());
2910 const QSize size = colorAtt.resolveTexture()->pixelSize();
2911 if (rbD->pixelSize() != size) {
2912 qWarning(msg: "Resolve source (%dx%d) and target (%dx%d) size does not match",
2913 rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
2914 }
2915 QGles2CommandBuffer::Command cmd;
2916 cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer;
2917 cmd.args.blitFromRb.renderbuffer = rbD->renderbuffer;
2918 cmd.args.blitFromRb.w = size.width();
2919 cmd.args.blitFromRb.h = size.height();
2920 QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
2921 const GLenum faceTargetBase = colorTexD->m_flags.testFlag(flag: QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X
2922 : colorTexD->target;
2923 cmd.args.blitFromRb.target = faceTargetBase + uint(colorAtt.resolveLayer());
2924 cmd.args.blitFromRb.texture = colorTexD->texture;
2925 cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel();
2926 cbD->commands.append(t: cmd);
2927 }
2928 }
2929 }
2930
2931 cbD->recordingPass = QGles2CommandBuffer::NoPass;
2932 cbD->currentTarget = nullptr;
2933
2934 if (resourceUpdates)
2935 enqueueResourceUpdates(cb, resourceUpdates);
2936}
2937
2938void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2939{
2940 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2941 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass);
2942
2943 if (resourceUpdates)
2944 enqueueResourceUpdates(cb, resourceUpdates);
2945
2946 enqueueBarriersForPass(cbD);
2947
2948 cbD->recordingPass = QGles2CommandBuffer::ComputePass;
2949
2950 cbD->resetCachedState();
2951}
2952
2953void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2954{
2955 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2956 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
2957
2958 cbD->recordingPass = QGles2CommandBuffer::NoPass;
2959
2960 if (resourceUpdates)
2961 enqueueResourceUpdates(cb, resourceUpdates);
2962}
2963
2964void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
2965{
2966 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2967 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
2968 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, ps);
2969 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
2970
2971 if (pipelineChanged) {
2972 cbD->currentGraphicsPipeline = nullptr;
2973 cbD->currentComputePipeline = ps;
2974 cbD->currentPipelineGeneration = psD->generation;
2975
2976 QGles2CommandBuffer::Command cmd;
2977 cmd.cmd = QGles2CommandBuffer::Command::BindComputePipeline;
2978 cmd.args.bindComputePipeline.ps = ps;
2979 cbD->commands.append(t: cmd);
2980 }
2981}
2982
2983template<typename T>
2984inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
2985 QRhiShaderResourceBinding::Type bindingType,
2986 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
2987{
2988 int access = 0;
2989 if (bindingType == loadTypeVal) {
2990 access = QGles2CommandBuffer::ComputePassState::Read;
2991 } else {
2992 access = QGles2CommandBuffer::ComputePassState::Write;
2993 if (bindingType == loadStoreTypeVal)
2994 access |= QGles2CommandBuffer::ComputePassState::Read;
2995 }
2996 auto it = writtenResources->find(resource);
2997 if (it != writtenResources->end())
2998 it->first |= access;
2999 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
3000 writtenResources->insert(resource, { access, true });
3001}
3002
3003void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
3004{
3005 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
3006 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
3007
3008 if (cbD->currentComputeSrb) {
3009 GLbitfield barriers = 0;
3010
3011 // The key in the writtenResources map indicates that the resource was
3012 // written in a previous dispatch, whereas the value accumulates the
3013 // access mask in the current one.
3014 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
3015 accessAndIsNewFlag = { 0, false };
3016
3017 QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, cbD->currentComputeSrb);
3018 const int bindingCount = srbD->m_bindings.count();
3019 for (int i = 0; i < bindingCount; ++i) {
3020 const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(idx: i).data();
3021 switch (b->type) {
3022 case QRhiShaderResourceBinding::ImageLoad:
3023 case QRhiShaderResourceBinding::ImageStore:
3024 case QRhiShaderResourceBinding::ImageLoadStore:
3025 qrhigl_accumulateComputeResource(writtenResources: &cbD->computePassState.writtenResources,
3026 resource: b->u.simage.tex,
3027 bindingType: b->type,
3028 loadTypeVal: QRhiShaderResourceBinding::ImageLoad,
3029 storeTypeVal: QRhiShaderResourceBinding::ImageStore,
3030 loadStoreTypeVal: QRhiShaderResourceBinding::ImageLoadStore);
3031 break;
3032 case QRhiShaderResourceBinding::BufferLoad:
3033 case QRhiShaderResourceBinding::BufferStore:
3034 case QRhiShaderResourceBinding::BufferLoadStore:
3035 qrhigl_accumulateComputeResource(writtenResources: &cbD->computePassState.writtenResources,
3036 resource: b->u.sbuf.buf,
3037 bindingType: b->type,
3038 loadTypeVal: QRhiShaderResourceBinding::BufferLoad,
3039 storeTypeVal: QRhiShaderResourceBinding::BufferStore,
3040 loadStoreTypeVal: QRhiShaderResourceBinding::BufferLoadStore);
3041 break;
3042 default:
3043 break;
3044 }
3045 }
3046
3047 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
3048 const int accessInThisDispatch = it->first;
3049 const bool isNewInThisDispatch = it->second;
3050 if (accessInThisDispatch && !isNewInThisDispatch) {
3051 if (it.key()->resourceType() == QRhiResource::Texture)
3052 barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
3053 else
3054 barriers |= GL_SHADER_STORAGE_BARRIER_BIT;
3055 }
3056 // Anything that was previously written, but is only read now, can be
3057 // removed from the written list (because that previous write got a
3058 // corresponding barrier now).
3059 if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
3060 it = cbD->computePassState.writtenResources.erase(it);
3061 else
3062 ++it;
3063 }
3064
3065 if (barriers) {
3066 QGles2CommandBuffer::Command cmd;
3067 cmd.cmd = QGles2CommandBuffer::Command::Barrier;
3068 cmd.args.barrier.barriers = barriers;
3069 cbD->commands.append(t: cmd);
3070 }
3071 }
3072
3073 QGles2CommandBuffer::Command cmd;
3074 cmd.cmd = QGles2CommandBuffer::Command::Dispatch;
3075 cmd.args.dispatch.x = GLuint(x);
3076 cmd.args.dispatch.y = GLuint(y);
3077 cmd.args.dispatch.z = GLuint(z);
3078 cbD->commands.append(t: cmd);
3079}
3080
3081static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
3082{
3083 switch (type) {
3084 case QRhiShaderStage::Vertex:
3085 return GL_VERTEX_SHADER;
3086 case QRhiShaderStage::Fragment:
3087 return GL_FRAGMENT_SHADER;
3088 case QRhiShaderStage::Compute:
3089 return GL_COMPUTE_SHADER;
3090 default:
3091 Q_UNREACHABLE();
3092 return GL_VERTEX_SHADER;
3093 }
3094}
3095
3096QByteArray QRhiGles2::shaderSource(const QRhiShaderStage &shaderStage, int *glslVersion)
3097{
3098 const QShader bakedShader = shaderStage.shader();
3099 QVector<int> versionsToTry;
3100 QByteArray source;
3101 if (caps.gles) {
3102 if (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)) {
3103 versionsToTry << 320 << 310 << 300 << 100;
3104 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
3105 versionsToTry << 310 << 300 << 100;
3106 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
3107 versionsToTry << 300 << 100;
3108 } else {
3109 versionsToTry << 100;
3110 }
3111 for (int v : versionsToTry) {
3112 QShaderVersion ver(v, QShaderVersion::GlslEs);
3113 source = bakedShader.shader(key: { QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
3114 if (!source.isEmpty()) {
3115 if (glslVersion)
3116 *glslVersion = v;
3117 break;
3118 }
3119 }
3120 } else {
3121 if (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6)) {
3122 versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
3123 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 5) {
3124 versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
3125 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 4) {
3126 versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150;
3127 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 3) {
3128 versionsToTry << 430 << 420 << 410 << 400 << 330 << 150;
3129 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 2) {
3130 versionsToTry << 420 << 410 << 400 << 330 << 150;
3131 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 1) {
3132 versionsToTry << 410 << 400 << 330 << 150;
3133 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 0) {
3134 versionsToTry << 400 << 330 << 150;
3135 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 3) {
3136 versionsToTry << 330 << 150;
3137 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 2) {
3138 versionsToTry << 150;
3139 }
3140 if (!caps.coreProfile)
3141 versionsToTry << 120;
3142 for (int v : versionsToTry) {
3143 source = bakedShader.shader(key: { QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
3144 if (!source.isEmpty()) {
3145 if (glslVersion)
3146 *glslVersion = v;
3147 break;
3148 }
3149 }
3150 }
3151 if (source.isEmpty()) {
3152 qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
3153 << ") in baked shader" << bakedShader;
3154 }
3155 return source;
3156}
3157
3158bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage, int *glslVersion)
3159{
3160 const QByteArray source = shaderSource(shaderStage, glslVersion);
3161 if (source.isEmpty())
3162 return false;
3163
3164 GLuint shader;
3165 auto cacheIt = m_shaderCache.constFind(akey: shaderStage);
3166 if (cacheIt != m_shaderCache.constEnd()) {
3167 shader = *cacheIt;
3168 } else {
3169 shader = f->glCreateShader(type: toGlShaderType(type: shaderStage.type()));
3170 const char *srcStr = source.constData();
3171 const GLint srcLength = source.count();
3172 f->glShaderSource(shader, count: 1, string: &srcStr, length: &srcLength);
3173 f->glCompileShader(shader);
3174 GLint compiled = 0;
3175 f->glGetShaderiv(shader, GL_COMPILE_STATUS, params: &compiled);
3176 if (!compiled) {
3177 GLint infoLogLength = 0;
3178 f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, params: &infoLogLength);
3179 QByteArray log;
3180 if (infoLogLength > 1) {
3181 GLsizei length = 0;
3182 log.resize(size: infoLogLength);
3183 f->glGetShaderInfoLog(shader, bufsize: infoLogLength, length: &length, infolog: log.data());
3184 }
3185 qWarning(msg: "Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
3186 return false;
3187 }
3188 if (m_shaderCache.count() >= MAX_SHADER_CACHE_ENTRIES) {
3189 // Use the simplest strategy: too many cached shaders -> drop them all.
3190 for (uint shader : m_shaderCache)
3191 f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
3192 m_shaderCache.clear();
3193 }
3194 m_shaderCache.insert(akey: shaderStage, avalue: shader);
3195 }
3196
3197 f->glAttachShader(program, shader);
3198
3199 return true;
3200}
3201
3202bool QRhiGles2::linkProgram(GLuint program)
3203{
3204 f->glLinkProgram(program);
3205 GLint linked = 0;
3206 f->glGetProgramiv(program, GL_LINK_STATUS, params: &linked);
3207 if (!linked) {
3208 GLint infoLogLength = 0;
3209 f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, params: &infoLogLength);
3210 QByteArray log;
3211 if (infoLogLength > 1) {
3212 GLsizei length = 0;
3213 log.resize(size: infoLogLength);
3214 f->glGetProgramInfoLog(program, bufsize: infoLogLength, length: &length, infolog: log.data());
3215 }
3216 qWarning(msg: "Failed to link shader program: %s", log.constData());
3217 return false;
3218 }
3219 return true;
3220}
3221
3222void QRhiGles2::registerUniformIfActive(const QShaderDescription::BlockVariable &var,
3223 const QByteArray &namePrefix,
3224 int binding,
3225 int baseOffset,
3226 GLuint program,
3227 QVector<QGles2UniformDescription> *dst)
3228{
3229 if (var.type == QShaderDescription::Struct) {
3230 qWarning(msg: "Nested structs are not supported at the moment. '%s' ignored.",
3231 qPrintable(var.name));
3232 return;
3233 }
3234 QGles2UniformDescription uniform;
3235 uniform.type = var.type;
3236 const QByteArray name = namePrefix + var.name.toUtf8();
3237 uniform.glslLocation = f->glGetUniformLocation(program, name: name.constData());
3238 if (uniform.glslLocation >= 0) {
3239 if (var.arrayDims.count() > 1) {
3240 qWarning(msg: "Array '%s' has more than one dimension. This is not supported.",
3241 qPrintable(var.name));
3242 return;
3243 }
3244 uniform.binding = binding;
3245 uniform.offset = uint(baseOffset + var.offset);
3246 uniform.size = var.size;
3247 uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
3248 dst->append(t: uniform);
3249 }
3250}
3251
3252void QRhiGles2::gatherUniforms(GLuint program,
3253 const QShaderDescription::UniformBlock &ub,
3254 QVector<QGles2UniformDescription> *dst)
3255{
3256 QByteArray prefix = ub.structName.toUtf8() + '.';
3257 for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
3258 if (blockMember.type == QShaderDescription::Struct) {
3259 QByteArray structPrefix = prefix + blockMember.name.toUtf8();
3260
3261 const int baseOffset = blockMember.offset;
3262 if (blockMember.arrayDims.isEmpty()) {
3263 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
3264 registerUniformIfActive(var: structMember, namePrefix: structPrefix, binding: ub.binding, baseOffset, program, dst);
3265 } else {
3266 if (blockMember.arrayDims.count() > 1) {
3267 qWarning(msg: "Array of struct '%s' has more than one dimension. Only the first dimension is used.",
3268 qPrintable(blockMember.name));
3269 }
3270 const int dim = blockMember.arrayDims.first();
3271 const int elemSize = blockMember.size / dim;
3272 int elemOffset = baseOffset;
3273 for (int di = 0; di < dim; ++di) {
3274 const QByteArray arrayPrefix = structPrefix + '[' + QByteArray::number(di) + ']' + '.';
3275 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
3276 registerUniformIfActive(var: structMember, namePrefix: arrayPrefix, binding: ub.binding, baseOffset: elemOffset, program, dst);
3277 elemOffset += elemSize;
3278 }
3279 }
3280 } else {
3281 registerUniformIfActive(var: blockMember, namePrefix: prefix, binding: ub.binding, baseOffset: 0, program, dst);
3282 }
3283 }
3284}
3285
3286void QRhiGles2::gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
3287 QVector<QGles2SamplerDescription> *dst)
3288{
3289 QGles2SamplerDescription sampler;
3290 const QByteArray name = v.name.toUtf8();
3291 sampler.glslLocation = f->glGetUniformLocation(program, name: name.constData());
3292 if (sampler.glslLocation >= 0) {
3293 sampler.binding = v.binding;
3294 dst->append(t: sampler);
3295 }
3296}
3297
3298bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
3299{
3300 static QOpenGLProgramBinarySupportCheckWrapper checker;
3301 return checker.get(context: ctx)->isSupported();
3302}
3303
3304Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
3305
3306static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
3307{
3308 switch (type) {
3309 case QRhiShaderStage::Vertex:
3310 return QShader::VertexStage;
3311 case QRhiShaderStage::Fragment:
3312 return QShader::FragmentStage;
3313 case QRhiShaderStage::Compute:
3314 return QShader::ComputeStage;
3315 default:
3316 Q_UNREACHABLE();
3317 return QShader::VertexStage;
3318 }
3319}
3320
3321QRhiGles2::DiskCacheResult QRhiGles2::tryLoadFromDiskCache(const QRhiShaderStage *stages, int stageCount,
3322 GLuint program, QByteArray *cacheKey)
3323{
3324 QRhiGles2::DiskCacheResult result = QRhiGles2::DiskCacheMiss;
3325 QByteArray diskCacheKey;
3326
3327 if (isProgramBinaryDiskCacheEnabled()) {
3328 QOpenGLProgramBinaryCache::ProgramDesc binaryProgram;
3329 for (int i = 0; i < stageCount; ++i) {
3330 const QRhiShaderStage &stage(stages[i]);
3331 const QByteArray source = shaderSource(shaderStage: stage, glslVersion: nullptr);
3332 if (source.isEmpty())
3333 return QRhiGles2::DiskCacheError;
3334 binaryProgram.shaders.append(t: QOpenGLProgramBinaryCache::ShaderDesc(toShaderStage(type: stage.type()), source));
3335 }
3336
3337 diskCacheKey = binaryProgram.cacheKey();
3338 if (qrhi_programBinaryCache()->load(cacheKey: diskCacheKey, programId: program)) {
3339 qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
3340 program, diskCacheKey.constData());
3341 result = QRhiGles2::DiskCacheHit;
3342 }
3343 }
3344
3345 if (cacheKey)
3346 *cacheKey = diskCacheKey;
3347
3348 return result;
3349}
3350
3351void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
3352{
3353 if (isProgramBinaryDiskCacheEnabled()) {
3354 qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
3355 program, cacheKey.constData());
3356 qrhi_programBinaryCache()->save(cacheKey, programId: program);
3357 }
3358}
3359
3360QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
3361 : QRhiBuffer(rhi, type, usage, size)
3362{
3363}
3364
3365QGles2Buffer::~QGles2Buffer()
3366{
3367 release();
3368}
3369
3370void QGles2Buffer::release()
3371{
3372 if (!buffer)
3373 return;
3374
3375 QRhiGles2::DeferredReleaseEntry e;
3376 e.type = QRhiGles2::DeferredReleaseEntry::Buffer;
3377
3378 e.buffer.buffer = buffer;
3379
3380 buffer = 0;
3381
3382 QRHI_RES_RHI(QRhiGles2);
3383 rhiD->releaseQueue.append(t: e);
3384 QRHI_PROF;
3385 QRHI_PROF_F(releaseBuffer(this));
3386 rhiD->unregisterResource(res: this);
3387}
3388
3389bool QGles2Buffer::build()
3390{
3391 if (buffer)
3392 release();
3393
3394 QRHI_RES_RHI(QRhiGles2);
3395 QRHI_PROF;
3396
3397 const int nonZeroSize = m_size <= 0 ? 256 : m_size;
3398
3399 if (m_usage.testFlag(flag: QRhiBuffer::UniformBuffer)) {
3400 if (int(m_usage) != QRhiBuffer::UniformBuffer) {
3401 qWarning(msg: "Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
3402 return false;
3403 }
3404 ubuf.resize(size: nonZeroSize);
3405 QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), 0, 1));
3406 return true;
3407 }
3408
3409 if (!rhiD->ensureContext())
3410 return false;
3411
3412 targetForDataOps = GL_ARRAY_BUFFER;
3413 if (m_usage.testFlag(flag: QRhiBuffer::IndexBuffer))
3414 targetForDataOps = GL_ELEMENT_ARRAY_BUFFER;
3415 else if (m_usage.testFlag(flag: QRhiBuffer::StorageBuffer))
3416 targetForDataOps = GL_SHADER_STORAGE_BUFFER;
3417
3418 rhiD->f->glGenBuffers(n: 1, buffers: &buffer);
3419 rhiD->f->glBindBuffer(target: targetForDataOps, buffer);
3420 rhiD->f->glBufferData(target: targetForDataOps, size: nonZeroSize, data: nullptr, usage: m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
3421
3422 usageState.access = AccessNone;
3423
3424 QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), 1, 0));
3425 rhiD->registerResource(res: this);
3426 return true;
3427}
3428
3429QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer()
3430{
3431 if (m_usage.testFlag(flag: QRhiBuffer::UniformBuffer))
3432 return { .objects: {}, .slotCount: 0 };
3433
3434 return { .objects: { &buffer }, .slotCount: 1 };
3435}
3436
3437QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
3438 int sampleCount, QRhiRenderBuffer::Flags flags)
3439 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
3440{
3441}
3442
3443QGles2RenderBuffer::~QGles2RenderBuffer()
3444{
3445 release();
3446}
3447
3448void QGles2RenderBuffer::release()
3449{
3450 if (!renderbuffer)
3451 return;
3452
3453 QRhiGles2::DeferredReleaseEntry e;
3454 e.type = QRhiGles2::DeferredReleaseEntry::RenderBuffer;
3455
3456 e.renderbuffer.renderbuffer = renderbuffer;
3457 e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
3458
3459 renderbuffer = 0;
3460 stencilRenderbuffer = 0;
3461
3462 QRHI_RES_RHI(QRhiGles2);
3463 rhiD->releaseQueue.append(t: e);
3464 QRHI_PROF;
3465 QRHI_PROF_F(releaseRenderBuffer(this));
3466 rhiD->unregisterResource(res: this);
3467}
3468
3469bool QGles2RenderBuffer::build()
3470{
3471 if (renderbuffer)
3472 release();
3473
3474 QRHI_RES_RHI(QRhiGles2);
3475 QRHI_PROF;
3476 samples = rhiD->effectiveSampleCount(sampleCount: m_sampleCount);
3477
3478 if (m_flags.testFlag(flag: UsedWithSwapChainOnly)) {
3479 if (m_type == DepthStencil) {
3480 QRHI_PROF_F(newRenderBuffer(this, false, true, samples));
3481 return true;
3482 }
3483
3484 qWarning(msg: "RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
3485 }
3486
3487 if (!rhiD->ensureContext())
3488 return false;
3489
3490 rhiD->f->glGenRenderbuffers(n: 1, renderbuffers: &renderbuffer);
3491 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
3492
3493 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
3494
3495 switch (m_type) {
3496 case QRhiRenderBuffer::DepthStencil:
3497 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
3498 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
3499 width: size.width(), height: size.height());
3500 stencilRenderbuffer = 0;
3501 } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
3502 const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
3503 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalformat: storage,
3504 width: size.width(), height: size.height());
3505 stencilRenderbuffer = 0;
3506 } else {
3507 GLenum depthStorage = GL_DEPTH_COMPONENT;
3508 if (rhiD->caps.gles) {
3509 if (rhiD->caps.depth24)
3510 depthStorage = GL_DEPTH_COMPONENT24;
3511 else
3512 depthStorage = GL_DEPTH_COMPONENT16; // plain ES 2.0 only has this
3513 }
3514 const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
3515 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalformat: depthStorage,
3516 width: size.width(), height: size.height());
3517 rhiD->f->glGenRenderbuffers(n: 1, renderbuffers: &stencilRenderbuffer);
3518 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: stencilRenderbuffer);
3519 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalformat: stencilStorage,
3520 width: size.width(), height: size.height());
3521 }
3522 QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
3523 break;
3524 case QRhiRenderBuffer::Color:
3525 if (rhiD->caps.msaaRenderBuffer && samples > 1)
3526 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8,
3527 width: size.width(), height: size.height());
3528 else
3529 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalformat: rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA4,
3530 width: size.width(), height: size.height());
3531 QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
3532 break;
3533 default:
3534 Q_UNREACHABLE();
3535 break;
3536 }
3537
3538 rhiD->registerResource(res: this);
3539 return true;
3540}
3541
3542QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
3543{
3544 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3545}
3546
3547QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
3548 int sampleCount, Flags flags)
3549 : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
3550{
3551}
3552
3553QGles2Texture::~QGles2Texture()
3554{
3555 release();
3556}
3557
3558void QGles2Texture::release()
3559{
3560 if (!texture)
3561 return;
3562
3563 QRhiGles2::DeferredReleaseEntry e;
3564 e.type = QRhiGles2::DeferredReleaseEntry::Texture;
3565
3566 e.texture.texture = texture;
3567
3568 texture = 0;
3569 specified = false;
3570
3571 QRHI_RES_RHI(QRhiGles2);
3572 if (owns)
3573 rhiD->releaseQueue.append(t: e);
3574 QRHI_PROF;
3575 QRHI_PROF_F(releaseTexture(this));
3576 rhiD->unregisterResource(res: this);
3577}
3578
3579bool QGles2Texture::prepareBuild(QSize *adjustedSize)
3580{
3581 if (texture)
3582 release();
3583
3584 QRHI_RES_RHI(QRhiGles2);
3585 if (!rhiD->ensureContext())
3586 return false;
3587
3588 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
3589
3590 const bool isCube = m_flags.testFlag(flag: CubeMap);
3591 const bool hasMipMaps = m_flags.testFlag(flag: MipMapped);
3592 const bool isCompressed = rhiD->isCompressedFormat(format: m_format);
3593
3594 target = isCube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
3595 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3596 gltype = GL_UNSIGNED_BYTE;
3597
3598 if (isCompressed) {
3599 if (m_flags.testFlag(flag: UsedWithLoadStore)) {
3600 qWarning(msg: "Compressed texture cannot be used with image load/store");
3601 return false;
3602 }
3603 glintformat = toGlCompressedTextureFormat(format: m_format, flags: m_flags);
3604 if (!glintformat) {
3605 qWarning(msg: "Compressed format %d not mappable to GL compressed format", m_format);
3606 return false;
3607 }
3608 glsizedintformat = glintformat;
3609 glformat = GL_RGBA;
3610 } else {
3611 switch (m_format) {
3612 case QRhiTexture::RGBA8:
3613 glintformat = GL_RGBA;
3614 glsizedintformat = rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
3615 glformat = GL_RGBA;
3616 break;
3617 case QRhiTexture::BGRA8:
3618 glintformat = rhiD->caps.bgraInternalFormat ? GL_BGRA : GL_RGBA;
3619 glsizedintformat = rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
3620 glformat = GL_BGRA;
3621 break;
3622 case QRhiTexture::R16:
3623 glintformat = GL_R16;
3624 glsizedintformat = glintformat;
3625 glformat = GL_RED;
3626 gltype = GL_UNSIGNED_SHORT;
3627 break;
3628 case QRhiTexture::R8:
3629 glintformat = GL_R8;
3630 glsizedintformat = glintformat;
3631 glformat = GL_RED;
3632 break;
3633 case QRhiTexture::RED_OR_ALPHA8:
3634 glintformat = rhiD->caps.coreProfile ? GL_R8 : GL_ALPHA;
3635 glsizedintformat = glintformat;
3636 glformat = rhiD->caps.coreProfile ? GL_RED : GL_ALPHA;
3637 break;
3638 case QRhiTexture::RGBA16F:
3639 glintformat = GL_RGBA16F;
3640 glsizedintformat = glintformat;
3641 glformat = GL_RGBA;
3642 gltype = GL_HALF_FLOAT;
3643 break;
3644 case QRhiTexture::RGBA32F:
3645 glintformat = GL_RGBA32F;
3646 glsizedintformat = glintformat;
3647 glformat = GL_RGBA;
3648 gltype = GL_FLOAT;
3649 break;
3650 case QRhiTexture::R16F:
3651 glintformat = GL_R16F;
3652 glsizedintformat = glintformat;
3653 glformat = GL_RED;
3654 gltype = GL_HALF_FLOAT;
3655 break;
3656 case QRhiTexture::R32F:
3657 glintformat = GL_R32F;
3658 glsizedintformat = glintformat;
3659 glformat = GL_RED;
3660 gltype = GL_FLOAT;
3661 break;
3662 case QRhiTexture::D16:
3663 glintformat = GL_DEPTH_COMPONENT16;
3664 glsizedintformat = glintformat;
3665 glformat = GL_DEPTH_COMPONENT;
3666 gltype = GL_UNSIGNED_SHORT;
3667 break;
3668 case QRhiTexture::D32F:
3669 glintformat = GL_DEPTH_COMPONENT32F;
3670 glsizedintformat = glintformat;
3671 glformat = GL_DEPTH_COMPONENT;
3672 gltype = GL_FLOAT;
3673 break;
3674 default:
3675 Q_UNREACHABLE();
3676 glintformat = GL_RGBA;
3677 glsizedintformat = rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
3678 glformat = GL_RGBA;
3679 break;
3680 }
3681 }
3682
3683 samplerState = QGles2SamplerData();
3684
3685 usageState.access = AccessNone;
3686
3687 if (adjustedSize)
3688 *adjustedSize = size;
3689
3690 return true;
3691}
3692
3693bool QGles2Texture::build()
3694{
3695 QSize size;
3696 if (!prepareBuild(adjustedSize: &size))
3697 return false;
3698
3699 QRHI_RES_RHI(QRhiGles2);
3700 rhiD->f->glGenTextures(n: 1, textures: &texture);
3701
3702 const bool isCube = m_flags.testFlag(flag: CubeMap);
3703 const bool hasMipMaps = m_flags.testFlag(flag: MipMapped);
3704 const bool isCompressed = rhiD->isCompressedFormat(format: m_format);
3705 if (!isCompressed) {
3706 rhiD->f->glBindTexture(target, texture);
3707 if (!m_flags.testFlag(flag: UsedWithLoadStore)) {
3708 if (hasMipMaps || isCube) {
3709 const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
3710 for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
3711 for (int level = 0; level != mipLevelCount; ++level) {
3712 const QSize mipSize = rhiD->q->sizeForMipLevel(mipLevel: level, baseLevelSize: size);
3713 rhiD->f->glTexImage2D(target: faceTargetBase + uint(layer), level, internalformat: GLint(glintformat),
3714 width: mipSize.width(), height: mipSize.height(), border: 0,
3715 format: glformat, type: gltype, pixels: nullptr);
3716 }
3717 }
3718 } else {
3719 rhiD->f->glTexImage2D(target, level: 0, internalformat: GLint(glintformat), width: size.width(), height: size.height(),
3720 border: 0, format: glformat, type: gltype, pixels: nullptr);
3721 }
3722 } else {
3723 // Must be specified with immutable storage functions otherwise
3724 // bindImageTexture may fail. Also, the internal format must be a
3725 // sized format here.
3726 rhiD->f->glTexStorage2D(target, levels: mipLevelCount, internalformat: glsizedintformat, width: size.width(), height: size.height());
3727 }
3728 specified = true;
3729 } else {
3730 // Cannot use glCompressedTexImage2D without valid data, so defer.
3731 // Compressed textures will not be used as render targets so this is
3732 // not an issue.
3733 specified = false;
3734 }
3735
3736 QRHI_PROF;
3737 QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
3738
3739 owns = true;
3740
3741 generation += 1;
3742 rhiD->registerResource(res: this);
3743 return true;
3744}
3745
3746bool QGles2Texture::buildFrom(QRhiTexture::NativeTexture src)
3747{
3748 const uint *textureId = static_cast<const uint *>(src.object);
3749 if (!textureId || !*textureId)
3750 return false;
3751
3752 if (!prepareBuild())
3753 return false;
3754
3755 texture = *textureId;
3756 specified = true;
3757
3758 QRHI_RES_RHI(QRhiGles2);
3759 QRHI_PROF;
3760 QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1));
3761
3762 owns = false;
3763
3764 generation += 1;
3765 rhiD->registerResource(res: this);
3766 return true;
3767}
3768
3769QRhiTexture::NativeTexture QGles2Texture::nativeTexture()
3770{
3771 return {.object: &texture, .layout: 0};
3772}
3773
3774QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
3775 AddressMode u, AddressMode v, AddressMode w)
3776 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
3777{
3778}
3779
3780QGles2Sampler::~QGles2Sampler()
3781{
3782 release();
3783}
3784
3785void QGles2Sampler::release()
3786{
3787 // nothing to do here
3788}
3789
3790bool QGles2Sampler::build()
3791{
3792 d.glminfilter = toGlMinFilter(f: m_minFilter, m: m_mipmapMode);
3793 d.glmagfilter = toGlMagFilter(f: m_magFilter);
3794 d.glwraps = toGlWrapMode(m: m_addressU);
3795 d.glwrapt = toGlWrapMode(m: m_addressV);
3796 d.glwrapr = toGlWrapMode(m: m_addressW);
3797 d.gltexcomparefunc = toGlTextureCompareFunc(op: m_compareOp);
3798
3799 generation += 1;
3800 return true;
3801}
3802
3803// dummy, no Vulkan-style RenderPass+Framebuffer concept here
3804QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi)
3805 : QRhiRenderPassDescriptor(rhi)
3806{
3807}
3808
3809QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
3810{
3811 release();
3812}
3813
3814void QGles2RenderPassDescriptor::release()
3815{
3816 // nothing to do here
3817}
3818
3819bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
3820{
3821 Q_UNUSED(other);
3822 return true;
3823}
3824
3825QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
3826 : QRhiRenderTarget(rhi),
3827 d(rhi)
3828{
3829}
3830
3831QGles2ReferenceRenderTarget::~QGles2ReferenceRenderTarget()
3832{
3833 release();
3834}
3835
3836void QGles2ReferenceRenderTarget::release()
3837{
3838 // nothing to do here
3839}
3840
3841QSize QGles2ReferenceRenderTarget::pixelSize() const
3842{
3843 return d.pixelSize;
3844}
3845
3846float QGles2ReferenceRenderTarget::devicePixelRatio() const
3847{
3848 return d.dpr;
3849}
3850
3851int QGles2ReferenceRenderTarget::sampleCount() const
3852{
3853 return d.sampleCount;
3854}
3855
3856QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi,
3857 const QRhiTextureRenderTargetDescription &desc,
3858 Flags flags)
3859 : QRhiTextureRenderTarget(rhi, desc, flags),
3860 d(rhi)
3861{
3862}
3863
3864QGles2TextureRenderTarget::~QGles2TextureRenderTarget()
3865{
3866 release();
3867}
3868
3869void QGles2TextureRenderTarget::release()
3870{
3871 if (!framebuffer)
3872 return;
3873
3874 QRhiGles2::DeferredReleaseEntry e;
3875 e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget;
3876
3877 e.textureRenderTarget.framebuffer = framebuffer;
3878
3879 framebuffer = 0;
3880
3881 QRHI_RES_RHI(QRhiGles2);
3882 rhiD->releaseQueue.append(t: e);
3883
3884 rhiD->unregisterResource(res: this);
3885}
3886
3887QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
3888{
3889 return new QGles2RenderPassDescriptor(m_rhi);
3890}
3891
3892bool QGles2TextureRenderTarget::build()
3893{
3894 QRHI_RES_RHI(QRhiGles2);
3895
3896 if (framebuffer)
3897 release();
3898
3899 const bool hasColorAttachments = m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments();
3900 Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
3901 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
3902 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
3903
3904 if (hasColorAttachments) {
3905 const int count = m_desc.cendColorAttachments() - m_desc.cbeginColorAttachments();
3906 if (count > rhiD->caps.maxDrawBuffers) {
3907 qWarning(msg: "QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
3908 count, rhiD->caps.maxDrawBuffers);
3909 }
3910 }
3911 if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
3912 qWarning(msg: "QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
3913
3914 if (!rhiD->ensureContext())
3915 return false;
3916
3917 rhiD->f->glGenFramebuffers(n: 1, framebuffers: &framebuffer);
3918 rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3919
3920 d.colorAttCount = 0;
3921 int attIndex = 0;
3922 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
3923 d.colorAttCount += 1;
3924 const QRhiColorAttachment &colorAtt(*it);
3925 QRhiTexture *texture = colorAtt.texture();
3926 QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
3927 Q_ASSERT(texture || renderBuffer);
3928 if (texture) {
3929 QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
3930 Q_ASSERT(texD->texture && texD->specified);
3931 const GLenum faceTargetBase = texD->flags().testFlag(flag: QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
3932 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), textarget: faceTargetBase + uint(colorAtt.layer()),
3933 texture: texD->texture, level: colorAtt.level());
3934 if (attIndex == 0) {
3935 d.pixelSize = texD->pixelSize();
3936 d.sampleCount = 1;
3937 }
3938 } else if (renderBuffer) {
3939 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
3940 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, renderbuffer: rbD->renderbuffer);
3941 if (attIndex == 0) {
3942 d.pixelSize = rbD->pixelSize();
3943 d.sampleCount = rbD->samples;
3944 }
3945 }
3946 }
3947
3948 if (hasDepthStencil) {
3949 if (m_desc.depthStencilBuffer()) {
3950 QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer());
3951 if (rhiD->caps.needsDepthStencilCombinedAttach) {
3952 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
3953 renderbuffer: depthRbD->renderbuffer);
3954 } else {
3955 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
3956 renderbuffer: depthRbD->renderbuffer);
3957 if (depthRbD->stencilRenderbuffer)
3958 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
3959 renderbuffer: depthRbD->stencilRenderbuffer);
3960 else // packed
3961 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
3962 renderbuffer: depthRbD->renderbuffer);
3963 }
3964 if (d.colorAttCount == 0) {
3965 d.pixelSize = depthRbD->pixelSize();
3966 d.sampleCount = depthRbD->samples;
3967 }
3968 } else {
3969 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
3970 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture: depthTexD->texture, level: 0);
3971 if (d.colorAttCount == 0) {
3972 d.pixelSize = depthTexD->pixelSize();
3973 d.sampleCount = 1;
3974 }
3975 }
3976 d.dsAttCount = 1;
3977 } else {
3978 d.dsAttCount = 0;
3979 }
3980
3981 d.dpr = 1;
3982 d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
3983
3984 GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
3985 if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
3986 qWarning(msg: "Framebuffer incomplete: 0x%x", status);
3987 return false;
3988 }
3989
3990 rhiD->registerResource(res: this);
3991 return true;
3992}
3993
3994QSize QGles2TextureRenderTarget::pixelSize() const
3995{
3996 return d.pixelSize;
3997}
3998
3999float QGles2TextureRenderTarget::devicePixelRatio() const
4000{
4001 return d.dpr;
4002}
4003
4004int QGles2TextureRenderTarget::sampleCount() const
4005{
4006 return d.sampleCount;
4007}
4008
4009QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi)
4010 : QRhiShaderResourceBindings(rhi)
4011{
4012}
4013
4014QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
4015{
4016 release();
4017}
4018
4019void QGles2ShaderResourceBindings::release()
4020{
4021 // nothing to do here
4022}
4023
4024bool QGles2ShaderResourceBindings::build()
4025{
4026 generation += 1;
4027 return true;
4028}
4029
4030QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi)
4031 : QRhiGraphicsPipeline(rhi)
4032{
4033}
4034
4035QGles2GraphicsPipeline::~QGles2GraphicsPipeline()
4036{
4037 release();
4038}
4039
4040void QGles2GraphicsPipeline::release()
4041{
4042 if (!program)
4043 return;
4044
4045 QRhiGles2::DeferredReleaseEntry e;
4046 e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
4047
4048 e.pipeline.program = program;
4049
4050 program = 0;
4051 uniforms.clear();
4052 samplers.clear();
4053
4054 QRHI_RES_RHI(QRhiGles2);
4055 rhiD->releaseQueue.append(t: e);
4056
4057 rhiD->unregisterResource(res: this);
4058}
4059
4060bool QGles2GraphicsPipeline::build()
4061{
4062 QRHI_RES_RHI(QRhiGles2);
4063
4064 if (program)
4065 release();
4066
4067 if (!rhiD->ensureContext())
4068 return false;
4069
4070 if (!rhiD->sanityCheckGraphicsPipeline(ps: this))
4071 return false;
4072
4073 drawMode = toGlTopology(t: m_topology);
4074
4075 program = rhiD->f->glCreateProgram();
4076
4077 QByteArray diskCacheKey;
4078 QRhiGles2::DiskCacheResult diskCacheResult = rhiD->tryLoadFromDiskCache(stages: m_shaderStages.constData(),
4079 stageCount: m_shaderStages.count(),
4080 program,
4081 cacheKey: &diskCacheKey);
4082 if (diskCacheResult == QRhiGles2::DiskCacheError)
4083 return false;
4084
4085 const bool needsCompile = diskCacheResult == QRhiGles2::DiskCacheMiss;
4086
4087 QShaderDescription vsDesc;
4088 QShaderDescription fsDesc;
4089 for (const QRhiShaderStage &shaderStage : qAsConst(t&: m_shaderStages)) {
4090 const bool isVertex = shaderStage.type() == QRhiShaderStage::Vertex;
4091 const bool isFragment = shaderStage.type() == QRhiShaderStage::Fragment;
4092 if (isVertex) {
4093 if (needsCompile && !rhiD->compileShader(program, shaderStage, glslVersion: nullptr))
4094 return false;
4095 vsDesc = shaderStage.shader().description();
4096 } else if (isFragment) {
4097 if (needsCompile && !rhiD->compileShader(program, shaderStage, glslVersion: nullptr))
4098 return false;
4099 fsDesc = shaderStage.shader().description();
4100 }
4101 }
4102
4103 for (auto inVar : vsDesc.inputVariables()) {
4104 const QByteArray name = inVar.name.toUtf8();
4105 rhiD->f->glBindAttribLocation(program, index: GLuint(inVar.location), name: name.constData());
4106 }
4107
4108 if (needsCompile && !rhiD->linkProgram(program))
4109 return false;
4110
4111 if (needsCompile)
4112 rhiD->trySaveToDiskCache(program, cacheKey: diskCacheKey);
4113
4114 for (const QShaderDescription::UniformBlock &ub : vsDesc.uniformBlocks())
4115 rhiD->gatherUniforms(program, ub, dst: &uniforms);
4116
4117 for (const QShaderDescription::UniformBlock &ub : fsDesc.uniformBlocks())
4118 rhiD->gatherUniforms(program, ub, dst: &uniforms);
4119
4120 for (const QShaderDescription::InOutVariable &v : vsDesc.combinedImageSamplers())
4121 rhiD->gatherSamplers(program, v, dst: &samplers);
4122
4123 for (const QShaderDescription::InOutVariable &v : fsDesc.combinedImageSamplers())
4124 rhiD->gatherSamplers(program, v, dst: &samplers);
4125
4126 generation += 1;
4127 rhiD->registerResource(res: this);
4128 return true;
4129}
4130
4131QGles2ComputePipeline::QGles2ComputePipeline(QRhiImplementation *rhi)
4132 : QRhiComputePipeline(rhi)
4133{
4134}
4135
4136QGles2ComputePipeline::~QGles2ComputePipeline()
4137{
4138 release();
4139}
4140
4141void QGles2ComputePipeline::release()
4142{
4143 if (!program)
4144 return;
4145
4146 QRhiGles2::DeferredReleaseEntry e;
4147 e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
4148
4149 e.pipeline.program = program;
4150
4151 program = 0;
4152 uniforms.clear();
4153 samplers.clear();
4154
4155 QRHI_RES_RHI(QRhiGles2);
4156 rhiD->releaseQueue.append(t: e);
4157
4158 rhiD->unregisterResource(res: this);
4159}
4160
4161bool QGles2ComputePipeline::build()
4162{
4163 QRHI_RES_RHI(QRhiGles2);
4164
4165 if (program)
4166 release();
4167
4168 if (!rhiD->ensureContext())
4169 return false;
4170
4171 program = rhiD->f->glCreateProgram();
4172 QShaderDescription csDesc;
4173
4174 QByteArray diskCacheKey;
4175 QRhiGles2::DiskCacheResult diskCacheResult = rhiD->tryLoadFromDiskCache(stages: &m_shaderStage, stageCount: 1, program, cacheKey: &diskCacheKey);
4176 if (diskCacheResult == QRhiGles2::DiskCacheError)
4177 return false;
4178
4179 const bool needsCompile = diskCacheResult == QRhiGles2::DiskCacheMiss;
4180
4181 if (needsCompile && !rhiD->compileShader(program, shaderStage: m_shaderStage, glslVersion: nullptr))
4182 return false;
4183
4184 csDesc = m_shaderStage.shader().description();
4185
4186 if (needsCompile && !rhiD->linkProgram(program))
4187 return false;
4188
4189 if (needsCompile)
4190 rhiD->trySaveToDiskCache(program, cacheKey: diskCacheKey);
4191
4192 for (const QShaderDescription::UniformBlock &ub : csDesc.uniformBlocks())
4193 rhiD->gatherUniforms(program, ub, dst: &uniforms);
4194 for (const QShaderDescription::InOutVariable &v : csDesc.combinedImageSamplers())
4195 rhiD->gatherSamplers(program, v, dst: &samplers);
4196
4197 // storage images and buffers need no special steps here
4198
4199 generation += 1;
4200 rhiD->registerResource(res: this);
4201 return true;
4202}
4203
4204QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
4205 : QRhiCommandBuffer(rhi)
4206{
4207 resetState();
4208}
4209
4210QGles2CommandBuffer::~QGles2CommandBuffer()
4211{
4212 release();
4213}
4214
4215void QGles2CommandBuffer::release()
4216{
4217 // nothing to do here
4218}
4219
4220QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
4221 : QRhiSwapChain(rhi),
4222 rt(rhi),
4223 cb(rhi)
4224{
4225}
4226
4227QGles2SwapChain::~QGles2SwapChain()
4228{
4229 release();
4230}
4231
4232void QGles2SwapChain::release()
4233{
4234 QRHI_PROF;
4235 QRHI_PROF_F(releaseSwapChain(this));
4236}
4237
4238QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
4239{
4240 return &cb;
4241}
4242
4243QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
4244{
4245 return &rt;
4246}
4247
4248QSize QGles2SwapChain::surfacePixelSize()
4249{
4250 Q_ASSERT(m_window);
4251 return m_window->size() * m_window->devicePixelRatio();
4252}
4253
4254QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
4255{
4256 return new QGles2RenderPassDescriptor(m_rhi);
4257}
4258
4259bool QGles2SwapChain::buildOrResize()
4260{
4261 surface = m_window;
4262 m_currentPixelSize = surfacePixelSize();
4263 pixelSize = m_currentPixelSize;
4264
4265 if (m_depthStencil && m_depthStencil->flags().testFlag(flag: QRhiRenderBuffer::UsedWithSwapChainOnly)
4266 && m_depthStencil->pixelSize() != pixelSize)
4267 {
4268 m_depthStencil->setPixelSize(pixelSize);
4269 m_depthStencil->build();
4270 }
4271
4272 rt.d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
4273 rt.d.pixelSize = pixelSize;
4274 rt.d.dpr = float(m_window->devicePixelRatio());
4275 rt.d.sampleCount = qBound(min: 1, val: m_sampleCount, max: 64);
4276 rt.d.colorAttCount = 1;
4277 rt.d.dsAttCount = m_depthStencil ? 1 : 0;
4278 rt.d.srgbUpdateAndBlend = m_flags.testFlag(flag: QRhiSwapChain::sRGB);
4279
4280 frameCount = 0;
4281
4282 QRHI_PROF;
4283 // make something up
4284 QRHI_PROF_F(resizeSwapChain(this, 2, m_sampleCount > 1 ? 2 : 0, m_sampleCount));
4285
4286 return true;
4287}
4288
4289QT_END_NAMESPACE
4290

source code of qtbase/src/gui/rhi/qrhigles2.cpp