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 
45 QT_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 
Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)292 Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)
293 
294 /*!
295     Constructs a new QRhiGles2InitParams.
296 
297     \l format is set to QSurfaceFormat::defaultFormat().
298  */
299 QRhiGles2InitParams::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  */
adjustedFormat(const QSurfaceFormat & format)311 QSurfaceFormat 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  */
newFallbackSurface(const QSurfaceFormat & format)336 QOffscreenSurface *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("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 
QRhiGles2(QRhiGles2InitParams * params,QRhiGles2NativeHandles * importDevice)356 QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
357     : ofr(this)
358 {
359     requestedFormat = QRhiGles2InitParams::adjustedFormat(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("No OpenGL context given, cannot import");
368             importedContext = false;
369         }
370     }
371 }
372 
ensureContext(QSurface * surface) const373 bool 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("QRhiGles2: Failed to make context current. Expect bad things to happen.");
392         } else {
393             qWarning("QRhiGles2: Context is lost.");
394             contextLost = true;
395         }
396         return false;
397     }
398 
399     return true;
400 }
401 
create(QRhi::Flags flags)402 bool 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("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(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, &n);
437     supportedCompressedFormats.resize(n);
438     if (n > 0)
439         f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, supportedCompressedFormats.data());
440 
441     f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
442 
443     if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) {
444         f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
445         f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
446         caps.maxSamples = qMax(1, caps.maxSamples);
447     } else {
448         caps.maxDrawBuffers = 1;
449         caps.maxSamples = 1;
450     }
451 
452     caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
453             && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
454 
455     caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
456             && f->hasOpenGLFeature(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(QOpenGLExtensions::BGRATextureFormat);
468     caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
469     caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
470     caps.r16Format = f->hasOpenGLExtension(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(QOpenGLExtensions::PackedDepthStencil);
474 #ifdef Q_OS_WASM
475     caps.needsDepthStencilCombinedAttach = true;
476 #else
477     caps.needsDepthStencilCombinedAttach = false;
478 #endif
479     caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(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(QOpenGLExtensions::ElementIndexUint);
488     caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
489     caps.rgba8Format = f->hasOpenGLExtension(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(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 
destroy()531 void QRhiGles2::destroy()
532 {
533     if (!f)
534         return;
535 
536     ensureContext();
537     executeDeferredReleases();
538 
539     if (vao) {
540         f->glDeleteVertexArrays(1, &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 
executeDeferredReleases()556 void 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(1, &e.buffer.buffer);
563             break;
564         case QRhiGles2::DeferredReleaseEntry::Pipeline:
565             f->glDeleteProgram(e.pipeline.program);
566             break;
567         case QRhiGles2::DeferredReleaseEntry::Texture:
568             f->glDeleteTextures(1, &e.texture.texture);
569             break;
570         case QRhiGles2::DeferredReleaseEntry::RenderBuffer:
571             f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
572             f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
573             break;
574         case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget:
575             f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
576             break;
577         default:
578             Q_UNREACHABLE();
579             break;
580         }
581         releaseQueue.removeAt(i);
582     }
583 }
584 
supportedSampleCounts() const585 QVector<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(i);
591     }
592     return supportedSampleCountList;
593 }
594 
effectiveSampleCount(int sampleCount) const595 int 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(1, sampleCount, 64);
599     if (!supportedSampleCounts().contains(s)) {
600         qWarning("Attempted to set unsupported sample count %d", sampleCount);
601         return 1;
602     }
603     return s;
604 }
605 
createSwapChain()606 QRhiSwapChain *QRhiGles2::createSwapChain()
607 {
608     return new QGles2SwapChain(this);
609 }
610 
createBuffer(QRhiBuffer::Type type,QRhiBuffer::UsageFlags usage,int size)611 QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
612 {
613     return new QGles2Buffer(this, type, usage, size);
614 }
615 
ubufAlignment() const616 int 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 
isYUpInFramebuffer() const623 bool QRhiGles2::isYUpInFramebuffer() const
624 {
625     return true;
626 }
627 
isYUpInNDC() const628 bool QRhiGles2::isYUpInNDC() const
629 {
630     return true;
631 }
632 
isClipDepthZeroToOne() const633 bool QRhiGles2::isClipDepthZeroToOne() const
634 {
635     return false;
636 }
637 
clipSpaceCorrMatrix() const638 QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const
639 {
640     return QMatrix4x4(); // identity
641 }
642 
toGlCompressedTextureFormat(QRhiTexture::Format format,QRhiTexture::Flags flags)643 static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
644 {
645     const bool srgb = flags.testFlag(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 
isTextureFormatSupported(QRhiTexture::Format format,QRhiTexture::Flags flags) const695 bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
696 {
697     if (isCompressedFormat(format))
698         return supportedCompressedFormats.contains(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 
isFeatureSupported(QRhi::Feature feature) const729 bool 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 
resourceLimit(QRhi::ResourceLimit limit) const780 int 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 
nativeHandles()801 const QRhiNativeHandles *QRhiGles2::nativeHandles()
802 {
803     return &nativeHandlesStruct;
804 }
805 
sendVMemStatsToProfiler()806 void QRhiGles2::sendVMemStatsToProfiler()
807 {
808     // nothing to do here
809 }
810 
makeThreadLocalNativeContextCurrent()811 bool QRhiGles2::makeThreadLocalNativeContextCurrent()
812 {
813     if (inFrame && !ofr.active)
814         return ensureContext(currentSwapChain->surface);
815     else
816         return ensureContext();
817 }
818 
releaseCachedResources()819 void 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 
isDeviceLost() const830 bool QRhiGles2::isDeviceLost() const
831 {
832     return contextLost;
833 }
834 
createRenderBuffer(QRhiRenderBuffer::Type type,const QSize & pixelSize,int sampleCount,QRhiRenderBuffer::Flags flags)835 QRhiRenderBuffer *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 
createTexture(QRhiTexture::Format format,const QSize & pixelSize,int sampleCount,QRhiTexture::Flags flags)841 QRhiTexture *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 
createSampler(QRhiSampler::Filter magFilter,QRhiSampler::Filter minFilter,QRhiSampler::Filter mipmapMode,QRhiSampler::AddressMode u,QRhiSampler::AddressMode v,QRhiSampler::AddressMode w)847 QRhiSampler *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 
createTextureRenderTarget(const QRhiTextureRenderTargetDescription & desc,QRhiTextureRenderTarget::Flags flags)854 QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
855                                                               QRhiTextureRenderTarget::Flags flags)
856 {
857     return new QGles2TextureRenderTarget(this, desc, flags);
858 }
859 
createGraphicsPipeline()860 QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline()
861 {
862     return new QGles2GraphicsPipeline(this);
863 }
864 
createShaderResourceBindings()865 QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings()
866 {
867     return new QGles2ShaderResourceBindings(this);
868 }
869 
createComputePipeline()870 QRhiComputePipeline *QRhiGles2::createComputePipeline()
871 {
872     return new QGles2ComputePipeline(this);
873 }
874 
setGraphicsPipeline(QRhiCommandBuffer * cb,QRhiGraphicsPipeline * ps)875 void 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(cmd);
891     }
892 }
893 
setShaderResources(QRhiCommandBuffer * cb,QRhiShaderResourceBindings * srb,int dynamicOffsetCount,const QRhiCommandBuffer::DynamicOffset * dynamicOffsets)894 void 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(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,
924                                        QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
925                                        QRhiPassResourceTracker::TexSample,
926                                        QRhiPassResourceTracker::toPassTrackerTextureStage(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, texD, access,
942                                    QRhiPassResourceTracker::toPassTrackerTextureStage(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, bufD, access,
958                                   QRhiPassResourceTracker::toPassTrackerBufferStage(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("Too many dynamic offsets (%d, max is %d)",
996                          dynamicOffsetCount, QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS);
997             }
998         }
999         cbD->commands.append(cmd);
1000     }
1001 }
1002 
setVertexInput(QRhiCommandBuffer * cb,int startBinding,int bindingCount,const QRhiCommandBuffer::VertexInput * bindings,QRhiBuffer * indexBuf,quint32 indexOffset,QRhiCommandBuffer::IndexFormat indexFormat)1003 void 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(cmd);
1024 
1025         trackedRegisterBuffer(&passResTracker, bufD, QRhiPassResourceTracker::BufVertexInput,
1026                               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(cmd);
1039 
1040         trackedRegisterBuffer(&passResTracker, ibufD, QRhiPassResourceTracker::BufIndexRead,
1041                               QRhiPassResourceTracker::BufVertexInputStage);
1042     }
1043 }
1044 
setViewport(QRhiCommandBuffer * cb,const QRhiViewport & viewport)1045 void 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(cmd);
1064 }
1065 
setScissor(QRhiCommandBuffer * cb,const QRhiScissor & scissor)1066 void 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(cmd);
1083 }
1084 
setBlendConstants(QRhiCommandBuffer * cb,const QColor & c)1085 void 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(cmd);
1097 }
1098 
setStencilRef(QRhiCommandBuffer * cb,quint32 refValue)1099 void 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(cmd);
1109 }
1110 
draw(QRhiCommandBuffer * cb,quint32 vertexCount,quint32 instanceCount,quint32 firstVertex,quint32 firstInstance)1111 void 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(cmd);
1125 }
1126 
drawIndexed(QRhiCommandBuffer * cb,quint32 indexCount,quint32 instanceCount,quint32 firstIndex,qint32 vertexOffset,quint32 firstInstance)1127 void 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(cmd);
1142 }
1143 
debugMarkBegin(QRhiCommandBuffer * cb,const QByteArray & name)1144 void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
1145 {
1146     if (!debugMarkers)
1147         return;
1148 
1149     Q_UNUSED(cb);
1150     Q_UNUSED(name);
1151 }
1152 
debugMarkEnd(QRhiCommandBuffer * cb)1153 void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
1154 {
1155     if (!debugMarkers)
1156         return;
1157 
1158     Q_UNUSED(cb);
1159 }
1160 
debugMarkMsg(QRhiCommandBuffer * cb,const QByteArray & msg)1161 void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
1162 {
1163     if (!debugMarkers)
1164         return;
1165 
1166     Q_UNUSED(cb);
1167     Q_UNUSED(msg);
1168 }
1169 
nativeHandles(QRhiCommandBuffer * cb)1170 const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
1171 {
1172     Q_UNUSED(cb);
1173     return nullptr;
1174 }
1175 
addBoundaryCommand(QGles2CommandBuffer * cbD,QGles2CommandBuffer::Command::Cmd type)1176 static void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type)
1177 {
1178     QGles2CommandBuffer::Command cmd;
1179     cmd.cmd = type;
1180     cbD->commands.append(cmd);
1181 }
1182 
beginExternal(QRhiCommandBuffer * cb)1183 void 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(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(cmd);
1204     }
1205 
1206     executeCommandBuffer(cbD);
1207 
1208     cbD->resetCommands();
1209 
1210     if (vao)
1211         f->glBindVertexArray(0);
1212 }
1213 
endExternal(QRhiCommandBuffer * cb)1214 void 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, QGles2CommandBuffer::Command::ResetFrame);
1230 
1231     if (cbD->currentTarget)
1232         enqueueBindFramebuffer(cbD->currentTarget, cbD);
1233 }
1234 
beginFrame(QRhiSwapChain * swapChain,QRhi::BeginFrameFlags flags)1235 QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
1236 {
1237     Q_UNUSED(flags);
1238 
1239     QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
1240     if (!ensureContext(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(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame);
1252 
1253     return QRhi::FrameOpSuccess;
1254 }
1255 
endFrame(QRhiSwapChain * swapChain,QRhi::EndFrameFlags flags)1256 QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1257 {
1258     QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
1259     Q_ASSERT(currentSwapChain == swapChainD);
1260 
1261     addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame);
1262 
1263     if (!ensureContext(swapChainD->surface))
1264         return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1265 
1266     executeCommandBuffer(&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(QRhi::SkipPresent)) {
1273         ctx->swapBuffers(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 
beginOffscreenFrame(QRhiCommandBuffer ** cb,QRhi::BeginFrameFlags flags)1284 QRhi::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(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame);
1296     *cb = &ofr.cbWrapper;
1297 
1298     return QRhi::FrameOpSuccess;
1299 }
1300 
endOffscreenFrame(QRhi::EndFrameFlags flags)1301 QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
1302 {
1303     Q_UNUSED(flags);
1304     Q_ASSERT(ofr.active);
1305     ofr.active = false;
1306 
1307     addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame);
1308 
1309     if (!ensureContext())
1310         return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1311 
1312     executeCommandBuffer(&ofr.cbWrapper);
1313 
1314     return QRhi::FrameOpSuccess;
1315 }
1316 
finish()1317 QRhi::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(&ofr.cbWrapper);
1326             ofr.cbWrapper.resetCommands();
1327         } else {
1328             Q_ASSERT(currentSwapChain);
1329             Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
1330             if (!ensureContext(currentSwapChain->surface))
1331                 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
1332             executeCommandBuffer(&currentSwapChain->cb);
1333             currentSwapChain->cb.resetCommands();
1334         }
1335     }
1336     return QRhi::FrameOpSuccess;
1337 }
1338 
bufferAccessIsWrite(QGles2Buffer::Access access)1339 static bool bufferAccessIsWrite(QGles2Buffer::Access access)
1340 {
1341     return access == QGles2Buffer::AccessStorageWrite
1342         || access == QGles2Buffer::AccessStorageReadWrite
1343         || access == QGles2Buffer::AccessUpdate;
1344 }
1345 
textureAccessIsWrite(QGles2Texture::Access access)1346 static 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 
trackedBufferBarrier(QGles2CommandBuffer * cbD,QGles2Buffer * bufD,QGles2Buffer::Access access)1354 void 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(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(cmd);
1370     }
1371 
1372     bufD->usageState.access = access;
1373 }
1374 
trackedImageBarrier(QGles2CommandBuffer * cbD,QGles2Texture * texD,QGles2Texture::Access access)1375 void 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(prevAccess)) {
1383         QGles2CommandBuffer::Command cmd;
1384         cmd.cmd = QGles2CommandBuffer::Command::Barrier;
1385         cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
1386         cbD->commands.append(cmd);
1387     }
1388 
1389     texD->usageState.access = access;
1390 }
1391 
enqueueSubresUpload(QGles2Texture * texD,QGles2CommandBuffer * cbD,int layer,int level,const QRhiTextureSubresourceUploadDescription & subresDesc)1392 void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
1393                                     int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
1394 {
1395     trackedImageBarrier(cbD, texD, QGles2Texture::AccessUpdate);
1396     const bool isCompressed = isCompressedFormat(texD->m_format);
1397     const bool isCubeMap = texD->m_flags.testFlag(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(sp.x(), sp.y(), size.width(), 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(img);
1424         cbD->commands.append(cmd);
1425     } else if (!rawData.isEmpty() && isCompressed) {
1426         const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, 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(rawData);
1442             cbD->commands.append(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(rawData);
1455             cbD->commands.append(cmd);
1456         }
1457     } else if (!rawData.isEmpty()) {
1458         const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1459                                                              : subresDesc.sourceSize();
1460         quint32 bpl = 0;
1461         textureFormatInfo(texD->m_format, size, &bpl, 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(rawData);
1479         cbD->commands.append(cmd);
1480     } else {
1481         qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
1482     }
1483 }
1484 
enqueueResourceUpdates(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)1485 void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1486 {
1487     QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1488     QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(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(QRhiBuffer::UniformBuffer)) {
1495                 memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size()));
1496             } else {
1497                 trackedBufferBarrier(cbD, bufD, 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(u.data);
1505                 cbD->commands.append(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(QRhiBuffer::UniformBuffer)) {
1512                 memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size()));
1513             } else {
1514                 trackedBufferBarrier(cbD, bufD, 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(u.data);
1522                 cbD->commands.append(cmd);
1523             }
1524         } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
1525             QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
1526             if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
1527                 u.result->data.resize(u.readSize);
1528                 memcpy(u.result->data.data(), bufD->ubuf.constData() + u.offset, 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(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(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, srcD, QGles2Texture::AccessRead);
1560             trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate);
1561 
1562             const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), 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(QRhiTexture::CubeMap)
1569                     ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
1570             const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(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(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, QGles2Texture::AccessRead);
1600             cmd.args.readPixels.texture = texD ? texD->texture : 0;
1601             if (texD) {
1602                 const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), 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(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(cmd);
1612         } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
1613             QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
1614             trackedImageBarrier(cbD, texD, 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(cmd);
1620         }
1621     }
1622 
1623     ud->free();
1624 }
1625 
toGlTopology(QRhiGraphicsPipeline::Topology t)1626 static 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 
toGlCullMode(QRhiGraphicsPipeline::CullMode c)1647 static 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 
toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)1660 static 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 
toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)1673 static 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("Unsupported blend factor %d", f);
1711         return GL_ZERO;
1712     default:
1713         Q_UNREACHABLE();
1714         return GL_ZERO;
1715     }
1716 }
1717 
toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)1718 static 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 
toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)1737 static 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 
toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)1762 static 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 
toGlMinFilter(QRhiSampler::Filter f,QRhiSampler::Filter m)1787 static 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 
toGlMagFilter(QRhiSampler::Filter f)1806 static 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 
toGlWrapMode(QRhiSampler::AddressMode m)1819 static 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 
toGlTextureCompareFunc(QRhiSampler::CompareOp op)1834 static 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 
toGlAccess(QRhiPassResourceTracker::BufferAccess access)1859 static 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 
toPassTrackerUsageState(const QGles2Buffer::UsageState & bufUsage)1881 static 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 
toGlAccess(QRhiPassResourceTracker::TextureAccess access)1890 static 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 
toPassTrackerUsageState(const QGles2Texture::UsageState & texUsage)1912 static 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 
trackedRegisterBuffer(QRhiPassResourceTracker * passResTracker,QGles2Buffer * bufD,QRhiPassResourceTracker::BufferAccess access,QRhiPassResourceTracker::BufferStage stage)1921 void 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(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
1928     u.access = toGlAccess(access);
1929 }
1930 
trackedRegisterTexture(QRhiPassResourceTracker * passResTracker,QGles2Texture * texD,QRhiPassResourceTracker::TextureAccess access,QRhiPassResourceTracker::TextureStage stage)1931 void 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(texD, &access, &stage, toPassTrackerUsageState(u));
1938     u.access = toGlAccess(access);
1939 }
1940 
executeCommandBuffer(QRhiCommandBuffer * cb)1941 void 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(enabledAttribArrays, 0, sizeof(enabledAttribArrays));
1951 
1952     for (const QGles2CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
1953         switch (cmd.cmd) {
1954         case QGles2CommandBuffer::Command::BeginFrame:
1955             if (caps.coreProfile) {
1956                 if (!vao)
1957                     f->glGenVertexArrays(1, &vao);
1958                 f->glBindVertexArray(vao);
1959             }
1960             break;
1961         case QGles2CommandBuffer::Command::EndFrame:
1962             if (vao)
1963                 f->glBindVertexArray(0);
1964             break;
1965         case QGles2CommandBuffer::Command::ResetFrame:
1966             if (vao)
1967                 f->glBindVertexArray(vao);
1968             break;
1969         case QGles2CommandBuffer::Command::Viewport:
1970             f->glViewport(GLint(cmd.args.viewport.x), GLint(cmd.args.viewport.y), GLsizei(cmd.args.viewport.w), GLsizei(cmd.args.viewport.h));
1971             f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
1972             break;
1973         case QGles2CommandBuffer::Command::Scissor:
1974             f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
1975             break;
1976         case QGles2CommandBuffer::Command::BlendConstants:
1977             f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, 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, toGlCompareOp(psD->m_stencilFront.compareOp), ref, psD->m_stencilReadMask);
1985                 f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), ref, psD->m_stencilReadMask);
1986                 cbD->graphicsPassState.dynamic.stencilRef = ref;
1987             } else {
1988                 qWarning("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, currentArrayBuffer);
2007                     }
2008 
2009                     const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(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(GLuint(locationIdx), size, type, normalize, stride,
2053                                              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(GLuint(locationIdx));
2058                     }
2059                     if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing)
2060                         f->glVertexAttribDivisor(GLuint(locationIdx), GLuint(inputBinding->instanceStepRate()));
2061                 }
2062             } else {
2063                 qWarning("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, 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(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount));
2079                 } else {
2080                     f->glDrawArraysInstanced(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount),
2081                                              GLsizei(cmd.args.draw.instanceCount));
2082                 }
2083             } else {
2084                 qWarning("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(psD->drawMode,
2097                                                     GLsizei(cmd.args.drawIndexed.indexCount),
2098                                                     indexType,
2099                                                     ofs,
2100                                                     cmd.args.drawIndexed.baseVertex);
2101                     } else {
2102                         f->glDrawElements(psD->drawMode,
2103                                           GLsizei(cmd.args.drawIndexed.indexCount),
2104                                           indexType,
2105                                           ofs);
2106                     }
2107                 } else {
2108                     if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
2109                         f->glDrawElementsInstancedBaseVertex(psD->drawMode,
2110                                                              GLsizei(cmd.args.drawIndexed.indexCount),
2111                                                              indexType,
2112                                                              ofs,
2113                                                              GLsizei(cmd.args.drawIndexed.instanceCount),
2114                                                              cmd.args.drawIndexed.baseVertex);
2115                     } else {
2116                         f->glDrawElementsInstanced(psD->drawMode,
2117                                                    GLsizei(cmd.args.drawIndexed.indexCount),
2118                                                    indexType,
2119                                                    ofs,
2120                                                    GLsizei(cmd.args.drawIndexed.instanceCount));
2121                     }
2122                 }
2123             } else {
2124                 qWarning("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(cmd.args.bindShaderResources.maybeGraphicsPs,
2133                                 cmd.args.bindShaderResources.maybeComputePs,
2134                                 cmd.args.bindShaderResources.srb,
2135                                 cmd.args.bindShaderResources.dynamicOffsetPairs,
2136                                 cmd.args.bindShaderResources.dynamicOffsetCount);
2137             break;
2138         case QGles2CommandBuffer::Command::BindFramebuffer:
2139             if (cmd.args.bindFramebuffer.fbo) {
2140                 f->glBindFramebuffer(GL_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(colorAttCount, bufs.constData());
2147                 }
2148             } else {
2149                 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2150                 if (caps.maxDrawBuffers > 1) {
2151                     GLenum bufs = GL_BACK;
2152                     f->glDrawBuffers(1, &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(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
2167             }
2168             if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
2169                 f->glDepthMask(GL_TRUE);
2170                 f->glClearDepthf(cmd.args.clear.d);
2171             }
2172             if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT)
2173                 f->glClearStencil(GLint(cmd.args.clear.s));
2174             f->glClear(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(cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
2179             f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
2180                                cmd.args.bufferSubData.data);
2181             break;
2182         case QGles2CommandBuffer::Command::GetBufferSubData:
2183         {
2184             QRhiBufferReadbackResult *result = cmd.args.getBufferSubData.result;
2185             f->glBindBuffer(cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
2186             if (caps.gles) {
2187                 if (caps.properMapBuffer) {
2188                     void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target,
2189                                                   cmd.args.getBufferSubData.offset,
2190                                                   cmd.args.getBufferSubData.size,
2191                                                   GL_MAP_READ_BIT);
2192                     if (p) {
2193                         result->data.resize(cmd.args.getBufferSubData.size);
2194                         memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size));
2195                         f->glUnmapBuffer(cmd.args.getBufferSubData.target);
2196                     }
2197                 }
2198             } else {
2199                 result->data.resize(cmd.args.getBufferSubData.size);
2200                 f->glGetBufferSubData(cmd.args.getBufferSubData.target,
2201                                       cmd.args.getBufferSubData.offset,
2202                                       cmd.args.getBufferSubData.size,
2203                                       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(1, &fbo);
2213             f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2214             f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2215                                       cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
2216             f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
2217             f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
2218                                    cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
2219                                    cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
2220                                    cmd.args.copyTex.w, cmd.args.copyTex.h);
2221             f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2222             f->glDeleteFramebuffers(1, &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(1, &fbo);
2237                     f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2238                     f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2239                                               cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, 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(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(0, 0, result->pixelSize.width(), result->pixelSize.height(),
2250                                 GL_RGBA, GL_UNSIGNED_BYTE,
2251                                 result->data.data());
2252             } else {
2253                 result->data.fill('\0');
2254             }
2255             if (fbo) {
2256                 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2257                 f->glDeleteFramebuffers(1, &fbo);
2258             }
2259             if (result->completed)
2260                 result->completed();
2261         }
2262             break;
2263         case QGles2CommandBuffer::Command::SubImage:
2264             f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
2265             if (cmd.args.subImage.rowStartAlign != 4)
2266                 f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
2267             f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
2268                                cmd.args.subImage.dx, cmd.args.subImage.dy,
2269                                cmd.args.subImage.w, cmd.args.subImage.h,
2270                                cmd.args.subImage.glformat, cmd.args.subImage.gltype,
2271                                cmd.args.subImage.data);
2272             if (cmd.args.subImage.rowStartAlign != 4)
2273                 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
2274             break;
2275         case QGles2CommandBuffer::Command::CompressedImage:
2276             f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
2277             f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
2278                                       cmd.args.compressedImage.glintformat,
2279                                       cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0,
2280                                       cmd.args.compressedImage.size, cmd.args.compressedImage.data);
2281             break;
2282         case QGles2CommandBuffer::Command::CompressedSubImage:
2283             f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
2284             f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
2285                                          cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
2286                                          cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
2287                                          cmd.args.compressedSubImage.glintformat,
2288                                          cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
2289             break;
2290         case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
2291         {
2292             GLuint fbo[2];
2293             f->glGenFramebuffers(2, fbo);
2294             f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
2295             f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2296                                          GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer);
2297             f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
2298 
2299             f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target,
2300                                       cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel);
2301             f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
2302                                  0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
2303                                  GL_COLOR_BUFFER_BIT,
2304                                  GL_LINEAR);
2305             f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2306         }
2307             break;
2308         case QGles2CommandBuffer::Command::GenMip:
2309             f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
2310             f->glGenerateMipmap(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(psD->program);
2316         }
2317             break;
2318         case QGles2CommandBuffer::Command::Dispatch:
2319             f->glDispatchCompute(cmd.args.dispatch.x, cmd.args.dispatch.y, 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(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(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(cmd.args.barrier.barriers);
2349             break;
2350         default:
2351             break;
2352         }
2353     }
2354 }
2355 
executeBindGraphicsPipeline(QGles2CommandBuffer * cbD,QGles2GraphicsPipeline * psD)2356 void 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(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(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(cullMode);
2379         } else {
2380             f->glDisable(GL_CULL_FACE);
2381         }
2382     }
2383 
2384     const GLenum frontFace = toGlFrontFace(psD->m_frontFace);
2385     if (forceUpdate || frontFace != state.frontFace) {
2386         state.frontFace = frontFace;
2387         f->glFrontFace(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             targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::R),
2401             targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::G),
2402             targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::B),
2403             targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::A)
2404         };
2405         if (forceUpdate || colorMask != state.colorMask) {
2406             state.colorMask = colorMask;
2407             f->glColorMask(colorMask.r, colorMask.g, colorMask.b, colorMask.a);
2408         }
2409 
2410         const bool blendEnabled = targetBlend.enable;
2411         const QGles2CommandBuffer::GraphicsPassState::Blend blend = {
2412             toGlBlendFactor(targetBlend.srcColor),
2413             toGlBlendFactor(targetBlend.dstColor),
2414             toGlBlendFactor(targetBlend.srcAlpha),
2415             toGlBlendFactor(targetBlend.dstAlpha),
2416             toGlBlendOp(targetBlend.opColor),
2417             toGlBlendOp(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(blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
2425                 f->glBlendEquationSeparate(blend.opColor, blend.opAlpha);
2426             } else {
2427                 f->glDisable(GL_BLEND);
2428             }
2429         }
2430     } else {
2431         const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { true, true, true, 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(depthWrite);
2456     }
2457 
2458     const GLenum depthFunc = toGlCompareOp(psD->m_depthOp);
2459     if (forceUpdate || depthFunc != state.depthFunc) {
2460         state.depthFunc = depthFunc;
2461         f->glDepthFunc(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         toGlCompareOp(psD->m_stencilFront.compareOp),
2469         toGlStencilOp(psD->m_stencilFront.failOp),
2470         toGlStencilOp(psD->m_stencilFront.depthFailOp),
2471         toGlStencilOp(psD->m_stencilFront.passOp)
2472     };
2473     const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilBack = {
2474         toGlCompareOp(psD->m_stencilBack.compareOp),
2475         toGlStencilOp(psD->m_stencilBack.failOp),
2476         toGlStencilOp(psD->m_stencilBack.depthFailOp),
2477         toGlStencilOp(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, stencilFront.func, state.dynamic.stencilRef, stencilReadMask);
2494             f->glStencilOpSeparate(GL_FRONT, stencilFront.failOp, stencilFront.zfailOp, stencilFront.zpassOp);
2495             f->glStencilMaskSeparate(GL_FRONT, stencilWriteMask);
2496 
2497             f->glStencilFuncSeparate(GL_BACK, stencilBack.func, state.dynamic.stencilRef, stencilReadMask);
2498             f->glStencilOpSeparate(GL_BACK, stencilBack.failOp, stencilBack.zfailOp, stencilBack.zpassOp);
2499             f->glStencilMaskSeparate(GL_BACK, stencilWriteMask);
2500         } else {
2501             f->glDisable(GL_STENCIL_TEST);
2502         }
2503     }
2504 
2505     const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(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(polyOffsetFactor, 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(lineWidth);
2527         }
2528     }
2529 
2530     f->glUseProgram(psD->program);
2531 }
2532 
qrhi_std140_to_packed(float * dst,int vecSize,int elemCount,const void * src)2533 static 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 
bindShaderResources(QRhiGraphicsPipeline * maybeGraphicsPs,QRhiComputePipeline * maybeComputePs,QRhiShaderResourceBindings * srb,const uint * dynOfsPairs,int dynOfsCount)2543 void 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(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                                                                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("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(uniform.glslLocation, *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(elemCount);
2604                             qrhi_std140_to_packed(packedFloatArray.data(), 1, elemCount, src);
2605                             f->glUniform1fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
2606                         }
2607                     }
2608                         break;
2609                     case QShaderDescription::Vec2:
2610                     {
2611                         const int elemCount = uniform.arrayDim;
2612                         if (elemCount < 1) {
2613                             f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
2614                         } else {
2615                             packedFloatArray.resize(elemCount * 2);
2616                             qrhi_std140_to_packed(packedFloatArray.data(), 2, elemCount, src);
2617                             f->glUniform2fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
2618                         }
2619                     }
2620                         break;
2621                     case QShaderDescription::Vec3:
2622                     {
2623                         const int elemCount = uniform.arrayDim;
2624                         if (elemCount < 1) {
2625                             f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
2626                         } else {
2627                             packedFloatArray.resize(elemCount * 3);
2628                             qrhi_std140_to_packed(packedFloatArray.data(), 3, elemCount, src);
2629                             f->glUniform3fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
2630                         }
2631                     }
2632                         break;
2633                     case QShaderDescription::Vec4:
2634                         f->glUniform4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const float *>(src));
2635                         break;
2636                     case QShaderDescription::Mat2:
2637                         f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, 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(mat, srcMat, 3 * sizeof(float));
2645                         memcpy(mat + 3, srcMat + 4, 3 * sizeof(float));
2646                         memcpy(mat + 6, srcMat + 8, 3 * sizeof(float));
2647                         f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, mat);
2648                     }
2649                         break;
2650                     case QShaderDescription::Mat4:
2651                         f->glUniformMatrix4fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
2652                         break;
2653                     case QShaderDescription::Int:
2654                         f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
2655                         break;
2656                     case QShaderDescription::Int2:
2657                         f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
2658                         break;
2659                     case QShaderDescription::Int3:
2660                         f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
2661                         break;
2662                     case QShaderDescription::Int4:
2663                         f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
2664                         break;
2665                     case QShaderDescription::Uint:
2666                         f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
2667                         break;
2668                     case QShaderDescription::Uint2:
2669                         f->glUniform2uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
2670                         break;
2671                     case QShaderDescription::Uint3:
2672                         f->glUniform3uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
2673                         break;
2674                     case QShaderDescription::Uint4:
2675                         f->glUniform4uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
2676                         break;
2677                     case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
2678                         f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
2679                         break;
2680                     case QShaderDescription::Bool2:
2681                         f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
2682                         break;
2683                     case QShaderDescription::Bool3:
2684                         f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
2685                         break;
2686                     case QShaderDescription::Bool4:
2687                         f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
2688                         break;
2689                     default:
2690                         qWarning("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(texD->target, texD->texture);
2709 
2710                         if (texD->samplerState != samplerD->d) {
2711                             f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
2712                             f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
2713                             f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
2714                             f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, 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(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
2720                                     f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
2721                                 } else {
2722                                     f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
2723                                 }
2724                             }
2725                             texD->samplerState = samplerD->d;
2726                         }
2727 
2728                         f->glUniform1i(sampler.glslLocation + elem, 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(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(GLuint(b->binding), texD->texture,
2747                                   b->u.simage.level, layered, 0,
2748                                   access, 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, GLuint(b->binding), bufD->buffer);
2758             else
2759                 f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer,
2760                                      b->u.sbuf.offset, 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 
resourceUpdate(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)2773 void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2774 {
2775     Q_ASSERT(QRHI_RES(QGles2CommandBuffer, cb)->recordingPass == QGles2CommandBuffer::NoPass);
2776 
2777     enqueueResourceUpdates(cb, resourceUpdates);
2778 }
2779 
enqueueBindFramebuffer(QRhiRenderTarget * rt,QGles2CommandBuffer * cbD,bool * wantsColorClear,bool * wantsDsClear)2780 QGles2RenderTargetData *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(QRhiTextureRenderTarget::PreserveColorContents);
2805         if (wantsDsClear)
2806             *wantsDsClear = !rtTex->m_flags.testFlag(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, texD,
2818                                        QRhiPassResourceTracker::TexColorOutput,
2819                                        QRhiPassResourceTracker::TexColorOutputStage);
2820             }
2821             if (resolveTexD) {
2822                 trackedRegisterTexture(&passResTracker, resolveTexD,
2823                                        QRhiPassResourceTracker::TexColorOutput,
2824                                        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, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
2831                                    QRhiPassResourceTracker::TexDepthOutput,
2832                                    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(fbCmd);
2843 
2844     return rtD;
2845 }
2846 
enqueueBarriersForPass(QGles2CommandBuffer * cbD)2847 void QRhiGles2::enqueueBarriersForPass(QGles2CommandBuffer *cbD)
2848 {
2849     cbD->passResTrackers.append(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(cmd);
2855 }
2856 
beginPass(QRhiCommandBuffer * cb,QRhiRenderTarget * rt,const QColor & colorClearValue,const QRhiDepthStencilClearValue & depthStencilClearValue,QRhiResourceUpdateBatch * resourceUpdates)2857 void 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, &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(clearCmd);
2890 
2891     cbD->recordingPass = QGles2CommandBuffer::RenderPass;
2892     cbD->currentTarget = rt;
2893 
2894     cbD->resetCachedState();
2895 }
2896 
endPass(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)2897 void 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("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(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(cmd);
2927             }
2928         }
2929     }
2930 
2931     cbD->recordingPass = QGles2CommandBuffer::NoPass;
2932     cbD->currentTarget = nullptr;
2933 
2934     if (resourceUpdates)
2935         enqueueResourceUpdates(cb, resourceUpdates);
2936 }
2937 
beginComputePass(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)2938 void 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 
endComputePass(QRhiCommandBuffer * cb,QRhiResourceUpdateBatch * resourceUpdates)2953 void 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 
setComputePipeline(QRhiCommandBuffer * cb,QRhiComputePipeline * ps)2964 void 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(cmd);
2980     }
2981 }
2982 
2983 template<typename T>
qrhigl_accumulateComputeResource(T * writtenResources,QRhiResource * resource,QRhiShaderResourceBinding::Type bindingType,int loadTypeVal,int storeTypeVal,int loadStoreTypeVal)2984 inline 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 
dispatch(QRhiCommandBuffer * cb,int x,int y,int z)3003 void 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(i).data();
3021             switch (b->type) {
3022             case QRhiShaderResourceBinding::ImageLoad:
3023             case QRhiShaderResourceBinding::ImageStore:
3024             case QRhiShaderResourceBinding::ImageLoadStore:
3025                 qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
3026                                                  b->u.simage.tex,
3027                                                  b->type,
3028                                                  QRhiShaderResourceBinding::ImageLoad,
3029                                                  QRhiShaderResourceBinding::ImageStore,
3030                                                  QRhiShaderResourceBinding::ImageLoadStore);
3031                 break;
3032             case QRhiShaderResourceBinding::BufferLoad:
3033             case QRhiShaderResourceBinding::BufferStore:
3034             case QRhiShaderResourceBinding::BufferLoadStore:
3035                 qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
3036                                                  b->u.sbuf.buf,
3037                                                  b->type,
3038                                                  QRhiShaderResourceBinding::BufferLoad,
3039                                                  QRhiShaderResourceBinding::BufferStore,
3040                                                  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(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(cmd);
3079 }
3080 
toGlShaderType(QRhiShaderStage::Type type)3081 static 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 
shaderSource(const QRhiShaderStage & shaderStage,int * glslVersion)3096 QByteArray 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({ 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({ 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 
compileShader(GLuint program,const QRhiShaderStage & shaderStage,int * glslVersion)3158 bool 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(shaderStage);
3166     if (cacheIt != m_shaderCache.constEnd()) {
3167         shader = *cacheIt;
3168     } else {
3169         shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
3170         const char *srcStr = source.constData();
3171         const GLint srcLength = source.count();
3172         f->glShaderSource(shader, 1, &srcStr, &srcLength);
3173         f->glCompileShader(shader);
3174         GLint compiled = 0;
3175         f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
3176         if (!compiled) {
3177             GLint infoLogLength = 0;
3178             f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
3179             QByteArray log;
3180             if (infoLogLength > 1) {
3181                 GLsizei length = 0;
3182                 log.resize(infoLogLength);
3183                 f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
3184             }
3185             qWarning("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(shaderStage, shader);
3195     }
3196 
3197     f->glAttachShader(program, shader);
3198 
3199     return true;
3200 }
3201 
linkProgram(GLuint program)3202 bool QRhiGles2::linkProgram(GLuint program)
3203 {
3204     f->glLinkProgram(program);
3205     GLint linked = 0;
3206     f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
3207     if (!linked) {
3208         GLint infoLogLength = 0;
3209         f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
3210         QByteArray log;
3211         if (infoLogLength > 1) {
3212             GLsizei length = 0;
3213             log.resize(infoLogLength);
3214             f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
3215         }
3216         qWarning("Failed to link shader program: %s", log.constData());
3217         return false;
3218     }
3219     return true;
3220 }
3221 
registerUniformIfActive(const QShaderDescription::BlockVariable & var,const QByteArray & namePrefix,int binding,int baseOffset,GLuint program,QVector<QGles2UniformDescription> * dst)3222 void 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("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.constData());
3238     if (uniform.glslLocation >= 0) {
3239         if (var.arrayDims.count() > 1) {
3240             qWarning("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(uniform);
3249     }
3250 }
3251 
gatherUniforms(GLuint program,const QShaderDescription::UniformBlock & ub,QVector<QGles2UniformDescription> * dst)3252 void 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(structMember, structPrefix, ub.binding, baseOffset, program, dst);
3265             } else {
3266                 if (blockMember.arrayDims.count() > 1) {
3267                     qWarning("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(structMember, arrayPrefix, ub.binding, elemOffset, program, dst);
3277                     elemOffset += elemSize;
3278                 }
3279             }
3280         } else {
3281             registerUniformIfActive(blockMember, prefix, ub.binding, 0, program, dst);
3282         }
3283     }
3284 }
3285 
gatherSamplers(GLuint program,const QShaderDescription::InOutVariable & v,QVector<QGles2SamplerDescription> * dst)3286 void 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.constData());
3292     if (sampler.glslLocation >= 0) {
3293         sampler.binding = v.binding;
3294         dst->append(sampler);
3295     }
3296 }
3297 
isProgramBinaryDiskCacheEnabled() const3298 bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
3299 {
3300     static QOpenGLProgramBinarySupportCheckWrapper checker;
3301     return checker.get(ctx)->isSupported();
3302 }
3303 
3304 Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
3305 
toShaderStage(QRhiShaderStage::Type type)3306 static 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 
tryLoadFromDiskCache(const QRhiShaderStage * stages,int stageCount,GLuint program,QByteArray * cacheKey)3321 QRhiGles2::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(stage, nullptr);
3332             if (source.isEmpty())
3333                 return QRhiGles2::DiskCacheError;
3334             binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(toShaderStage(stage.type()), source));
3335         }
3336 
3337         diskCacheKey = binaryProgram.cacheKey();
3338         if (qrhi_programBinaryCache()->load(diskCacheKey, 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 
trySaveToDiskCache(GLuint program,const QByteArray & cacheKey)3351 void 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, program);
3357     }
3358 }
3359 
QGles2Buffer(QRhiImplementation * rhi,Type type,UsageFlags usage,int size)3360 QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
3361     : QRhiBuffer(rhi, type, usage, size)
3362 {
3363 }
3364 
~QGles2Buffer()3365 QGles2Buffer::~QGles2Buffer()
3366 {
3367     release();
3368 }
3369 
release()3370 void 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(e);
3384     QRHI_PROF;
3385     QRHI_PROF_F(releaseBuffer(this));
3386     rhiD->unregisterResource(this);
3387 }
3388 
build()3389 bool 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(QRhiBuffer::UniformBuffer)) {
3400         if (int(m_usage) != QRhiBuffer::UniformBuffer) {
3401             qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
3402             return false;
3403         }
3404         ubuf.resize(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(QRhiBuffer::IndexBuffer))
3414         targetForDataOps = GL_ELEMENT_ARRAY_BUFFER;
3415     else if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
3416         targetForDataOps = GL_SHADER_STORAGE_BUFFER;
3417 
3418     rhiD->f->glGenBuffers(1, &buffer);
3419     rhiD->f->glBindBuffer(targetForDataOps, buffer);
3420     rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, 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(this);
3426     return true;
3427 }
3428 
nativeBuffer()3429 QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer()
3430 {
3431     if (m_usage.testFlag(QRhiBuffer::UniformBuffer))
3432         return { {}, 0 };
3433 
3434     return { { &buffer }, 1 };
3435 }
3436 
QGles2RenderBuffer(QRhiImplementation * rhi,Type type,const QSize & pixelSize,int sampleCount,QRhiRenderBuffer::Flags flags)3437 QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
3438                                        int sampleCount, QRhiRenderBuffer::Flags flags)
3439     : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
3440 {
3441 }
3442 
~QGles2RenderBuffer()3443 QGles2RenderBuffer::~QGles2RenderBuffer()
3444 {
3445     release();
3446 }
3447 
release()3448 void 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(e);
3464     QRHI_PROF;
3465     QRHI_PROF_F(releaseRenderBuffer(this));
3466     rhiD->unregisterResource(this);
3467 }
3468 
build()3469 bool QGles2RenderBuffer::build()
3470 {
3471     if (renderbuffer)
3472         release();
3473 
3474     QRHI_RES_RHI(QRhiGles2);
3475     QRHI_PROF;
3476     samples = rhiD->effectiveSampleCount(m_sampleCount);
3477 
3478     if (m_flags.testFlag(UsedWithSwapChainOnly)) {
3479         if (m_type == DepthStencil) {
3480             QRHI_PROF_F(newRenderBuffer(this, false, true, samples));
3481             return true;
3482         }
3483 
3484         qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
3485     }
3486 
3487     if (!rhiD->ensureContext())
3488         return false;
3489 
3490     rhiD->f->glGenRenderbuffers(1, &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                                                       size.width(), 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, storage,
3504                                            size.width(), 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, depthStorage,
3516                                            size.width(), size.height());
3517             rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
3518             rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
3519             rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
3520                                            size.width(), 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                                                       size.width(), size.height());
3528         else
3529             rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, rhiD->caps.rgba8Format ? GL_RGBA8 : GL_RGBA4,
3530                                            size.width(), 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(this);
3539     return true;
3540 }
3541 
backingFormat() const3542 QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
3543 {
3544     return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3545 }
3546 
QGles2Texture(QRhiImplementation * rhi,Format format,const QSize & pixelSize,int sampleCount,Flags flags)3547 QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
3548                              int sampleCount, Flags flags)
3549     : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
3550 {
3551 }
3552 
~QGles2Texture()3553 QGles2Texture::~QGles2Texture()
3554 {
3555     release();
3556 }
3557 
release()3558 void 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(e);
3574     QRHI_PROF;
3575     QRHI_PROF_F(releaseTexture(this));
3576     rhiD->unregisterResource(this);
3577 }
3578 
prepareBuild(QSize * adjustedSize)3579 bool 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(CubeMap);
3591     const bool hasMipMaps = m_flags.testFlag(MipMapped);
3592     const bool isCompressed = rhiD->isCompressedFormat(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(UsedWithLoadStore)) {
3600             qWarning("Compressed texture cannot be used with image load/store");
3601             return false;
3602         }
3603         glintformat = toGlCompressedTextureFormat(m_format, m_flags);
3604         if (!glintformat) {
3605             qWarning("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 
build()3693 bool QGles2Texture::build()
3694 {
3695     QSize size;
3696     if (!prepareBuild(&size))
3697         return false;
3698 
3699     QRHI_RES_RHI(QRhiGles2);
3700     rhiD->f->glGenTextures(1, &texture);
3701 
3702     const bool isCube = m_flags.testFlag(CubeMap);
3703     const bool hasMipMaps = m_flags.testFlag(MipMapped);
3704     const bool isCompressed = rhiD->isCompressedFormat(m_format);
3705     if (!isCompressed) {
3706         rhiD->f->glBindTexture(target, texture);
3707         if (!m_flags.testFlag(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(level, size);
3713                         rhiD->f->glTexImage2D(faceTargetBase + uint(layer), level, GLint(glintformat),
3714                                               mipSize.width(), mipSize.height(), 0,
3715                                               glformat, gltype, nullptr);
3716                     }
3717                 }
3718             } else {
3719                 rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
3720                                       0, glformat, gltype, 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, mipLevelCount, glsizedintformat, size.width(), 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(this);
3743     return true;
3744 }
3745 
buildFrom(QRhiTexture::NativeTexture src)3746 bool 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(this);
3766     return true;
3767 }
3768 
nativeTexture()3769 QRhiTexture::NativeTexture QGles2Texture::nativeTexture()
3770 {
3771     return {&texture, 0};
3772 }
3773 
QGles2Sampler(QRhiImplementation * rhi,Filter magFilter,Filter minFilter,Filter mipmapMode,AddressMode u,AddressMode v,AddressMode w)3774 QGles2Sampler::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 
~QGles2Sampler()3780 QGles2Sampler::~QGles2Sampler()
3781 {
3782     release();
3783 }
3784 
release()3785 void QGles2Sampler::release()
3786 {
3787     // nothing to do here
3788 }
3789 
build()3790 bool QGles2Sampler::build()
3791 {
3792     d.glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode);
3793     d.glmagfilter = toGlMagFilter(m_magFilter);
3794     d.glwraps = toGlWrapMode(m_addressU);
3795     d.glwrapt = toGlWrapMode(m_addressV);
3796     d.glwrapr = toGlWrapMode(m_addressW);
3797     d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
3798 
3799     generation += 1;
3800     return true;
3801 }
3802 
3803 // dummy, no Vulkan-style RenderPass+Framebuffer concept here
QGles2RenderPassDescriptor(QRhiImplementation * rhi)3804 QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi)
3805     : QRhiRenderPassDescriptor(rhi)
3806 {
3807 }
3808 
~QGles2RenderPassDescriptor()3809 QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
3810 {
3811     release();
3812 }
3813 
release()3814 void QGles2RenderPassDescriptor::release()
3815 {
3816     // nothing to do here
3817 }
3818 
isCompatible(const QRhiRenderPassDescriptor * other) const3819 bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
3820 {
3821     Q_UNUSED(other);
3822     return true;
3823 }
3824 
QGles2ReferenceRenderTarget(QRhiImplementation * rhi)3825 QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
3826     : QRhiRenderTarget(rhi),
3827       d(rhi)
3828 {
3829 }
3830 
~QGles2ReferenceRenderTarget()3831 QGles2ReferenceRenderTarget::~QGles2ReferenceRenderTarget()
3832 {
3833     release();
3834 }
3835 
release()3836 void QGles2ReferenceRenderTarget::release()
3837 {
3838     // nothing to do here
3839 }
3840 
pixelSize() const3841 QSize QGles2ReferenceRenderTarget::pixelSize() const
3842 {
3843     return d.pixelSize;
3844 }
3845 
devicePixelRatio() const3846 float QGles2ReferenceRenderTarget::devicePixelRatio() const
3847 {
3848     return d.dpr;
3849 }
3850 
sampleCount() const3851 int QGles2ReferenceRenderTarget::sampleCount() const
3852 {
3853     return d.sampleCount;
3854 }
3855 
QGles2TextureRenderTarget(QRhiImplementation * rhi,const QRhiTextureRenderTargetDescription & desc,Flags flags)3856 QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi,
3857                                                      const QRhiTextureRenderTargetDescription &desc,
3858                                                      Flags flags)
3859     : QRhiTextureRenderTarget(rhi, desc, flags),
3860       d(rhi)
3861 {
3862 }
3863 
~QGles2TextureRenderTarget()3864 QGles2TextureRenderTarget::~QGles2TextureRenderTarget()
3865 {
3866     release();
3867 }
3868 
release()3869 void 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(e);
3883 
3884     rhiD->unregisterResource(this);
3885 }
3886 
newCompatibleRenderPassDescriptor()3887 QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
3888 {
3889     return new QGles2RenderPassDescriptor(m_rhi);
3890 }
3891 
build()3892 bool 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("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("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
3913 
3914     if (!rhiD->ensureContext())
3915         return false;
3916 
3917     rhiD->f->glGenFramebuffers(1, &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(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
3932             rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
3933                                             texD->texture, 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, 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                                                    depthRbD->renderbuffer);
3954             } else {
3955                 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
3956                                                    depthRbD->renderbuffer);
3957                 if (depthRbD->stencilRenderbuffer)
3958                     rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
3959                                                        depthRbD->stencilRenderbuffer);
3960                 else // packed
3961                     rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
3962                                                        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, depthTexD->texture, 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("Framebuffer incomplete: 0x%x", status);
3987         return false;
3988     }
3989 
3990     rhiD->registerResource(this);
3991     return true;
3992 }
3993 
pixelSize() const3994 QSize QGles2TextureRenderTarget::pixelSize() const
3995 {
3996     return d.pixelSize;
3997 }
3998 
devicePixelRatio() const3999 float QGles2TextureRenderTarget::devicePixelRatio() const
4000 {
4001     return d.dpr;
4002 }
4003 
sampleCount() const4004 int QGles2TextureRenderTarget::sampleCount() const
4005 {
4006     return d.sampleCount;
4007 }
4008 
QGles2ShaderResourceBindings(QRhiImplementation * rhi)4009 QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi)
4010     : QRhiShaderResourceBindings(rhi)
4011 {
4012 }
4013 
~QGles2ShaderResourceBindings()4014 QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
4015 {
4016     release();
4017 }
4018 
release()4019 void QGles2ShaderResourceBindings::release()
4020 {
4021     // nothing to do here
4022 }
4023 
build()4024 bool QGles2ShaderResourceBindings::build()
4025 {
4026     generation += 1;
4027     return true;
4028 }
4029 
QGles2GraphicsPipeline(QRhiImplementation * rhi)4030 QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi)
4031     : QRhiGraphicsPipeline(rhi)
4032 {
4033 }
4034 
~QGles2GraphicsPipeline()4035 QGles2GraphicsPipeline::~QGles2GraphicsPipeline()
4036 {
4037     release();
4038 }
4039 
release()4040 void 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(e);
4056 
4057     rhiD->unregisterResource(this);
4058 }
4059 
build()4060 bool 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(this))
4071         return false;
4072 
4073     drawMode = toGlTopology(m_topology);
4074 
4075     program = rhiD->f->glCreateProgram();
4076 
4077     QByteArray diskCacheKey;
4078     QRhiGles2::DiskCacheResult diskCacheResult = rhiD->tryLoadFromDiskCache(m_shaderStages.constData(),
4079                                                                             m_shaderStages.count(),
4080                                                                             program,
4081                                                                             &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(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, nullptr))
4094                 return false;
4095             vsDesc = shaderStage.shader().description();
4096         } else if (isFragment) {
4097             if (needsCompile && !rhiD->compileShader(program, shaderStage, 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, GLuint(inVar.location), name.constData());
4106     }
4107 
4108     if (needsCompile && !rhiD->linkProgram(program))
4109         return false;
4110 
4111     if (needsCompile)
4112         rhiD->trySaveToDiskCache(program, diskCacheKey);
4113 
4114     for (const QShaderDescription::UniformBlock &ub : vsDesc.uniformBlocks())
4115         rhiD->gatherUniforms(program, ub, &uniforms);
4116 
4117     for (const QShaderDescription::UniformBlock &ub : fsDesc.uniformBlocks())
4118         rhiD->gatherUniforms(program, ub, &uniforms);
4119 
4120     for (const QShaderDescription::InOutVariable &v : vsDesc.combinedImageSamplers())
4121         rhiD->gatherSamplers(program, v, &samplers);
4122 
4123     for (const QShaderDescription::InOutVariable &v : fsDesc.combinedImageSamplers())
4124         rhiD->gatherSamplers(program, v, &samplers);
4125 
4126     generation += 1;
4127     rhiD->registerResource(this);
4128     return true;
4129 }
4130 
QGles2ComputePipeline(QRhiImplementation * rhi)4131 QGles2ComputePipeline::QGles2ComputePipeline(QRhiImplementation *rhi)
4132     : QRhiComputePipeline(rhi)
4133 {
4134 }
4135 
~QGles2ComputePipeline()4136 QGles2ComputePipeline::~QGles2ComputePipeline()
4137 {
4138     release();
4139 }
4140 
release()4141 void 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(e);
4157 
4158     rhiD->unregisterResource(this);
4159 }
4160 
build()4161 bool 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(&m_shaderStage, 1, program, &diskCacheKey);
4176     if (diskCacheResult == QRhiGles2::DiskCacheError)
4177         return false;
4178 
4179     const bool needsCompile = diskCacheResult == QRhiGles2::DiskCacheMiss;
4180 
4181     if (needsCompile && !rhiD->compileShader(program, m_shaderStage, 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, diskCacheKey);
4191 
4192     for (const QShaderDescription::UniformBlock &ub : csDesc.uniformBlocks())
4193         rhiD->gatherUniforms(program, ub, &uniforms);
4194     for (const QShaderDescription::InOutVariable &v : csDesc.combinedImageSamplers())
4195         rhiD->gatherSamplers(program, v, &samplers);
4196 
4197     // storage images and buffers need no special steps here
4198 
4199     generation += 1;
4200     rhiD->registerResource(this);
4201     return true;
4202 }
4203 
QGles2CommandBuffer(QRhiImplementation * rhi)4204 QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
4205     : QRhiCommandBuffer(rhi)
4206 {
4207     resetState();
4208 }
4209 
~QGles2CommandBuffer()4210 QGles2CommandBuffer::~QGles2CommandBuffer()
4211 {
4212     release();
4213 }
4214 
release()4215 void QGles2CommandBuffer::release()
4216 {
4217     // nothing to do here
4218 }
4219 
QGles2SwapChain(QRhiImplementation * rhi)4220 QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
4221     : QRhiSwapChain(rhi),
4222       rt(rhi),
4223       cb(rhi)
4224 {
4225 }
4226 
~QGles2SwapChain()4227 QGles2SwapChain::~QGles2SwapChain()
4228 {
4229     release();
4230 }
4231 
release()4232 void QGles2SwapChain::release()
4233 {
4234     QRHI_PROF;
4235     QRHI_PROF_F(releaseSwapChain(this));
4236 }
4237 
currentFrameCommandBuffer()4238 QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
4239 {
4240     return &cb;
4241 }
4242 
currentFrameRenderTarget()4243 QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
4244 {
4245     return &rt;
4246 }
4247 
surfacePixelSize()4248 QSize QGles2SwapChain::surfacePixelSize()
4249 {
4250     Q_ASSERT(m_window);
4251     return m_window->size() * m_window->devicePixelRatio();
4252 }
4253 
newCompatibleRenderPassDescriptor()4254 QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
4255 {
4256     return new QGles2RenderPassDescriptor(m_rhi);
4257 }
4258 
buildOrResize()4259 bool QGles2SwapChain::buildOrResize()
4260 {
4261     surface = m_window;
4262     m_currentPixelSize = surfacePixelSize();
4263     pixelSize = m_currentPixelSize;
4264 
4265     if (m_depthStencil && m_depthStencil->flags().testFlag(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(1, m_sampleCount, 64);
4276     rt.d.colorAttCount = 1;
4277     rt.d.dsAttCount = m_depthStencil ? 1 : 0;
4278     rt.d.srgbUpdateAndBlend = m_flags.testFlag(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 
4289 QT_END_NAMESPACE
4290