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