Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrhigles2.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qrhigles2_p.h"
6#include <QOffscreenSurface>
7#include <QOpenGLContext>
8#include <QtCore/qendian.h>
9#include <QtCore/qmap.h>
10#include <QtGui/private/qguiapplication_p.h>
11#include <QtGui/private/qopenglextensions_p.h>
12#include <QtGui/private/qopenglprogrambinarycache_p.h>
13#include <QtGui/private/qwindow_p.h>
14#include <kernel/qplatformintegration.h>
15#include <qpa/qplatformopenglcontext.h>
16#include <qmath.h>
17
18#include <limits>
19
21
22/*
23 OpenGL backend. Binding vertex attribute locations and decomposing uniform
24 buffers into uniforms are handled transparently to the application via the
25 reflection data (QShaderDescription). Real uniform buffers are never used,
26 regardless of the GLSL version. Textures and buffers feature no special
27 logic, it's all just glTexSubImage2D and glBufferSubData (with "dynamic"
28 buffers set to GL_DYNAMIC_DRAW). The swapchain and the associated
29 renderbuffer for depth-stencil will be dummies since we have no control over
30 the underlying buffers here. While the baseline here is plain GLES 2.0, some
31 modern GL(ES) features like multisample renderbuffers, blits, and compute are
32 used when available. Also functional with core profile contexts.
33*/
34
35/*!
36 \class QRhiGles2InitParams
37 \inmodule QtGuiPrivate
38 \inheaderfile rhi/qrhi.h
39 \since 6.6
40 \brief OpenGL specific initialization parameters.
41
42 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
43 for details.
44
45 An OpenGL-based QRhi needs an already created QSurface that can be used in
46 combination with QOpenGLContext. Most commonly, this is a QOffscreenSurface
47 in practice. Additionally, while optional, it is recommended that the QWindow
48 the first QRhiSwapChain will target is passed in as well.
49
50 \badcode
51 QOffscreenSurface *fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
52 QRhiGles2InitParams params;
53 params.fallbackSurface = fallbackSurface;
54 params.window = window;
55 rhi = QRhi::create(QRhi::OpenGLES2, &params);
56 \endcode
57
58 By default QRhi creates a QOpenGLContext on its own. This approach works
59 well in most cases, including threaded scenarios, where there is a dedicated
60 QRhi for each rendering thread. As there will be a QOpenGLContext for each
61 QRhi, the OpenGL context requirements (a context can only be current on one
62 thread) are satisfied. The implicitly created context is destroyed
63 automatically together with the QRhi.
64
65 The QSurfaceFormat for the context is specified in \c format. The
66 constructor sets this to QSurfaceFormat::defaultFormat() so applications
67 that call QSurfaceFormat::setDefaultFormat() with the appropriate settings
68 before the constructor runs will not need to change the value of \c format.
69
70 \note Remember to set the depth and stencil buffer sizes to 24 and 8 when
71 the renderer relies on depth or stencil testing, either in the global
72 default QSurfaceFormat, or, alternatively, separately in all the involved
73 QSurfaceFormat instances: in \c format, the format argument passed to
74 newFallbackSurface(), and on any QWindow that is used with the QRhi.
75
76 A QSurface has to be specified in \c fallbackSurface. In order to prevent
77 mistakes in threaded situations, this is never created automatically by the
78 QRhi because, like QWindow, instances of QSurface subclasses can often be
79 created on the gui/main thread only.
80
81 As a convenience, applications can use newFallbackSurface() which creates
82 and returns a QOffscreenSurface that is compatible with the QOpenGLContext
83 that is going to be created by the QRhi afterwards. Note that the ownership
84 of the returned QOffscreenSurface is transferred to the caller and the QRhi
85 will not destroy it.
86
87 \note With the OpenGL backend, QRhiSwapChain can only target QWindow
88 instances that have their surface type set to QSurface::OpenGLSurface.
89
90 \note \c window is optional. It is recommended to specify it whenever
91 possible, in order to avoid problems on multi-adapter and multi-screen
92 systems. When \c window is not set, the very first
93 QOpenGLContext::makeCurrent() happens with \c fallbackSurface which may be
94 an invisible window on some platforms (for example, Windows) and that may
95 trigger unexpected problems in some cases.
96
97 In case resource sharing with an existing QOpenGLContext is desired, \c
98 shareContext can be set to an existing QOpenGLContext. Alternatively,
99 Qt::AA_ShareOpenGLContexts is honored as well, when enabled.
100
101 \section1 Working with existing OpenGL contexts
102
103 When interoperating with another graphics engine, it may be necessary to
104 get a QRhi instance that uses the same OpenGL context. This can be achieved
105 by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The
106 \c{QRhiGles2NativeHandles::context} must be set to a non-null value then.
107
108 An alternative approach is to create a QOpenGLContext that
109 \l{QOpenGLContext::setShareContext()}{shares resources} with the other
110 engine's context and passing in that context via QRhiGles2NativeHandles.
111
112 The QRhi does not take ownership of the QOpenGLContext passed in via
113 QRhiGles2NativeHandles.
114 */
115
116/*!
117 \variable QRhiGles2InitParams::format
118
119 The QSurfaceFormat, initialized to QSurfaceFormat::defaultFormat() by default.
120*/
121
122/*!
123 \variable QRhiGles2InitParams::fallbackSurface
124
125 A QSurface compatible with \l format. Typically a QOffscreenSurface.
126 Providing this is mandatory. Be aware of the threading implications: a
127 QOffscreenSurface, like QWindow, must only ever be created and destroyed on
128 the main (gui) thread, even if the QRhi is created and operates on another
129 thread.
130*/
131
132/*!
133 \variable QRhiGles2InitParams::window
134
135 Optional, but setting it is recommended when targeting a QWindow with the
136 QRhi.
137*/
138
139/*!
140 \variable QRhiGles2InitParams::shareContext
141
142 Optional, the QOpenGLContext to share resources with. QRhi creates its own
143 context, and setting this member to a valid QOpenGLContext leads to calling
144 \l{QOpenGLContext::setShareContext()}{setShareContext()} with it.
145*/
146
147/*!
148 \class QRhiGles2NativeHandles
149 \inmodule QtGuiPrivate
150 \inheaderfile rhi/qrhi.h
151 \since 6.6
152 \brief Holds the OpenGL context used by the QRhi.
153
154 \note This is a RHI API with limited compatibility guarantees, see \l QRhi
155 for details.
156 */
157
158/*!
159 \variable QRhiGles2NativeHandles::context
160*/
161
162#ifndef GL_BGRA
163#define GL_BGRA 0x80E1
164#endif
165
166#ifndef GL_R8
167#define GL_R8 0x8229
168#endif
169
170#ifndef GL_R8I
171#define GL_R8I 0x8231
172#endif
173
174#ifndef GL_R8UI
175#define GL_R8UI 0x8232
176#endif
177
178#ifndef GL_R32I
179#define GL_R32I 0x8235
180#endif
181
182#ifndef GL_R32UI
183#define GL_R32UI 0x8236
184#endif
185
186#ifndef GL_RG32I
187#define GL_RG32I 0x823B
188#endif
189
190#ifndef GL_RG32UI
191#define GL_RG32UI 0x823C
192#endif
193
194#ifndef GL_RGBA32I
195#define GL_RGBA32I 0x8D82
196#endif
197
198#ifndef GL_RGBA32UI
199#define GL_RGBA32UI 0x8D70
200#endif
201
202#ifndef GL_RG8
203#define GL_RG8 0x822B
204#endif
205
206#ifndef GL_RG
207#define GL_RG 0x8227
208#endif
209
210#ifndef GL_RG_INTEGER
211#define GL_RG_INTEGER 0x8228
212#endif
213
214#ifndef GL_R16
215#define GL_R16 0x822A
216#endif
217
218#ifndef GL_RG16
219#define GL_RG16 0x822C
220#endif
221
222#ifndef GL_RED
223#define GL_RED 0x1903
224#endif
225
226#ifndef GL_RED_INTEGER
227#define GL_RED_INTEGER 0x8D94
228#endif
229
230#ifndef GL_RGBA_INTEGER
231#define GL_RGBA_INTEGER 0x8D99
232#endif
233
234#ifndef GL_RGBA8
235#define GL_RGBA8 0x8058
236#endif
237
238#ifndef GL_RGBA32F
239#define GL_RGBA32F 0x8814
240#endif
241
242#ifndef GL_RGBA16F
243#define GL_RGBA16F 0x881A
244#endif
245
246#ifndef GL_R16F
247#define GL_R16F 0x822D
248#endif
249
250#ifndef GL_R32F
251#define GL_R32F 0x822E
252#endif
253
254#ifndef GL_HALF_FLOAT
255#define GL_HALF_FLOAT 0x140B
256#endif
257
258#ifndef GL_DEPTH_COMPONENT16
259#define GL_DEPTH_COMPONENT16 0x81A5
260#endif
261
262#ifndef GL_DEPTH_COMPONENT24
263#define GL_DEPTH_COMPONENT24 0x81A6
264#endif
265
266#ifndef GL_DEPTH_COMPONENT32F
267#define GL_DEPTH_COMPONENT32F 0x8CAC
268#endif
269
270#ifndef GL_DEPTH32F_STENCIL8
271#define GL_DEPTH32F_STENCIL8 0x8CAD
272#endif
273
274#ifndef GL_FLOAT_32_UNSIGNED_INT_24_8_REV
275#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
276#endif
277
278#ifndef GL_UNSIGNED_INT_24_8
279#define GL_UNSIGNED_INT_24_8 0x84FA
280#endif
281
282#ifndef GL_STENCIL_INDEX
283#define GL_STENCIL_INDEX 0x1901
284#endif
285
286#ifndef GL_STENCIL_INDEX8
287#define GL_STENCIL_INDEX8 0x8D48
288#endif
289
290#ifndef GL_DEPTH24_STENCIL8
291#define GL_DEPTH24_STENCIL8 0x88F0
292#endif
293
294#ifndef GL_DEPTH_STENCIL_ATTACHMENT
295#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
296#endif
297
298#ifndef GL_DEPTH_STENCIL
299#define GL_DEPTH_STENCIL 0x84F9
300#endif
301
302#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
303#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
304#endif
305
306#ifndef GL_FRAMEBUFFER_SRGB
307#define GL_FRAMEBUFFER_SRGB 0x8DB9
308#endif
309
310#ifndef GL_SRGB_ALPHA
311#define GL_SRGB_ALPHA 0x8C42
312#endif
313
314#ifndef GL_SRGB8_ALPHA8
315#define GL_SRGB8_ALPHA8 0x8C43
316#endif
317
318#ifndef GL_READ_FRAMEBUFFER
319#define GL_READ_FRAMEBUFFER 0x8CA8
320#endif
321
322#ifndef GL_DRAW_FRAMEBUFFER
323#define GL_DRAW_FRAMEBUFFER 0x8CA9
324#endif
325
326#ifndef GL_MAX_DRAW_BUFFERS
327#define GL_MAX_DRAW_BUFFERS 0x8824
328#endif
329
330#ifndef GL_TEXTURE_COMPARE_MODE
331#define GL_TEXTURE_COMPARE_MODE 0x884C
332#endif
333
334#ifndef GL_COMPARE_REF_TO_TEXTURE
335#define GL_COMPARE_REF_TO_TEXTURE 0x884E
336#endif
337
338#ifndef GL_TEXTURE_COMPARE_FUNC
339#define GL_TEXTURE_COMPARE_FUNC 0x884D
340#endif
341
342#ifndef GL_MAX_SAMPLES
343#define GL_MAX_SAMPLES 0x8D57
344#endif
345
346#ifndef GL_SHADER_STORAGE_BUFFER
347#define GL_SHADER_STORAGE_BUFFER 0x90D2
348#endif
349
350#ifndef GL_COPY_READ_BUFFER
351#define GL_COPY_READ_BUFFER 0x8F36
352#endif
353
354#ifndef GL_COPY_WRITE_BUFFER
355#define GL_COPY_WRITE_BUFFER 0x8F37
356#endif
357
358#ifndef GL_READ_ONLY
359#define GL_READ_ONLY 0x88B8
360#endif
361
362#ifndef GL_WRITE_ONLY
363#define GL_WRITE_ONLY 0x88B9
364#endif
365
366#ifndef GL_READ_WRITE
367#define GL_READ_WRITE 0x88BA
368#endif
369
370#ifndef GL_COMPUTE_SHADER
371#define GL_COMPUTE_SHADER 0x91B9
372#endif
373
374#ifndef GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
375#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
376#endif
377
378#ifndef GL_ELEMENT_ARRAY_BARRIER_BIT
379#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
380#endif
381
382#ifndef GL_UNIFORM_BARRIER_BIT
383#define GL_UNIFORM_BARRIER_BIT 0x00000004
384#endif
385
386#ifndef GL_BUFFER_UPDATE_BARRIER_BIT
387#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200
388#endif
389
390#ifndef GL_COMMAND_BARRIER_BIT
391#define GL_COMMAND_BARRIER_BIT 0x00000040
392#endif
393
394#ifndef GL_SHADER_STORAGE_BARRIER_BIT
395#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
396#endif
397
398#ifndef GL_TEXTURE_FETCH_BARRIER_BIT
399#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
400#endif
401
402#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
403#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
404#endif
405
406#ifndef GL_PIXEL_BUFFER_BARRIER_BIT
407#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080
408#endif
409
410#ifndef GL_TEXTURE_UPDATE_BARRIER_BIT
411#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100
412#endif
413
414#ifndef GL_FRAMEBUFFER_BARRIER_BIT
415#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
416#endif
417
418#ifndef GL_ALL_BARRIER_BITS
419#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
420#endif
421
422#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
423#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
424#endif
425
426#ifndef GL_POINT_SPRITE
427#define GL_POINT_SPRITE 0x8861
428#endif
429
430#ifndef GL_MAP_READ_BIT
431#define GL_MAP_READ_BIT 0x0001
432#endif
433
434#ifndef GL_MAP_WRITE_BIT
435#define GL_MAP_WRITE_BIT 0x0002
436#endif
437
438#ifndef GL_MAP_INVALIDATE_BUFFER_BIT
439#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
440#endif
441
442#ifndef GL_TEXTURE_2D_MULTISAMPLE
443#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
444#endif
445
446#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY
447#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
448#endif
449
450#ifndef GL_TEXTURE_EXTERNAL_OES
451#define GL_TEXTURE_EXTERNAL_OES 0x8D65
452#endif
453
454#ifndef GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS
455#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB
456#endif
457
458#ifndef GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS
459#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6
460#endif
461
462#ifndef GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS
463#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA
464#endif
465
466#ifndef GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS
467#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD
468#endif
469
470#ifndef GL_MAX_COMPUTE_WORK_GROUP_COUNT
471#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE
472#endif
473
474#ifndef GL_MAX_COMPUTE_WORK_GROUP_SIZE
475#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF
476#endif
477
478#ifndef GL_TEXTURE_CUBE_MAP_SEAMLESS
479#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
480#endif
481
482#ifndef GL_CONTEXT_LOST
483#define GL_CONTEXT_LOST 0x0507
484#endif
485
486#ifndef GL_PROGRAM_BINARY_LENGTH
487#define GL_PROGRAM_BINARY_LENGTH 0x8741
488#endif
489
490#ifndef GL_NUM_PROGRAM_BINARY_FORMATS
491#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
492#endif
493
495#define GL_UNPACK_ROW_LENGTH 0x0CF2
496#endif
497
498#ifndef GL_TEXTURE_3D
499#define GL_TEXTURE_3D 0x806F
500#endif
501
502#ifndef GL_TEXTURE_WRAP_R
503#define GL_TEXTURE_WRAP_R 0x8072
504#endif
505
506#ifndef GL_TEXTURE_RECTANGLE
507#define GL_TEXTURE_RECTANGLE 0x84F5
508#endif
509
510#ifndef GL_TEXTURE_2D_ARRAY
511#define GL_TEXTURE_2D_ARRAY 0x8C1A
512#endif
513
514#ifndef GL_MAX_ARRAY_TEXTURE_LAYERS
515#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
516#endif
517
518#ifndef GL_MAX_VERTEX_UNIFORM_COMPONENTS
519#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
520#endif
521
522#ifndef GL_MAX_FRAGMENT_UNIFORM_COMPONENTS
523#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
524#endif
525
526#ifndef GL_MAX_VERTEX_UNIFORM_VECTORS
527#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
528#endif
529
530#ifndef GL_MAX_FRAGMENT_UNIFORM_VECTORS
531#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
532#endif
533
534#ifndef GL_RGB10_A2
535#define GL_RGB10_A2 0x8059
536#endif
537
539#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
540#endif
541
542#ifndef GL_MAX_VARYING_COMPONENTS
543#define GL_MAX_VARYING_COMPONENTS 0x8B4B
544#endif
545
546#ifndef GL_MAX_VARYING_FLOATS
547#define GL_MAX_VARYING_FLOATS 0x8B4B
548#endif
549
550#ifndef GL_MAX_VARYING_VECTORS
551#define GL_MAX_VARYING_VECTORS 0x8DFC
552#endif
553
554#ifndef GL_TESS_CONTROL_SHADER
555#define GL_TESS_CONTROL_SHADER 0x8E88
556#endif
557
558#ifndef GL_TESS_EVALUATION_SHADER
559#define GL_TESS_EVALUATION_SHADER 0x8E87
560#endif
561
562#ifndef GL_PATCH_VERTICES
563#define GL_PATCH_VERTICES 0x8E72
564#endif
565
566#ifndef GL_LINE
567#define GL_LINE 0x1B01
568#endif
569
570#ifndef GL_FILL
571#define GL_FILL 0x1B02
572#endif
573
574#ifndef GL_PATCHES
575#define GL_PATCHES 0x000E
576#endif
577
578#ifndef GL_GEOMETRY_SHADER
579#define GL_GEOMETRY_SHADER 0x8DD9
580#endif
581
582#ifndef GL_BACK_LEFT
583#define GL_BACK_LEFT 0x0402
584#endif
585
586#ifndef GL_BACK_RIGHT
587#define GL_BACK_RIGHT 0x0403
588#endif
589
590#ifndef GL_TEXTURE_1D
591# define GL_TEXTURE_1D 0x0DE0
592#endif
593
594#ifndef GL_TEXTURE_1D_ARRAY
595# define GL_TEXTURE_1D_ARRAY 0x8C18
596#endif
597
598#ifndef GL_HALF_FLOAT
599#define GL_HALF_FLOAT 0x140B
600#endif
601
602#ifndef GL_MAX_VERTEX_OUTPUT_COMPONENTS
603#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
604#endif
605
606#ifndef GL_TIMESTAMP
607#define GL_TIMESTAMP 0x8E28
608#endif
609
610#ifndef GL_QUERY_RESULT
611#define GL_QUERY_RESULT 0x8866
612#endif
613
614#ifndef GL_QUERY_RESULT_AVAILABLE
615#define GL_QUERY_RESULT_AVAILABLE 0x8867
616#endif
617
618#ifndef GL_BUFFER
619#define GL_BUFFER 0x82E0
620#endif
621
622#ifndef GL_PROGRAM
623#define GL_PROGRAM 0x82E2
624#endif
625
626#ifndef GL_DEPTH_CLAMP
627#define GL_DEPTH_CLAMP 0x864F
628#endif
629
630#ifndef GL_DRAW_INDIRECT_BUFFER
631#define GL_DRAW_INDIRECT_BUFFER 0x8F3F
632#endif
633
634#ifndef GL_DISPATCH_INDIRECT_BUFFER
635#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE
636#endif
637
638#ifndef GL_PARAMETER_BUFFER
639#define GL_PARAMETER_BUFFER 0x80EE
640#endif
641
642/*!
643 Constructs a new QRhiGles2InitParams.
644
645 \l format is set to QSurfaceFormat::defaultFormat().
646 */
647QRhiGles2InitParams::QRhiGles2InitParams()
648{
649 format = QSurfaceFormat::defaultFormat();
650}
651
652/*!
653 \return a new QOffscreenSurface that can be used with a QRhi by passing it
654 via a QRhiGles2InitParams.
655
656 When \a format is not specified, its default value is the global default
657 format settable via QSurfaceFormat::setDefaultFormat().
658
659 \a format is adjusted as appropriate in order to avoid having problems
660 afterwards due to an incompatible context and surface.
661
662 \note This function must only be called on the gui/main thread or if
663 the platform integration supports offscreen surfaces.
664
665 \note It is the application's responsibility to destroy the returned
666 QOffscreenSurface on the gui/main thread once the associated QRhi has been
667 destroyed. The QRhi will not destroy the QOffscreenSurface.
668 */
669QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
670{
671 Q_ASSERT(QThread::isMainThread()
672 || QGuiApplicationPrivate::platformIntegration()->hasCapability(
673 QPlatformIntegration::OffscreenSurface));
674
675 QSurfaceFormat fmt = format;
676
677 // To resolve all fields in the format as much as possible, create a context.
678 // This may be heavy, but allows avoiding BAD_MATCH on some systems.
679 QOpenGLContext tempContext;
680 tempContext.setFormat(fmt);
681 if (tempContext.create())
682 fmt = tempContext.format();
683 else
684 qWarning("QRhiGles2: Failed to create temporary context");
685
686 QOffscreenSurface *s = new QOffscreenSurface;
687 s->setFormat(fmt);
688 s->create();
689
690 return s;
691}
692
693QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
694 : ofr(this)
695{
696 requestedFormat = params->format;
697 fallbackSurface = params->fallbackSurface;
698 maybeWindow = params->window; // may be null
699 maybeShareContext = params->shareContext; // may be null
700
701 importedContext = importDevice != nullptr;
702 if (importedContext) {
703 ctx = importDevice->context;
704 if (!ctx) {
705 qWarning("No OpenGL context given, cannot import");
706 importedContext = false;
707 }
708 }
709}
710
711static inline QSurface *currentSurfaceForCurrentContext(QOpenGLContext *ctx)
712{
713 static const bool doNotTrustCurrentContext =
714 qEnvironmentVariableIntValue("QT_GL_BROKEN_CONTEXT_TRACKING") != 0;
715 if (doNotTrustCurrentContext)
716 return nullptr; // -> makeCurrent all the time -> may have perf. implications
717
718 if (QOpenGLContext::currentContext() != ctx)
719 return nullptr;
720
721 QSurface *currentSurface = ctx->surface();
722 if (!currentSurface)
723 return nullptr;
724
725 if (currentSurface->surfaceClass() == QSurface::Window && !currentSurface->surfaceHandle())
726 return nullptr;
727
728 return currentSurface;
729}
730
732{
733 // With Apple's deprecated OpenGL support we need to minimize the usage of
734 // QOffscreenSurface since delicate problems can pop up with
735 // NSOpenGLContext and drawables.
736#if defined(Q_OS_MACOS)
737 return maybeWindow && maybeWindow->handle() ? static_cast<QSurface *>(maybeWindow) : fallbackSurface;
738#else
739 return fallbackSurface;
740#endif
741}
742
743bool QRhiGles2::ensureContext(QSurface *surface) const
744{
745 if (!surface) {
746 // null means any surface is good because not going to render
747
748 // Bail out if the makeCurrent is not necessary - important, given the
749 // frequency this is called at, and the varying implications of ending
750 // up in the EGL/GLX/etc. makeCurrent repeatedly (e.g., leading to,
751 // depending on the platform, unnecessary command stream flushes,
752 // ultimately degrading performance)
753 if (currentSurfaceForCurrentContext(ctx))
754 return true;
755
756 // if the context is not already current with a valid surface, use our
757 // fallback surface, but platform specific quirks may apply
758 surface = evaluateFallbackSurface();
759 } else if (surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
760 // the window is not usable anymore (no native window underneath), behave as if offscreen
761 surface = evaluateFallbackSurface();
762 } else if (!needsMakeCurrentDueToSwap && currentSurfaceForCurrentContext(ctx) == surface) {
763 // bail out if the makeCurrent is not necessary; same perf. reason as above
764 return true;
765 }
767
768 if (!ctx->makeCurrent(surface)) {
769 if (ctx->isValid()) {
770 qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen.");
771 } else {
772 qWarning("QRhiGles2: Context is lost.");
773 contextLost = true;
774 }
775 return false;
776 }
777
778 return true;
779}
780
781static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
782{
783 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
784 switch (format) {
785 case QRhiTexture::BC1:
786 return srgb ? 0x8C4C : 0x83F0;
787 case QRhiTexture::BC2:
788 return srgb ? 0x8C4E : 0x83F2;
789 case QRhiTexture::BC3:
790 return srgb ? 0x8C4F : 0x83F3;
791
792 case QRhiTexture::ETC2_RGB8:
793 return srgb ? 0x9275 : 0x9274;
794 case QRhiTexture::ETC2_RGB8A1:
795 return srgb ? 0x9277 : 0x9276;
796 case QRhiTexture::ETC2_RGBA8:
797 return srgb ? 0x9279 : 0x9278;
798
799 case QRhiTexture::ASTC_4x4:
800 return srgb ? 0x93D0 : 0x93B0;
801 case QRhiTexture::ASTC_5x4:
802 return srgb ? 0x93D1 : 0x93B1;
803 case QRhiTexture::ASTC_5x5:
804 return srgb ? 0x93D2 : 0x93B2;
805 case QRhiTexture::ASTC_6x5:
806 return srgb ? 0x93D3 : 0x93B3;
807 case QRhiTexture::ASTC_6x6:
808 return srgb ? 0x93D4 : 0x93B4;
809 case QRhiTexture::ASTC_8x5:
810 return srgb ? 0x93D5 : 0x93B5;
811 case QRhiTexture::ASTC_8x6:
812 return srgb ? 0x93D6 : 0x93B6;
813 case QRhiTexture::ASTC_8x8:
814 return srgb ? 0x93D7 : 0x93B7;
815 case QRhiTexture::ASTC_10x5:
816 return srgb ? 0x93D8 : 0x93B8;
817 case QRhiTexture::ASTC_10x6:
818 return srgb ? 0x93D9 : 0x93B9;
819 case QRhiTexture::ASTC_10x8:
820 return srgb ? 0x93DA : 0x93BA;
821 case QRhiTexture::ASTC_10x10:
822 return srgb ? 0x93DB : 0x93BB;
823 case QRhiTexture::ASTC_12x10:
824 return srgb ? 0x93DC : 0x93BC;
825 case QRhiTexture::ASTC_12x12:
826 return srgb ? 0x93DD : 0x93BD;
827
828 default:
829 return 0; // this is reachable, just return an invalid format
830 }
831}
832
833bool QRhiGles2::create(QRhi::Flags flags)
834{
835 Q_ASSERT(fallbackSurface);
836 rhiFlags = flags;
837
838 if (!importedContext) {
839 ctx = new QOpenGLContext;
840 ctx->setFormat(requestedFormat);
841 if (maybeShareContext) {
842 ctx->setShareContext(maybeShareContext);
843 if (maybeWindow)
844 ctx->setScreen(maybeWindow->screen());
845 else
846 ctx->setScreen(maybeShareContext->screen());
847 } else if (QOpenGLContext *shareContext = QOpenGLContext::globalShareContext()) {
848 ctx->setShareContext(shareContext);
849 if (maybeWindow)
850 ctx->setScreen(maybeWindow->screen());
851 else
852 ctx->setScreen(shareContext->screen());
853 } else if (maybeWindow) {
854 ctx->setScreen(maybeWindow->screen());
855 }
856 if (!ctx->create()) {
857 qWarning("QRhiGles2: Failed to create context");
858 delete ctx;
859 ctx = nullptr;
860 return false;
861 }
862 qCDebug(QRHI_LOG_INFO) << "Created OpenGL context" << ctx->format();
863 }
864
865 if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
866 return false;
867
868 f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
869 const QSurfaceFormat actualFormat = ctx->format();
870 caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
871
872 if (!caps.gles) {
873 glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
874 ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
875
876 glTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
877 GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void *)>(
878 ctx->getProcAddress(QByteArrayLiteral("glTexImage1D")));
879
880 glTexStorage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei)>(
881 ctx->getProcAddress(QByteArrayLiteral("glTexStorage1D")));
882
883 glTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
884 GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *)>(
885 ctx->getProcAddress(QByteArrayLiteral("glTexSubImage1D")));
886
887 glCopyTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint,
888 GLint, GLsizei)>(
889 ctx->getProcAddress(QByteArrayLiteral("glCopyTexSubImage1D")));
890
891 glCompressedTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
892 GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *)>(
893 ctx->getProcAddress(QByteArrayLiteral("glCompressedTexImage1D")));
894
895 glCompressedTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
896 GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *)>(
897 ctx->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage1D")));
898
899 glFramebufferTexture1D =
900 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint)>(
901 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture1D")));
902 }
903
904 const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
905 const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
906 const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
907 if (vendor && renderer && version)
908 qCDebug(QRHI_LOG_INFO, "OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
909
910 if (vendor) {
911 driverInfoStruct.deviceName += QByteArray(vendor);
912 driverInfoStruct.deviceName += ' ';
913 }
914 if (renderer) {
915 driverInfoStruct.deviceName += QByteArray(renderer);
916 driverInfoStruct.deviceName += ' ';
917 }
918 if (version)
919 driverInfoStruct.deviceName += QByteArray(version);
920
921 caps.ctxMajor = actualFormat.majorVersion();
922 caps.ctxMinor = actualFormat.minorVersion();
923
924 if (caps.gles) {
925 caps.srgbTextureFormat = caps.ctxMajor >= 3 || ctx->hasExtension("GL_EXT_sRGB");
926 caps.srgbGenerateMipmap = caps.ctxMajor >= 3
927 || ctx->hasExtension("GL_NV_generate_mipmap_sRGB");
929 } else {
930 const bool hasCoreSrgbTexture = caps.ctxMajor > 2
931 || (caps.ctxMajor == 2 && caps.ctxMinor >= 1);
932 caps.srgbTextureFormat = hasCoreSrgbTexture
933 || ctx->hasExtension("GL_EXT_texture_sRGB");
935 // Before OpenGL 3.0, framebuffer sRGB extensions do not guarantee
936 // that an sRGB texture attached to an FBO is sRGB-capable.
937 caps.srgbTextureRenderTarget = caps.ctxMajor >= 3;
938 }
939
940 GLint n = 0;
941 f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
942 if (n > 0) {
943 QVarLengthArray<GLint, 16> compressedTextureFormats(n);
944 f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compressedTextureFormats.data());
945 for (GLint format : compressedTextureFormats)
946 supportedCompressedFormats.insert(format);
947
948 }
949 // The above looks nice, if only it worked always. With GLES the list we
950 // query is likely the full list of compressed formats (mostly anything
951 // that can be decoded). With OpenGL however the list is not required to
952 // include all formats due to the way the spec is worded. For instance, we
953 // cannot rely on ASTC formats being present in the list on non-ES. Some
954 // drivers do include them (Intel, NVIDIA), some don't (Mesa). On the other
955 // hand, relying on extension strings only is not ok: for example, Intel
956 // reports GL_KHR_texture_compression_astc_ldr whereas NVIDIA doesn't. So
957 // the only reasonable thing to do is to query the list always and then see
958 // if there is something we can add - if not already in there.
959 std::array<QRhiTexture::Flags, 2> textureVariantFlags;
960 textureVariantFlags[0] = {};
961 textureVariantFlags[1] = QRhiTexture::sRGB;
962 if (f->hasOpenGLExtension(QOpenGLExtensions::DDSTextureCompression)) {
963 for (QRhiTexture::Flags f : textureVariantFlags) {
964 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::BC1, f));
965 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::BC2, f));
966 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::BC3, f));
967 }
968 }
969 if (f->hasOpenGLExtension(QOpenGLExtensions::ETC2TextureCompression)) {
970 for (QRhiTexture::Flags f : textureVariantFlags) {
971 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ETC2_RGB8, f));
972 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ETC2_RGB8A1, f));
973 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ETC2_RGBA8, f));
974 }
975 }
976 if (f->hasOpenGLExtension(QOpenGLExtensions::ASTCTextureCompression)) {
977 for (QRhiTexture::Flags f : textureVariantFlags) {
978 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_4x4, f));
979 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_5x4, f));
980 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_5x5, f));
981 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_6x5, f));
982 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_6x6, f));
983 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_8x5, f));
984 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_8x6, f));
985 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_8x8, f));
986 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_10x5, f));
987 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_10x8, f));
988 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_10x10, f));
989 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_12x10, f));
990 supportedCompressedFormats.insert(toGlCompressedTextureFormat(QRhiTexture::ASTC_12x12, f));
991 }
992 }
993
994 f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
995
996 if (!caps.gles || caps.ctxMajor >= 3) {
997 // non-ES or ES 3.0+
998 f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
999 caps.hasDrawBuffersFunc = true;
1000 f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
1001 caps.maxSamples = qMax(1, caps.maxSamples);
1002 } else {
1003 // ES 2.0 / WebGL 1
1004 caps.maxDrawBuffers = 1;
1005 caps.hasDrawBuffersFunc = false;
1006 // This does not mean MSAA is not supported, just that we cannot query
1007 // the supported sample counts. Assume that 4x is always supported.
1008 caps.maxSamples = 4;
1009 }
1010
1011 caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
1012 && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
1013
1014 caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
1015 && f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
1016
1017 if (caps.gles)
1018 caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
1019 else
1020 caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
1021
1023#ifdef Q_OS_WASM
1024 // WebGL 2 behaves as if GL_PRIMITIVE_RESTART_FIXED_INDEX was always
1025 // enabled (i.e. matching D3D/Metal), and the value cannot be passed to
1026 // glEnable, so skip the call.
1027#else
1029#endif
1030 }
1031
1032 caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
1034 caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
1035
1036 if (caps.gles)
1037 caps.r32uiFormat = (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1)) && caps.r8Format; // ES 3.1
1038 else
1039 caps.r32uiFormat = true;
1040
1041 if (caps.gles)
1042 caps.imageLoadStore = caps.r32uiFormat;
1043 else
1044 caps.imageLoadStore = (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 2));
1045
1046 caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
1047 caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
1048 caps.rgb10Formats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
1049 caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
1050 caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
1051#ifdef Q_OS_WASM
1052 caps.needsDepthStencilCombinedAttach = true;
1053#else
1055#endif
1056
1057 // QOpenGLExtensions::SRGBFrameBuffer is not useful here. We need to know if
1058 // controlling the sRGB-on-shader-write state is supported, not that if the
1059 // default framebuffer is sRGB-capable. And there are two different
1060 // extensions for desktop and ES.
1061 if (caps.gles) {
1062 caps.srgbWriteControl = ctx->hasExtension("GL_EXT_sRGB_write_control");
1063 } else {
1064 const bool hasCoreSrgbWriteControl = caps.ctxMajor >= 3;
1065 caps.srgbWriteControl = hasCoreSrgbWriteControl
1066 || ctx->hasExtension("GL_ARB_framebuffer_sRGB")
1067 || ctx->hasExtension("GL_EXT_framebuffer_sRGB");
1068 }
1069
1070 caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
1071
1072 // Core profile has no usable default vertex array object. On OpenGL ES
1073 // object 0 works for ordinary draws, but the indirect draw commands are
1074 // specified to fail with INVALID_OPERATION unless a non-zero one is bound.
1075 caps.vertexArrayObject = caps.coreProfile || (caps.gles && caps.ctxMajor >= 3); // ES 3.0
1076
1077 if (caps.gles)
1078 caps.uniformBuffers = caps.ctxMajor >= 3; // ES 3.0
1079 else
1080 caps.uniformBuffers = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // 3.1
1081
1082 caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
1083 caps.depth24 = !caps.gles || f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
1084 caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats);
1085
1086 if (caps.gles)
1087 caps.instancing = caps.ctxMajor >= 3; // ES 3.0
1088 else
1089 caps.instancing = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3); // 3.3
1090
1091 caps.baseVertex = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2 or ES 3.2
1092
1093 if (caps.gles)
1094 caps.compute = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
1095 else
1096 caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
1097
1098 if (caps.compute) {
1099 GLint ssboBindings = 0;
1100 f->glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &ssboBindings);
1101 GLint blocks = 0;
1102 f->glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &blocks);
1103 caps.maxVertexStorageBuffers = qMin(blocks, ssboBindings);
1104 blocks = 0;
1105 f->glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &blocks);
1106 caps.maxFragmentStorageBuffers = qMin(blocks, ssboBindings);
1108 GLint tgPerDim[3];
1109 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &tgPerDim[0]);
1110 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &tgPerDim[1]);
1111 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &tgPerDim[2]);
1112 caps.maxThreadGroupsPerDimension = qMin(tgPerDim[0], qMin(tgPerDim[1], tgPerDim[2]));
1113 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &caps.maxThreadGroupsX);
1114 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &caps.maxThreadGroupsY);
1115 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &caps.maxThreadGroupsZ);
1116 }
1117
1118 if (caps.gles)
1119 caps.depthClamp = false;
1120 else
1121 caps.depthClamp = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // Desktop 3.2
1122 if (!caps.depthClamp)
1123 caps.depthClamp = ctx->hasExtension("GL_EXT_depth_clamp") || ctx->hasExtension("GL_ARB_depth_clamp");
1124
1125 if (caps.gles)
1126 caps.textureCompareMode = caps.ctxMajor >= 3; // ES 3.0
1127 else
1128 caps.textureCompareMode = true;
1129
1130 // proper as in ES 3.0 (glMapBufferRange), not the old glMapBuffer
1131 // extension(s) (which is not in ES 3.0...messy)
1132 caps.properMapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange);
1133
1134 if (caps.gles)
1135 caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0
1136 else
1138
1139 caps.texelFetch = caps.ctxMajor >= 3; // 3.0 or ES 3.0
1140 caps.intAttributes = caps.ctxMajor >= 3; // 3.0 or ES 3.0
1141 caps.screenSpaceDerivatives = f->hasOpenGLExtension(QOpenGLExtensions::StandardDerivatives);
1142
1143 if (caps.gles)
1144 caps.multisampledTexture = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
1145 else
1146 caps.multisampledTexture = caps.ctxMajor >= 3; // 3.0
1147
1148 // Program binary support: only the core stuff, do not bother with the old
1149 // extensions like GL_OES_get_program_binary
1150 if (caps.gles)
1151 caps.programBinary = caps.ctxMajor >= 3; // ES 3.0
1152 else
1153 caps.programBinary = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 1); // 4.1
1154
1155 if (caps.programBinary) {
1156 GLint fmtCount = 0;
1157 f->glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &fmtCount);
1158 if (fmtCount < 1)
1159 caps.programBinary = false;
1160 }
1161
1162 caps.texture3D = caps.ctxMajor >= 3; // 3.0
1163
1164 if (caps.gles)
1165 caps.texture1D = false; // ES
1166 else
1167 caps.texture1D = glTexImage1D && (caps.ctxMajor >= 2); // 2.0
1168
1169 if (caps.gles)
1170 caps.tessellation = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
1171 else
1172 caps.tessellation = caps.ctxMajor >= 4; // 4.0
1173
1174 if (caps.gles)
1175 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
1176 else
1177 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2
1178
1179 if (caps.ctxMajor >= 3) { // 3.0 or ES 3.0
1180 GLint maxArraySize = 0;
1181 f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArraySize);
1182 caps.maxTextureArraySize = maxArraySize;
1183 } else {
1184 caps.maxTextureArraySize = 0;
1185 }
1186
1187 // The ES 2.0 spec only has MAX_xxxx_VECTORS. ES 3.0 and up has both
1188 // *VECTORS and *COMPONENTS. OpenGL 2.0-4.0 only has MAX_xxxx_COMPONENTS.
1189 // 4.1 and above has both. What a mess.
1190 if (caps.gles) {
1191 GLint maxVertexUniformVectors = 0;
1192 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniformVectors);
1193 GLint maxFragmentUniformVectors = 0;
1194 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniformVectors);
1195 caps.maxUniformVectors = qMin(maxVertexUniformVectors, maxFragmentUniformVectors);
1196 } else {
1197 GLint maxVertexUniformComponents = 0;
1198 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxVertexUniformComponents);
1199 GLint maxFragmentUniformComponents = 0;
1200 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &maxFragmentUniformComponents);
1201 caps.maxUniformVectors = qMin(maxVertexUniformComponents, maxFragmentUniformComponents) / 4;
1202 }
1203
1204 f->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.maxVertexInputs);
1205
1206 if (caps.gles) {
1207 f->glGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.maxVertexOutputs);
1208 } else if (caps.ctxMajor >= 3) {
1209 GLint components = 0;
1210 f->glGetIntegerv(caps.coreProfile ? GL_MAX_VERTEX_OUTPUT_COMPONENTS : GL_MAX_VARYING_COMPONENTS, &components);
1211 caps.maxVertexOutputs = components / 4;
1212 } else {
1213 // OpenGL before 3.0 only has this, and not the same as
1214 // MAX_VARYING_COMPONENTS strictly speaking, but will do.
1215 GLint components = 0;
1216 f->glGetIntegerv(GL_MAX_VARYING_FLOATS, &components);
1217 if (components > 0)
1218 caps.maxVertexOutputs = components / 4;
1219 }
1220
1221 if (!caps.gles) {
1223 if (!caps.coreProfile)
1224 f->glEnable(GL_POINT_SPRITE);
1225 } // else (with gles) these are always on
1226
1227 // Match D3D and others when it comes to seamless cubemap filtering.
1228 // ES 3.0+ has this always enabled. (hopefully)
1229 // ES 2.0 and GL < 3.2 will not have it.
1230 if (!caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)))
1232
1233 caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
1234
1235 // We always require GL_OVR_multiview2 for symmetry with other backends.
1236 caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView)
1237 && f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended);
1238 if (caps.multiView) {
1239 glFramebufferTextureMultiviewOVR =
1240 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>(
1241 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
1242 }
1243
1244 // Only do timestamp queries on OpenGL 3.3+.
1245 caps.timestamps = !caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3));
1246 if (caps.timestamps) {
1247 glQueryCounter = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum)>(
1248 ctx->getProcAddress(QByteArrayLiteral("glQueryCounter")));
1249 glGetQueryObjectui64v = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum, quint64 *)>(
1250 ctx->getProcAddress(QByteArrayLiteral("glGetQueryObjectui64v")));
1252 caps.timestamps = false;
1253 }
1254
1255 // glObjectLabel is available on OpenGL ES 3.2+ and OpenGL 4.3+
1256 if (caps.gles)
1257 caps.objectLabel = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1258 else
1259 caps.objectLabel = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
1260 if (caps.objectLabel) {
1261 glObjectLabel = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLuint, GLsizei, const GLchar *)>(
1262 ctx->getProcAddress(QByteArrayLiteral("glObjectLabel")));
1263 }
1264
1265 if (caps.gles) {
1266 // This is the third way to get multisample rendering with GLES. (1. is
1267 // multisample render buffer -> resolve to texture; 2. is multisample
1268 // texture with GLES 3.1; 3. is this, avoiding the explicit multisample
1269 // buffer and should be more efficient with tiled architectures.
1270 // Interesting also because 2. does not seem to work in practice on
1271 // devices such as the Quest 3)
1272 caps.glesMultisampleRenderToTexture = ctx->hasExtension("GL_EXT_multisampled_render_to_texture");
1274 glFramebufferTexture2DMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei)>(
1275 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture2DMultisampleEXT")));
1276 glRenderbufferStorageMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLsizei, GLenum, GLsizei, GLsizei)>(
1277 ctx->getProcAddress(QByteArrayLiteral("glRenderbufferStorageMultisampleEXT")));
1278 }
1279 caps.glesMultiviewMultisampleRenderToTexture = ctx->hasExtension("GL_OVR_multiview_multisampled_render_to_texture");
1281 glFramebufferTextureMultisampleMultiviewOVR = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei)>(
1282 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultisampleMultiviewOVR")));
1283 }
1284 } else {
1287 }
1288
1289 caps.unpackRowLength = !caps.gles || caps.ctxMajor >= 3;
1290
1291 if (caps.gles)
1292 caps.perRenderTargetBlending = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1293 else
1294 caps.perRenderTargetBlending = caps.ctxMajor >= 4;
1295
1296 if (caps.gles) {
1297 if (caps.ctxMajor == 3 && caps.ctxMinor < 2) {
1298 caps.sampleVariables = ctx->hasExtension("GL_OES_sample_variables");
1299 } else {
1300 caps.sampleVariables = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1301 }
1302 } else {
1303 caps.sampleVariables = caps.ctxMajor >= 4;
1304 }
1305
1306 if (caps.gles)
1307 caps.drawIndirect = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1);
1308 else
1309 caps.drawIndirect = caps.ctxMajor >= 4;
1310 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_draw_indirect")))
1311 caps.drawIndirect = true;
1312
1313 // glDraw*BaseInstance: core in 4.2+, ARB on older desktop. Not enabled on
1314 // GLES even when GL_EXT_base_instance is present, for lack of testing.
1315 // Requires instancing as well: the ARB extension can be advertised on
1316 // contexts below 3.3, where the instanced draw path, including the
1317 // attribute divisors, is not taken at all.
1318 if (caps.gles) {
1319 caps.baseInstance = false;
1320 } else {
1321 caps.baseInstance = caps.instancing
1322 && (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 2)
1323 || ctx->hasExtension(QByteArrayLiteral("GL_ARB_base_instance")));
1324 }
1325
1326 if (caps.baseInstance) {
1327 glDrawArraysInstancedBaseInstance = reinterpret_cast<decltype(glDrawArraysInstancedBaseInstance)>(
1328 ctx->getProcAddress(QByteArrayLiteral("glDrawArraysInstancedBaseInstance")));
1329 glDrawElementsInstancedBaseInstance = reinterpret_cast<decltype(glDrawElementsInstancedBaseInstance)>(
1330 ctx->getProcAddress(QByteArrayLiteral("glDrawElementsInstancedBaseInstance")));
1331 glDrawElementsInstancedBaseVertexBaseInstance = reinterpret_cast<decltype(glDrawElementsInstancedBaseVertexBaseInstance)>(
1332 ctx->getProcAddress(QByteArrayLiteral("glDrawElementsInstancedBaseVertexBaseInstance")));
1336 qWarning("Failed to resolve glDraw{Arrays,Elements}InstancedBaseInstance{,BaseVertex}; disabling base instance support.");
1337 caps.baseInstance = false;
1338 }
1339 }
1340
1341 if (caps.gles)
1342 caps.drawIndirectMulti = false;
1343 else
1344 caps.drawIndirectMulti = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
1345 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_multi_draw_indirect")) ||
1346 ctx->hasExtension(QByteArrayLiteral("GL_EXT_multi_draw_indirect")))
1347 caps.drawIndirectMulti = true;
1348
1349 if (caps.drawIndirectMulti) {
1350 glMultiDrawArraysIndirect = reinterpret_cast<decltype(glMultiDrawArraysIndirect)>(
1351 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirect")));
1352 if (!glMultiDrawArraysIndirect)
1353 glMultiDrawArraysIndirect = reinterpret_cast<decltype(glMultiDrawArraysIndirect)>(
1354 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectARB")));
1355 if (!glMultiDrawArraysIndirect)
1356 glMultiDrawArraysIndirect = reinterpret_cast<decltype(glMultiDrawArraysIndirect)>(
1357 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectEXT")));
1358 glMultiDrawElementsIndirect = reinterpret_cast<decltype(glMultiDrawElementsIndirect)>(
1359 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirect")));
1360 if (!glMultiDrawElementsIndirect)
1361 glMultiDrawElementsIndirect = reinterpret_cast<decltype(glMultiDrawElementsIndirect)>(
1362 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectARB")));
1363 if (!glMultiDrawElementsIndirect)
1364 glMultiDrawElementsIndirect = reinterpret_cast<decltype(glMultiDrawElementsIndirect)>(
1365 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectEXT")));
1367 qWarning("Failed to resolve glMultiDrawArraysIndirect or glMultiDrawElementsIndirect.");
1368 caps.drawIndirectMulti = false;
1369 }
1370 }
1371
1372 if (caps.gles)
1373 caps.shaderDrawParameters = false;
1374 else
1375 caps.shaderDrawParameters = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6);
1376 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_shader_draw_parameters")))
1377 caps.shaderDrawParameters = true;
1378 // glDispatchComputeIndirect was introduced together with the compute
1379 // pipeline in OpenGL 4.3 and OpenGL ES 3.1, so its availability matches
1380 // caps.compute. The function pointer is exposed by QOpenGLExtraFunctions.
1381 caps.dispatchIndirect = caps.compute;
1382
1383 // glCopyBufferSubData is core in OpenGL 3.1 and OpenGL ES 3.0, and is
1384 // available via GL_ARB_copy_buffer on older desktop versions. Either way
1385 // the function pointer is exposed by QOpenGLExtraFunctions, but only when
1386 // it can actually be resolved.
1387 if (caps.gles)
1388 caps.copyBuffer = caps.ctxMajor >= 3;
1389 else
1390 caps.copyBuffer = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1);
1391 if (!caps.copyBuffer && ctx->hasExtension(QByteArrayLiteral("GL_ARB_copy_buffer")))
1392 caps.copyBuffer = ctx->getProcAddress(QByteArrayLiteral("glCopyBufferSubData")) != nullptr;
1393
1394 if (!caps.gles
1395 && (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3)
1396 || ctx->hasExtension(QByteArrayLiteral("GL_ARB_clear_buffer_object"))))
1397 {
1398 glClearBufferSubData = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLintptr, GLsizeiptr,
1399 GLenum, GLenum, const void *)>(
1400 ctx->getProcAddress(QByteArrayLiteral("glClearBufferSubData")));
1401 caps.clearBuffer = glClearBufferSubData != nullptr;
1402 }
1403
1404 // glMultiDraw*IndirectCount: core in 4.6, ARB on older desktop.
1405 // No counterpart in OpenGL ES.
1406 if (caps.gles)
1407 caps.drawIndirectCount = false;
1408 else
1409 caps.drawIndirectCount = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6);
1410 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_indirect_parameters")))
1411 caps.drawIndirectCount = true;
1412
1413 if (caps.drawIndirectCount) {
1414 glMultiDrawArraysIndirectCount = reinterpret_cast<decltype(glMultiDrawArraysIndirectCount)>(
1415 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectCount")));
1416 if (!glMultiDrawArraysIndirectCount)
1417 glMultiDrawArraysIndirectCount = reinterpret_cast<decltype(glMultiDrawArraysIndirectCount)>(
1418 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectCountARB")));
1419 glMultiDrawElementsIndirectCount = reinterpret_cast<decltype(glMultiDrawElementsIndirectCount)>(
1420 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectCount")));
1421 if (!glMultiDrawElementsIndirectCount)
1422 glMultiDrawElementsIndirectCount = reinterpret_cast<decltype(glMultiDrawElementsIndirectCount)>(
1423 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectCountARB")));
1425 qWarning("Failed to resolve glMultiDrawArraysIndirectCount or glMultiDrawElementsIndirectCount.");
1426 caps.drawIndirectCount = false;
1427 }
1428 }
1429
1430 nativeHandlesStruct.context = ctx;
1431
1432 contextLost = false;
1433
1434 return true;
1435}
1436
1438{
1439 if (!f)
1440 return;
1441
1442 if (ensureContext()) {
1444
1445 if (ofr.tsQueries[0]) {
1446 f->glDeleteQueries(2, ofr.tsQueries);
1447 ofr.tsQueries[0] = ofr.tsQueries[1] = 0;
1448 }
1449
1450 if (vao) {
1451 f->glDeleteVertexArrays(1, &vao);
1452 vao = 0;
1453 }
1454
1455 for (uint shader : std::as_const(m_shaderCache))
1456 f->glDeleteShader(shader);
1457 m_shaderCache.clear();
1458 }
1459
1460 if (!importedContext) {
1461 delete ctx;
1462 ctx = nullptr;
1463 }
1464
1465 f = nullptr;
1466}
1467
1469{
1470 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
1471 const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
1472 switch (e.type) {
1474 f->glDeleteBuffers(1, &e.buffer.buffer);
1475 break;
1477 f->glDeleteProgram(e.pipeline.program);
1478 break;
1480 f->glDeleteTextures(1, &e.texture.texture);
1481 break;
1483 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
1484 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
1485 break;
1487 f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
1488 f->glDeleteTextures(1, &e.textureRenderTarget.nonMsaaThrowawayDepthTexture);
1489 break;
1490 default:
1491 Q_UNREACHABLE();
1492 break;
1493 }
1494 releaseQueue.removeAt(i);
1495 }
1496}
1497
1499{
1500 if (supportedSampleCountList.isEmpty()) {
1501 // 1, 2, 4, 8, ...
1502 for (int i = 1; i <= caps.maxSamples; i *= 2)
1503 supportedSampleCountList.append(i);
1504 }
1505 return supportedSampleCountList;
1506}
1507
1509{
1510 Q_UNUSED(sampleCount);
1511 return { QSize(1, 1) };
1512}
1513
1515{
1516 return new QGles2SwapChain(this);
1517}
1518
1519QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
1520{
1521 return new QGles2Buffer(this, type, usage, size);
1522}
1523
1525{
1526 // No real uniform buffers are used so no need to pretend there is any
1527 // alignment requirement.
1528 return 1;
1529}
1530
1532{
1533 return true;
1534}
1535
1537{
1538 return true;
1539}
1540
1542{
1543 return false;
1544}
1545
1547{
1548 return QMatrix4x4(); // identity
1549}
1550
1551static inline void toGlTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags,
1552 const QRhiGles2::Caps &caps,
1553 GLenum *glintformat, GLenum *glsizedintformat,
1554 GLenum *glformat, GLenum *gltype)
1555{
1556 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
1557 const bool legacyGlesSrgb = srgb && caps.gles && caps.ctxMajor < 3;
1558 switch (format) {
1559 case QRhiTexture::RGBA8:
1560 *glintformat = srgb ? (legacyGlesSrgb ? GL_SRGB_ALPHA : GL_SRGB8_ALPHA8) : GL_RGBA;
1561 *glsizedintformat = srgb ? GL_SRGB8_ALPHA8 : (caps.rgba8Format ? GL_RGBA8 : GL_RGBA);
1562 *glformat = legacyGlesSrgb ? GL_SRGB_ALPHA : GL_RGBA;
1563 *gltype = GL_UNSIGNED_BYTE;
1564 break;
1565 case QRhiTexture::BGRA8:
1566 *glintformat = srgb ? GL_SRGB8_ALPHA8 : (caps.bgraInternalFormat ? GL_BGRA : GL_RGBA);
1567 *glsizedintformat = srgb ? GL_SRGB8_ALPHA8 : (caps.rgba8Format ? GL_RGBA8 : GL_RGBA);
1568 *glformat = GL_BGRA;
1569 *gltype = GL_UNSIGNED_BYTE;
1570 break;
1571 case QRhiTexture::R16:
1572 *glintformat = GL_R16;
1573 *glsizedintformat = *glintformat;
1574 *glformat = GL_RED;
1575 *gltype = GL_UNSIGNED_SHORT;
1576 break;
1577 case QRhiTexture::RG16:
1578 *glintformat = GL_RG16;
1579 *glsizedintformat = *glintformat;
1580 *glformat = GL_RG;
1581 *gltype = GL_UNSIGNED_SHORT;
1582 break;
1583 case QRhiTexture::R8:
1584 // GL_EXT_texture_rg on ES 2.0 only accepts unsized GL_RED here; same split as RGBA8 above.
1585 *glintformat = caps.gles && caps.ctxMajor < 3 ? GL_RED : GL_R8;
1586 *glsizedintformat = GL_R8;
1587 *glformat = GL_RED;
1588 *gltype = GL_UNSIGNED_BYTE;
1589 break;
1590 case QRhiTexture::R8SI:
1591 *glintformat = GL_R8I;
1592 *glsizedintformat = *glintformat;
1593 *glformat = GL_RED_INTEGER;
1594 *gltype = GL_BYTE;
1595 break;
1596 case QRhiTexture::R8UI:
1597 *glintformat = GL_R8UI;
1598 *glsizedintformat = *glintformat;
1599 *glformat = GL_RED_INTEGER;
1600 *gltype = GL_UNSIGNED_BYTE;
1601 break;
1602 case QRhiTexture::RG8:
1603 *glintformat = caps.gles && caps.ctxMajor < 3 ? GL_RG : GL_RG8;
1604 *glsizedintformat = GL_RG8;
1605 *glformat = GL_RG;
1606 *gltype = GL_UNSIGNED_BYTE;
1607 break;
1608 case QRhiTexture::RED_OR_ALPHA8:
1609 *glintformat = caps.coreProfile ? GL_R8 : GL_ALPHA;
1610 *glsizedintformat = *glintformat;
1611 *glformat = caps.coreProfile ? GL_RED : GL_ALPHA;
1612 *gltype = GL_UNSIGNED_BYTE;
1613 break;
1614 case QRhiTexture::RGBA16F:
1615 *glintformat = GL_RGBA16F;
1616 *glsizedintformat = *glintformat;
1617 *glformat = GL_RGBA;
1618 *gltype = GL_HALF_FLOAT;
1619 break;
1620 case QRhiTexture::RGBA32F:
1621 *glintformat = GL_RGBA32F;
1622 *glsizedintformat = *glintformat;
1623 *glformat = GL_RGBA;
1624 *gltype = GL_FLOAT;
1625 break;
1626 case QRhiTexture::R16F:
1627 *glintformat = GL_R16F;
1628 *glsizedintformat = *glintformat;
1629 *glformat = GL_RED;
1630 *gltype = GL_HALF_FLOAT;
1631 break;
1632 case QRhiTexture::R32F:
1633 *glintformat = GL_R32F;
1634 *glsizedintformat = *glintformat;
1635 *glformat = GL_RED;
1636 *gltype = GL_FLOAT;
1637 break;
1638 case QRhiTexture::RGB10A2:
1639 *glintformat = GL_RGB10_A2;
1640 *glsizedintformat = *glintformat;
1641 *glformat = GL_RGBA;
1643 break;
1644 case QRhiTexture::R32SI:
1645 *glintformat = GL_R32I;
1646 *glsizedintformat = *glintformat;
1647 *glformat = GL_RED_INTEGER;
1648 *gltype = GL_INT;
1649 break;
1650 case QRhiTexture::R32UI:
1651 *glintformat = GL_R32UI;
1652 *glsizedintformat = *glintformat;
1653 *glformat = GL_RED_INTEGER;
1654 *gltype = GL_UNSIGNED_INT;
1655 break;
1656 case QRhiTexture::RG32SI:
1657 *glintformat = GL_RG32I;
1658 *glsizedintformat = *glintformat;
1659 *glformat = GL_RG_INTEGER;
1660 *gltype = GL_INT;
1661 break;
1662 case QRhiTexture::RG32UI:
1663 *glintformat = GL_RG32UI;
1664 *glsizedintformat = *glintformat;
1665 *glformat = GL_RG_INTEGER;
1666 *gltype = GL_UNSIGNED_INT;
1667 break;
1668 case QRhiTexture::RGBA32SI:
1669 *glintformat = GL_RGBA32I;
1670 *glsizedintformat = *glintformat;
1671 *glformat = GL_RGBA_INTEGER;
1672 *gltype = GL_INT;
1673 break;
1674 case QRhiTexture::RGBA32UI:
1675 *glintformat = GL_RGBA32UI;
1676 *glsizedintformat = *glintformat;
1677 *glformat = GL_RGBA_INTEGER;
1678 *gltype = GL_UNSIGNED_INT;
1679 break;
1680 case QRhiTexture::D16:
1681 *glintformat = GL_DEPTH_COMPONENT16;
1682 *glsizedintformat = *glintformat;
1683 *glformat = GL_DEPTH_COMPONENT;
1684 *gltype = GL_UNSIGNED_SHORT;
1685 break;
1686 case QRhiTexture::D24:
1687 *glintformat = GL_DEPTH_COMPONENT24;
1688 *glsizedintformat = *glintformat;
1689 *glformat = GL_DEPTH_COMPONENT;
1690 *gltype = GL_UNSIGNED_INT;
1691 break;
1692 case QRhiTexture::D24S8:
1693 *glintformat = GL_DEPTH24_STENCIL8;
1694 *glsizedintformat = *glintformat;
1695 *glformat = GL_DEPTH_STENCIL;
1696 *gltype = GL_UNSIGNED_INT_24_8;
1697 break;
1698 case QRhiTexture::D32F:
1699 *glintformat = GL_DEPTH_COMPONENT32F;
1700 *glsizedintformat = *glintformat;
1701 *glformat = GL_DEPTH_COMPONENT;
1702 *gltype = GL_FLOAT;
1703 break;
1704 case QRhiTexture::D32FS8:
1705 *glintformat = GL_DEPTH32F_STENCIL8;
1706 *glsizedintformat = *glintformat;
1707 *glformat = GL_DEPTH_STENCIL;
1709 break;
1710 default:
1711 Q_UNREACHABLE();
1712 *glintformat = GL_RGBA;
1713 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1714 *glformat = GL_RGBA;
1715 *gltype = GL_UNSIGNED_BYTE;
1716 break;
1717 }
1718}
1719
1720static inline bool isDepthTextureFormat(QRhiTexture::Format format)
1721{
1722 switch (format) {
1723 case QRhiTexture::D16:
1724 case QRhiTexture::D24:
1725 case QRhiTexture::D24S8:
1726 case QRhiTexture::D32F:
1727 case QRhiTexture::D32FS8:
1728 return true;
1729 default:
1730 return false;
1731 }
1732}
1733
1734bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
1735{
1736 if (isCompressedFormat(format))
1737 return supportedCompressedFormats.contains(GLint(toGlCompressedTextureFormat(format, flags)));
1738
1739 if ((flags & QRhiTexture::UsedWithLoadStore) && !caps.imageLoadStore)
1740 return false;
1741
1742 if (flags.testFlag(QRhiTexture::sRGB)) {
1743 if (format != QRhiTexture::RGBA8 && format != QRhiTexture::BGRA8)
1744 return false;
1745 if (!caps.srgbTextureFormat || flags.testFlag(QRhiTexture::UsedWithLoadStore))
1746 return false;
1747 if (format == QRhiTexture::BGRA8 && caps.gles)
1748 return false;
1749 if (flags.testFlag(QRhiTexture::UsedWithGenerateMips) && !caps.srgbGenerateMipmap)
1750 return false;
1751 if (flags.testFlag(QRhiTexture::RenderTarget) && !caps.srgbTextureRenderTarget)
1752 return false;
1753 }
1754
1755 switch (format) {
1756 case QRhiTexture::D16:
1757 case QRhiTexture::D32F:
1758 case QRhiTexture::D32FS8:
1759 return caps.depthTexture;
1760
1761 case QRhiTexture::D24:
1762 return caps.depthTexture && caps.depth24;
1763
1764 case QRhiTexture::D24S8:
1765 return caps.depthTexture && caps.depth24 && caps.packedDepthStencil;
1766
1767 case QRhiTexture::BGRA8:
1768 return caps.bgraExternalFormat;
1769
1770 case QRhiTexture::R8:
1771 case QRhiTexture::R8SI:
1772 case QRhiTexture::R8UI:
1773 return caps.r8Format;
1774
1775 case QRhiTexture::R32SI:
1776 case QRhiTexture::R32UI:
1777 case QRhiTexture::RG32SI:
1778 case QRhiTexture::RG32UI:
1779 case QRhiTexture::RGBA32SI:
1780 case QRhiTexture::RGBA32UI:
1781 return caps.r32uiFormat;
1782
1783 case QRhiTexture::RG8:
1784 return caps.r8Format;
1785
1786 case QRhiTexture::R16:
1787 return caps.r16Format;
1788
1789 case QRhiTexture::RG16:
1790 return caps.r16Format;
1791
1792 case QRhiTexture::RGBA16F:
1793 case QRhiTexture::RGBA32F:
1794 return caps.floatFormats;
1795
1796 case QRhiTexture::R16F:
1797 case QRhiTexture::R32F:
1798 return caps.floatFormats;
1799
1800 case QRhiTexture::RGB10A2:
1801 return caps.rgb10Formats;
1802
1803 default:
1804 break;
1805 }
1806
1807 return true;
1808}
1809
1810bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
1811{
1812 switch (feature) {
1813 case QRhi::MultisampleTexture:
1814 return caps.multisampledTexture;
1815 case QRhi::MultisampleRenderBuffer:
1816 return caps.msaaRenderBuffer;
1817 case QRhi::DebugMarkers:
1818 return false;
1819 case QRhi::Timestamps:
1820 return caps.timestamps;
1821 case QRhi::Instancing:
1822 return caps.instancing;
1823 case QRhi::CustomInstanceStepRate:
1824 return false;
1825 case QRhi::PrimitiveRestart:
1826 return caps.fixedIndexPrimitiveRestart;
1827 case QRhi::NonDynamicUniformBuffers:
1828 return true;
1829 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
1830 return true;
1831 case QRhi::NPOTTextureRepeat:
1832 return caps.npotTextureFull;
1833 case QRhi::RedOrAlpha8IsRed:
1834 return caps.coreProfile;
1835 case QRhi::ElementIndexUint:
1836 return caps.elementIndexUint;
1837 case QRhi::Compute:
1838 return caps.compute;
1839 case QRhi::WideLines:
1840 return !caps.coreProfile;
1841 case QRhi::VertexShaderPointSize:
1842 return true;
1843 case QRhi::BaseVertex:
1844 return caps.baseVertex;
1845 case QRhi::BaseInstance:
1846 // gl_InstanceID on GL starts at 0 regardless of baseInstance, unlike
1847 // Vulkan's gl_InstanceIndex. QRhi::InstanceIndexIncludesBaseInstance
1848 // stays false on GL.
1849 return caps.baseInstance;
1850 case QRhi::TriangleFanTopology:
1851 return true;
1852 case QRhi::ReadBackNonUniformBuffer:
1853 return !caps.gles || caps.properMapBuffer;
1854 case QRhi::ReadBackNonBaseMipLevel:
1856 case QRhi::TexelFetch:
1857 return caps.texelFetch;
1858 case QRhi::RenderToNonBaseMipLevel:
1860 case QRhi::IntAttributes:
1861 return caps.intAttributes;
1862 case QRhi::ScreenSpaceDerivatives:
1863 return caps.screenSpaceDerivatives;
1864 case QRhi::ReadBackAnyTextureFormat:
1865 return false;
1866 case QRhi::PipelineCacheDataLoadSave:
1867 return caps.programBinary;
1868 case QRhi::ImageDataStride:
1869 return caps.unpackRowLength;
1870 case QRhi::RenderBufferImport:
1871 return true;
1872 case QRhi::ThreeDimensionalTextures:
1873 return caps.texture3D;
1874 case QRhi::RenderTo3DTextureSlice:
1875 return caps.texture3D;
1876 case QRhi::TextureArrays:
1877 return caps.maxTextureArraySize > 0;
1878 case QRhi::Tessellation:
1879 return caps.tessellation;
1880 case QRhi::GeometryShader:
1881 return caps.geometryShader;
1882 case QRhi::TextureArrayRange:
1883 return false;
1884 case QRhi::NonFillPolygonMode:
1885 return !caps.gles;
1886 case QRhi::OneDimensionalTextures:
1887 return caps.texture1D;
1888 case QRhi::OneDimensionalTextureMipmaps:
1889 return caps.texture1D;
1890 case QRhi::HalfAttributes:
1891 return caps.halfAttributes;
1892 case QRhi::RenderToOneDimensionalTexture:
1893 return caps.texture1D;
1894 case QRhi::ThreeDimensionalTextureMipmaps:
1895 return caps.texture3D;
1896 case QRhi::MultiView:
1897 return caps.multiView && caps.maxTextureArraySize > 0;
1898 case QRhi::TextureViewFormat:
1899 return false;
1900 case QRhi::ResolveDepthStencil:
1901 return true;
1902 case QRhi::VariableRateShading:
1903 return false;
1904 case QRhi::VariableRateShadingMap:
1905 case QRhi::VariableRateShadingMapWithTexture:
1906 return false;
1907 case QRhi::PerRenderTargetBlending:
1908 return caps.perRenderTargetBlending;
1909 case QRhi::SampleVariables:
1910 return caps.sampleVariables;
1911 case QRhi::InstanceIndexIncludesBaseInstance:
1912 return false; // gl_InstanceID always starts at 0 on GL
1913 case QRhi::DepthClamp:
1914 return caps.depthClamp;
1915 case QRhi::DrawIndirect:
1916 return caps.drawIndirect;
1917 case QRhi::DrawIndirectMulti:
1918 return caps.drawIndirectMulti;
1919 case QRhi::ShaderDrawParameters:
1920 return caps.shaderDrawParameters;
1921 case QRhi::DispatchIndirect:
1922 return caps.dispatchIndirect;
1923 case QRhi::PushConstants:
1924 return true;
1925 case QRhi::DrawIndirectCount:
1926 return caps.drawIndirectCount;
1927 case QRhi::BufferToBufferCopy:
1928 return caps.copyBuffer;
1929 case QRhi::StaticBuffersOnGpuTimeline:
1930 return true;
1931 default:
1932 Q_UNREACHABLE_RETURN(false);
1933 }
1934}
1935
1936int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
1937{
1938 switch (limit) {
1939 case QRhi::TextureSizeMin:
1940 return 1;
1941 case QRhi::TextureSizeMax:
1942 return caps.maxTextureSize;
1943 case QRhi::MaxColorAttachments:
1944 return caps.maxDrawBuffers;
1945 case QRhi::FramesInFlight:
1946 // From our perspective. What the GL impl does internally is another
1947 // question, but that's out of our hands and does not concern us here.
1948 return 1;
1949 case QRhi::MaxAsyncReadbackFrames:
1950 return 1;
1951 case QRhi::MaxThreadGroupsPerDimension:
1953 case QRhi::MaxThreadsPerThreadGroup:
1954 return caps.maxThreadsPerThreadGroup;
1955 case QRhi::MaxThreadGroupX:
1956 return caps.maxThreadGroupsX;
1957 case QRhi::MaxThreadGroupY:
1958 return caps.maxThreadGroupsY;
1959 case QRhi::MaxThreadGroupZ:
1960 return caps.maxThreadGroupsZ;
1961 case QRhi::TextureArraySizeMax:
1962 return 2048;
1963 case QRhi::MaxUniformBufferRange:
1964 return int(qMin<qint64>(INT_MAX, caps.maxUniformVectors * qint64(16)));
1965 case QRhi::MaxVertexInputs:
1966 return caps.maxVertexInputs;
1967 case QRhi::MaxVertexOutputs:
1968 return caps.maxVertexOutputs;
1969 case QRhi::MaxVertexStorageBuffers:
1970 return caps.maxVertexStorageBuffers;
1971 case QRhi::MaxPushConstantsSize:
1972 // Emulated with plain uniforms, so the real limit is the uniform
1973 // budget shared with everything else. Report the portable minimum.
1974 return int(MAX_PUSH_CONSTANTS_SIZE);
1975 case QRhi::MaxFragmentStorageBuffers:
1976 return caps.maxFragmentStorageBuffers;
1977 case QRhi::ShadingRateImageTileSize:
1978 return 0;
1979 default:
1980 Q_UNREACHABLE_RETURN(0);
1981 }
1982}
1983
1985{
1986 return &nativeHandlesStruct;
1987}
1988
1990{
1991 return driverInfoStruct;
1992}
1993
1995{
1996 QRhiStats result;
1997 result.totalPipelineCreationTime = totalPipelineCreationTime();
1998 return result;
1999}
2000
2002{
2003 if (inFrame && !ofr.active)
2004 return ensureContext(currentSwapChain->surface);
2005 else
2006 return ensureContext();
2007}
2008
2009void QRhiGles2::setQueueSubmitParams(QRhiNativeHandles *)
2010{
2011 // not applicable
2012}
2013
2015{
2016 if (!ensureContext())
2017 return;
2018
2019 for (uint shader : std::as_const(m_shaderCache))
2020 f->glDeleteShader(shader);
2021
2022 m_shaderCache.clear();
2023
2024 m_pipelineCache.clear();
2025}
2026
2028{
2029 return contextLost;
2030}
2031
2040
2042{
2043 Q_STATIC_ASSERT(sizeof(QGles2PipelineCacheDataHeader) == 256);
2044
2045 if (m_pipelineCache.data.isEmpty())
2046 return QByteArray();
2047
2049 memset(&header, 0, sizeof(header));
2050 header.rhiId = pipelineCacheRhiId();
2051 header.arch = quint32(sizeof(void*));
2052 header.programBinaryCount = m_pipelineCache.data.size();
2053 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
2054 if (driverStrLen)
2055 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
2056 header.driver[driverStrLen] = '\0';
2057
2058 const size_t dataOffset = sizeof(header);
2059 size_t dataSize = 0;
2060 for (auto it = m_pipelineCache.data.cbegin(), end = m_pipelineCache.data.cend(); it != end; ++it) {
2061 dataSize += sizeof(quint32) + it.key().size()
2062 + sizeof(quint32) + it->data.size()
2063 + sizeof(quint32);
2064 }
2065
2066 QByteArray buf(dataOffset + dataSize, Qt::Uninitialized);
2067 char *p = buf.data() + dataOffset;
2068 for (auto it = m_pipelineCache.data.cbegin(), end = m_pipelineCache.data.cend(); it != end; ++it) {
2069 const QByteArray key = it.key();
2070 const QByteArray data = it->data;
2071 const quint32 format = it->format;
2072
2073 quint32 i = key.size();
2074 memcpy(p, &i, 4);
2075 p += 4;
2076 memcpy(p, key.constData(), key.size());
2077 p += key.size();
2078
2079 i = data.size();
2080 memcpy(p, &i, 4);
2081 p += 4;
2082 memcpy(p, data.constData(), data.size());
2083 p += data.size();
2084
2085 memcpy(p, &format, 4);
2086 p += 4;
2087 }
2088 Q_ASSERT(p == buf.data() + dataOffset + dataSize);
2089
2090 header.dataSize = quint32(dataSize);
2091 memcpy(buf.data(), &header, sizeof(header));
2092
2093 return buf;
2094}
2095
2096void QRhiGles2::setPipelineCacheData(const QByteArray &data)
2097{
2098 if (data.isEmpty())
2099 return;
2100
2101 const size_t headerSize = sizeof(QGles2PipelineCacheDataHeader);
2102 if (data.size() < qsizetype(headerSize)) {
2103 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
2104 return;
2105 }
2106 const size_t dataOffset = headerSize;
2108 memcpy(&header, data.constData(), headerSize);
2109
2110 const quint32 rhiId = pipelineCacheRhiId();
2111 if (header.rhiId != rhiId) {
2112 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
2113 rhiId, header.rhiId);
2114 return;
2115 }
2116 const quint32 arch = quint32(sizeof(void*));
2117 if (header.arch != arch) {
2118 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
2119 arch, header.arch);
2120 return;
2121 }
2122 if (header.programBinaryCount == 0)
2123 return;
2124
2125 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
2126 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
2127 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OpenGL vendor/renderer/version does not match");
2128 return;
2129 }
2130
2131 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
2132 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
2133 return;
2134 }
2135
2136 m_pipelineCache.clear();
2137
2138 QRhiPipelineCacheDataReader reader(data.constData() + dataOffset, header.dataSize);
2139 for (quint32 i = 0; i < header.programBinaryCount; ++i) {
2140 QByteArray key;
2141 QByteArray binary;
2142 quint32 format = 0;
2143 if (!reader.readByteArray(&key) || !reader.readByteArray(&binary) || !reader.readUInt32(&format)) {
2144 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob (truncated or corrupt program binary data)");
2145 m_pipelineCache.clear();
2146 return;
2147 }
2148 m_pipelineCache.insertWithCapacityLimit(key, { format, binary });
2149 }
2150
2151 qCDebug(QRHI_LOG_INFO, "Seeded pipeline cache with %d program binaries", int(m_pipelineCache.data.size()));
2152}
2153
2154QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
2155 int sampleCount, QRhiRenderBuffer::Flags flags,
2156 QRhiTexture::Format backingFormatHint)
2157{
2158 return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
2159}
2160
2161QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format,
2162 const QSize &pixelSize, int depth, int arraySize,
2163 int sampleCount, QRhiTexture::Flags flags)
2164{
2165 return new QGles2Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
2166}
2167
2168QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
2169 QRhiSampler::Filter mipmapMode,
2170 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
2171{
2172 return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
2173}
2174
2175QRhiShadingRateMap *QRhiGles2::createShadingRateMap()
2176{
2177 return nullptr;
2178}
2179
2180QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
2181 QRhiTextureRenderTarget::Flags flags)
2182{
2183 return new QGles2TextureRenderTarget(this, desc, flags);
2184}
2185
2187{
2188 return new QGles2GraphicsPipeline(this);
2189}
2190
2192{
2193 return new QGles2ShaderResourceBindings(this);
2194}
2195
2197{
2198 return new QGles2ComputePipeline(this);
2199}
2200
2201void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
2202{
2203 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2206 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
2207
2208 if (pipelineChanged) {
2209 cbD->currentGraphicsPipeline = ps;
2210 cbD->currentComputePipeline = nullptr;
2212 if (psD->lastUsedInFrameNo != frameNo) {
2214 psD->currentSrb = nullptr;
2216 }
2217
2218 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2220 cmd.args.bindGraphicsPipeline.ps = ps;
2221 }
2222}
2223
2224void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
2225 int dynamicOffsetCount,
2226 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
2227{
2228 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2230 QGles2GraphicsPipeline *gfxPsD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
2231 QGles2ComputePipeline *compPsD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
2232
2233 if (!srb) {
2234 if (gfxPsD)
2235 srb = gfxPsD->m_shaderResourceBindings;
2236 else
2237 srb = compPsD->m_shaderResourceBindings;
2238 }
2239
2242 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2243 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
2244 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
2245 switch (b->type) {
2246 case QRhiShaderResourceBinding::UniformBuffer:
2247 // no BufUniformRead / AccessUniform because no real uniform buffers are used
2248 break;
2249 case QRhiShaderResourceBinding::SampledTexture:
2250 case QRhiShaderResourceBinding::Texture:
2251 for (int elem = 0; elem < b->stex.count(); ++elem) {
2252 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->stex.texSamplers[elem].tex);
2253 sanityCheckResourceOwnership(texD);
2254 trackedRegisterTexture(&passResTracker,
2255 texD,
2257 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
2258 }
2259 break;
2260 case QRhiShaderResourceBinding::ImageLoad:
2261 case QRhiShaderResourceBinding::ImageStore:
2262 case QRhiShaderResourceBinding::ImageLoadStore:
2263 {
2264 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
2265 sanityCheckResourceOwnership(texD);
2267 if (b->type == QRhiShaderResourceBinding::ImageLoad)
2269 else if (b->type == QRhiShaderResourceBinding::ImageStore)
2271 else
2273 trackedRegisterTexture(&passResTracker, texD, access,
2274 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
2275 }
2276 break;
2277 case QRhiShaderResourceBinding::BufferLoad:
2278 case QRhiShaderResourceBinding::BufferStore:
2279 case QRhiShaderResourceBinding::BufferLoadStore:
2280 {
2281 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
2282 sanityCheckResourceOwnership(bufD);
2284 if (b->type == QRhiShaderResourceBinding::BufferLoad)
2286 else if (b->type == QRhiShaderResourceBinding::BufferStore)
2288 else
2290 trackedRegisterBuffer(&passResTracker, bufD, access,
2291 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
2292 }
2293 break;
2294 default:
2295 break;
2296 }
2297 }
2298 }
2299
2300 bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
2301
2302 // The Command::BindShaderResources command generated below is what will
2303 // cause uniforms to be set (glUniformNxx). This needs some special
2304 // handling here in this backend without real uniform buffers, because,
2305 // like in other backends, we optimize out the setShaderResources when the
2306 // srb that was set before is attempted to be set again on the command
2307 // buffer, but that is incorrect if the same srb is now used with another
2308 // pipeline. (because that could mean a glUseProgram not followed by
2309 // up-to-date glUniform calls, i.e. with GL we have a strong dependency
2310 // between the pipeline (== program) and the srb, unlike other APIs) This
2311 // is the reason there is a second level of srb(+generation) tracking in
2312 // the pipeline objects.
2313 if (gfxPsD && (gfxPsD->currentSrb != srb || gfxPsD->currentSrbGeneration != srbD->generation)) {
2314 srbChanged = true;
2315 gfxPsD->currentSrb = srb;
2317 } else if (compPsD && (compPsD->currentSrb != srb || compPsD->currentSrbGeneration != srbD->generation)) {
2318 srbChanged = true;
2319 compPsD->currentSrb = srb;
2321 }
2322
2323 if (srbChanged || cbD->currentSrbGeneration != srbD->generation || srbD->hasDynamicOffset) {
2324 if (gfxPsD) {
2325 cbD->currentGraphicsSrb = srb;
2326 cbD->currentComputeSrb = nullptr;
2327 } else {
2328 cbD->currentGraphicsSrb = nullptr;
2329 cbD->currentComputeSrb = srb;
2330 }
2332
2333 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2335 cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
2336 cmd.args.bindShaderResources.maybeComputePs = compPsD;
2337 cmd.args.bindShaderResources.srb = srb;
2338 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
2339 cmd.args.bindShaderResources.dynamicOffsetPoolOffset = 0;
2340 if (srbD->hasDynamicOffset && dynamicOffsetCount > 0) {
2341 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
2342 cmd.args.bindShaderResources.dynamicOffsetPoolOffset = quint32(cbD->dynamicOffsetPool.size());
2343 for (int i = 0; i < dynamicOffsetCount; ++i) {
2344 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
2345 cbD->dynamicOffsetPool.append(uint(dynOfs.first));
2346 cbD->dynamicOffsetPool.append(dynOfs.second);
2347 }
2348 }
2349 }
2350}
2351
2352void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
2353 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
2354 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
2355{
2356 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2358 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2359
2360 for (int i = 0; i < bindingCount; ++i) {
2361 QRhiBuffer *buf = bindings[i].first;
2362 quint32 ofs = bindings[i].second;
2363 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
2364 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
2365
2366 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2368 cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
2369 cmd.args.bindVertexBuffer.buffer = bufD->buffer;
2370 cmd.args.bindVertexBuffer.offset = ofs;
2371 cmd.args.bindVertexBuffer.binding = startBinding + i;
2372
2376 }
2377 }
2378
2379 if (indexBuf) {
2380 QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
2381 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
2382
2383 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2385 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
2386 cmd.args.bindIndexBuffer.offset = indexOffset;
2387 cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
2388
2392 }
2393 }
2394}
2395
2396void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
2397{
2398 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2400
2401 const std::array<float, 4> r = viewport.viewport();
2402 // A negative width or height is an error. A negative x or y is not.
2403 if (r[2] < 0.0f || r[3] < 0.0f)
2404 return;
2405
2406 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2408 cmd.args.viewport.x = r[0];
2409 cmd.args.viewport.y = r[1];
2410 cmd.args.viewport.w = r[2];
2411 cmd.args.viewport.h = r[3];
2412 cmd.args.viewport.d0 = viewport.minDepth();
2413 cmd.args.viewport.d1 = viewport.maxDepth();
2414}
2415
2416void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
2417{
2418 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2420
2421 const std::array<int, 4> r = scissor.scissor();
2422 // A negative width or height is an error. A negative x or y is not.
2423 if (r[2] < 0 || r[3] < 0)
2424 return;
2425
2426 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2428 cmd.args.scissor.x = r[0];
2429 cmd.args.scissor.y = r[1];
2430 cmd.args.scissor.w = r[2];
2431 cmd.args.scissor.h = r[3];
2432}
2433
2434void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
2435{
2436 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2438
2439 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2441 cmd.args.blendConstants.r = c.redF();
2442 cmd.args.blendConstants.g = c.greenF();
2443 cmd.args.blendConstants.b = c.blueF();
2444 cmd.args.blendConstants.a = c.alphaF();
2445}
2446
2447void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
2448{
2449 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2451
2452 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2454 cmd.args.stencilRef.ref = refValue;
2455 cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
2456}
2457
2458void QRhiGles2::setPushConstants(QRhiCommandBuffer *cb, quint32 offset, quint32 size, const void *data)
2459{
2460 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2462
2463 QRhiGraphicsPipeline *maybeGraphicsPs = nullptr;
2464 QRhiComputePipeline *maybeComputePs = nullptr;
2465 quint32 blockSize = 0;
2467 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
2468 if (!psD)
2469 return;
2470 maybeComputePs = cbD->currentComputePipeline;
2471 blockSize = psD->pushConstantSize;
2472 } else {
2473 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
2474 if (!psD)
2475 return;
2476 maybeGraphicsPs = cbD->currentGraphicsPipeline;
2477 blockSize = psD->pushConstantSize;
2478 }
2479 if (!blockSize) {
2480 qWarning("No pipeline with a push constant block is active; setPushConstants ignored");
2481 return;
2482 }
2483
2484 if (offset + size > MAX_PUSH_CONSTANTS_SIZE) {
2485 qWarning("Push constant data of %u bytes at offset %u exceeds the %u byte maximum; "
2486 "setPushConstants ignored", size, offset, MAX_PUSH_CONSTANTS_SIZE);
2487 return;
2488 }
2489
2490 // A partial update has to be honored, so the block is kept on the command
2491 // buffer and the whole of it is submitted every time.
2492 const quint32 total = qMin(qMax(blockSize, offset + size), MAX_PUSH_CONSTANTS_SIZE);
2493 if (quint32(cbD->pushConstantData.size()) < total)
2494 cbD->pushConstantData.resize(int(total), 0);
2495 memcpy(cbD->pushConstantData.data() + offset, data, size);
2496
2497 const quint32 dataOffset = quint32(cbD->pushConstantPool.size());
2498 cbD->pushConstantPool.append(cbD->pushConstantData.constData(), total);
2499
2500 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2502 cmd.args.setPushConstants.maybeGraphicsPs = maybeGraphicsPs;
2503 cmd.args.setPushConstants.maybeComputePs = maybeComputePs;
2504 cmd.args.setPushConstants.dataOffset = dataOffset;
2505 cmd.args.setPushConstants.size = total;
2506}
2507
2508void QRhiGles2::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
2509{
2510 Q_UNUSED(cb);
2511 Q_UNUSED(coarsePixelSize);
2512}
2513
2514void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
2515 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2516{
2517 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2519
2520 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2522 cmd.args.draw.ps = cbD->currentGraphicsPipeline;
2523 cmd.args.draw.vertexCount = vertexCount;
2524 cmd.args.draw.firstVertex = firstVertex;
2525 cmd.args.draw.instanceCount = instanceCount;
2526 cmd.args.draw.baseInstance = firstInstance;
2527}
2528
2529void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
2530 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2531{
2532 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2534
2535 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2537 cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
2538 cmd.args.drawIndexed.indexCount = indexCount;
2539 cmd.args.drawIndexed.firstIndex = firstIndex;
2540 cmd.args.drawIndexed.instanceCount = instanceCount;
2541 cmd.args.drawIndexed.baseInstance = firstInstance;
2542 cmd.args.drawIndexed.baseVertex = vertexOffset;
2543}
2544
2545void QRhiGles2::drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
2546 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2547{
2548 if (!caps.drawIndirect)
2549 return;
2550
2551 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2553
2554 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2555
2557 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2558 trackedRegisterBuffer(&passResTracker, indirectBufD,
2561 }
2562
2563 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2565 cmd.args.drawIndirect.ps = cbD->currentGraphicsPipeline;
2566 cmd.args.drawIndirect.buffer = indirectBufD->buffer;
2567 cmd.args.drawIndirect.offset = indirectBufferOffset;
2568 cmd.args.drawIndirect.drawCount = drawCount;
2569 cmd.args.drawIndirect.stride = stride;
2570}
2571
2572void QRhiGles2::drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
2573 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2574{
2575 if (!caps.drawIndirect)
2576 return;
2577
2578 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2580
2581 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2582
2584 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2585 trackedRegisterBuffer(&passResTracker, indirectBufD,
2588 }
2589
2590 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2592 cmd.args.drawIndexedIndirect.ps = cbD->currentGraphicsPipeline;
2593 cmd.args.drawIndexedIndirect.buffer = indirectBufD->buffer;
2594 cmd.args.drawIndexedIndirect.offset = indirectBufferOffset;
2595 cmd.args.drawIndexedIndirect.drawCount = drawCount;
2596 cmd.args.drawIndexedIndirect.stride = stride;
2597}
2598
2599void QRhiGles2::drawIndirectCount(QRhiCommandBuffer *cb,
2600 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
2601 QRhiBuffer *countBuffer, quint32 countBufferOffset,
2602 quint32 maxDrawCount, quint32 stride)
2603{
2604 if (!caps.drawIndirectCount) {
2605 qWarning("drawIndirectCount called but the DrawIndirectCount feature is not supported");
2606 return;
2607 }
2608
2609 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2611
2612 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2613 QGles2Buffer *countBufD = QRHI_RES(QGles2Buffer, countBuffer);
2614
2616 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2617 trackedRegisterBuffer(&passResTracker, indirectBufD,
2620 trackedRegisterBuffer(&passResTracker, countBufD,
2623 }
2624
2625 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2627 cmd.args.drawIndirectCount.ps = cbD->currentGraphicsPipeline;
2628 cmd.args.drawIndirectCount.buffer = indirectBufD->buffer;
2629 cmd.args.drawIndirectCount.offset = indirectBufferOffset;
2630 cmd.args.drawIndirectCount.countBuffer = countBufD->buffer;
2631 cmd.args.drawIndirectCount.countOffset = countBufferOffset;
2632 cmd.args.drawIndirectCount.maxDrawCount = maxDrawCount;
2633 cmd.args.drawIndirectCount.stride = stride;
2634}
2635
2636void QRhiGles2::drawIndexedIndirectCount(QRhiCommandBuffer *cb,
2637 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
2638 QRhiBuffer *countBuffer, quint32 countBufferOffset,
2639 quint32 maxDrawCount, quint32 stride)
2640{
2641 if (!caps.drawIndirectCount) {
2642 qWarning("drawIndexedIndirectCount called but the DrawIndirectCount feature is not supported");
2643 return;
2644 }
2645
2646 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2648
2649 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2650 QGles2Buffer *countBufD = QRHI_RES(QGles2Buffer, countBuffer);
2651
2653 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2654 trackedRegisterBuffer(&passResTracker, indirectBufD,
2657 trackedRegisterBuffer(&passResTracker, countBufD,
2660 }
2661
2662 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2664 cmd.args.drawIndexedIndirectCount.ps = cbD->currentGraphicsPipeline;
2665 cmd.args.drawIndexedIndirectCount.buffer = indirectBufD->buffer;
2666 cmd.args.drawIndexedIndirectCount.offset = indirectBufferOffset;
2667 cmd.args.drawIndexedIndirectCount.countBuffer = countBufD->buffer;
2668 cmd.args.drawIndexedIndirectCount.countOffset = countBufferOffset;
2669 cmd.args.drawIndexedIndirectCount.maxDrawCount = maxDrawCount;
2670 cmd.args.drawIndexedIndirectCount.stride = stride;
2671}
2672
2673void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
2674{
2675 if (!debugMarkers)
2676 return;
2677
2678 Q_UNUSED(cb);
2679 Q_UNUSED(name);
2680}
2681
2682void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
2683{
2684 if (!debugMarkers)
2685 return;
2686
2687 Q_UNUSED(cb);
2688}
2689
2690void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
2691{
2692 if (!debugMarkers)
2693 return;
2694
2695 Q_UNUSED(cb);
2696 Q_UNUSED(msg);
2697}
2698
2699const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
2700{
2701 Q_UNUSED(cb);
2702 return nullptr;
2703}
2704
2705static inline void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type, GLuint tsQuery = 0)
2706{
2707 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2708 cmd.cmd = type;
2710 cmd.args.beginFrame.timestampQuery = tsQuery;
2712 cmd.args.endFrame.timestampQuery = tsQuery;
2713}
2714
2715void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
2716{
2717 if (ofr.active) {
2718 Q_ASSERT(!currentSwapChain);
2719 if (!ensureContext())
2720 return;
2721 } else {
2722 Q_ASSERT(currentSwapChain);
2723 if (!ensureContext(currentSwapChain->surface))
2724 return;
2725 }
2726
2727 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2728
2730 && !cbD->computePassState.writtenResources.isEmpty())
2731 {
2732 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2734 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
2735 }
2736
2738
2740
2741 // ARRAY_BUFFER and SHADER_STORAGE_BUFFER are context state, not vertex
2742 // array object state, so going back to object 0 does not clear them.
2743 if (vao)
2744 f->glBindVertexArray(0);
2745 f->glBindBuffer(GL_ARRAY_BUFFER, 0);
2746 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2747 if (caps.compute)
2748 f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
2749}
2750
2751void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
2752{
2753 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2754 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
2755
2757
2759 // Commands that come after this point need a resource tracker and also
2760 // a BarriersForPass command enqueued. (the ones we had from
2761 // beginPass() are now gone since beginExternal() processed all that
2762 // due to calling executeCommandBuffer()).
2764 }
2765
2767
2768 if (cbD->currentTarget)
2769 enqueueBindFramebuffer(cbD->currentTarget, cbD);
2770}
2771
2772double QRhiGles2::lastCompletedGpuTime(QRhiCommandBuffer *cb)
2773{
2774 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2775 return cbD->lastGpuTime;
2776}
2777
2779{
2780 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2781 if (!ensureContext(swapChainD->surface))
2782 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2783
2784 ctx->handle()->beginFrame();
2785
2786 currentSwapChain = swapChainD;
2787
2789 swapChainD->cb.resetState();
2790 frameNo += 1;
2791
2792 if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
2793 double elapsedSec = 0;
2794 if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, this, &elapsedSec))
2795 swapChainD->cb.lastGpuTime = elapsedSec;
2796 }
2797
2798 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2799 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2800 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2801
2802 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame, recordTimestamps ? tsStart : 0);
2803
2804 return QRhi::FrameOpSuccess;
2805}
2806
2807QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
2808{
2809 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2810 Q_ASSERT(currentSwapChain == swapChainD);
2811
2812 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2813 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2814 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2815 if (recordTimestamps) {
2816 swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
2818 }
2819
2820 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame, recordTimestamps ? tsEnd : 0);
2821
2822 if (!ensureContext(swapChainD->surface))
2823 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2824
2825 executeCommandBuffer(&swapChainD->cb);
2826
2827 if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
2828 ctx->swapBuffers(swapChainD->surface);
2830 } else {
2831 f->glFlush();
2832 }
2833
2834 currentSwapChain = nullptr;
2835
2836 ctx->handle()->endFrame();
2837
2838 return QRhi::FrameOpSuccess;
2839}
2840
2842{
2843 if (!ensureContext())
2844 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2845
2846 ofr.active = true;
2847
2849 ofr.cbWrapper.resetState();
2850
2851 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && caps.timestamps) {
2852 if (!ofr.tsQueries[0])
2853 f->glGenQueries(2, ofr.tsQueries);
2854 }
2855
2856 addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame, ofr.tsQueries[0]);
2857 *cb = &ofr.cbWrapper;
2858
2859 return QRhi::FrameOpSuccess;
2860}
2861
2862QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
2863{
2864 Q_UNUSED(flags);
2865 Q_ASSERT(ofr.active);
2866 ofr.active = false;
2867
2868 addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame, ofr.tsQueries[1]);
2869
2870 if (!ensureContext())
2871 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2872
2873 executeCommandBuffer(&ofr.cbWrapper);
2874
2875 // Just as endFrame() does a flush when skipping the swapBuffers(), do it
2876 // here as well. This has the added benefit of playing nice when rendering
2877 // to a texture from a context and then consuming that texture from
2878 // another, sharing context.
2879 f->glFlush();
2880
2881 if (ofr.tsQueries[0]) {
2882 quint64 timestamps[2];
2883 glGetQueryObjectui64v(ofr.tsQueries[1], GL_QUERY_RESULT, &timestamps[1]);
2884 glGetQueryObjectui64v(ofr.tsQueries[0], GL_QUERY_RESULT, &timestamps[0]);
2885 if (timestamps[1] >= timestamps[0]) {
2886 const quint64 nanoseconds = timestamps[1] - timestamps[0];
2887 ofr.cbWrapper.lastGpuTime = nanoseconds / 1000000000.0; // seconds
2888 }
2889 }
2890
2891 return QRhi::FrameOpSuccess;
2892}
2893
2895{
2896 if (inFrame) {
2897 if (ofr.active) {
2898 Q_ASSERT(!currentSwapChain);
2899 Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
2900 if (!ensureContext())
2901 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2902 executeCommandBuffer(&ofr.cbWrapper);
2903 ofr.cbWrapper.resetCommands();
2904 } else {
2905 Q_ASSERT(currentSwapChain);
2906 Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
2907 if (!ensureContext(currentSwapChain->surface))
2908 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2910 currentSwapChain->cb.resetCommands();
2911 }
2912 // Do an actual glFinish(). May seem superfluous, but this is what
2913 // matches most other backends e.g. Vulkan/Metal that do a heavyweight
2914 // wait-for-idle blocking in their finish(). More importantly, this
2915 // allows clients simply call finish() in threaded or shared context
2916 // situations where one explicitly needs to do a glFlush or Finish.
2917 f->glFinish();
2918 }
2919 return QRhi::FrameOpSuccess;
2920}
2921
2923{
2924 return access == QGles2Buffer::AccessStorageWrite
2926 || access == QGles2Buffer::AccessUpdate;
2927}
2928
2930{
2931 return access == QGles2Texture::AccessStorageWrite
2933 || access == QGles2Texture::AccessUpdate
2935}
2936
2946
2955
2957{
2958 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2959 if (!bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer))
2960 return;
2961
2962 const QGles2Buffer::Access prevAccess = bufD->usageState.access;
2963 if (access == prevAccess)
2964 return;
2965
2966 if (bufferAccessIsWrite(prevAccess)) {
2967 // Generating the minimal barrier set is way too complicated to do
2968 // correctly (prevAccess is overwritten so we won't have proper
2969 // tracking across multiple passes) so setting all barrier bits will do
2970 // for now.
2971 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2973 cmd.args.barrier.barriers = barriersForBuffer();
2974 }
2975
2976 bufD->usageState.access = access;
2977}
2978
2980{
2981 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2982 if (!texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
2983 return;
2984
2985 const QGles2Texture::Access prevAccess = texD->usageState.access;
2986 if (access == prevAccess)
2987 return;
2988
2989 if (textureAccessIsWrite(prevAccess)) {
2990 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2992 cmd.args.barrier.barriers = barriersForTexture();
2993 }
2994
2995 texD->usageState.access = access;
2996}
2997
2999 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
3000{
3002 const bool isCompressed = isCompressedFormat(texD->m_format);
3003 const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
3004 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3005 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
3006 const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
3007 const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
3008 const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
3009 const QPoint dp = subresDesc.destinationTopLeft();
3010 const QByteArray rawData = subresDesc.data();
3011
3012 auto setCmdByNotCompressedData = [&](const void* data, QSize size, quint32 dataStride)
3013 {
3014 quint32 bytesPerLine = 0;
3015 quint32 bytesPerPixel = 0;
3016 textureFormatInfo(texD->m_format, size, &bytesPerLine, nullptr, &bytesPerPixel);
3017
3018 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3020 cmd.args.subImage.target = texD->target;
3021 cmd.args.subImage.texture = texD->texture;
3022 cmd.args.subImage.faceTarget = effectiveTarget;
3023 cmd.args.subImage.level = level;
3024 cmd.args.subImage.dx = dp.x();
3025 cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
3026 cmd.args.subImage.dz = is3D || isArray ? layer : 0;
3027 cmd.args.subImage.w = size.width();
3028 cmd.args.subImage.h = size.height();
3029 cmd.args.subImage.glformat = texD->glformat;
3030 cmd.args.subImage.gltype = texD->gltype;
3031
3032 if (dataStride == 0)
3033 dataStride = bytesPerLine;
3034
3035 cmd.args.subImage.rowStartAlign = (dataStride & 3) ? 1 : 4;
3036 cmd.args.subImage.rowLength = caps.unpackRowLength ? (bytesPerPixel ? dataStride / bytesPerPixel : 0) : 0;
3037
3038 cmd.args.subImage.data = data;
3039 };
3040
3041 if (!subresDesc.image().isNull()) {
3042 QImage img = subresDesc.image();
3043 QSize size = img.size();
3044 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3045 const QPoint sp = subresDesc.sourceTopLeft();
3046 if (!subresDesc.sourceSize().isEmpty())
3047 size = subresDesc.sourceSize();
3048 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3049 if (caps.unpackRowLength) {
3050 cbD->retainImage(img);
3051 // create a non-owning wrapper for the subimage
3052 const uchar *data = img.constBits() + sp.y() * img.bytesPerLine() + sp.x() * (qMax(1, img.depth() / 8));
3053 img = QImage(data, size.width(), size.height(), img.bytesPerLine(), img.format());
3054 } else {
3055 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
3056 }
3057 } else {
3058 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3059 }
3060
3061 setCmdByNotCompressedData(cbD->retainImage(img), size, img.bytesPerLine());
3062 } else if (!rawData.isEmpty() && isCompressed) {
3063 const int depth = qMax(1, texD->m_depth);
3064 const int arraySize = qMax(0, texD->m_arraySize);
3065 if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray)
3066 && !texD->zeroInitialized)
3067 {
3068 // Create on first upload since glCompressedTexImage2D cannot take
3069 // nullptr data. We have a rule in the QRhi docs that the first
3070 // upload for a compressed texture must cover the entire image, but
3071 // that is clearly not ideal when building a texture atlas, or when
3072 // having a 3D texture with per-slice data.
3073 quint32 levelByteSize = 0;
3074 compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &levelByteSize, nullptr);
3075 quint64 byteSize = levelByteSize;
3076 if (is3D)
3077 byteSize *= quint64(depth);
3078 if (isArray)
3079 byteSize *= quint64(arraySize);
3080 if (byteSize > quint64(std::numeric_limits<int>::max())) {
3081 qWarning("Compressed texture zero-initialization would need %llu bytes "
3082 "which is too large; skipping", byteSize);
3083 return;
3084 }
3085 QByteArray zeroBuf(qsizetype(byteSize), 0);
3086 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3088 cmd.args.compressedImage.target = texD->target;
3089 cmd.args.compressedImage.texture = texD->texture;
3090 cmd.args.compressedImage.faceTarget = effectiveTarget;
3091 cmd.args.compressedImage.level = level;
3092 cmd.args.compressedImage.glintformat = texD->glintformat;
3093 cmd.args.compressedImage.w = texD->m_pixelSize.width();
3094 cmd.args.compressedImage.h = is1D && isArray ? arraySize : texD->m_pixelSize.height();
3095 cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
3096 cmd.args.compressedImage.size = int(byteSize);
3097 cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
3098 texD->zeroInitialized = true;
3099 }
3100
3101 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
3102 : subresDesc.sourceSize();
3103 if (texD->specified || texD->zeroInitialized) {
3104 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3106 cmd.args.compressedSubImage.target = texD->target;
3107 cmd.args.compressedSubImage.texture = texD->texture;
3108 cmd.args.compressedSubImage.faceTarget = effectiveTarget;
3109 cmd.args.compressedSubImage.level = level;
3110 cmd.args.compressedSubImage.dx = dp.x();
3111 cmd.args.compressedSubImage.dy = is1D && isArray ? layer : dp.y();
3112 cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
3113 cmd.args.compressedSubImage.w = size.width();
3114 cmd.args.compressedSubImage.h = size.height();
3115 cmd.args.compressedSubImage.glintformat = texD->glintformat;
3116 cmd.args.compressedSubImage.size = rawData.size();
3117 cmd.args.compressedSubImage.data = cbD->retainData(rawData);
3118 } else {
3119 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3121 cmd.args.compressedImage.target = texD->target;
3122 cmd.args.compressedImage.texture = texD->texture;
3123 cmd.args.compressedImage.faceTarget = effectiveTarget;
3124 cmd.args.compressedImage.level = level;
3125 cmd.args.compressedImage.glintformat = texD->glintformat;
3126 cmd.args.compressedImage.w = size.width();
3127 cmd.args.compressedImage.h = is1D && isArray ? arraySize : size.height();
3128 cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
3129 cmd.args.compressedImage.size = rawData.size();
3130 cmd.args.compressedImage.data = cbD->retainData(rawData);
3131 }
3132 } else if (!rawData.isEmpty()) {
3133 QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
3134 : subresDesc.sourceSize();
3135 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3136 quint32 bytesPerPixel = 0;
3137 textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
3138 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3139 bytesPerPixel, rawData.size());
3140 if (size.isEmpty())
3141 return;
3142
3143 setCmdByNotCompressedData(cbD->retainData(rawData), size, subresDesc.dataStride());
3144 } else {
3145 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3146 }
3147}
3148
3149void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3150{
3151 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
3153
3154 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
3155 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
3157 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3158 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3159 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
3160 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
3161 } else {
3163 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3165 cmd.args.bufferSubData.target = bufD->targetForDataOps;
3166 cmd.args.bufferSubData.buffer = bufD->buffer;
3167 cmd.args.bufferSubData.offset = u.offset;
3168 cmd.args.bufferSubData.size = u.data.size();
3169 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
3170 }
3172 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3173 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3174 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
3175 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
3176 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
3177 } else {
3179 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3181 cmd.args.bufferSubData.target = bufD->targetForDataOps;
3182 cmd.args.bufferSubData.buffer = bufD->buffer;
3183 cmd.args.bufferSubData.offset = u.offset;
3184 cmd.args.bufferSubData.size = u.data.size();
3185 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
3186 }
3188 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3189 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
3190 u.result->data.resize(u.readSize);
3191 memcpy(u.result->data.data(), bufD->data.constData() + u.offset, size_t(u.readSize));
3192 if (u.result->completed)
3193 u.result->completed();
3194 } else {
3195 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3197 cmd.args.getBufferSubData.result = u.result;
3198 cmd.args.getBufferSubData.target = bufD->targetForDataOps;
3199 cmd.args.getBufferSubData.buffer = bufD->buffer;
3200 cmd.args.getBufferSubData.offset = u.offset;
3201 cmd.args.getBufferSubData.size = u.readSize;
3202 }
3204 if (!caps.copyBuffer)
3205 continue;
3206
3207 QGles2Buffer *dstD = QRHI_RES(QGles2Buffer, u.buf);
3208 QGles2Buffer *srcD = QRHI_RES(QGles2Buffer, u.src);
3209 Q_ASSERT(dstD->buffer && srcD->buffer);
3210
3213
3214 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3216 cmd.args.copyBuf.srcBuffer = srcD->buffer;
3217 cmd.args.copyBuf.dstBuffer = dstD->buffer;
3218 cmd.args.copyBuf.srcOffset = u.srcOffset;
3219 cmd.args.copyBuf.dstOffset = u.offset;
3220 cmd.args.copyBuf.size = u.readSize;
3222 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3223 Q_ASSERT(bufD->buffer);
3224
3226
3227 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3228 if (caps.clearBuffer) {
3230 cmd.args.clearBufferSubData.target = bufD->targetForDataOps;
3231 cmd.args.clearBufferSubData.buffer = bufD->buffer;
3232 cmd.args.clearBufferSubData.offset = u.offset;
3233 cmd.args.clearBufferSubData.size = u.readSize;
3234 cmd.args.clearBufferSubData.value = u.fillValue;
3235 } else {
3236 QRhiBufferData fillData;
3237 fillData.assign(QByteArray(u.readSize, char(u.fillValue)));
3239 cmd.args.bufferSubData.target = bufD->targetForDataOps;
3240 cmd.args.bufferSubData.buffer = bufD->buffer;
3241 cmd.args.bufferSubData.offset = u.offset;
3242 cmd.args.bufferSubData.size = u.readSize;
3243 cmd.args.bufferSubData.data = cbD->retainBufferData(fillData);
3244 }
3245 }
3246 }
3247
3248 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
3249 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
3251 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
3252 for (const auto &subres : u.subresDesc)
3253 enqueueSubresUpload(texD, cbD, subres.layer, subres.level, subres.desc);
3254 texD->specified = true;
3256 Q_ASSERT(u.src && u.dst);
3257 QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.src);
3258 QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.dst);
3259
3262
3263 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3264 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3265 // do not translate coordinates, even if sp is bottom-left from gl's pov
3266 const QPoint sp = u.desc.sourceTopLeft();
3267 const QPoint dp = u.desc.destinationTopLeft();
3268
3269 const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
3270 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
3271 const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
3272 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
3273
3274 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3276
3277 const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
3278 const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
3279 const bool dstIs1dArray = dstD->m_flags.testFlag(QRhiTexture::OneDimensional)
3280 && dstD->m_flags.testFlag(QRhiTexture::TextureArray);
3281
3282 cmd.args.copyTex.srcTarget = srcD->target;
3283 cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
3284 cmd.args.copyTex.srcTexture = srcD->texture;
3285 cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
3286 cmd.args.copyTex.srcX = sp.x();
3287 cmd.args.copyTex.srcY = sp.y();
3288 cmd.args.copyTex.srcZ = srcHasZ ? u.desc.sourceLayer() : 0;
3289
3290 cmd.args.copyTex.dstTarget = dstD->target;
3291 cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + (dstHasZ ? 0u : uint(u.desc.destinationLayer()));
3292 cmd.args.copyTex.dstTexture = dstD->texture;
3293 cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
3294 cmd.args.copyTex.dstX = dp.x();
3295 cmd.args.copyTex.dstY = dstIs1dArray ? u.desc.destinationLayer() : dp.y();
3296 cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
3297
3298 cmd.args.copyTex.w = copySize.width();
3299 cmd.args.copyTex.h = copySize.height();
3301 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3303 cmd.args.readPixels.result = u.result;
3304 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.rb.texture());
3305 if (texD)
3307 cmd.args.readPixels.texture = texD ? texD->texture : 0;
3308 cmd.args.readPixels.slice3D = -1;
3309 if (texD) {
3310 if (u.rb.rect().isValid()) {
3311 cmd.args.readPixels.x = u.rb.rect().x();
3312 cmd.args.readPixels.y = u.rb.rect().y();
3313 cmd.args.readPixels.w = u.rb.rect().width();
3314 cmd.args.readPixels.h = u.rb.rect().height();
3315 }
3316 else {
3317 const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
3318 cmd.args.readPixels.x = 0;
3319 cmd.args.readPixels.y = 0;
3320 cmd.args.readPixels.w = readImageSize.width();
3321 cmd.args.readPixels.h = readImageSize.height();
3322 }
3323 cmd.args.readPixels.format = texD->m_format;
3324 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
3325 || texD->m_flags.testFlag(QRhiTexture::TextureArray))
3326 {
3327 cmd.args.readPixels.readTarget = texD->target;
3328 cmd.args.readPixels.slice3D = u.rb.layer();
3329 } else {
3330 const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
3331 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
3332 cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer());
3333 }
3334 cmd.args.readPixels.level = u.rb.level();
3335 }
3336 else { // swapchain
3337 if (u.rb.rect().isValid()) {
3338 cmd.args.readPixels.x = u.rb.rect().x();
3339 cmd.args.readPixels.y = u.rb.rect().y();
3340 cmd.args.readPixels.w = u.rb.rect().width();
3341 cmd.args.readPixels.h = u.rb.rect().height();
3342 }
3343 else {
3344 cmd.args.readPixels.x = 0;
3345 cmd.args.readPixels.y = 0;
3346 cmd.args.readPixels.w = currentSwapChain->pixelSize.width();
3347 cmd.args.readPixels.h = currentSwapChain->pixelSize.height();
3348 }
3349 }
3351 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
3353 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3355 cmd.args.genMip.target = texD->target;
3356 cmd.args.genMip.texture = texD->texture;
3357 }
3358 }
3359
3360 ud->free();
3361}
3362
3363static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
3364{
3365 switch (t) {
3366 case QRhiGraphicsPipeline::Triangles:
3367 return GL_TRIANGLES;
3368 case QRhiGraphicsPipeline::TriangleStrip:
3369 return GL_TRIANGLE_STRIP;
3370 case QRhiGraphicsPipeline::TriangleFan:
3371 return GL_TRIANGLE_FAN;
3372 case QRhiGraphicsPipeline::Lines:
3373 return GL_LINES;
3374 case QRhiGraphicsPipeline::LineStrip:
3375 return GL_LINE_STRIP;
3376 case QRhiGraphicsPipeline::Points:
3377 return GL_POINTS;
3378 case QRhiGraphicsPipeline::Patches:
3379 return GL_PATCHES;
3380 default:
3381 Q_UNREACHABLE_RETURN(GL_TRIANGLES);
3382 }
3383}
3384
3385static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
3386{
3387 switch (c) {
3388 case QRhiGraphicsPipeline::Front:
3389 return GL_FRONT;
3390 case QRhiGraphicsPipeline::Back:
3391 return GL_BACK;
3392 default:
3393 Q_UNREACHABLE_RETURN(GL_BACK);
3394 }
3395}
3396
3397static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
3398{
3399 switch (f) {
3400 case QRhiGraphicsPipeline::CCW:
3401 return GL_CCW;
3402 case QRhiGraphicsPipeline::CW:
3403 return GL_CW;
3404 default:
3405 Q_UNREACHABLE_RETURN(GL_CCW);
3406 }
3407}
3408
3409static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
3410{
3411 switch (f) {
3412 case QRhiGraphicsPipeline::Zero:
3413 return GL_ZERO;
3414 case QRhiGraphicsPipeline::One:
3415 return GL_ONE;
3416 case QRhiGraphicsPipeline::SrcColor:
3417 return GL_SRC_COLOR;
3418 case QRhiGraphicsPipeline::OneMinusSrcColor:
3419 return GL_ONE_MINUS_SRC_COLOR;
3420 case QRhiGraphicsPipeline::DstColor:
3421 return GL_DST_COLOR;
3422 case QRhiGraphicsPipeline::OneMinusDstColor:
3423 return GL_ONE_MINUS_DST_COLOR;
3424 case QRhiGraphicsPipeline::SrcAlpha:
3425 return GL_SRC_ALPHA;
3426 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
3427 return GL_ONE_MINUS_SRC_ALPHA;
3428 case QRhiGraphicsPipeline::DstAlpha:
3429 return GL_DST_ALPHA;
3430 case QRhiGraphicsPipeline::OneMinusDstAlpha:
3431 return GL_ONE_MINUS_DST_ALPHA;
3432 case QRhiGraphicsPipeline::ConstantColor:
3433 return GL_CONSTANT_COLOR;
3434 case QRhiGraphicsPipeline::OneMinusConstantColor:
3436 case QRhiGraphicsPipeline::ConstantAlpha:
3437 return GL_CONSTANT_ALPHA;
3438 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
3440 case QRhiGraphicsPipeline::SrcAlphaSaturate:
3441 return GL_SRC_ALPHA_SATURATE;
3442 case QRhiGraphicsPipeline::Src1Color:
3443 case QRhiGraphicsPipeline::OneMinusSrc1Color:
3444 case QRhiGraphicsPipeline::Src1Alpha:
3445 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
3446 qWarning("Unsupported blend factor %d", f);
3447 return GL_ZERO;
3448 default:
3449 Q_UNREACHABLE_RETURN(GL_ZERO);
3450 }
3451}
3452
3453static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
3454{
3455 switch (op) {
3456 case QRhiGraphicsPipeline::Add:
3457 return GL_FUNC_ADD;
3458 case QRhiGraphicsPipeline::Subtract:
3459 return GL_FUNC_SUBTRACT;
3460 case QRhiGraphicsPipeline::ReverseSubtract:
3462 case QRhiGraphicsPipeline::Min:
3463 return GL_MIN;
3464 case QRhiGraphicsPipeline::Max:
3465 return GL_MAX;
3466 default:
3467 Q_UNREACHABLE_RETURN(GL_FUNC_ADD);
3468 }
3469}
3470
3471static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
3472{
3473 switch (op) {
3474 case QRhiGraphicsPipeline::Never:
3475 return GL_NEVER;
3476 case QRhiGraphicsPipeline::Less:
3477 return GL_LESS;
3478 case QRhiGraphicsPipeline::Equal:
3479 return GL_EQUAL;
3480 case QRhiGraphicsPipeline::LessOrEqual:
3481 return GL_LEQUAL;
3482 case QRhiGraphicsPipeline::Greater:
3483 return GL_GREATER;
3484 case QRhiGraphicsPipeline::NotEqual:
3485 return GL_NOTEQUAL;
3486 case QRhiGraphicsPipeline::GreaterOrEqual:
3487 return GL_GEQUAL;
3488 case QRhiGraphicsPipeline::Always:
3489 return GL_ALWAYS;
3490 default:
3491 Q_UNREACHABLE_RETURN(GL_ALWAYS);
3492 }
3493}
3494
3495static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
3496{
3497 switch (op) {
3498 case QRhiGraphicsPipeline::StencilZero:
3499 return GL_ZERO;
3500 case QRhiGraphicsPipeline::Keep:
3501 return GL_KEEP;
3502 case QRhiGraphicsPipeline::Replace:
3503 return GL_REPLACE;
3504 case QRhiGraphicsPipeline::IncrementAndClamp:
3505 return GL_INCR;
3506 case QRhiGraphicsPipeline::DecrementAndClamp:
3507 return GL_DECR;
3508 case QRhiGraphicsPipeline::Invert:
3509 return GL_INVERT;
3510 case QRhiGraphicsPipeline::IncrementAndWrap:
3511 return GL_INCR_WRAP;
3512 case QRhiGraphicsPipeline::DecrementAndWrap:
3513 return GL_DECR_WRAP;
3514 default:
3515 Q_UNREACHABLE_RETURN(GL_KEEP);
3516 }
3517}
3518
3519static inline GLenum toGlPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
3520{
3521 switch (mode) {
3522 case QRhiGraphicsPipeline::PolygonMode::Fill:
3523 return GL_FILL;
3524 case QRhiGraphicsPipeline::PolygonMode::Line:
3525 return GL_LINE;
3526 default:
3527 Q_UNREACHABLE_RETURN(GL_FILL);
3528 }
3529}
3530
3531static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
3532{
3533 switch (f) {
3534 case QRhiSampler::Nearest:
3535 if (m == QRhiSampler::None)
3536 return GL_NEAREST;
3537 else
3538 return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
3539 case QRhiSampler::Linear:
3540 if (m == QRhiSampler::None)
3541 return GL_LINEAR;
3542 else
3543 return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
3544 default:
3545 Q_UNREACHABLE_RETURN(GL_LINEAR);
3546 }
3547}
3548
3549static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
3550{
3551 switch (f) {
3552 case QRhiSampler::Nearest:
3553 return GL_NEAREST;
3554 case QRhiSampler::Linear:
3555 return GL_LINEAR;
3556 default:
3557 Q_UNREACHABLE_RETURN(GL_LINEAR);
3558 }
3559}
3560
3561static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
3562{
3563 switch (m) {
3564 case QRhiSampler::Repeat:
3565 return GL_REPEAT;
3566 case QRhiSampler::ClampToEdge:
3567 return GL_CLAMP_TO_EDGE;
3568 case QRhiSampler::Mirror:
3569 return GL_MIRRORED_REPEAT;
3570 default:
3571 Q_UNREACHABLE_RETURN(GL_CLAMP_TO_EDGE);
3572 }
3573}
3574
3575static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
3576{
3577 switch (op) {
3578 case QRhiSampler::Never:
3579 return GL_NEVER;
3580 case QRhiSampler::Less:
3581 return GL_LESS;
3582 case QRhiSampler::Equal:
3583 return GL_EQUAL;
3584 case QRhiSampler::LessOrEqual:
3585 return GL_LEQUAL;
3586 case QRhiSampler::Greater:
3587 return GL_GREATER;
3588 case QRhiSampler::NotEqual:
3589 return GL_NOTEQUAL;
3590 case QRhiSampler::GreaterOrEqual:
3591 return GL_GEQUAL;
3592 case QRhiSampler::Always:
3593 return GL_ALWAYS;
3594 default:
3595 Q_UNREACHABLE_RETURN(GL_NEVER);
3596 }
3597}
3598
3622
3624{
3626 u.layout = 0; // N/A
3627 u.access = bufUsage.access;
3628 u.stage = 0; // N/A
3629 return u;
3630}
3631
3654
3656{
3658 u.layout = 0; // N/A
3659 u.access = texUsage.access;
3660 u.stage = 0; // N/A
3661 return u;
3662}
3663
3665 QGles2Buffer *bufD,
3668{
3670 passResTracker->registerBuffer(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
3671 u.access = toGlAccess(access);
3672}
3673
3675 QGles2Texture *texD,
3678{
3680 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
3681 u.access = toGlAccess(access);
3682}
3683
3703
3704// Helper that must be used in executeCommandBuffer() whenever changing the
3705// ARRAY or ELEMENT_ARRAY buffer binding outside of Command::BindVertexBuffer
3706// and Command::BindIndexBuffer.
3708 QOpenGLExtensions *f,
3709 GLenum target,
3710 GLuint buffer)
3711{
3712 state->currentArrayBuffer = 0;
3713 state->currentElementArrayBuffer = 0;
3714 state->lastBindVertexBuffer.buffer = 0;
3715 f->glBindBuffer(target, buffer);
3716}
3717
3718void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
3719{
3721 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
3722
3723 for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
3724 const QGles2CommandBuffer::Command &cmd(*it);
3725 switch (cmd.cmd) {
3727 if (cmd.args.beginFrame.timestampQuery)
3728 glQueryCounter(cmd.args.beginFrame.timestampQuery, GL_TIMESTAMP);
3729 if (caps.vertexArrayObject) {
3730 if (!vao)
3731 f->glGenVertexArrays(1, &vao);
3732 f->glBindVertexArray(vao);
3733 }
3734 break;
3736 if (state.instancedAttributesUsed) {
3737 for (int i = 0; i < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; ++i) {
3738 if (state.nonzeroAttribDivisor[i])
3739 f->glVertexAttribDivisor(GLuint(i), 0);
3740 }
3742 f->glVertexAttribDivisor(GLuint(i), 0);
3743 state.instancedAttributesUsed = false;
3744 }
3745 // The enables are vertex array object state, so without this they
3746 // would persist across frames, pointing into deleted buffers.
3747 for (int i = 0; i < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; ++i) {
3748 if (state.enabledAttribArrays[i]) {
3749 f->glDisableVertexAttribArray(GLuint(i));
3750 state.enabledAttribArrays[i] = false;
3751 }
3752 }
3753 if (vao)
3754 f->glBindVertexArray(0);
3755 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3756 if (cmd.args.endFrame.timestampQuery)
3757 glQueryCounter(cmd.args.endFrame.timestampQuery, GL_TIMESTAMP);
3758 break;
3760 if (vao)
3761 f->glBindVertexArray(vao);
3762 break;
3764 f->glViewport(GLint(cmd.args.viewport.x), GLint(cmd.args.viewport.y), GLsizei(cmd.args.viewport.w), GLsizei(cmd.args.viewport.h));
3765 f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
3766 break;
3768 f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
3769 break;
3771 f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
3772 break;
3774 {
3775 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps);
3776 if (psD) {
3777 const GLint ref = GLint(cmd.args.stencilRef.ref);
3778 f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), ref, psD->m_stencilReadMask);
3779 f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), ref, psD->m_stencilReadMask);
3780 cbD->graphicsPassState.dynamic.stencilRef = ref;
3781 } else {
3782 qWarning("No graphics pipeline active for setStencilRef; ignored");
3783 }
3784 }
3785 break;
3787 {
3788 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
3789 if (psD) {
3790 if (state.lastBindVertexBuffer.ps == psD
3791 && state.lastBindVertexBuffer.buffer == cmd.args.bindVertexBuffer.buffer
3792 && state.lastBindVertexBuffer.offset == cmd.args.bindVertexBuffer.offset
3793 && state.lastBindVertexBuffer.binding == cmd.args.bindVertexBuffer.binding)
3794 {
3795 // The pipeline and so the vertex input layout is
3796 // immutable, no point in issuing the exact same set of
3797 // glVertexAttribPointer again and again for the same buffer.
3798 break;
3799 }
3800 state.lastBindVertexBuffer.ps = psD;
3801 state.lastBindVertexBuffer.buffer = cmd.args.bindVertexBuffer.buffer;
3802 state.lastBindVertexBuffer.offset = cmd.args.bindVertexBuffer.offset;
3803 state.lastBindVertexBuffer.binding = cmd.args.bindVertexBuffer.binding;
3804
3805 if (cmd.args.bindVertexBuffer.buffer != state.currentArrayBuffer) {
3806 state.currentArrayBuffer = cmd.args.bindVertexBuffer.buffer;
3807 // we do not support more than one vertex buffer
3808 f->glBindBuffer(GL_ARRAY_BUFFER, state.currentArrayBuffer);
3809 }
3810 for (auto it = psD->m_vertexInputLayout.cbeginAttributes(), itEnd = psD->m_vertexInputLayout.cendAttributes();
3811 it != itEnd; ++it)
3812 {
3813 const int bindingIdx = it->binding();
3814 if (bindingIdx != cmd.args.bindVertexBuffer.binding)
3815 continue;
3816
3817 const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(bindingIdx);
3818 const int stride = int(inputBinding->stride());
3819 int size = 1;
3820 GLenum type = GL_FLOAT;
3821 bool normalize = false;
3822 switch (it->format()) {
3823 case QRhiVertexInputAttribute::Float4:
3824 type = GL_FLOAT;
3825 size = 4;
3826 break;
3827 case QRhiVertexInputAttribute::Float3:
3828 type = GL_FLOAT;
3829 size = 3;
3830 break;
3831 case QRhiVertexInputAttribute::Float2:
3832 type = GL_FLOAT;
3833 size = 2;
3834 break;
3835 case QRhiVertexInputAttribute::Float:
3836 type = GL_FLOAT;
3837 size = 1;
3838 break;
3839 case QRhiVertexInputAttribute::UNormByte4:
3840 type = GL_UNSIGNED_BYTE;
3841 normalize = true;
3842 size = 4;
3843 break;
3844 case QRhiVertexInputAttribute::UNormByte2:
3845 type = GL_UNSIGNED_BYTE;
3846 normalize = true;
3847 size = 2;
3848 break;
3849 case QRhiVertexInputAttribute::UNormByte:
3850 type = GL_UNSIGNED_BYTE;
3851 normalize = true;
3852 size = 1;
3853 break;
3854 case QRhiVertexInputAttribute::UInt4:
3855 type = GL_UNSIGNED_INT;
3856 size = 4;
3857 break;
3858 case QRhiVertexInputAttribute::UInt3:
3859 type = GL_UNSIGNED_INT;
3860 size = 3;
3861 break;
3862 case QRhiVertexInputAttribute::UInt2:
3863 type = GL_UNSIGNED_INT;
3864 size = 2;
3865 break;
3866 case QRhiVertexInputAttribute::UInt:
3867 type = GL_UNSIGNED_INT;
3868 size = 1;
3869 break;
3870 case QRhiVertexInputAttribute::SInt4:
3871 type = GL_INT;
3872 size = 4;
3873 break;
3874 case QRhiVertexInputAttribute::SInt3:
3875 type = GL_INT;
3876 size = 3;
3877 break;
3878 case QRhiVertexInputAttribute::SInt2:
3879 type = GL_INT;
3880 size = 2;
3881 break;
3882 case QRhiVertexInputAttribute::SInt:
3883 type = GL_INT;
3884 size = 1;
3885 break;
3886 case QRhiVertexInputAttribute::Half4:
3887 type = GL_HALF_FLOAT;
3888 size = 4;
3889 break;
3890 case QRhiVertexInputAttribute::Half3:
3891 type = GL_HALF_FLOAT;
3892 size = 3;
3893 break;
3894 case QRhiVertexInputAttribute::Half2:
3895 type = GL_HALF_FLOAT;
3896 size = 2;
3897 break;
3898 case QRhiVertexInputAttribute::Half:
3899 type = GL_HALF_FLOAT;
3900 size = 1;
3901 break;
3902 case QRhiVertexInputAttribute::UShort4:
3903 type = GL_UNSIGNED_SHORT;
3904 size = 4;
3905 break;
3906 case QRhiVertexInputAttribute::UShort3:
3907 type = GL_UNSIGNED_SHORT;
3908 size = 3;
3909 break;
3910 case QRhiVertexInputAttribute::UShort2:
3911 type = GL_UNSIGNED_SHORT;
3912 size = 2;
3913 break;
3914 case QRhiVertexInputAttribute::UShort:
3915 type = GL_UNSIGNED_SHORT;
3916 size = 1;
3917 break;
3918 case QRhiVertexInputAttribute::SShort4:
3919 type = GL_SHORT;
3920 size = 4;
3921 break;
3922 case QRhiVertexInputAttribute::SShort3:
3923 type = GL_SHORT;
3924 size = 3;
3925 break;
3926 case QRhiVertexInputAttribute::SShort2:
3927 type = GL_SHORT;
3928 size = 2;
3929 break;
3930 case QRhiVertexInputAttribute::SShort:
3931 type = GL_SHORT;
3932 size = 1;
3933 break;
3934 default:
3935 break;
3936 }
3937
3938 const int locationIdx = it->location();
3939 quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset;
3940 if (type == GL_UNSIGNED_INT || type == GL_INT) {
3941 if (caps.intAttributes) {
3942 f->glVertexAttribIPointer(GLuint(locationIdx), size, type, stride,
3943 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3944 } else {
3945 qWarning("Current RHI backend does not support IntAttributes. Check supported features.");
3946 // This is a trick to disable this attribute
3948 state.enabledAttribArrays[locationIdx] = true;
3949 }
3950 } else {
3951 f->glVertexAttribPointer(GLuint(locationIdx), size, type, normalize, stride,
3952 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3953 }
3954 if (locationIdx >= CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT || !state.enabledAttribArrays[locationIdx]) {
3956 state.enabledAttribArrays[locationIdx] = true;
3957 f->glEnableVertexAttribArray(GLuint(locationIdx));
3958 }
3959 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing) {
3960 f->glVertexAttribDivisor(GLuint(locationIdx), inputBinding->instanceStepRate());
3961 if (Q_LIKELY(locationIdx < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT))
3962 state.nonzeroAttribDivisor[locationIdx] = true;
3963 else
3965 state.instancedAttributesUsed = true;
3967 && state.nonzeroAttribDivisor[locationIdx])
3968 || Q_UNLIKELY(locationIdx >= CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT
3969 && locationIdx <= state.maxUntrackedInstancedAttribute))
3970 {
3971 f->glVertexAttribDivisor(GLuint(locationIdx), 0);
3973 state.nonzeroAttribDivisor[locationIdx] = false;
3974 }
3975 }
3976 } else {
3977 qWarning("No graphics pipeline active for setVertexInput; ignored");
3978 }
3979 }
3980 break;
3982 state.indexType = cmd.args.bindIndexBuffer.type;
3983 state.indexStride = state.indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
3984 state.indexOffset = cmd.args.bindIndexBuffer.offset;
3985 if (state.currentElementArrayBuffer != cmd.args.bindIndexBuffer.buffer) {
3986 state.currentElementArrayBuffer = cmd.args.bindIndexBuffer.buffer;
3987 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.currentElementArrayBuffer);
3988 }
3989 break;
3991 {
3992 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps);
3993 if (psD) {
3994 const bool useBaseInstance = (cmd.args.draw.baseInstance != 0 && caps.baseInstance);
3995 // Same rationale as DrawIndexed below.
3996 const bool needsInstancedPath = caps.instancing
3997 && (cmd.args.draw.instanceCount != 1 || useBaseInstance);
3998
3999 if (!needsInstancedPath) {
4000 f->glDrawArrays(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount));
4001 } else if (useBaseInstance) {
4003 GLint(cmd.args.draw.firstVertex),
4004 GLsizei(cmd.args.draw.vertexCount),
4005 GLsizei(cmd.args.draw.instanceCount),
4006 cmd.args.draw.baseInstance);
4007 } else {
4008 f->glDrawArraysInstanced(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount),
4009 GLsizei(cmd.args.draw.instanceCount));
4010 }
4011 } else {
4012 qWarning("No graphics pipeline active for draw; ignored");
4013 }
4014 }
4015 break;
4017 {
4018 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
4019 if (psD) {
4020 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4021 quintptr(cmd.args.drawIndexed.firstIndex * state.indexStride + state.indexOffset));
4022 const bool useBaseVertex = (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex);
4023 const bool useBaseInstance = (cmd.args.drawIndexed.baseInstance != 0 && caps.baseInstance);
4024 // Take the *Instanced path with a non-zero baseInstance even
4025 // when instanceCount is 1: only those calls fetch per-instance
4026 // attributes at the base instance offset.
4027 const bool needsInstancedPath = caps.instancing
4028 && (cmd.args.drawIndexed.instanceCount != 1 || useBaseInstance);
4029
4030 if (!needsInstancedPath) {
4031 if (useBaseVertex) {
4032 f->glDrawElementsBaseVertex(psD->drawMode,
4033 GLsizei(cmd.args.drawIndexed.indexCount),
4034 state.indexType,
4035 ofs,
4036 cmd.args.drawIndexed.baseVertex);
4037 } else {
4038 f->glDrawElements(psD->drawMode,
4039 GLsizei(cmd.args.drawIndexed.indexCount),
4040 state.indexType,
4041 ofs);
4042 }
4043 } else if (useBaseInstance && useBaseVertex) {
4045 GLsizei(cmd.args.drawIndexed.indexCount),
4046 state.indexType,
4047 ofs,
4048 GLsizei(cmd.args.drawIndexed.instanceCount),
4049 cmd.args.drawIndexed.baseVertex,
4050 cmd.args.drawIndexed.baseInstance);
4051 } else if (useBaseInstance) {
4053 GLsizei(cmd.args.drawIndexed.indexCount),
4054 state.indexType,
4055 ofs,
4056 GLsizei(cmd.args.drawIndexed.instanceCount),
4057 cmd.args.drawIndexed.baseInstance);
4058 } else if (useBaseVertex) {
4059 f->glDrawElementsInstancedBaseVertex(psD->drawMode,
4060 GLsizei(cmd.args.drawIndexed.indexCount),
4061 state.indexType,
4062 ofs,
4063 GLsizei(cmd.args.drawIndexed.instanceCount),
4064 cmd.args.drawIndexed.baseVertex);
4065 } else {
4066 f->glDrawElementsInstanced(psD->drawMode,
4067 GLsizei(cmd.args.drawIndexed.indexCount),
4068 state.indexType,
4069 ofs,
4070 GLsizei(cmd.args.drawIndexed.instanceCount));
4071 }
4072 } else {
4073 qWarning("No graphics pipeline active for drawIndexed; ignored");
4074 }
4075 }
4076 break;
4078 {
4079 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndirect.ps);
4080 if (psD) {
4081 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndirect.buffer);
4083 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4084 quintptr(cmd.args.drawIndirect.offset));
4085 glMultiDrawArraysIndirect(psD->drawMode,
4086 ofs,
4087 cmd.args.drawIndirect.drawCount,
4088 cmd.args.drawIndirect.stride);
4089 } else { // Fallback to issuing multiple single indirect draws
4090 for (quint32 i = 0; i < cmd.args.drawIndirect.drawCount; ++i) {
4091 const quintptr indirectOffset = quintptr(cmd.args.drawIndirect.offset)
4092 + quintptr(i) * cmd.args.drawIndirect.stride;
4093 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(indirectOffset);
4094 f->glDrawArraysIndirect(psD->drawMode,
4095 ofs);
4096 }
4097 }
4098 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4099 } else {
4100 qWarning("No graphics pipeline active for drawIndirect; ignored");
4101 }
4102 }
4103 break;
4105 {
4106 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexedIndirect.ps);
4107 if (psD) {
4108 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndexedIndirect.buffer);
4110 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4111 quintptr(cmd.args.drawIndexedIndirect.offset));
4113 state.indexType,
4114 ofs,
4115 cmd.args.drawIndexedIndirect.drawCount,
4116 cmd.args.drawIndexedIndirect.stride);
4117 } else { // Fallback to issuing multiple single indirect draws
4118 for (quint32 i = 0; i < cmd.args.drawIndexedIndirect.drawCount; ++i) {
4119 const quintptr indirectOffset = quintptr(cmd.args.drawIndexedIndirect.offset)
4120 + quintptr(i) * cmd.args.drawIndexedIndirect.stride;
4121 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(indirectOffset);
4122 f->glDrawElementsIndirect(psD->drawMode,
4123 state.indexType,
4124 ofs);
4125 }
4126 }
4127 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4128 } else {
4129 qWarning("No graphics pipeline active for drawIndexedIndirect; ignored");
4130 }
4131 }
4132 break;
4134 {
4135 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndirectCount.ps);
4136 if (psD && glMultiDrawArraysIndirectCount) {
4137 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndirectCount.buffer);
4138 f->glBindBuffer(GL_PARAMETER_BUFFER, cmd.args.drawIndirectCount.countBuffer);
4139 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4140 quintptr(cmd.args.drawIndirectCount.offset));
4142 ofs,
4143 GLintptr(cmd.args.drawIndirectCount.countOffset),
4144 cmd.args.drawIndirectCount.maxDrawCount,
4145 cmd.args.drawIndirectCount.stride);
4146 f->glBindBuffer(GL_PARAMETER_BUFFER, 0);
4147 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4148 } else {
4149 qWarning("No graphics pipeline active or glMultiDrawArraysIndirectCount unavailable; ignored");
4150 }
4151 }
4152 break;
4154 {
4155 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexedIndirectCount.ps);
4157 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndexedIndirectCount.buffer);
4158 f->glBindBuffer(GL_PARAMETER_BUFFER, cmd.args.drawIndexedIndirectCount.countBuffer);
4159 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4160 quintptr(cmd.args.drawIndexedIndirectCount.offset));
4162 state.indexType,
4163 ofs,
4164 GLintptr(cmd.args.drawIndexedIndirectCount.countOffset),
4165 cmd.args.drawIndexedIndirectCount.maxDrawCount,
4166 cmd.args.drawIndexedIndirectCount.stride);
4167 f->glBindBuffer(GL_PARAMETER_BUFFER, 0);
4168 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4169 } else {
4170 qWarning("No graphics pipeline active or glMultiDrawElementsIndirectCount unavailable; ignored");
4171 }
4172 }
4173 break;
4175 executeBindGraphicsPipeline(cbD, QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindGraphicsPipeline.ps));
4176 break;
4177 case QGles2CommandBuffer::Command::BindShaderResources:
4178 bindShaderResources(cbD,
4179 cmd.args.bindShaderResources.maybeGraphicsPs,
4180 cmd.args.bindShaderResources.maybeComputePs,
4181 cmd.args.bindShaderResources.srb,
4182 cbD->dynamicOffsetPool.constData() + cmd.args.bindShaderResources.dynamicOffsetPoolOffset,
4183 cmd.args.bindShaderResources.dynamicOffsetCount);
4184 break;
4186 {
4187 QRhiGraphicsPipeline *maybeGraphicsPs = cmd.args.setPushConstants.maybeGraphicsPs;
4188 QRhiComputePipeline *maybeComputePs = cmd.args.setPushConstants.maybeComputePs;
4189 const QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs
4190 ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->pushConstantUniforms
4191 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->pushConstantUniforms);
4192 QGles2UniformState *uniformState = maybeGraphicsPs
4193 ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState.data()
4194 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniformState.data();
4195 setUniformsFromBlock(uniforms, -1,
4196 cbD->pushConstantPool.constData() + cmd.args.setPushConstants.dataOffset,
4197 cmd.args.setPushConstants.size, 0, uniformState);
4198 }
4199 break;
4201 {
4202 QVarLengthArray<GLenum, 8> bufs;
4203 GLuint fbo = cmd.args.bindFramebuffer.fbo;
4204 if (!fbo)
4205 fbo = ctx->defaultFramebufferObject();
4206 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4207 if (fbo) {
4208 const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
4209 bufs.append(colorAttCount > 0 ? GL_COLOR_ATTACHMENT0 : GL_NONE);
4210 if (caps.maxDrawBuffers > 1) {
4211 for (int i = 1; i < colorAttCount; ++i)
4212 bufs.append(GL_COLOR_ATTACHMENT0 + uint(i));
4213 }
4214 } else {
4215 if (cmd.args.bindFramebuffer.stereo && cmd.args.bindFramebuffer.stereoTarget == QRhiSwapChain::RightBuffer)
4216 bufs.append(GL_BACK_RIGHT);
4217 else
4218 bufs.append(caps.gles ? GL_BACK : GL_BACK_LEFT);
4219 }
4220 if (caps.hasDrawBuffersFunc)
4221 f->glDrawBuffers(bufs.count(), bufs.constData());
4222 if (caps.srgbWriteControl) {
4223 if (cmd.args.bindFramebuffer.srgb)
4224 f->glEnable(GL_FRAMEBUFFER_SRGB);
4225 else
4226 f->glDisable(GL_FRAMEBUFFER_SRGB);
4227 }
4228 }
4229 break;
4231 f->glDisable(GL_SCISSOR_TEST);
4232 if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
4233 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4234 f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
4235 }
4236 if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
4237 f->glDepthMask(GL_TRUE);
4238 f->glClearDepthf(cmd.args.clear.d);
4239 }
4240 if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) {
4241 f->glStencilMask(0xFF);
4242 f->glClearStencil(GLint(cmd.args.clear.s));
4243 }
4244 f->glClear(cmd.args.clear.mask);
4245 cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking
4246 break;
4248 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
4249 f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
4250 cmd.args.bufferSubData.data);
4251 break;
4253 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.clearBufferSubData.target, cmd.args.clearBufferSubData.buffer);
4254 glClearBufferSubData(cmd.args.clearBufferSubData.target, GL_R8UI,
4255 cmd.args.clearBufferSubData.offset, cmd.args.clearBufferSubData.size,
4256 GL_RED_INTEGER, GL_UNSIGNED_BYTE, &cmd.args.clearBufferSubData.value);
4257 break;
4259 {
4260 QRhiReadbackResult *result = cmd.args.getBufferSubData.result;
4261 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
4262 if (caps.gles) {
4263 if (caps.properMapBuffer) {
4264 void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target,
4265 cmd.args.getBufferSubData.offset,
4266 cmd.args.getBufferSubData.size,
4268 if (p) {
4269 result->data.resize(cmd.args.getBufferSubData.size);
4270 memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size));
4271 f->glUnmapBuffer(cmd.args.getBufferSubData.target);
4272 }
4273 }
4274 } else {
4275 result->data.resize(cmd.args.getBufferSubData.size);
4276 f->glGetBufferSubData(cmd.args.getBufferSubData.target,
4277 cmd.args.getBufferSubData.offset,
4278 cmd.args.getBufferSubData.size,
4279 result->data.data());
4280 }
4281 if (result->completed)
4282 result->completed();
4283 }
4284 break;
4286 f->glBindBuffer(GL_COPY_READ_BUFFER, cmd.args.copyBuf.srcBuffer);
4287 f->glBindBuffer(GL_COPY_WRITE_BUFFER, cmd.args.copyBuf.dstBuffer);
4288 f->glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
4289 cmd.args.copyBuf.srcOffset, cmd.args.copyBuf.dstOffset,
4290 cmd.args.copyBuf.size);
4291 f->glBindBuffer(GL_COPY_READ_BUFFER, 0);
4292 f->glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
4293 break;
4295 {
4296 GLuint fbo;
4297 f->glGenFramebuffers(1, &fbo);
4298 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4299 if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D
4300 || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY
4301 || cmd.args.copyTex.srcTarget == GL_TEXTURE_1D_ARRAY) {
4302 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
4303 cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
4304 } else if (cmd.args.copyTex.srcTarget == GL_TEXTURE_1D) {
4305 glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4306 cmd.args.copyTex.srcTarget, cmd.args.copyTex.srcTexture,
4307 cmd.args.copyTex.srcLevel);
4308 } else {
4309 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4310 cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
4311 }
4312 f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
4313 if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D || cmd.args.copyTex.dstTarget == GL_TEXTURE_2D_ARRAY) {
4314 f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
4315 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
4316 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
4317 cmd.args.copyTex.w, cmd.args.copyTex.h);
4318 } else if (cmd.args.copyTex.dstTarget == GL_TEXTURE_1D) {
4319 glCopyTexSubImage1D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
4320 cmd.args.copyTex.dstX, cmd.args.copyTex.srcX,
4321 cmd.args.copyTex.srcY, cmd.args.copyTex.w);
4322 } else {
4323 f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
4324 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
4325 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
4326 cmd.args.copyTex.w, cmd.args.copyTex.h);
4327 }
4328 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4329 f->glDeleteFramebuffers(1, &fbo);
4330 }
4331 break;
4333 {
4334 QRhiReadbackResult *result = cmd.args.readPixels.result;
4335 GLuint tex = cmd.args.readPixels.texture;
4336 GLuint fbo = 0;
4337 int mipLevel = 0;
4338 result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
4339 if (tex) {
4340 result->format = cmd.args.readPixels.format;
4341 mipLevel = cmd.args.readPixels.level;
4342 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
4343 f->glGenFramebuffers(1, &fbo);
4344 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4345 const auto attach = [&](GLenum attachment) {
4346 if (cmd.args.readPixels.slice3D >= 0) {
4347 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
4348 tex, mipLevel, cmd.args.readPixels.slice3D);
4349 } else if (cmd.args.readPixels.readTarget == GL_TEXTURE_1D) {
4350 glFramebufferTexture1D(GL_FRAMEBUFFER, attachment,
4351 cmd.args.readPixels.readTarget, tex, mipLevel);
4352 } else {
4353 f->glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
4354 cmd.args.readPixels.readTarget, tex, mipLevel);
4355 }
4356 };
4357 // A depth(-stencil) texture cannot be attached as a color attachment.
4358 if (isDepthTextureFormat(result->format)) {
4359 attach(GL_DEPTH_ATTACHMENT);
4360 if (isStencilSupportingFormat(result->format))
4361 attach(GL_STENCIL_ATTACHMENT);
4362 // There is no color attachment, which would make the FBO incomplete
4363 // on desktop GL < 4.1 unless the read buffer is set to none.
4364 if (!caps.gles)
4365 f->glReadBuffer(GL_NONE);
4366 } else {
4367 attach(GL_COLOR_ATTACHMENT0);
4368 }
4369 }
4370 } else {
4371 result->format = QRhiTexture::RGBA8;
4372 // readPixels handles multisample resolving implicitly
4373 }
4374 const int x = cmd.args.readPixels.x;
4375 const int y = cmd.args.readPixels.y;
4376 const int w = cmd.args.readPixels.w;
4377 const int h = cmd.args.readPixels.h;
4378 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
4379 // With GLES, GL_RGBA is the only mandated readback format, so stick with it.
4380 // (and that's why we return false for the ReadBackAnyTextureFormat feature)
4381 if (result->format == QRhiTexture::R8 || result->format == QRhiTexture::RED_OR_ALPHA8) {
4382 result->data.resizeForOverwrite(w * h);
4383 QByteArray tmpBuf;
4384 tmpBuf.resizeForOverwrite(w * h * 4);
4385 f->glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tmpBuf.data());
4386 const quint8 *srcBase = reinterpret_cast<const quint8 *>(tmpBuf.constData());
4387 quint8 *dstBase = reinterpret_cast<quint8 *>(result->data.data());
4388 const int componentIndex = result->format == QRhiTexture::R8
4389 || isFeatureSupported(QRhi::RedOrAlpha8IsRed)
4390 ? 0
4391 : 3;
4392 for (int y = 0; y < h; ++y) {
4393 const quint8 *src = srcBase + y * w * 4;
4394 quint8 *dst = dstBase + y * w;
4395 int count = w;
4396 while (count-- > 0) {
4397 *dst++ = src[componentIndex];
4398 src += 4;
4399 }
4400 }
4401 } else {
4402 // For other formats try it because this can be relevant for some use cases;
4403 // if it works, then fine, if not, there's nothing we can do.
4404 [[maybe_unused]] GLenum glintformat;
4405 [[maybe_unused]] GLenum glsizedintformat;
4406 GLenum glformat;
4407 GLenum gltype;
4408 toGlTextureFormat(result->format, {}, caps,
4409 &glintformat, &glsizedintformat, &glformat, &gltype);
4410 quint32 byteSize;
4411 textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize, nullptr);
4412 result->data.resizeForOverwrite(byteSize);
4413 // The result is tightly packed, unlike with the default alignment of 4.
4414 f->glPixelStorei(GL_PACK_ALIGNMENT, 1);
4415 f->glReadPixels(x, y, w, h, glformat, gltype, result->data.data());
4416 f->glPixelStorei(GL_PACK_ALIGNMENT, 4);
4417 // QRhi guarantees the unused bits of a depth readback to be zero. With D24
4418 // the depth is normalized to 32 bits, but only the top 24 bits are
4419 // meaningful, with D32FS8 only the lowest 8 bits of the second word are.
4420 if (result->format == QRhiTexture::D24 || result->format == QRhiTexture::D32FS8) {
4421 const bool isD24 = result->format == QRhiTexture::D24;
4422 const quint32 mask = isD24 ? 0xFFFFFF00u : 0xFFu;
4423 char *p = result->data.data();
4424 for (qsizetype i = isD24 ? 0 : 1, count = result->data.size() / 4; i < count; i += isD24 ? 1 : 2)
4425 qToUnaligned(qFromUnaligned<quint32>(p + i * 4) & mask, p + i * 4);
4426 }
4427 }
4428 } else {
4429 // this level can't be read back; return a zero-filled image sized for the format
4430 quint32 byteSize = 0;
4431 textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize, nullptr);
4432 result->data.fill('\0', byteSize);
4433 }
4434 if (fbo) {
4435 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4436 f->glDeleteFramebuffers(1, &fbo);
4437 }
4438 if (result->completed)
4439 result->completed();
4440 }
4441 break;
4443 f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
4444 if (cmd.args.subImage.rowStartAlign != 4)
4445 f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
4446 if (cmd.args.subImage.rowLength != 0)
4447 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
4448 if (cmd.args.subImage.target == GL_TEXTURE_3D || cmd.args.subImage.target == GL_TEXTURE_2D_ARRAY) {
4449 f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level,
4450 cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
4451 cmd.args.subImage.w, cmd.args.subImage.h, 1,
4452 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
4453 cmd.args.subImage.data);
4454 } else if (cmd.args.subImage.target == GL_TEXTURE_1D) {
4455 glTexSubImage1D(cmd.args.subImage.target, cmd.args.subImage.level,
4456 cmd.args.subImage.dx, cmd.args.subImage.w,
4457 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
4458 cmd.args.subImage.data);
4459 } else {
4460 f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
4461 cmd.args.subImage.dx, cmd.args.subImage.dy,
4462 cmd.args.subImage.w, cmd.args.subImage.h,
4463 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
4464 cmd.args.subImage.data);
4465 }
4466 if (cmd.args.subImage.rowStartAlign != 4)
4467 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
4468 if (cmd.args.subImage.rowLength != 0)
4469 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4470 break;
4472 f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
4473 if (cmd.args.compressedImage.target == GL_TEXTURE_3D || cmd.args.compressedImage.target == GL_TEXTURE_2D_ARRAY) {
4474 f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
4475 cmd.args.compressedImage.glintformat,
4476 cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
4477 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
4478 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
4480 cmd.args.compressedImage.target, cmd.args.compressedImage.level,
4481 cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, 0,
4482 cmd.args.compressedImage.size, cmd.args.compressedImage.data);
4483 } else {
4484 f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
4485 cmd.args.compressedImage.glintformat,
4486 cmd.args.compressedImage.w, cmd.args.compressedImage.h,
4487 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
4488 }
4489 break;
4491 f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
4492 if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D || cmd.args.compressedSubImage.target == GL_TEXTURE_2D_ARRAY) {
4493 f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
4494 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
4495 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
4496 cmd.args.compressedSubImage.glintformat,
4497 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
4498 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
4500 cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
4501 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.w,
4502 cmd.args.compressedSubImage.glintformat, cmd.args.compressedSubImage.size,
4503 cmd.args.compressedSubImage.data);
4504 } else {
4505 f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
4506 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
4507 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
4508 cmd.args.compressedSubImage.glintformat,
4509 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
4510 }
4511 break;
4513 {
4514 // Altering the scissor state, so reset the stored state, although
4515 // not strictly required as long as blit is done in endPass() only.
4516 cbD->graphicsPassState.reset();
4517 f->glDisable(GL_SCISSOR_TEST);
4518 GLuint fbo[2];
4519 f->glGenFramebuffers(2, fbo);
4520 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
4521 const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil;
4522 const bool hasStencil = ds && cmd.args.blitFromRenderbuffer.hasStencil;
4523 if (ds) {
4524 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4525 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
4526 if (hasStencil) {
4527 // With a packed depth-stencil renderbuffer stencilRenderbuffer is 0.
4528 const GLuint srcStencilRb = cmd.args.blitFromRenderbuffer.stencilRenderbuffer
4529 ? cmd.args.blitFromRenderbuffer.stencilRenderbuffer
4530 : cmd.args.blitFromRenderbuffer.renderbuffer;
4531 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4532 GL_RENDERBUFFER, srcStencilRb);
4533 }
4534 } else {
4535 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4536 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
4537 }
4538 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
4539 if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) {
4540 if (ds) {
4541 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4542 cmd.args.blitFromRenderbuffer.dstTexture,
4543 cmd.args.blitFromRenderbuffer.dstLevel,
4544 cmd.args.blitFromRenderbuffer.dstLayer);
4545 if (hasStencil) {
4546 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4547 cmd.args.blitFromRenderbuffer.dstTexture,
4548 cmd.args.blitFromRenderbuffer.dstLevel,
4549 cmd.args.blitFromRenderbuffer.dstLayer);
4550 }
4551 } else {
4552 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4553 cmd.args.blitFromRenderbuffer.dstTexture,
4554 cmd.args.blitFromRenderbuffer.dstLevel,
4555 cmd.args.blitFromRenderbuffer.dstLayer);
4556 }
4557 } else {
4558 if (ds) {
4559 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
4560 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
4561 if (hasStencil) {
4562 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
4563 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
4564 }
4565 } else {
4566 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
4567 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
4568 }
4569 }
4570 if (ds && !caps.gles) {
4571 f->glReadBuffer(GL_NONE);
4572 const GLenum noBuf = GL_NONE;
4573 f->glDrawBuffers(1, &noBuf);
4574 }
4575 const GLbitfield rbBlitMask = ds ? (GL_DEPTH_BUFFER_BIT | (hasStencil ? GL_STENCIL_BUFFER_BIT : 0))
4576 : GL_COLOR_BUFFER_BIT;
4577 f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
4578 0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
4579 rbBlitMask,
4580 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
4581 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4582 f->glDeleteFramebuffers(2, fbo);
4583 }
4584 break;
4586 {
4587 // Altering the scissor state, so reset the stored state, although
4588 // not strictly required as long as blit is done in endPass() only.
4589 cbD->graphicsPassState.reset();
4590 f->glDisable(GL_SCISSOR_TEST);
4591 GLuint fbo[2];
4592 f->glGenFramebuffers(2, fbo);
4593 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
4594 const bool ds = cmd.args.blitFromTexture.isDepthStencil;
4595 const bool hasStencil = ds && cmd.args.blitFromTexture.hasStencil;
4596 if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
4597 if (ds) {
4598 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4599 cmd.args.blitFromTexture.srcTexture,
4600 cmd.args.blitFromTexture.srcLevel,
4601 cmd.args.blitFromTexture.srcLayer);
4602 if (hasStencil) {
4603 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4604 cmd.args.blitFromTexture.srcTexture,
4605 cmd.args.blitFromTexture.srcLevel,
4606 cmd.args.blitFromTexture.srcLayer);
4607 }
4608 } else {
4609 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4610 cmd.args.blitFromTexture.srcTexture,
4611 cmd.args.blitFromTexture.srcLevel,
4612 cmd.args.blitFromTexture.srcLayer);
4613 }
4614 } else {
4615 if (ds) {
4616 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
4617 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
4618 if (hasStencil) {
4619 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
4620 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
4621 }
4622 } else {
4623 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
4624 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
4625 }
4626 }
4627 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
4628 if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) {
4629 if (ds) {
4630 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4631 cmd.args.blitFromTexture.dstTexture,
4632 cmd.args.blitFromTexture.dstLevel,
4633 cmd.args.blitFromTexture.dstLayer);
4634 if (hasStencil) {
4635 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4636 cmd.args.blitFromTexture.dstTexture,
4637 cmd.args.blitFromTexture.dstLevel,
4638 cmd.args.blitFromTexture.dstLayer);
4639 }
4640 } else {
4641 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4642 cmd.args.blitFromTexture.dstTexture,
4643 cmd.args.blitFromTexture.dstLevel,
4644 cmd.args.blitFromTexture.dstLayer);
4645 }
4646 } else {
4647 if (ds) {
4648 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
4649 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
4650 if (hasStencil) {
4651 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
4652 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
4653 }
4654 } else {
4655 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
4656 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
4657 }
4658 }
4659 if (ds && !caps.gles) {
4660 f->glReadBuffer(GL_NONE);
4661 const GLenum noBuf = GL_NONE;
4662 f->glDrawBuffers(1, &noBuf);
4663 }
4664 const GLbitfield texBlitMask = ds ? (GL_DEPTH_BUFFER_BIT | (hasStencil ? GL_STENCIL_BUFFER_BIT : 0))
4665 : GL_COLOR_BUFFER_BIT;
4666 f->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
4667 0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
4668 texBlitMask,
4669 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
4670 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4671 f->glDeleteFramebuffers(2, fbo);
4672 }
4673 break;
4675 f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
4676 f->glGenerateMipmap(cmd.args.genMip.target);
4677 break;
4679 {
4680 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cmd.args.bindComputePipeline.ps);
4681 f->glUseProgram(psD->program);
4682 }
4683 break;
4685 f->glDispatchCompute(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
4686 break;
4688 {
4689 f->glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, cmd.args.dispatchIndirect.buffer);
4690 f->glDispatchComputeIndirect(GLintptr(cmd.args.dispatchIndirect.offset));
4691 f->glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0);
4692 }
4693 break;
4695 {
4696 if (!caps.compute)
4697 break;
4698 GLbitfield barriers = 0;
4699 QRhiPassResourceTracker &tracker(cbD->passResTrackers[cmd.args.barriersForPass.trackerIndex]);
4700 // we only care about after-write, not any other accesses, and
4701 // cannot tell if something was written in a shader several passes
4702 // ago: now the previously written resource may be used with an
4703 // access that was not in the previous passes, result in a missing
4704 // barrier in theory. Hence setting all barrier bits whenever
4705 // something previously written is used for the first time in a
4706 // subsequent pass.
4707 for (const auto &[rhiB, trackedB]: tracker.buffers()) {
4708 Q_UNUSED(rhiB)
4709 QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(trackedB.stateAtPassBegin.access);
4710 if (bufferAccessIsWrite(accessBeforePass))
4711 barriers |= barriersForBuffer();
4712 }
4713 for (const auto &[rhiT, trackedT]: tracker.textures()) {
4714 Q_UNUSED(rhiT)
4715 QGles2Texture::Access accessBeforePass = QGles2Texture::Access(trackedT.stateAtPassBegin.access);
4716 if (textureAccessIsWrite(accessBeforePass))
4717 barriers |= barriersForTexture();
4718 }
4719 if (barriers)
4720 f->glMemoryBarrier(barriers);
4721 }
4722 break;
4724 if (caps.compute)
4725 f->glMemoryBarrier(cmd.args.barrier.barriers);
4726 break;
4728 if (caps.gles && caps.ctxMajor >= 3) {
4729 f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.invalidateFramebuffer.fbo);
4730 f->glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
4731 cmd.args.invalidateFramebuffer.attCount,
4732 cmd.args.invalidateFramebuffer.att);
4733 }
4734 break;
4735 default:
4736 break;
4737 }
4738 }
4739 if (state.instancedAttributesUsed) {
4740 for (int i = 0; i < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; ++i) {
4741 if (state.nonzeroAttribDivisor[i])
4742 f->glVertexAttribDivisor(GLuint(i), 0);
4743 }
4745 f->glVertexAttribDivisor(GLuint(i), 0);
4746 }
4747}
4748
4750{
4751 QGles2CommandBuffer::GraphicsPassState &state(cbD->graphicsPassState);
4752 const bool forceUpdate = !state.valid;
4753 state.valid = true;
4754
4755 const bool scissor = psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor);
4756 if (forceUpdate || scissor != state.scissor) {
4757 state.scissor = scissor;
4758 if (scissor)
4759 f->glEnable(GL_SCISSOR_TEST);
4760 else
4761 f->glDisable(GL_SCISSOR_TEST);
4762 }
4763
4764 const bool cullFace = psD->m_cullMode != QRhiGraphicsPipeline::None;
4765 const GLenum cullMode = cullFace ? toGlCullMode(psD->m_cullMode) : GL_NONE;
4766 if (forceUpdate || cullFace != state.cullFace || cullMode != state.cullMode) {
4767 state.cullFace = cullFace;
4768 state.cullMode = cullMode;
4769 if (cullFace) {
4770 f->glEnable(GL_CULL_FACE);
4771 f->glCullFace(cullMode);
4772 } else {
4773 f->glDisable(GL_CULL_FACE);
4774 }
4775 }
4776
4777 const GLenum frontFace = toGlFrontFace(psD->m_frontFace);
4778 if (forceUpdate || frontFace != state.frontFace) {
4779 state.frontFace = frontFace;
4780 f->glFrontFace(frontFace);
4781 }
4782
4783 const GLenum polygonMode = toGlPolygonMode(psD->m_polygonMode);
4784 if (glPolygonMode && (forceUpdate || polygonMode != state.polygonMode)) {
4785 state.polygonMode = polygonMode;
4786 glPolygonMode(GL_FRONT_AND_BACK, polygonMode);
4787 }
4788
4789 if (!psD->m_targetBlends.isEmpty()) {
4790 GLint buffer = 0;
4791 bool anyBlendEnabled = false;
4792 for (const auto targetBlend : psD->m_targetBlends) {
4793 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = {
4794 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::R),
4795 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::G),
4796 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::B),
4797 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::A)
4798 };
4799 if (forceUpdate || colorMask != state.colorMask[buffer]) {
4800 state.colorMask[buffer] = colorMask;
4801 if (caps.perRenderTargetBlending)
4802 f->glColorMaski(buffer, colorMask.r, colorMask.g, colorMask.b, colorMask.a);
4803 else
4804 f->glColorMask(colorMask.r, colorMask.g, colorMask.b, colorMask.a);
4805 }
4806
4807 const bool blendEnabled = targetBlend.enable;
4808 const QGles2CommandBuffer::GraphicsPassState::Blend blend = {
4809 toGlBlendFactor(targetBlend.srcColor),
4810 toGlBlendFactor(targetBlend.dstColor),
4811 toGlBlendFactor(targetBlend.srcAlpha),
4812 toGlBlendFactor(targetBlend.dstAlpha),
4813 toGlBlendOp(targetBlend.opColor),
4814 toGlBlendOp(targetBlend.opAlpha)
4815 };
4816 anyBlendEnabled |= blendEnabled;
4817 if (forceUpdate || blendEnabled != state.blendEnabled[buffer] || (blendEnabled && blend != state.blend[buffer])) {
4818 state.blendEnabled[buffer] = blendEnabled;
4819 if (blendEnabled) {
4820 state.blend[buffer] = blend;
4821 if (caps.perRenderTargetBlending) {
4822 f->glBlendFuncSeparatei(buffer, blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
4823 f->glBlendEquationSeparatei(buffer, blend.opColor, blend.opAlpha);
4824 } else {
4825 f->glBlendFuncSeparate(blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
4826 f->glBlendEquationSeparate(blend.opColor, blend.opAlpha);
4827 }
4828 }
4829 }
4830 buffer++;
4831 if (!caps.perRenderTargetBlending)
4832 break;
4833 }
4834 if (anyBlendEnabled)
4835 f->glEnable(GL_BLEND);
4836 else
4837 f->glDisable(GL_BLEND);
4838 } else {
4839 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { true, true, true, true };
4840 if (forceUpdate || colorMask != state.colorMask[0]) {
4841 state.colorMask[0] = colorMask;
4842 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4843 }
4844 const bool blendEnabled = false;
4845 if (forceUpdate || blendEnabled != state.blendEnabled[0]) {
4846 state.blendEnabled[0] = blendEnabled;
4847 f->glDisable(GL_BLEND);
4848 }
4849 }
4850
4851 const bool depthTest = psD->m_depthTest;
4852 if (forceUpdate || depthTest != state.depthTest) {
4853 state.depthTest = depthTest;
4854 if (depthTest)
4855 f->glEnable(GL_DEPTH_TEST);
4856 else
4857 f->glDisable(GL_DEPTH_TEST);
4858 }
4859
4860 const bool depthWrite = psD->m_depthWrite;
4861 if (forceUpdate || depthWrite != state.depthWrite) {
4862 state.depthWrite = depthWrite;
4863 f->glDepthMask(depthWrite);
4864 }
4865
4866 const bool depthClamp = psD->m_depthClamp;
4867 if (caps.depthClamp && (forceUpdate || depthClamp != state.depthClamp)) {
4868 state.depthClamp = depthClamp;
4869 if (depthClamp)
4870 f->glEnable(GL_DEPTH_CLAMP);
4871 else
4872 f->glDisable(GL_DEPTH_CLAMP);
4873 }
4874
4875 const GLenum depthFunc = toGlCompareOp(psD->m_depthOp);
4876 if (forceUpdate || depthFunc != state.depthFunc) {
4877 state.depthFunc = depthFunc;
4878 f->glDepthFunc(depthFunc);
4879 }
4880
4881 const bool stencilTest = psD->m_stencilTest;
4882 const GLuint stencilReadMask = psD->m_stencilReadMask;
4883 const GLuint stencilWriteMask = psD->m_stencilWriteMask;
4884 const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilFront = {
4885 toGlCompareOp(psD->m_stencilFront.compareOp),
4886 toGlStencilOp(psD->m_stencilFront.failOp),
4887 toGlStencilOp(psD->m_stencilFront.depthFailOp),
4888 toGlStencilOp(psD->m_stencilFront.passOp)
4889 };
4890 const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilBack = {
4891 toGlCompareOp(psD->m_stencilBack.compareOp),
4892 toGlStencilOp(psD->m_stencilBack.failOp),
4893 toGlStencilOp(psD->m_stencilBack.depthFailOp),
4894 toGlStencilOp(psD->m_stencilBack.passOp)
4895 };
4896 if (forceUpdate || stencilTest != state.stencilTest
4897 || (stencilTest
4898 && (stencilReadMask != state.stencilReadMask || stencilWriteMask != state.stencilWriteMask
4899 || stencilFront != state.stencil[0] || stencilBack != state.stencil[1])))
4900 {
4901 state.stencilTest = stencilTest;
4902 if (stencilTest) {
4903 state.stencilReadMask = stencilReadMask;
4904 state.stencilWriteMask = stencilWriteMask;
4905 state.stencil[0] = stencilFront;
4906 state.stencil[1] = stencilBack;
4907
4908 f->glEnable(GL_STENCIL_TEST);
4909
4910 f->glStencilFuncSeparate(GL_FRONT, stencilFront.func, state.dynamic.stencilRef, stencilReadMask);
4911 f->glStencilOpSeparate(GL_FRONT, stencilFront.failOp, stencilFront.zfailOp, stencilFront.zpassOp);
4912 f->glStencilMaskSeparate(GL_FRONT, stencilWriteMask);
4913
4914 f->glStencilFuncSeparate(GL_BACK, stencilBack.func, state.dynamic.stencilRef, stencilReadMask);
4915 f->glStencilOpSeparate(GL_BACK, stencilBack.failOp, stencilBack.zfailOp, stencilBack.zpassOp);
4916 f->glStencilMaskSeparate(GL_BACK, stencilWriteMask);
4917 } else {
4918 f->glDisable(GL_STENCIL_TEST);
4919 }
4920 }
4921
4922 const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(psD->m_slopeScaledDepthBias);
4923 const float polyOffsetFactor = psD->m_slopeScaledDepthBias;
4924 const float polyOffsetUnits = psD->m_depthBias;
4925 if (forceUpdate || state.polyOffsetFill != polyOffsetFill
4926 || polyOffsetFactor != state.polyOffsetFactor || polyOffsetUnits != state.polyOffsetUnits)
4927 {
4928 state.polyOffsetFill = polyOffsetFill;
4929 state.polyOffsetFactor = polyOffsetFactor;
4930 state.polyOffsetUnits = polyOffsetUnits;
4931 if (polyOffsetFill) {
4932 f->glPolygonOffset(polyOffsetFactor, polyOffsetUnits);
4933 f->glEnable(GL_POLYGON_OFFSET_FILL);
4934 } else {
4935 f->glDisable(GL_POLYGON_OFFSET_FILL);
4936 }
4937 }
4938
4939 if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) {
4940 const float lineWidth = psD->m_lineWidth;
4941 if (forceUpdate || lineWidth != state.lineWidth) {
4942 state.lineWidth = lineWidth;
4943 f->glLineWidth(lineWidth);
4944 }
4945 }
4946
4947 if (psD->m_topology == QRhiGraphicsPipeline::Patches) {
4948 const int cpCount = psD->m_patchControlPointCount;
4949 if (forceUpdate || cpCount != state.cpCount) {
4950 state.cpCount = cpCount;
4951 f->glPatchParameteri(GL_PATCH_VERTICES, qMax(1, cpCount));
4952 }
4953 }
4954
4955 f->glUseProgram(psD->program);
4956}
4957
4958// The source is a uniform block (std140) or a push constant block (std430)
4959// laid out for the GPU, which is not what glUniform* wants: there the values
4960// are tightly packed. srcStride is the distance in bytes between the array
4961// elements, or between the matrix columns for matrix types.
4962template <typename T>
4963static inline void qrhi_block_to_packed(T *dst, int vecSize, int elemCount, const void *src, quint32 srcStride)
4964{
4965 const char *p = reinterpret_cast<const char *>(src);
4966 for (int i = 0; i < elemCount; ++i) {
4967 const T *e = reinterpret_cast<const T *>(p);
4968 for (int j = 0; j < vecSize; ++j)
4969 dst[vecSize * i + j] = e[j];
4970 p += srcStride;
4971 }
4972}
4973
4974// The default stride for both the arrays and the matrix columns in an std140
4975// uniform block. Push constant blocks are std430, where an array of scalars or
4976// vec2 is tightly packed, and so is a mat2; there the reflection-provided
4977// stride is used instead.
4979
4981{
4982 return uniform.elemStride ? uniform.elemStride : QRHI_GL_STD140_STRIDE;
4983}
4984
4985static inline qint64 qrhi_uniform_read_size(QShaderDescription::VariableType type, int arrayDim, quint32 stride)
4986{
4987 if (!stride)
4988 stride = QRHI_GL_STD140_STRIDE;
4989
4990 qint64 elemSize;
4991 qint64 elemStride = stride;
4992
4993 switch (type) {
4994 case QShaderDescription::Float:
4995 case QShaderDescription::Int:
4996 case QShaderDescription::Uint:
4997 elemSize = 4;
4998 break;
4999 case QShaderDescription::Vec2:
5000 case QShaderDescription::Int2:
5001 case QShaderDescription::Uint2:
5002 elemSize = 8;
5003 break;
5004 case QShaderDescription::Vec3:
5005 case QShaderDescription::Int3:
5006 case QShaderDescription::Uint3:
5007 elemSize = 12;
5008 break;
5009 case QShaderDescription::Vec4:
5010 case QShaderDescription::Int4:
5011 case QShaderDescription::Uint4:
5012 elemSize = 16;
5013 break;
5014 case QShaderDescription::Mat2:
5015 // two columns, stride is the column stride (16 with std140)
5016 elemSize = stride + 8;
5017 elemStride = 2 * stride;
5018 break;
5019 case QShaderDescription::Mat3:
5020 // three columns
5021 elemSize = 2 * stride + 12;
5022 elemStride = 3 * stride;
5023 break;
5024 case QShaderDescription::Mat4:
5025 elemSize = 3 * stride + 16;
5026 elemStride = 4 * stride;
5027 break;
5028 case QShaderDescription::Bool:
5029 return 4;
5030 case QShaderDescription::Bool2:
5031 return 8;
5032 case QShaderDescription::Bool3:
5033 return 12;
5034 case QShaderDescription::Bool4:
5035 return 16;
5036 default:
5037 return 0;
5038 }
5039
5040 return arrayDim < 1 ? elemSize : (arrayDim - 1) * elemStride + elemSize;
5041}
5042
5044 void *ps, uint psGeneration, int glslLocation,
5045 int *texUnit, bool *activeTexUnitAltered)
5046{
5047 const bool samplerStateValid = texD->samplerState == samplerD->d;
5048 const bool cachedStateInRange = *texUnit < 16;
5049 bool updateTextureBinding = true;
5050 if (samplerStateValid && cachedStateInRange) {
5051 // If we already encountered the same texture with
5052 // the same pipeline for this texture unit in the
5053 // current pass, then the shader program already
5054 // has the uniform set. As in a 3D scene one model
5055 // often has more than one associated texture map,
5056 // the savings here can become significant,
5057 // depending on the scene.
5058 if (cbD->textureUnitState[*texUnit].ps == ps
5059 && cbD->textureUnitState[*texUnit].psGeneration == psGeneration
5060 && cbD->textureUnitState[*texUnit].texture == texD->texture)
5061 {
5062 updateTextureBinding = false;
5063 }
5064 }
5065 if (updateTextureBinding) {
5066 f->glActiveTexture(GL_TEXTURE0 + uint(*texUnit));
5067 *activeTexUnitAltered = true;
5068 f->glBindTexture(texD->target, texD->texture);
5069 f->glUniform1i(glslLocation, *texUnit);
5070 if (cachedStateInRange) {
5071 cbD->textureUnitState[*texUnit].ps = ps;
5072 cbD->textureUnitState[*texUnit].psGeneration = psGeneration;
5073 cbD->textureUnitState[*texUnit].texture = texD->texture;
5074 }
5075 }
5076 ++(*texUnit);
5077 if (!samplerStateValid) {
5078 f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
5079 f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
5080 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
5081 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
5082 if (caps.texture3D && texD->target == GL_TEXTURE_3D)
5083 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, GLint(samplerD->d.glwrapr));
5084 if (caps.textureCompareMode) {
5085 if (samplerD->d.gltexcomparefunc != GL_NEVER) {
5086 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
5087 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
5088 } else {
5089 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
5090 }
5091 }
5092 texD->samplerState = samplerD->d;
5093 }
5094}
5095
5096void QRhiGles2::setUniformValue(const QGles2UniformDescription &uniform, const void *src, QGles2UniformState *uniformState)
5097{
5098 const quint32 stride = qrhi_uniform_stride(uniform);
5099
5100 switch (uniform.type) {
5101 case QShaderDescription::Float:
5102 {
5103 const int elemCount = uniform.arrayDim;
5104 if (elemCount < 1) {
5105 const float v = *reinterpret_cast<const float *>(src);
5106 if (uniform.stateIndex >= 0) {
5107 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5108 if (thisUniformState.componentCount != 1 || thisUniformState.v[0] != v) {
5109 thisUniformState.componentCount = 1;
5110 thisUniformState.v[0] = v;
5111 f->glUniform1f(uniform.glslLocation, v);
5112 }
5113 } else {
5114 f->glUniform1f(uniform.glslLocation, v);
5115 }
5116 } else {
5117 m_scratch.packedArray.resize(elemCount);
5118 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 1, elemCount, src, stride);
5119 f->glUniform1fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5120 }
5121 }
5122 break;
5123 case QShaderDescription::Vec2:
5124 {
5125 const int elemCount = uniform.arrayDim;
5126 if (elemCount < 1) {
5127 const float *v = reinterpret_cast<const float *>(src);
5128 if (uniform.stateIndex >= 0) {
5129 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5130 if (thisUniformState.componentCount != 2
5131 || thisUniformState.v[0] != v[0]
5132 || thisUniformState.v[1] != v[1])
5133 {
5134 thisUniformState.componentCount = 2;
5135 thisUniformState.v[0] = v[0];
5136 thisUniformState.v[1] = v[1];
5137 f->glUniform2fv(uniform.glslLocation, 1, v);
5138 }
5139 } else {
5140 f->glUniform2fv(uniform.glslLocation, 1, v);
5141 }
5142 } else {
5143 m_scratch.packedArray.resize(elemCount * 2);
5144 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 2, elemCount, src, stride);
5145 f->glUniform2fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5146 }
5147 }
5148 break;
5149 case QShaderDescription::Vec3:
5150 {
5151 const int elemCount = uniform.arrayDim;
5152 if (elemCount < 1) {
5153 const float *v = reinterpret_cast<const float *>(src);
5154 if (uniform.stateIndex >= 0) {
5155 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5156 if (thisUniformState.componentCount != 3
5157 || thisUniformState.v[0] != v[0]
5158 || thisUniformState.v[1] != v[1]
5159 || thisUniformState.v[2] != v[2])
5160 {
5161 thisUniformState.componentCount = 3;
5162 thisUniformState.v[0] = v[0];
5163 thisUniformState.v[1] = v[1];
5164 thisUniformState.v[2] = v[2];
5165 f->glUniform3fv(uniform.glslLocation, 1, v);
5166 }
5167 } else {
5168 f->glUniform3fv(uniform.glslLocation, 1, v);
5169 }
5170 } else {
5171 m_scratch.packedArray.resize(elemCount * 3);
5172 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 3, elemCount, src, stride);
5173 f->glUniform3fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5174 }
5175 }
5176 break;
5177 case QShaderDescription::Vec4:
5178 {
5179 const int elemCount = uniform.arrayDim;
5180 if (elemCount < 1) {
5181 const float *v = reinterpret_cast<const float *>(src);
5182 if (uniform.stateIndex >= 0) {
5183 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5184 if (thisUniformState.componentCount != 4
5185 || thisUniformState.v[0] != v[0]
5186 || thisUniformState.v[1] != v[1]
5187 || thisUniformState.v[2] != v[2]
5188 || thisUniformState.v[3] != v[3])
5189 {
5190 thisUniformState.componentCount = 4;
5191 thisUniformState.v[0] = v[0];
5192 thisUniformState.v[1] = v[1];
5193 thisUniformState.v[2] = v[2];
5194 thisUniformState.v[3] = v[3];
5195 f->glUniform4fv(uniform.glslLocation, 1, v);
5196 }
5197 } else {
5198 f->glUniform4fv(uniform.glslLocation, 1, v);
5199 }
5200 } else if (stride == 16) {
5201 f->glUniform4fv(uniform.glslLocation, elemCount, reinterpret_cast<const float *>(src));
5202 } else {
5203 m_scratch.packedArray.resize(elemCount * 4);
5204 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 4, elemCount, src, stride);
5205 f->glUniform4fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5206 }
5207 }
5208 break;
5209 case QShaderDescription::Mat2:
5210 {
5211 const int elemCount = uniform.arrayDim;
5212 m_scratch.packedArray.resize(qMax(1, elemCount) * 4);
5213 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 2, qMax(1, elemCount) * 2, src, stride);
5214 f->glUniformMatrix2fv(uniform.glslLocation, qMax(1, elemCount), GL_FALSE, &m_scratch.packedArray.constData()->f);
5215 }
5216 break;
5217 case QShaderDescription::Mat3:
5218 {
5219 const int elemCount = uniform.arrayDim;
5220 m_scratch.packedArray.resize(qMax(1, elemCount) * 9);
5221 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 3, qMax(1, elemCount) * 3, src, stride);
5222 f->glUniformMatrix3fv(uniform.glslLocation, qMax(1, elemCount), GL_FALSE, &m_scratch.packedArray.constData()->f);
5223 }
5224 break;
5225 case QShaderDescription::Mat4:
5226 if (stride == 16) {
5227 f->glUniformMatrix4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), GL_FALSE, reinterpret_cast<const float *>(src));
5228 } else {
5229 const int elemCount = qMax(1, uniform.arrayDim);
5230 m_scratch.packedArray.resize(elemCount * 16);
5231 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 4, elemCount * 4, src, stride);
5232 f->glUniformMatrix4fv(uniform.glslLocation, elemCount, GL_FALSE, &m_scratch.packedArray.constData()->f);
5233 }
5234 break;
5235 case QShaderDescription::Int:
5236 {
5237 const int elemCount = uniform.arrayDim;
5238 if (elemCount < 1) {
5239 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
5240 } else {
5241 m_scratch.packedArray.resize(elemCount);
5242 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 1, elemCount, src, stride);
5243 f->glUniform1iv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->i);
5244 }
5245 }
5246 break;
5247 case QShaderDescription::Int2:
5248 {
5249 const int elemCount = uniform.arrayDim;
5250 if (elemCount < 1) {
5251 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5252 } else {
5253 m_scratch.packedArray.resize(elemCount * 2);
5254 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 2, elemCount, src, stride);
5255 f->glUniform2iv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->i);
5256 }
5257 }
5258 break;
5259 case QShaderDescription::Int3:
5260 {
5261 const int elemCount = uniform.arrayDim;
5262 if (elemCount < 1) {
5263 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5264 } else {
5265 m_scratch.packedArray.resize(elemCount * 3);
5266 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 3, elemCount, src, stride);
5267 f->glUniform3iv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->i);
5268 }
5269 }
5270 break;
5271 case QShaderDescription::Int4:
5272 if (uniform.arrayDim < 1 || stride == 16) {
5273 f->glUniform4iv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const qint32 *>(src));
5274 } else {
5275 m_scratch.packedArray.resize(uniform.arrayDim * 4);
5276 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 4, uniform.arrayDim, src, stride);
5277 f->glUniform4iv(uniform.glslLocation, uniform.arrayDim, &m_scratch.packedArray.constData()->i);
5278 }
5279 break;
5280 case QShaderDescription::Uint:
5281 {
5282 const int elemCount = uniform.arrayDim;
5283 if (elemCount < 1) {
5284 f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
5285 } else {
5286 m_scratch.packedArray.resize(elemCount);
5287 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 1, elemCount, src, stride);
5288 f->glUniform1uiv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->u);
5289 }
5290 }
5291 break;
5292 case QShaderDescription::Uint2:
5293 {
5294 const int elemCount = uniform.arrayDim;
5295 if (elemCount < 1) {
5296 f->glUniform2uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
5297 } else {
5298 m_scratch.packedArray.resize(elemCount * 2);
5299 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 2, elemCount, src, stride);
5300 f->glUniform2uiv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->u);
5301 }
5302 }
5303 break;
5304 case QShaderDescription::Uint3:
5305 {
5306 const int elemCount = uniform.arrayDim;
5307 if (elemCount < 1) {
5308 f->glUniform3uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
5309 } else {
5310 m_scratch.packedArray.resize(elemCount * 3);
5311 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 3, elemCount, src, stride);
5312 f->glUniform3uiv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->u);
5313 }
5314 }
5315 break;
5316 case QShaderDescription::Uint4:
5317 if (uniform.arrayDim < 1 || stride == 16) {
5318 f->glUniform4uiv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const quint32 *>(src));
5319 } else {
5320 m_scratch.packedArray.resize(uniform.arrayDim * 4);
5321 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 4, uniform.arrayDim, src, stride);
5322 f->glUniform4uiv(uniform.glslLocation, uniform.arrayDim, &m_scratch.packedArray.constData()->u);
5323 }
5324 break;
5325 case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
5326 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
5327 break;
5328 case QShaderDescription::Bool2:
5329 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5330 break;
5331 case QShaderDescription::Bool3:
5332 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5333 break;
5334 case QShaderDescription::Bool4:
5335 f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5336 break;
5337 default:
5338 qWarning("Uniform with binding %d, offset %u has unsupported type %d",
5339 uniform.binding, uniform.offset, uniform.type);
5340 break;
5341 }
5342}
5343
5344static inline bool qrhi_gl_isTrackedUniform(const QGles2UniformDescription &uniform)
5345{
5346 // The logic here must match setUniformValue().
5347
5348 if (uniform.arrayDim > 0)
5349 return false;
5350
5351 switch (uniform.type) {
5352 case QShaderDescription::Float:
5353 case QShaderDescription::Vec2:
5354 case QShaderDescription::Vec3:
5355 case QShaderDescription::Vec4:
5356 return true;
5357 default:
5358 return false;
5359 }
5360}
5361
5362void QRhiGles2::setupUniformStateTracking(QGles2UniformDescriptionVector *uniforms,
5363 QGles2UniformDescriptionVector *pushConstantUniforms,
5364 QGles2UniformStateVector *uniformState)
5365{
5366 int count = 0;
5367 for (QGles2UniformDescriptionVector *list : { uniforms, pushConstantUniforms }) {
5368 for (QGles2UniformDescription &uniform : *list)
5369 uniform.stateIndex = qrhi_gl_isTrackedUniform(uniform) ? count++ : -1;
5370 }
5371 uniformState->resize(count);
5372 if (count)
5373 memset(uniformState->data(), 0, size_t(count) * sizeof(QGles2UniformState));
5374}
5375
5376// Sets all uniforms belonging to one block. blockData is the start of the
5377// source data (the uniform buffer's contents, or the push constant block),
5378// blockOffset the offset the block starts at within it. Pass a binding of -1
5379// to take all entries of uniforms, which is what push constant blocks, having
5380// no binding, do.
5381void QRhiGles2::setUniformsFromBlock(const QGles2UniformDescriptionVector &uniforms, int binding,
5382 const char *blockData, qint64 blockSize, quint32 blockOffset,
5383 QGles2UniformState *uniformState)
5384{
5385 for (const QGles2UniformDescription &uniform : uniforms) {
5386 if (binding >= 0 && uniform.binding != binding)
5387 continue;
5388
5389 const qint64 readOffset = qint64(blockOffset) + uniform.offset;
5390 const qint64 readSize = qrhi_uniform_read_size(uniform.type, uniform.arrayDim, uniform.elemStride);
5391 if (readOffset < 0 || readOffset + readSize > blockSize) {
5392 qWarning("Uniform with binding %d, offset %u, type %d, array dimension %d would read "
5393 "outside of the %lld bytes of source data. Skipping.",
5394 uniform.binding, uniform.offset, uniform.type, uniform.arrayDim, blockSize);
5395 continue;
5396 }
5397
5398#ifndef QT_NO_DEBUG
5399 if (uniform.arrayDim > 0
5400 && uniform.type != QShaderDescription::Float
5401 && uniform.type != QShaderDescription::Vec2
5402 && uniform.type != QShaderDescription::Vec3
5403 && uniform.type != QShaderDescription::Vec4
5404 && uniform.type != QShaderDescription::Int
5405 && uniform.type != QShaderDescription::Int2
5406 && uniform.type != QShaderDescription::Int3
5407 && uniform.type != QShaderDescription::Int4
5408 && uniform.type != QShaderDescription::Uint
5409 && uniform.type != QShaderDescription::Uint2
5410 && uniform.type != QShaderDescription::Uint3
5411 && uniform.type != QShaderDescription::Uint4
5412 && uniform.type != QShaderDescription::Mat2
5413 && uniform.type != QShaderDescription::Mat3
5414 && uniform.type != QShaderDescription::Mat4)
5415 {
5416 qWarning("Uniform with binding %d, offset %u, type %d is an array, "
5417 "but arrays are only supported for float, vec2, vec3, vec4, int, "
5418 "ivec2, ivec3, ivec4, uint, uvec2, uvec3, uvec4, mat2, mat3 "
5419 "and mat4. "
5420 "Only the first element will be set.",
5421 uniform.binding, uniform.offset, uniform.type);
5422 }
5423#endif
5424
5425 // in both a uniform block and a push constant block everything is at
5426 // least 4 byte aligned, so this should not cause unaligned reads
5427 setUniformValue(uniform, blockData + readOffset, uniformState);
5428 }
5429}
5430
5432 QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
5433 QRhiShaderResourceBindings *srb,
5434 const uint *dynOfsPairs, int dynOfsCount)
5435{
5437 int texUnit = 1; // start from unit 1, keep 0 for resource mgmt stuff to avoid clashes
5438 bool activeTexUnitAltered = false;
5439 QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
5440 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
5441 QGles2UniformState *uniformState = maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState.data()
5442 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniformState.data();
5443 m_scratch.separateTextureBindings.clear();
5444 m_scratch.separateSamplerBindings.clear();
5445
5446 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
5447 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
5448
5449 switch (b->type) {
5450 case QRhiShaderResourceBinding::UniformBuffer:
5451 {
5452 int viewOffset = b->u.ubuf.offset;
5453 for (int j = 0; j < dynOfsCount; ++j) {
5454 if (dynOfsPairs[2 * j] == uint(b->binding)) {
5455 viewOffset = int(dynOfsPairs[2 * j + 1]);
5456 break;
5457 }
5458 }
5459 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
5460 setUniformsFromBlock(uniforms, b->binding, bufD->data.constData(), bufD->data.size(),
5461 quint32(viewOffset), uniformState);
5462 }
5463 break;
5464 case QRhiShaderResourceBinding::SampledTexture:
5465 {
5466 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
5467 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
5468 void *ps;
5469 uint psGeneration;
5470 if (maybeGraphicsPs) {
5471 ps = maybeGraphicsPs;
5472 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
5473 } else {
5474 ps = maybeComputePs;
5475 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
5476 }
5477 for (int elem = 0; elem < b->stex.count(); ++elem) {
5478 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->stex.texSamplers[elem].tex);
5479 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->stex.texSamplers[elem].sampler);
5480 for (const QGles2SamplerDescription &shaderSampler : samplers) {
5481 if (shaderSampler.combinedBinding == b->binding) {
5482 const int loc = shaderSampler.glslLocation + elem;
5483 bindCombinedSampler(cbD, texD, samplerD, ps, psGeneration, loc, &texUnit, &activeTexUnitAltered);
5484 break;
5485 }
5486 }
5487 }
5488 }
5489 break;
5490 case QRhiShaderResourceBinding::Texture:
5491 for (int elem = 0; elem < b->stex.count(); ++elem) {
5492 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->stex.texSamplers[elem].tex);
5493 m_scratch.separateTextureBindings.append({ texD, b->binding, elem });
5494 }
5495 break;
5496 case QRhiShaderResourceBinding::Sampler:
5497 {
5498 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->stex.texSamplers[0].sampler);
5499 m_scratch.separateSamplerBindings.append({ samplerD, b->binding });
5500 }
5501 break;
5502 case QRhiShaderResourceBinding::ImageLoad:
5503 case QRhiShaderResourceBinding::ImageStore:
5504 case QRhiShaderResourceBinding::ImageLoadStore:
5505 {
5506 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
5507 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
5508 // arrays, cubemaps, and 3D textures expose the whole texture with all layers/slices
5509 const bool layered = texD->m_flags.testFlag(QRhiTexture::CubeMap)
5510 || texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
5511 || texD->m_flags.testFlag(QRhiTexture::TextureArray);
5512 GLenum access = GL_READ_WRITE;
5513 if (b->type == QRhiShaderResourceBinding::ImageLoad)
5514 access = GL_READ_ONLY;
5515 else if (b->type == QRhiShaderResourceBinding::ImageStore)
5516 access = GL_WRITE_ONLY;
5517 f->glBindImageTexture(GLuint(b->binding), texD->texture,
5518 b->u.simage.level, layered, 0,
5519 access, texD->glsizedintformat);
5520 }
5521 break;
5522 case QRhiShaderResourceBinding::BufferLoad:
5523 case QRhiShaderResourceBinding::BufferStore:
5524 case QRhiShaderResourceBinding::BufferLoadStore:
5525 {
5526 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
5527 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
5528 if (b->u.sbuf.offset == 0 && b->u.sbuf.maybeSize == 0)
5529 f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer);
5530 else
5531 f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer,
5532 b->u.sbuf.offset, b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->m_size);
5533 }
5534 break;
5535 default:
5536 Q_UNREACHABLE();
5537 break;
5538 }
5539 }
5540
5541 if (!m_scratch.separateTextureBindings.isEmpty() || !m_scratch.separateSamplerBindings.isEmpty()) {
5542 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
5543 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
5544 void *ps;
5545 uint psGeneration;
5546 if (maybeGraphicsPs) {
5547 ps = maybeGraphicsPs;
5548 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
5549 } else {
5550 ps = maybeComputePs;
5551 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
5552 }
5553 for (const QGles2SamplerDescription &shaderSampler : samplers) {
5554 if (shaderSampler.combinedBinding >= 0)
5555 continue;
5556 for (const Scratch::SeparateSampler &sepSampler : std::as_const(m_scratch.separateSamplerBindings)) {
5557 if (sepSampler.binding != shaderSampler.sbinding)
5558 continue;
5559 for (const Scratch::SeparateTexture &sepTex : std::as_const(m_scratch.separateTextureBindings)) {
5560 if (sepTex.binding != shaderSampler.tbinding)
5561 continue;
5562 const int loc = shaderSampler.glslLocation + sepTex.elem;
5563 bindCombinedSampler(cbD, sepTex.texture, sepSampler.sampler, ps, psGeneration,
5564 loc, &texUnit, &activeTexUnitAltered);
5565 }
5566 }
5567 }
5568 }
5569
5570 if (activeTexUnitAltered)
5571 f->glActiveTexture(GL_TEXTURE0);
5572}
5573
5574void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
5575{
5576 Q_ASSERT(QRHI_RES(QGles2CommandBuffer, cb)->recordingPass == QGles2CommandBuffer::NoPass);
5577
5578 enqueueResourceUpdates(cb, resourceUpdates);
5579}
5580
5581QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
5582 bool *wantsColorClear, bool *wantsDsClear)
5583{
5584 QGles2RenderTargetData *rtD = nullptr;
5585 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
5586
5587 QGles2CommandBuffer::Command &fbCmd(cbD->commands.get());
5588 fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer;
5589
5590 static const bool doClearBuffers = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_BUFFERS") == 0;
5591 static const bool doClearColorBuffer = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_COLOR_BUFFER") == 0;
5592
5593 switch (rt->resourceType()) {
5594 case QRhiResource::SwapChainRenderTarget:
5595 rtD = &QRHI_RES(QGles2SwapChainRenderTarget, rt)->d;
5596 if (wantsColorClear)
5597 *wantsColorClear = doClearBuffers && doClearColorBuffer;
5598 if (wantsDsClear)
5599 *wantsDsClear = doClearBuffers;
5600 fbCmd.args.bindFramebuffer.fbo = 0;
5601 fbCmd.args.bindFramebuffer.colorAttCount = 1;
5602 fbCmd.args.bindFramebuffer.stereo = rtD->stereoTarget.has_value();
5603 if (fbCmd.args.bindFramebuffer.stereo)
5604 fbCmd.args.bindFramebuffer.stereoTarget = rtD->stereoTarget.value();
5605 break;
5606 case QRhiResource::TextureRenderTarget:
5607 {
5608 QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, rt);
5609 rtD = &rtTex->d;
5610 if (wantsColorClear)
5611 *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
5612 if (wantsDsClear)
5613 *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
5614 fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
5615 fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
5616 fbCmd.args.bindFramebuffer.stereo = false;
5617
5618 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
5619 it != itEnd; ++it)
5620 {
5621 const QRhiColorAttachment &colorAtt(*it);
5622 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
5623 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5624 if (texD && cbD->passNeedsResourceTracking) {
5625 trackedRegisterTexture(&passResTracker, texD,
5626 QRhiPassResourceTracker::TexColorOutput,
5627 QRhiPassResourceTracker::TexColorOutputStage);
5628 }
5629 if (resolveTexD && cbD->passNeedsResourceTracking) {
5630 trackedRegisterTexture(&passResTracker, resolveTexD,
5631 QRhiPassResourceTracker::TexColorOutput,
5632 QRhiPassResourceTracker::TexColorOutputStage);
5633 }
5634 // renderbuffers cannot be written in shaders (no image store) so
5635 // they do not matter here
5636 }
5637 if (rtTex->m_desc.depthTexture() && cbD->passNeedsResourceTracking) {
5638 trackedRegisterTexture(&passResTracker, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
5639 QRhiPassResourceTracker::TexDepthOutput,
5640 QRhiPassResourceTracker::TexDepthOutputStage);
5641 }
5642 }
5643 break;
5644 default:
5645 Q_UNREACHABLE();
5646 break;
5647 }
5648
5649 fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
5650
5651 return rtD;
5652}
5653
5655{
5656 cbD->passResTrackers.emplace_back();
5657 cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
5658 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5660 cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
5661}
5662
5663void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
5664 QRhiRenderTarget *rt,
5665 const QColor &colorClearValue,
5666 const QRhiDepthStencilClearValue &depthStencilClearValue,
5667 QRhiResourceUpdateBatch *resourceUpdates,
5668 QRhiCommandBuffer::BeginPassFlags flags)
5669{
5670 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5672
5673 if (resourceUpdates)
5674 enqueueResourceUpdates(cb, resourceUpdates);
5675
5676 // Get a new resource tracker. Then add a command that will generate
5677 // glMemoryBarrier() calls based on that tracker when submitted.
5679
5680 if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
5682 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(rtTex->description(), rtTex->d.currentResIdList))
5683 rtTex->create();
5684 }
5685
5686 bool wantsColorClear, wantsDsClear;
5687 QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
5688
5689 QGles2CommandBuffer::Command &clearCmd(cbD->commands.get());
5691 clearCmd.args.clear.mask = 0;
5692 if (rtD->colorAttCount && wantsColorClear)
5693 clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
5694 if (rtD->dsAttCount && wantsDsClear)
5695 clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
5696 clearCmd.args.clear.c[0] = colorClearValue.redF();
5697 clearCmd.args.clear.c[1] = colorClearValue.greenF();
5698 clearCmd.args.clear.c[2] = colorClearValue.blueF();
5699 clearCmd.args.clear.c[3] = colorClearValue.alphaF();
5700 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
5701 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
5702
5704 cbD->passNeedsResourceTracking = !flags.testFlag(QRhiCommandBuffer::DoNotTrackResourcesForCompute) && caps.compute;
5705 cbD->currentTarget = rt;
5706
5708}
5709
5710void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
5711{
5712 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5714
5715 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
5716 QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
5717 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
5718 it != itEnd; ++it)
5719 {
5720 const QRhiColorAttachment &colorAtt(*it);
5721 if (!colorAtt.resolveTexture())
5722 continue;
5723
5724 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5725 const QSize size = resolveTexD->pixelSize();
5726 if (colorAtt.renderBuffer()) {
5727 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer());
5728 if (rbD->pixelSize() != size) {
5729 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
5730 rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
5731 }
5733 // colorAtt.renderBuffer() is not actually used for anything if OpenGL ES'
5734 // auto-resolving GL_EXT_multisampled_render_to_texture is used.
5735 } else {
5736 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5738 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
5739 cmd.args.blitFromRenderbuffer.stencilRenderbuffer = 0;
5740 cmd.args.blitFromRenderbuffer.w = size.width();
5741 cmd.args.blitFromRenderbuffer.h = size.height();
5742 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
5743 cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
5744 else
5745 cmd.args.blitFromRenderbuffer.target = resolveTexD->target;
5746 cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture;
5747 cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel();
5748 const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
5749 || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray);
5750 cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
5751 cmd.args.blitFromRenderbuffer.isDepthStencil = false;
5752 cmd.args.blitFromRenderbuffer.hasStencil = false;
5753 }
5754 } else if (caps.glesMultisampleRenderToTexture) {
5755 // Nothing to do, resolving into colorAtt.resolveTexture() is automatic,
5756 // colorAtt.texture() is in fact not used for anything.
5757 } else {
5758 Q_ASSERT(colorAtt.texture());
5759 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
5760 if (texD->pixelSize() != size) {
5761 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
5762 texD->pixelSize().width(), texD->pixelSize().height(), size.width(), size.height());
5763 }
5764 const int resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
5765 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
5766 const int srcLayer = colorAtt.layer() + resolveIdx;
5767 const int dstLayer = colorAtt.resolveLayer() + resolveIdx;
5768 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5770 if (texD->m_flags.testFlag(QRhiTexture::CubeMap))
5771 cmd.args.blitFromTexture.srcTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(srcLayer);
5772 else
5773 cmd.args.blitFromTexture.srcTarget = texD->target;
5774 cmd.args.blitFromTexture.srcTexture = texD->texture;
5775 cmd.args.blitFromTexture.srcLevel = colorAtt.level();
5776 cmd.args.blitFromTexture.srcLayer = 0;
5777 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || texD->m_flags.testFlag(QRhiTexture::TextureArray))
5778 cmd.args.blitFromTexture.srcLayer = srcLayer;
5779 cmd.args.blitFromTexture.w = size.width();
5780 cmd.args.blitFromTexture.h = size.height();
5781 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
5782 cmd.args.blitFromTexture.dstTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(dstLayer);
5783 else
5784 cmd.args.blitFromTexture.dstTarget = resolveTexD->target;
5785 cmd.args.blitFromTexture.dstTexture = resolveTexD->texture;
5786 cmd.args.blitFromTexture.dstLevel = colorAtt.resolveLevel();
5787 cmd.args.blitFromTexture.dstLayer = 0;
5788 if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray))
5789 cmd.args.blitFromTexture.dstLayer = dstLayer;
5790 cmd.args.blitFromTexture.isDepthStencil = false;
5791 cmd.args.blitFromTexture.hasStencil = false;
5792 }
5793 }
5794 }
5795
5796 if (rtTex->m_desc.depthResolveTexture()) {
5797 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture());
5798 const QSize size = depthResolveTexD->pixelSize();
5799 if (rtTex->m_desc.depthStencilBuffer()) {
5800 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer());
5801 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5803 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
5804 cmd.args.blitFromRenderbuffer.stencilRenderbuffer = rbD->stencilRenderbuffer;
5805 cmd.args.blitFromRenderbuffer.w = size.width();
5806 cmd.args.blitFromRenderbuffer.h = size.height();
5807 cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target;
5808 cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture;
5809 cmd.args.blitFromRenderbuffer.dstLevel = 0;
5810 cmd.args.blitFromRenderbuffer.dstLayer = 0;
5811 cmd.args.blitFromRenderbuffer.isDepthStencil = true;
5812 // The renderbuffer always has stencil, but the destination may
5813 // be depth-only, in which case attaching it to
5814 // GL_STENCIL_ATTACHMENT would make the framebuffer incomplete.
5815 cmd.args.blitFromRenderbuffer.hasStencil = isStencilSupportingFormat(depthResolveTexD->format());
5816 } else if (caps.glesMultisampleRenderToTexture) {
5817 // Nothing to do, resolving into depthResolveTexture() is automatic.
5818 } else {
5819 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture());
5820 // Only involve the stencil when both sides actually have it.
5821 const bool hasStencil = isStencilSupportingFormat(depthTexD->format()) && isStencilSupportingFormat(depthResolveTexD->format());
5822 const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1;
5823 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
5824 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5826 cmd.args.blitFromTexture.srcTarget = depthTexD->target;
5827 cmd.args.blitFromTexture.srcTexture = depthTexD->texture;
5828 cmd.args.blitFromTexture.srcLevel = 0;
5829 cmd.args.blitFromTexture.srcLayer = resolveIdx;
5830 cmd.args.blitFromTexture.w = size.width();
5831 cmd.args.blitFromTexture.h = size.height();
5832 cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target;
5833 cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture;
5834 cmd.args.blitFromTexture.dstLevel = 0;
5835 cmd.args.blitFromTexture.dstLayer = resolveIdx;
5836 cmd.args.blitFromTexture.isDepthStencil = true;
5837 cmd.args.blitFromTexture.hasStencil = hasStencil;
5838 }
5839 }
5840 }
5841
5842 const bool mayDiscardDepthStencil =
5843 (rtTex->m_desc.depthStencilBuffer()
5844 || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)))
5845 && !rtTex->m_desc.depthResolveTexture();
5846 if (mayDiscardDepthStencil) {
5847 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5849 cmd.args.invalidateFramebuffer.fbo = rtTex->framebuffer;
5851 cmd.args.invalidateFramebuffer.attCount = 1;
5852 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_STENCIL_ATTACHMENT;
5853 } else {
5854 cmd.args.invalidateFramebuffer.attCount = 2;
5855 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_ATTACHMENT;
5856 cmd.args.invalidateFramebuffer.att[1] = GL_STENCIL_ATTACHMENT;
5857 }
5858 }
5859 }
5860
5862 cbD->currentTarget = nullptr;
5863
5864 if (resourceUpdates)
5865 enqueueResourceUpdates(cb, resourceUpdates);
5866}
5867
5868void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb,
5869 QRhiResourceUpdateBatch *resourceUpdates,
5871{
5872 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5874
5875 if (resourceUpdates)
5876 enqueueResourceUpdates(cb, resourceUpdates);
5877
5879
5881
5883}
5884
5885void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
5886{
5887 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5889
5891
5892 if (resourceUpdates)
5893 enqueueResourceUpdates(cb, resourceUpdates);
5894}
5895
5896void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
5897{
5898 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5901 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
5902
5903 if (pipelineChanged) {
5904 cbD->currentGraphicsPipeline = nullptr;
5905 cbD->currentComputePipeline = ps;
5907 if (psD->lastUsedInFrameNo != frameNo) {
5909 psD->currentSrb = nullptr;
5911 }
5912
5913 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5915 cmd.args.bindComputePipeline.ps = ps;
5916 }
5917}
5918
5919template<typename T>
5920inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
5921 QRhiShaderResourceBinding::Type bindingType,
5922 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
5923{
5924 int access = 0;
5925 if (bindingType == loadTypeVal) {
5927 } else {
5929 if (bindingType == loadStoreTypeVal)
5931 }
5932 auto it = writtenResources->find(resource);
5933 if (it != writtenResources->end())
5934 it->first |= access;
5935 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
5936 writtenResources->insert(resource, { access, true });
5937}
5938
5939// The glMemoryBarrier() bits needed based on what previous dispatches in the
5940// pass wrote. Updates writtenResources, so call once per dispatch.
5942{
5943 if (!cbD->currentComputeSrb)
5944 return 0;
5945
5946 GLbitfield barriers = 0;
5947
5948 // The key in the writtenResources map indicates that the resource was
5949 // written in a previous dispatch, whereas the value accumulates the
5950 // access mask in the current one.
5951 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
5952 accessAndIsNewFlag = { 0, false };
5953
5955 const int bindingCount = srbD->m_bindings.size();
5956 for (int i = 0; i < bindingCount; ++i) {
5957 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
5958 switch (b->type) {
5959 case QRhiShaderResourceBinding::ImageLoad:
5960 case QRhiShaderResourceBinding::ImageStore:
5961 case QRhiShaderResourceBinding::ImageLoadStore:
5962 qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
5963 b->u.simage.tex,
5964 b->type,
5965 QRhiShaderResourceBinding::ImageLoad,
5966 QRhiShaderResourceBinding::ImageStore,
5967 QRhiShaderResourceBinding::ImageLoadStore);
5968 break;
5969 case QRhiShaderResourceBinding::BufferLoad:
5970 case QRhiShaderResourceBinding::BufferStore:
5971 case QRhiShaderResourceBinding::BufferLoadStore:
5972 qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
5973 b->u.sbuf.buf,
5974 b->type,
5975 QRhiShaderResourceBinding::BufferLoad,
5976 QRhiShaderResourceBinding::BufferStore,
5977 QRhiShaderResourceBinding::BufferLoadStore);
5978 break;
5979 default:
5980 break;
5981 }
5982 }
5983
5984 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
5985 const int accessInThisDispatch = it->first;
5986 const bool isNewInThisDispatch = it->second;
5987 if (accessInThisDispatch && !isNewInThisDispatch) {
5988 if (it.key()->resourceType() == QRhiResource::Texture)
5990 else
5992 }
5993 // Anything that was previously written, but is only read now, can be
5994 // removed from the written list (because that previous write got a
5995 // corresponding barrier now).
5996 if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
5997 it = cbD->computePassState.writtenResources.erase(it);
5998 else
5999 ++it;
6000 }
6001
6002 return barriers;
6003}
6004
6005void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
6006{
6007 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
6009
6010 if (const GLbitfield barriers = barriersForNextDispatch(cbD)) {
6011 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6013 cmd.args.barrier.barriers = barriers;
6014 }
6015
6016 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6018 cmd.args.dispatch.x = GLuint(x);
6019 cmd.args.dispatch.y = GLuint(y);
6020 cmd.args.dispatch.z = GLuint(z);
6021}
6022
6023void QRhiGles2::dispatchIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
6024 quint32 indirectBufferOffset)
6025{
6026 if (!caps.dispatchIndirect) {
6027 qWarning("dispatchIndirect called but the DispatchIndirect feature is not supported");
6028 return;
6029 }
6030
6031 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
6033
6034 // Sample before barriersForNextDispatch(), which drops entries for anything
6035 // the upcoming dispatch only reads - including this buffer, if the consuming
6036 // shader also binds it as a storage buffer.
6037 const bool indirectBufWrittenInThisPass =
6038 cbD->computePassState.writtenResources.contains(indirectBuffer);
6039
6040 GLbitfield barriers = barriersForNextDispatch(cbD);
6041
6042 // The work group counts are read via the command processor, not as a shader
6043 // storage read, hence GL_COMMAND_BARRIER_BIT.
6044 if (indirectBufWrittenInThisPass)
6045 barriers |= GL_COMMAND_BARRIER_BIT;
6046
6047 if (barriers) {
6048 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6050 cmd.args.barrier.barriers = barriers;
6051 }
6052
6053 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
6054
6056 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6057 // When already registered - typically as the storage buffer the
6058 // arguments were generated into - registerBuffer() would reject the
6059 // differing access with a warning. Skipping it is fine because
6060 // BarriersForPass sets all buffer barrier bits anyway.
6061 if (!passResTracker.buffers().contains(indirectBufD)) {
6062 trackedRegisterBuffer(&passResTracker, indirectBufD,
6065 }
6066 }
6067
6068 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6070 cmd.args.dispatchIndirect.buffer = indirectBufD->buffer;
6071 cmd.args.dispatchIndirect.offset = indirectBufferOffset;
6072}
6073
6074static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
6075{
6076 switch (type) {
6077 case QRhiShaderStage::Vertex:
6078 return GL_VERTEX_SHADER;
6079 case QRhiShaderStage::TessellationControl:
6081 case QRhiShaderStage::TessellationEvaluation:
6083 case QRhiShaderStage::Geometry:
6084 return GL_GEOMETRY_SHADER;
6085 case QRhiShaderStage::Fragment:
6086 return GL_FRAGMENT_SHADER;
6087 case QRhiShaderStage::Compute:
6088 return GL_COMPUTE_SHADER;
6089 default:
6090 Q_UNREACHABLE_RETURN(GL_VERTEX_SHADER);
6091 }
6092}
6093
6094static inline bool isGraphicsStage(const QRhiShaderStage &shaderStage)
6095{
6096 const QRhiShaderStage::Type t = shaderStage.type();
6097 return t == QRhiShaderStage::Vertex
6098 || t == QRhiShaderStage::TessellationControl
6099 || t == QRhiShaderStage::TessellationEvaluation
6100 || t == QRhiShaderStage::Geometry
6101 || t == QRhiShaderStage::Fragment;
6102}
6103
6105{
6106 QList<int> versionsToTry;
6107 if (caps.gles) {
6108 if (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)) {
6109 versionsToTry << 320 << 310 << 300 << 100;
6110 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
6111 versionsToTry << 310 << 300 << 100;
6112 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
6113 versionsToTry << 300 << 100;
6114 } else {
6115 versionsToTry << 100;
6116 }
6117 } else {
6118 if (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6)) {
6119 versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6120 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 5) {
6121 versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6122 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 4) {
6123 versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6124 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 3) {
6125 versionsToTry << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6126 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 2) {
6127 versionsToTry << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6128 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 1) {
6129 versionsToTry << 410 << 400 << 330 << 150 << 140 << 130;
6130 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 0) {
6131 versionsToTry << 400 << 330 << 150 << 140 << 130;
6132 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 3) {
6133 versionsToTry << 330 << 150 << 140 << 130;
6134 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 2) {
6135 versionsToTry << 150 << 140 << 130;
6136 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
6137 versionsToTry << 140 << 130;
6138 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
6139 versionsToTry << 130;
6140 }
6141 if (!caps.coreProfile)
6142 versionsToTry << 120;
6143 }
6144
6145 const QShaderVersion::Flags flags = caps.gles ? QShaderVersion::GlslEs : QShaderVersion::Flags();
6146 QList<QShaderVersion> versions;
6147 versions.reserve(versionsToTry.size());
6148 for (int v : std::as_const(versionsToTry))
6149 versions.append(QShaderVersion(v, flags));
6150 return versions;
6151}
6152
6153/*
6154 Returns the highest GLSL ES version all graphics stages in stages have code
6155 for, or nothing when they share none. GLSL ES cannot link shaders of
6156 different versions; non-es GLSL can, hence the caps.gles check.
6157*/
6159 int stageCount) const
6160{
6161 if (!caps.gles)
6162 return std::nullopt;
6163
6164 for (const QShaderVersion &ver : glslVersionsToTry()) {
6165 bool allStagesHaveIt = true;
6166 for (int i = 0; i < stageCount; ++i) {
6167 const QRhiShaderStage &stage(stages[i]);
6168 if (!isGraphicsStage(stage))
6169 continue;
6170 const QShaderKey key(QShader::GlslShader, ver, stage.shaderVariant());
6171 if (stage.shader().shader(key).shader().isEmpty()) {
6172 allStagesHaveIt = false;
6173 break;
6174 }
6175 }
6176 if (allStagesHaveIt)
6177 return ver;
6178 }
6179 return std::nullopt;
6180}
6181
6182QByteArray QRhiGles2::shaderSource(const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion,
6183 std::optional<QShaderVersion> commonVersion)
6184{
6185 const QShader bakedShader = shaderStage.shader();
6186 const QList<QShaderVersion> versionsToTry = commonVersion
6187 ? QList<QShaderVersion>{ *commonVersion }
6188 : glslVersionsToTry();
6189
6190 QByteArray source;
6191 for (const QShaderVersion &ver : versionsToTry) {
6192 source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
6193 if (!source.isEmpty()) {
6194 if (shaderVersion)
6195 *shaderVersion = ver;
6196 break;
6197 }
6198 }
6199 if (source.isEmpty()) {
6200 qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
6201 << ") in baked shader" << bakedShader;
6202 }
6203 return source;
6204}
6205
6206bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion,
6207 std::optional<QShaderVersion> commonVersion)
6208{
6209 QShaderVersion actualVersion;
6210 const QByteArray source = shaderSource(shaderStage, &actualVersion, commonVersion);
6211 if (source.isEmpty())
6212 return false;
6213 if (shaderVersion)
6214 *shaderVersion = actualVersion;
6215
6216 GLuint shader;
6217 const auto cacheKey = std::make_pair(shaderStage, actualVersion);
6218 auto cacheIt = m_shaderCache.constFind(cacheKey);
6219 if (cacheIt != m_shaderCache.constEnd()) {
6220 shader = *cacheIt;
6221 } else {
6222 shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
6223 const char *srcStr = source.constData();
6224 const GLint srcLength = source.size();
6225 f->glShaderSource(shader, 1, &srcStr, &srcLength);
6226 f->glCompileShader(shader);
6227 GLint compiled = 0;
6228 f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
6229 if (!compiled) {
6230 GLint infoLogLength = 0;
6231 f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
6232 QByteArray log;
6233 if (infoLogLength > 1) {
6234 GLsizei length = 0;
6235 log.resize(infoLogLength);
6236 f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
6237 }
6238 qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
6239 return false;
6240 }
6241 if (m_shaderCache.size() >= MAX_SHADER_CACHE_ENTRIES) {
6242 // Use the simplest strategy: too many cached shaders -> drop them all.
6243 for (uint shader : std::as_const(m_shaderCache))
6244 f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
6245 m_shaderCache.clear();
6246 }
6247 m_shaderCache.insert(cacheKey, shader);
6248 }
6249
6250 f->glAttachShader(program, shader);
6251
6252 return true;
6253}
6254
6255bool QRhiGles2::linkProgram(GLuint program)
6256{
6257 f->glLinkProgram(program);
6258 GLint linked = 0;
6259 f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
6260 if (!linked) {
6261 GLint infoLogLength = 0;
6262 f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
6263 QByteArray log;
6264 if (infoLogLength > 1) {
6265 GLsizei length = 0;
6266 log.resize(infoLogLength);
6267 f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
6268 }
6269 qWarning("Failed to link shader program: %s", log.constData());
6270 return false;
6271 }
6272 return true;
6273}
6274
6275void QRhiGles2::registerUniformIfActive(const QShaderDescription::BlockVariable &var,
6276 const QByteArray &namePrefix,
6277 int binding,
6278 int baseOffset,
6279 GLuint program,
6280 ActiveUniformLocationTracker *activeUniformLocations,
6281 QGles2UniformDescriptionVector *dst)
6282{
6283 if (var.type == QShaderDescription::Struct) {
6284 qWarning("Nested structs are not supported at the moment. '%s' ignored.",
6285 var.name.constData());
6286 return;
6287 }
6289 uniform.type = var.type;
6290 const QByteArray name = namePrefix + var.name;
6291 // Here we expect that the OpenGL implementation has proper active uniform
6292 // handling, meaning that a uniform that is declared but not accessed
6293 // elsewhere in the code is reported as -1 when querying the location. If
6294 // that is not the case, it won't break anything, but we'll generate
6295 // unnecessary glUniform* calls then.
6296 uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
6297 uniform.stateIndex = -1; // assigned later
6298 if (uniform.glslLocation >= 0 && !activeUniformLocations->hasSeen(uniform.glslLocation)) {
6299 if (var.arrayDims.size() > 1) {
6300 qWarning("Array '%s' has more than one dimension. This is not supported.",
6301 var.name.constData());
6302 return;
6303 }
6304 uniform.binding = binding;
6305 uniform.offset = uint(baseOffset + var.offset);
6306 uniform.size = var.size;
6307 uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
6308 // For matrices the distance between the columns is known from the
6309 // reflection. For arrays it has to be derived from the total size.
6310 // (std140 and std430 differ here: an array of scalars or vec2 is
6311 // tightly packed in the latter, which is what push constants use)
6312 if (var.matrixStride)
6313 uniform.elemStride = quint32(var.matrixStride);
6314 else if (uniform.arrayDim > 0 && var.size > 0)
6315 uniform.elemStride = quint32(var.size / uniform.arrayDim);
6316 else
6317 uniform.elemStride = 0;
6318 dst->append(uniform);
6319 }
6320}
6321
6323 const QByteArray &prefix,
6324 int binding,
6325 const QList<QShaderDescription::BlockVariable> &members,
6326 ActiveUniformLocationTracker *activeUniformLocations,
6327 QGles2UniformDescriptionVector *dst)
6328{
6329 for (const QShaderDescription::BlockVariable &blockMember : members) {
6330 if (blockMember.type == QShaderDescription::Struct) {
6331 QByteArray structPrefix = prefix + blockMember.name;
6332
6333 const int baseOffset = blockMember.offset;
6334 if (blockMember.arrayDims.isEmpty()) {
6335 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
6336 registerUniformIfActive(structMember, structPrefix + ".", binding,
6337 baseOffset, program, activeUniformLocations, dst);
6338 } else {
6339 if (blockMember.arrayDims.size() > 1) {
6340 qWarning("Array of struct '%s' has more than one dimension. Only the first "
6341 "dimension is used.",
6342 blockMember.name.constData());
6343 }
6344 const int dim = blockMember.arrayDims.first();
6345 if (dim < 1) {
6346 qWarning("Array of struct '%s' has an invalid first dimension (%d). Ignored.",
6347 blockMember.name.constData(), dim);
6348 continue;
6349 }
6350 const int elemSize = blockMember.size / dim;
6351 int elemOffset = baseOffset;
6352 for (int di = 0; di < dim; ++di) {
6353 const QByteArray arrayPrefix = structPrefix + '[' + QByteArray::number(di) + ']' + '.';
6354 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
6355 registerUniformIfActive(structMember, arrayPrefix, binding, elemOffset, program, activeUniformLocations, dst);
6356 elemOffset += elemSize;
6357 }
6358 }
6359 } else {
6360 registerUniformIfActive(blockMember, prefix, binding, 0, program, activeUniformLocations, dst);
6361 }
6362 }
6363}
6364
6365void QRhiGles2::gatherUniforms(GLuint program,
6366 const QShaderDescription::UniformBlock &ub,
6367 ActiveUniformLocationTracker *activeUniformLocations,
6368 QGles2UniformDescriptionVector *dst)
6369{
6370 gatherBlockMemberUniforms(program, ub.structName + '.', ub.binding, ub.members,
6371 activeUniformLocations, dst);
6372}
6373
6374void QRhiGles2::gatherPushConstantUniforms(GLuint program,
6375 const QShaderDescription::PushConstantBlock &pcb,
6376 ActiveUniformLocationTracker *activeUniformLocations,
6377 QGles2UniformDescriptionVector *dst)
6378{
6379 gatherBlockMemberUniforms(program, pcb.name + '.', -1, pcb.members, activeUniformLocations, dst);
6380}
6381
6382void QRhiGles2::gatherSamplers(GLuint program,
6383 const QShaderDescription::InOutVariable &v,
6384 QGles2SamplerDescriptionVector *dst)
6385{
6386 QGles2SamplerDescription sampler;
6387 sampler.glslLocation = f->glGetUniformLocation(program, v.name.constData());
6388 if (sampler.glslLocation >= 0) {
6389 sampler.combinedBinding = v.binding;
6390 sampler.tbinding = -1;
6391 sampler.sbinding = -1;
6392 dst->append(sampler);
6393 }
6394}
6395
6396void QRhiGles2::gatherGeneratedSamplers(GLuint program,
6397 const QShader::SeparateToCombinedImageSamplerMapping &mapping,
6398 QGles2SamplerDescriptionVector *dst)
6399{
6400 QGles2SamplerDescription sampler;
6401 sampler.glslLocation = f->glGetUniformLocation(program, mapping.combinedSamplerName.constData());
6402 if (sampler.glslLocation >= 0) {
6403 sampler.combinedBinding = -1;
6404 sampler.tbinding = mapping.textureBinding;
6405 sampler.sbinding = mapping.samplerBinding;
6406 dst->append(sampler);
6407 }
6408}
6409
6410void QRhiGles2::sanityCheckVertexFragmentInterface(const QShaderDescription &vsDesc, const QShaderDescription &fsDesc)
6411{
6412 if (!vsDesc.isValid() || !fsDesc.isValid())
6413 return;
6414
6415 // Print a warning if the fragment shader input for a given location uses a
6416 // name that does not match the vertex shader output at the same location.
6417 // This is not an error with any other API and not with GLSL >= 330 either,
6418 // but matters for older GLSL code that has no location qualifiers.
6419 const auto vsOutputs = vsDesc.outputVariables();
6420 const auto fsInputs = fsDesc.inputVariables();
6421 for (const QShaderDescription::InOutVariable &outVar : vsOutputs) {
6422 for (const QShaderDescription::InOutVariable &inVar : fsInputs) {
6423 if (inVar.location == outVar.location) {
6424 if (inVar.name != outVar.name) {
6425 qWarning("Vertex output name '%s' does not match fragment input '%s'. "
6426 "This should be avoided because it causes problems with older GLSL versions.",
6427 outVar.name.constData(), inVar.name.constData());
6428 }
6429 break;
6430 }
6431 }
6432 }
6433}
6434
6435bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
6436{
6437 static QOpenGLProgramBinarySupportCheckWrapper checker;
6438 return checker.get(ctx)->isSupported();
6439}
6440
6441Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
6442
6443static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
6444{
6445 switch (type) {
6446 case QRhiShaderStage::Vertex:
6447 return QShader::VertexStage;
6448 case QRhiShaderStage::TessellationControl:
6449 return QShader::TessellationControlStage;
6450 case QRhiShaderStage::TessellationEvaluation:
6451 return QShader::TessellationEvaluationStage;
6452 case QRhiShaderStage::Geometry:
6453 return QShader::GeometryStage;
6454 case QRhiShaderStage::Fragment:
6455 return QShader::FragmentStage;
6456 case QRhiShaderStage::Compute:
6457 return QShader::ComputeStage;
6458 default:
6459 Q_UNREACHABLE_RETURN(QShader::VertexStage);
6460 }
6461}
6462
6463QRhiGles2::ProgramCacheResult QRhiGles2::tryLoadFromDiskOrPipelineCache(const QRhiShaderStage *stages,
6464 int stageCount,
6465 GLuint program,
6466 const QVector<QShaderDescription::InOutVariable> &inputVars,
6467 QByteArray *cacheKey,
6468 std::optional<QShaderVersion> commonVersion)
6469{
6470 Q_ASSERT(cacheKey);
6471
6472 // the traditional QOpenGL disk cache since Qt 5.9
6473 const bool legacyDiskCacheEnabled = isProgramBinaryDiskCacheEnabled();
6474
6475 // QRhi's own (set)PipelineCacheData()
6476 const bool pipelineCacheEnabled = caps.programBinary && !m_pipelineCache.data.isEmpty();
6477
6478 // calculating the cache key based on the source code is common for both types of caches
6479 if (legacyDiskCacheEnabled || pipelineCacheEnabled) {
6480 QOpenGLProgramBinaryCache::ProgramDesc binaryProgram;
6481 for (int i = 0; i < stageCount; ++i) {
6482 const QRhiShaderStage &stage(stages[i]);
6483 // commonVersion covers the graphics stages only.
6484 QByteArray source = shaderSource(stage, nullptr,
6485 isGraphicsStage(stage) ? commonVersion : std::nullopt);
6486 if (source.isEmpty())
6487 return QRhiGles2::ProgramCacheError;
6488
6489 if (stage.type() == QRhiShaderStage::Vertex) {
6490 // Now add something to the key that indicates the vertex input locations.
6491 // A GLSL shader lower than 330 (150, 140, ...) will not have location
6492 // qualifiers. This means that the shader source code is the same
6493 // regardless of what locations inputVars contains. This becomes a problem
6494 // because we'll glBindAttribLocation the shader based on inputVars, but
6495 // that's only when compiling/linking when there was no cache hit. Picking
6496 // from the cache afterwards should take the input locations into account
6497 // since if inputVars has now different locations for the attributes, then
6498 // it is not ok to reuse a program binary that used different attribute
6499 // locations. For a lot of clients this would not be an issue since they
6500 // typically hardcode and use the same vertex locations on every run. Some
6501 // systems that dynamically generate shaders may end up with a non-stable
6502 // order (and so location numbers), however. This is sub-optimal because
6503 // it makes caching inefficient, and said clients should be fixed, but in
6504 // any case this should not break rendering. Hence including the locations
6505 // in the cache key.
6506 QMap<QByteArray, int> inputLocations; // sorted by key when iterating
6507 for (const QShaderDescription::InOutVariable &var : inputVars)
6508 inputLocations.insert(var.name, var.location);
6509 source += QByteArrayLiteral("\n // "); // just to be nice; treated as an arbitrary string regardless
6510 for (auto it = inputLocations.cbegin(), end = inputLocations.cend(); it != end; ++it) {
6511 source += it.key();
6512 source += QByteArray::number(it.value());
6513 }
6514 source += QByteArrayLiteral("\n");
6515 }
6516
6517 binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(toShaderStage(stage.type()), source));
6518 }
6519
6520 *cacheKey = binaryProgram.cacheKey();
6521
6522 // Try our pipeline cache simulation first, if it got seeded with
6523 // setPipelineCacheData and there's a hit, then no need to go to the
6524 // filesystem at all.
6525 if (pipelineCacheEnabled) {
6526 auto it = m_pipelineCache.data.constFind(*cacheKey);
6527 if (it != m_pipelineCache.data.constEnd()) {
6528 GLenum err;
6529 for ( ; ; ) {
6530 err = f->glGetError();
6531 if (err == GL_NO_ERROR || err == GL_CONTEXT_LOST)
6532 break;
6533 }
6534 f->glProgramBinary(program, it->format, it->data.constData(), it->data.size());
6535 err = f->glGetError();
6536 if (err == GL_NO_ERROR) {
6537 GLint linkStatus = 0;
6538 f->glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
6539 if (linkStatus == GL_TRUE)
6540 return QRhiGles2::ProgramCacheHit;
6541 }
6542 }
6543 }
6544
6545 if (legacyDiskCacheEnabled && qrhi_programBinaryCache()->load(*cacheKey, program)) {
6546 // use the logging category QOpenGLShaderProgram would
6547 qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
6548 program, cacheKey->constData());
6549 return QRhiGles2::ProgramCacheHit;
6550 }
6551 }
6552
6553 return QRhiGles2::ProgramCacheMiss;
6554}
6555
6556void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
6557{
6558 // This is only for the traditional QOpenGL disk cache since Qt 5.9.
6559
6560 if (isProgramBinaryDiskCacheEnabled()) {
6561 // use the logging category QOpenGLShaderProgram would
6562 qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
6563 program, cacheKey.constData());
6564 qrhi_programBinaryCache()->save(cacheKey, program);
6565 }
6566}
6567
6568void QRhiGles2::trySaveToPipelineCache(GLuint program, const QByteArray &cacheKey, bool force)
6569{
6570 // This handles our own simulated "pipeline cache". (specific to QRhi, not
6571 // shared with legacy QOpenGL* stuff)
6572
6573 if (caps.programBinary && (force || !m_pipelineCache.data.contains(cacheKey))) {
6574 GLint blobSize = 0;
6575 f->glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &blobSize);
6576 QByteArray blob(blobSize, Qt::Uninitialized);
6577 GLint outSize = 0;
6578 GLenum binaryFormat = 0;
6579 f->glGetProgramBinary(program, blobSize, &outSize, &binaryFormat, blob.data());
6580 if (blobSize == outSize)
6581 m_pipelineCache.insertWithCapacityLimit(cacheKey, { binaryFormat, blob });
6582 }
6583}
6584
6585QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
6586 : QRhiBuffer(rhi, type, usage, size)
6587{
6588}
6589
6590QGles2Buffer::~QGles2Buffer()
6591{
6592 destroy();
6593}
6594
6595void QGles2Buffer::destroy()
6596{
6597 data.clear();
6598 if (!buffer)
6599 return;
6600
6601 QRhiGles2::DeferredReleaseEntry e;
6602 e.type = QRhiGles2::DeferredReleaseEntry::Buffer;
6603
6604 e.buffer.buffer = buffer;
6605 buffer = 0;
6606
6607 QRHI_RES_RHI(QRhiGles2);
6608 if (rhiD) {
6609 rhiD->releaseQueue.append(e);
6610 rhiD->unregisterResource(this);
6611 }
6612}
6613
6614bool QGles2Buffer::create()
6615{
6616 if (buffer)
6617 destroy();
6618
6619 QRHI_RES_RHI(QRhiGles2);
6620
6621 nonZeroSize = m_size <= 0 ? 256 : m_size;
6622
6623 if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
6624 if (int(m_usage) != QRhiBuffer::UniformBuffer) {
6625 qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
6626 return false;
6627 }
6628 data.resize(nonZeroSize);
6629 return true;
6630 }
6631
6632 if (!rhiD->ensureContext())
6633 return false;
6634
6635 targetForDataOps = GL_ARRAY_BUFFER;
6636 if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
6637 targetForDataOps = GL_ELEMENT_ARRAY_BUFFER;
6638 else if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
6639 targetForDataOps = GL_SHADER_STORAGE_BUFFER;
6640 else if (m_usage.testFlag(QRhiBuffer::IndirectBuffer))
6641 targetForDataOps = GL_DRAW_INDIRECT_BUFFER;
6642
6643 rhiD->f->glGenBuffers(1, &buffer);
6644 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6645 rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
6646
6647 if (rhiD->glObjectLabel)
6648 rhiD->glObjectLabel(GL_BUFFER, buffer, -1, m_objectName.constData());
6649
6650 usageState.access = AccessNone;
6651
6652 rhiD->registerResource(this);
6653 return true;
6654}
6655
6656QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer()
6657{
6658 if (m_usage.testFlag(QRhiBuffer::UniformBuffer))
6659 return { {}, 0 };
6660
6661 return { { &buffer }, 1 };
6662}
6663
6664char *QGles2Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
6665{
6666 Q_ASSERT(m_type == Dynamic);
6667 if (!m_usage.testFlag(UniformBuffer)) {
6668 QRHI_RES_RHI(QRhiGles2);
6669 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6670 if (rhiD->caps.properMapBuffer) {
6671 return static_cast<char *>(rhiD->f->glMapBufferRange(targetForDataOps, 0, nonZeroSize,
6673 } else {
6674 // Need some storage for the data, use the otherwise unused 'data' member.
6675 if (data.isEmpty())
6676 data.resize(nonZeroSize);
6677 }
6678 }
6679 return data.data();
6680}
6681
6682void QGles2Buffer::endFullDynamicBufferUpdateForCurrentFrame()
6683{
6684 if (!m_usage.testFlag(UniformBuffer)) {
6685 QRHI_RES_RHI(QRhiGles2);
6686 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6687 if (rhiD->caps.properMapBuffer)
6688 rhiD->f->glUnmapBuffer(targetForDataOps);
6689 else
6690 rhiD->f->glBufferSubData(targetForDataOps, 0, nonZeroSize, data.data());
6691 }
6692}
6693
6694void QGles2Buffer::fullDynamicBufferUpdateForCurrentFrame(const void *bufferData, quint32 size)
6695{
6696 const quint32 copySize = size > 0 ? size : m_size;
6697 if (!m_usage.testFlag(UniformBuffer)) {
6698 QRHI_RES_RHI(QRhiGles2);
6699 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6700 rhiD->f->glBufferSubData(targetForDataOps, 0, copySize, bufferData);
6701 } else {
6702 memcpy(data.data(), bufferData, copySize);
6703 }
6704}
6705
6706QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
6707 int sampleCount, QRhiRenderBuffer::Flags flags,
6708 QRhiTexture::Format backingFormatHint)
6709 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
6710{
6711}
6712
6713QGles2RenderBuffer::~QGles2RenderBuffer()
6714{
6715 destroy();
6716}
6717
6718void QGles2RenderBuffer::destroy()
6719{
6720 if (!renderbuffer)
6721 return;
6722
6723 QRhiGles2::DeferredReleaseEntry e;
6724 e.type = QRhiGles2::DeferredReleaseEntry::RenderBuffer;
6725
6726 e.renderbuffer.renderbuffer = renderbuffer;
6727 e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
6728
6729 renderbuffer = 0;
6730 stencilRenderbuffer = 0;
6731
6732 QRHI_RES_RHI(QRhiGles2);
6733 if (rhiD) {
6734 if (owns)
6735 rhiD->releaseQueue.append(e);
6736 rhiD->unregisterResource(this);
6737 }
6738}
6739
6740bool QGles2RenderBuffer::create()
6741{
6742 if (renderbuffer)
6743 destroy();
6744
6745 QRHI_RES_RHI(QRhiGles2);
6746 samples = rhiD->effectiveSampleCount(m_sampleCount);
6747
6748 if (m_flags.testFlag(UsedWithSwapChainOnly)) {
6749 if (m_type == DepthStencil)
6750 return true;
6751
6752 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
6753 }
6754
6755 if (!rhiD->ensureContext())
6756 return false;
6757
6758 rhiD->f->glGenRenderbuffers(1, &renderbuffer);
6759 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
6760
6761 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
6762
6763 switch (m_type) {
6764 case QRhiRenderBuffer::DepthStencil:
6765 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
6766 if (rhiD->caps.glesMultisampleRenderToTexture) {
6767 // Must match the logic in QGles2TextureRenderTarget::create().
6768 // EXT and non-EXT are not the same thing.
6769 rhiD->glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
6770 size.width(), size.height());
6771 } else {
6772 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
6773 size.width(), size.height());
6774 }
6775 stencilRenderbuffer = 0;
6776 } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
6777 const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
6778 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage,
6779 size.width(), size.height());
6780 stencilRenderbuffer = 0;
6781 } else {
6782 GLenum depthStorage = GL_DEPTH_COMPONENT;
6783 if (rhiD->caps.gles) {
6784 if (rhiD->caps.depth24)
6785 depthStorage = GL_DEPTH_COMPONENT24;
6786 else
6787 depthStorage = GL_DEPTH_COMPONENT16; // plain ES 2.0 only has this
6788 }
6789 const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
6790 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage,
6791 size.width(), size.height());
6792 rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
6793 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
6794 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
6795 size.width(), size.height());
6796 }
6797 break;
6798 case QRhiRenderBuffer::Color:
6799 {
6800 GLenum internalFormat = GL_RGBA4; // ES 2.0
6801 if (rhiD->caps.rgba8Format) {
6802 internalFormat = GL_RGBA8;
6803 if (m_backingFormatHint != QRhiTexture::UnknownFormat) {
6804 GLenum glintformat, glformat, gltype;
6805 // only care about the sized internal format, the rest is not used here
6806 toGlTextureFormat(m_backingFormatHint, {}, rhiD->caps,
6807 &glintformat, &internalFormat, &glformat, &gltype);
6808 }
6809 }
6810 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
6811 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalFormat,
6812 size.width(), size.height());
6813 } else {
6814 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat,
6815 size.width(), size.height());
6816 }
6817 }
6818 break;
6819 default:
6820 Q_UNREACHABLE();
6821 break;
6822 }
6823
6824 if (rhiD->glObjectLabel)
6825 rhiD->glObjectLabel(GL_RENDERBUFFER, renderbuffer, -1, m_objectName.constData());
6826
6827 owns = true;
6828 generation += 1;
6829 rhiD->registerResource(this);
6830 return true;
6831}
6832
6833bool QGles2RenderBuffer::createFrom(NativeRenderBuffer src)
6834{
6835 if (!src.object)
6836 return false;
6837
6838 if (renderbuffer)
6839 destroy();
6840
6841 QRHI_RES_RHI(QRhiGles2);
6842 samples = rhiD->effectiveSampleCount(m_sampleCount);
6843
6844 if (m_flags.testFlag(UsedWithSwapChainOnly))
6845 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless when importing an existing native object");
6846
6847 if (!rhiD->ensureContext())
6848 return false;
6849
6850 renderbuffer = src.object;
6851
6852 owns = false;
6853 generation += 1;
6854 rhiD->registerResource(this);
6855 return true;
6856}
6857
6858QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
6859{
6860 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
6861 return m_backingFormatHint;
6862 else
6863 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
6864}
6865
6866QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
6867 int arraySize, int sampleCount, Flags flags)
6868 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
6869{
6870}
6871
6872QGles2Texture::~QGles2Texture()
6873{
6874 destroy();
6875}
6876
6877void QGles2Texture::destroy()
6878{
6879 if (!texture)
6880 return;
6881
6882 QRhiGles2::DeferredReleaseEntry e;
6883 e.type = QRhiGles2::DeferredReleaseEntry::Texture;
6884
6885 e.texture.texture = texture;
6886
6887 texture = 0;
6888 specified = false;
6889 zeroInitialized = false;
6890
6891 QRHI_RES_RHI(QRhiGles2);
6892 if (rhiD) {
6893 if (owns)
6894 rhiD->releaseQueue.append(e);
6895 rhiD->unregisterResource(this);
6896 }
6897}
6898
6899bool QGles2Texture::prepareCreate(QSize *adjustedSize)
6900{
6901 if (texture)
6902 destroy();
6903
6904 QRHI_RES_RHI(QRhiGles2);
6905 if (!rhiD->ensureContext())
6906 return false;
6907 if (!rhiD->isTextureFormatSupported(m_format, m_flags))
6908 return false;
6909
6910 const bool isCube = m_flags.testFlag(CubeMap);
6911 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
6912 const bool is3D = m_flags.testFlag(ThreeDimensional);
6913 const bool hasMipMaps = m_flags.testFlag(MipMapped);
6914 const bool isCompressed = rhiD->isCompressedFormat(m_format);
6915 const bool is1D = m_flags.testFlag(OneDimensional);
6916
6917 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
6918 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
6919
6920 samples = rhiD->effectiveSampleCount(m_sampleCount);
6921
6922 if (is3D && !rhiD->caps.texture3D) {
6923 qWarning("3D textures are not supported");
6924 return false;
6925 }
6926 if (isCube && is3D) {
6927 qWarning("Texture cannot be both cube and 3D");
6928 return false;
6929 }
6930 if (isArray && is3D) {
6931 qWarning("Texture cannot be both array and 3D");
6932 return false;
6933 }
6934 if (is1D && !rhiD->caps.texture1D) {
6935 qWarning("1D textures are not supported");
6936 return false;
6937 }
6938 if (is1D && is3D) {
6939 qWarning("Texture cannot be both 1D and 3D");
6940 return false;
6941 }
6942 if (is1D && isCube) {
6943 qWarning("Texture cannot be both 1D and cube");
6944 return false;
6945 }
6946
6947 if (m_depth > 1 && !is3D) {
6948 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
6949 return false;
6950 }
6951 if (m_arraySize > 0 && !isArray) {
6952 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
6953 return false;
6954 }
6955 if (m_arraySize < 1 && isArray) {
6956 qWarning("Texture is an array but array size is %d", m_arraySize);
6957 return false;
6958 }
6959
6960 target = isCube ? GL_TEXTURE_CUBE_MAP
6962 : (is3D ? GL_TEXTURE_3D
6963 : (is1D ? (isArray ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D)
6964 : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)));
6965
6966 if (m_flags.testFlag(ExternalOES))
6967 target = GL_TEXTURE_EXTERNAL_OES;
6968 else if (m_flags.testFlag(TextureRectangleGL))
6969 target = GL_TEXTURE_RECTANGLE;
6970
6971 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
6972 gltype = GL_UNSIGNED_BYTE;
6973
6974 if (isCompressed) {
6975 if (m_flags.testFlag(UsedWithLoadStore)) {
6976 qWarning("Compressed texture cannot be used with image load/store");
6977 return false;
6978 }
6979 glintformat = toGlCompressedTextureFormat(m_format, m_flags);
6980 if (!glintformat) {
6981 qWarning("Compressed format %d not mappable to GL compressed format", m_format);
6982 return false;
6983 }
6984 glsizedintformat = glintformat;
6985 glformat = GL_RGBA;
6986 } else {
6987 toGlTextureFormat(m_format, m_flags, rhiD->caps,
6988 &glintformat, &glsizedintformat, &glformat, &gltype);
6989 }
6990
6991 samplerState = QGles2SamplerData();
6992
6993 usageState.access = AccessNone;
6994
6995 if (!rhiD->textureFormatInfo(m_format, size, nullptr, nullptr, nullptr))
6996 return false;
6997
6998 if (adjustedSize)
6999 *adjustedSize = size;
7000
7001 return true;
7002}
7003
7004bool QGles2Texture::create()
7005{
7006 QSize size;
7007 if (!prepareCreate(&size))
7008 return false;
7009
7010 QRHI_RES_RHI(QRhiGles2);
7011 rhiD->f->glGenTextures(1, &texture);
7012
7013 const bool isCube = m_flags.testFlag(CubeMap);
7014 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
7015 const bool is3D = m_flags.testFlag(ThreeDimensional);
7016 const bool hasMipMaps = m_flags.testFlag(MipMapped);
7017 const bool isCompressed = rhiD->isCompressedFormat(m_format);
7018 const bool is1D = m_flags.testFlag(OneDimensional);
7019
7020 if (!isCompressed) {
7021 rhiD->f->glBindTexture(target, texture);
7022 if (!m_flags.testFlag(UsedWithLoadStore)) {
7023 if (is1D) {
7024 for (int level = 0; level < mipLevelCount; ++level) {
7025 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7026 if (isArray)
7027 rhiD->f->glTexImage2D(target, level, GLint(glintformat), mipSize.width(),
7028 qMax(0, m_arraySize), 0, glformat, gltype, nullptr);
7029 else
7030 rhiD->glTexImage1D(target, level, GLint(glintformat), mipSize.width(), 0,
7031 glformat, gltype, nullptr);
7032 }
7033 } else if (isArray) {
7034 const int layerCount = qMax(0, m_arraySize);
7035 if (hasMipMaps) {
7036 for (int level = 0; level != mipLevelCount; ++level) {
7037 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7038 rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), layerCount,
7039 0, glformat, gltype, nullptr);
7040 }
7041 } else {
7042 rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), layerCount,
7043 0, glformat, gltype, nullptr);
7044 }
7045 } else if (is3D) {
7046 if (hasMipMaps) {
7047 const int depth = qMax(1, m_depth);
7048 for (int level = 0; level != mipLevelCount; ++level) {
7049 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7050 const int mipDepth = rhiD->q->sizeForMipLevel(level, QSize(depth, depth)).width();
7051 rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), mipDepth,
7052 0, glformat, gltype, nullptr);
7053 }
7054 } else {
7055 rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), qMax(1, m_depth),
7056 0, glformat, gltype, nullptr);
7057 }
7058 } else if (hasMipMaps || isCube) {
7059 const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
7060 for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
7061 for (int level = 0; level != mipLevelCount; ++level) {
7062 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7063 rhiD->f->glTexImage2D(faceTargetBase + uint(layer), level, GLint(glintformat),
7064 mipSize.width(), mipSize.height(), 0,
7065 glformat, gltype, nullptr);
7066 }
7067 }
7068 } else {
7069 // 2D texture. For multisample textures the GLES 3.1
7070 // glStorage2DMultisample must be used for portability.
7071 if (samples > 1 && rhiD->caps.multisampledTexture) {
7072 // internal format must be sized
7073 rhiD->f->glTexStorage2DMultisample(target, samples, glsizedintformat,
7074 size.width(), size.height(), GL_TRUE);
7075 } else {
7076 rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
7077 0, glformat, gltype, nullptr);
7078 }
7079 }
7080 } else {
7081 // Must be specified with immutable storage functions otherwise
7082 // bindImageTexture may fail. Also, the internal format must be a
7083 // sized format here.
7084 if (is1D && !isArray)
7085 rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
7086 else if (!is1D && (is3D || isArray))
7087 rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(),
7088 is3D ? qMax(1, m_depth) : qMax(0, m_arraySize));
7089 else if (samples > 1)
7090 rhiD->f->glTexStorage2DMultisample(target, samples, glsizedintformat,
7091 size.width(), size.height(), GL_TRUE);
7092 else
7093 rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
7094 is1D ? qMax(0, m_arraySize) : size.height());
7095 }
7096 // Make sure the min filter is set to something non-mipmap-based already
7097 // here, given the ridiculous default of GL. It is changed based on
7098 // the sampler later, but there could be cases when one pulls the native
7099 // object out via nativeTexture() right away.
7100 rhiD->f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
7101 specified = true;
7102 } else {
7103 // Cannot use glCompressedTexImage2D without valid data, so defer.
7104 // Compressed textures will not be used as render targets so this is
7105 // not an issue.
7106 specified = false;
7107 }
7108
7109 if (rhiD->glObjectLabel)
7110 rhiD->glObjectLabel(GL_TEXTURE, texture, -1, m_objectName.constData());
7111
7112 owns = true;
7113
7114 generation += 1;
7115 rhiD->registerResource(this);
7116 return true;
7117}
7118
7119bool QGles2Texture::createFrom(QRhiTexture::NativeTexture src)
7120{
7121 const uint textureId = uint(src.object);
7122 if (textureId == 0)
7123 return false;
7124
7125 if (!prepareCreate())
7126 return false;
7127
7128 texture = textureId;
7129 specified = true;
7130 zeroInitialized = true;
7131
7132 owns = false;
7133
7134 generation += 1;
7135 QRHI_RES_RHI(QRhiGles2);
7136 rhiD->registerResource(this);
7137 return true;
7138}
7139
7140QRhiTexture::NativeTexture QGles2Texture::nativeTexture()
7141{
7142 return {texture, 0};
7143}
7144
7145QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
7146 AddressMode u, AddressMode v, AddressMode w)
7147 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
7148{
7149}
7150
7151QGles2Sampler::~QGles2Sampler()
7152{
7153 destroy();
7154}
7155
7156void QGles2Sampler::destroy()
7157{
7158 QRHI_RES_RHI(QRhiGles2);
7159 if (rhiD)
7160 rhiD->unregisterResource(this);
7161}
7162
7163bool QGles2Sampler::create()
7164{
7165 d.glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode);
7166 d.glmagfilter = toGlMagFilter(m_magFilter);
7167 d.glwraps = toGlWrapMode(m_addressU);
7168 d.glwrapt = toGlWrapMode(m_addressV);
7169 d.glwrapr = toGlWrapMode(m_addressW);
7170 d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
7171
7172 generation += 1;
7173 QRHI_RES_RHI(QRhiGles2);
7174 rhiD->registerResource(this, false);
7175 return true;
7176}
7177
7178// dummy, no Vulkan-style RenderPass+Framebuffer concept here
7179QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi)
7180 : QRhiRenderPassDescriptor(rhi)
7181{
7182}
7183
7184QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
7185{
7186 destroy();
7187}
7188
7189void QGles2RenderPassDescriptor::destroy()
7190{
7191 QRHI_RES_RHI(QRhiGles2);
7192 if (rhiD)
7193 rhiD->unregisterResource(this);
7194}
7195
7196bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
7197{
7198 Q_UNUSED(other);
7199 return true;
7200}
7201
7202QRhiRenderPassDescriptor *QGles2RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
7203{
7204 QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
7205 QRHI_RES_RHI(QRhiGles2);
7206 rhiD->registerResource(rpD, false);
7207 return rpD;
7208}
7209
7210QVector<quint32> QGles2RenderPassDescriptor::serializedFormat() const
7211{
7212 return {};
7213}
7214
7215QGles2SwapChainRenderTarget::QGles2SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
7216 : QRhiSwapChainRenderTarget(rhi, swapchain),
7217 d(rhi)
7218{
7219}
7220
7221QGles2SwapChainRenderTarget::~QGles2SwapChainRenderTarget()
7222{
7223 destroy();
7224}
7225
7226void QGles2SwapChainRenderTarget::destroy()
7227{
7228 // nothing to do here
7229}
7230
7231QSize QGles2SwapChainRenderTarget::pixelSize() const
7232{
7233 return d.pixelSize;
7234}
7235
7236float QGles2SwapChainRenderTarget::devicePixelRatio() const
7237{
7238 return d.dpr;
7239}
7240
7241int QGles2SwapChainRenderTarget::sampleCount() const
7242{
7243 return d.sampleCount;
7244}
7245
7246QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi,
7247 const QRhiTextureRenderTargetDescription &desc,
7248 Flags flags)
7249 : QRhiTextureRenderTarget(rhi, desc, flags),
7250 d(rhi)
7251{
7252}
7253
7254QGles2TextureRenderTarget::~QGles2TextureRenderTarget()
7255{
7256 destroy();
7257}
7258
7259void QGles2TextureRenderTarget::destroy()
7260{
7261 if (!framebuffer)
7262 return;
7263
7264 QRhiGles2::DeferredReleaseEntry e;
7265 e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget;
7266
7267 e.textureRenderTarget.framebuffer = framebuffer;
7268 e.textureRenderTarget.nonMsaaThrowawayDepthTexture = nonMsaaThrowawayDepthTexture;
7269
7270 framebuffer = 0;
7271 nonMsaaThrowawayDepthTexture = 0;
7272
7273 QRHI_RES_RHI(QRhiGles2);
7274 if (rhiD) {
7275 rhiD->releaseQueue.append(e);
7276 rhiD->unregisterResource(this);
7277 }
7278}
7279
7280QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
7281{
7282 QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
7283 QRHI_RES_RHI(QRhiGles2);
7284 rhiD->registerResource(rpD, false);
7285 return rpD;
7286}
7287
7288bool QGles2TextureRenderTarget::create()
7289{
7290 QRHI_RES_RHI(QRhiGles2);
7291
7292 if (framebuffer)
7293 destroy();
7294
7295 const bool hasColorAttachments = m_desc.colorAttachmentCount() > 0;
7296 Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
7297 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
7298 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
7299
7300 if (hasColorAttachments) {
7301 const int count = int(m_desc.colorAttachmentCount());
7302 if (count > rhiD->caps.maxDrawBuffers) {
7303 qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
7304 count, rhiD->caps.maxDrawBuffers);
7305 }
7306 }
7307 if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
7308 qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
7309
7310 if (!rhiD->ensureContext())
7311 return false;
7312
7313 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments();
7314 it != itEnd; ++it) {
7315 const QRhiColorAttachment &colorAtt(*it);
7316 const QRhiTexture *resolveTexture = colorAtt.resolveTexture();
7317 if (!resolveTexture)
7318 continue;
7319
7320 const QRhiTexture *texture = colorAtt.texture();
7321 const QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
7322 Q_ASSERT(texture || renderBuffer);
7323 const QRhiTexture::Format sourceFormat = texture ? texture->format()
7324 : renderBuffer->backingFormat();
7325 const bool sourceIsSrgb = texture && texture->flags().testFlag(QRhiTexture::sRGB);
7326 const bool resolveIsSrgb = resolveTexture->flags().testFlag(QRhiTexture::sRGB);
7327 if (sourceFormat != resolveTexture->format() || sourceIsSrgb != resolveIsSrgb) {
7328 qWarning("Multisample resolve between different formats "
7329 "(%d, sRGB %d) and (%d, sRGB %d) is not supported.",
7330 int(sourceFormat), int(sourceIsSrgb),
7331 int(resolveTexture->format()), int(resolveIsSrgb));
7332 return false;
7333 }
7334 }
7335
7336 rhiD->f->glGenFramebuffers(1, &framebuffer);
7337 rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
7338
7339 d.colorAttCount = 0;
7340 d.srgbUpdateAndBlend = false;
7341 int attIndex = 0;
7342 int multiViewCount = 0;
7343 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
7344 d.colorAttCount += 1;
7345 const QRhiColorAttachment &colorAtt(*it);
7346 QRhiTexture *texture = colorAtt.texture();
7347 QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
7348 Q_ASSERT(texture || renderBuffer);
7349 if (texture) {
7350 QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
7351 Q_ASSERT(texD->texture && texD->specified);
7352 d.srgbUpdateAndBlend |= texD->flags().testFlag(QRhiTexture::sRGB);
7353 if (colorAtt.resolveTexture())
7354 d.srgbUpdateAndBlend |= colorAtt.resolveTexture()->flags().testFlag(QRhiTexture::sRGB);
7355 if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
7356 if (colorAtt.multiViewCount() < 2) {
7357 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
7358 colorAtt.level(), colorAtt.layer());
7359 } else {
7360 multiViewCount = colorAtt.multiViewCount();
7361 if (texD->samples > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture && colorAtt.resolveTexture()) {
7362 // Special path for GLES and GL_OVR_multiview_multisampled_render_to_texture:
7363 // ignore the color attachment's (multisample) texture
7364 // array and give the resolve texture array to GL. (no
7365 // explicit resolving is needed by us later on)
7366 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
7367 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7368 GL_COLOR_ATTACHMENT0 + uint(attIndex),
7369 resolveTexD->texture,
7370 colorAtt.resolveLevel(),
7371 texD->samples,
7372 colorAtt.resolveLayer(),
7373 multiViewCount);
7374 } else {
7375 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER,
7376 GL_COLOR_ATTACHMENT0 + uint(attIndex),
7377 texD->texture,
7378 colorAtt.level(),
7379 colorAtt.layer(),
7380 multiViewCount);
7381 }
7382 }
7383 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
7384 rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
7385 texD->target + uint(colorAtt.layer()), texD->texture,
7386 colorAtt.level());
7387 } else {
7388 if (texD->samples > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
7389 // Special path for GLES and GL_EXT_multisampled_render_to_texture:
7390 // ignore the color attachment's (multisample) texture and
7391 // give the resolve texture to GL. (no explicit resolving is
7392 // needed by us later on)
7393 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
7394 const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
7395 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
7396 resolveTexD->texture, colorAtt.level(), texD->samples);
7397 } else {
7398 const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
7399 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
7400 texD->texture, colorAtt.level());
7401 }
7402 }
7403 if (attIndex == 0) {
7404 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
7405 d.sampleCount = texD->samples;
7406 }
7407 } else if (renderBuffer) {
7408 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
7409 if (colorAtt.resolveTexture())
7410 d.srgbUpdateAndBlend |= colorAtt.resolveTexture()->flags().testFlag(QRhiTexture::sRGB);
7411 if (rbD->samples > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
7412 // Special path for GLES and GL_EXT_multisampled_render_to_texture: ignore
7413 // the (multisample) renderbuffer and give the resolve texture to GL. (so
7414 // no explicit resolve; depending on GL implementation internals, this may
7415 // play nicer with tiled architectures)
7416 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
7417 const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
7418 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
7419 resolveTexD->texture, colorAtt.level(), rbD->samples);
7420 } else {
7421 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer);
7422 }
7423 if (attIndex == 0) {
7424 d.pixelSize = rbD->pixelSize();
7425 d.sampleCount = rbD->samples;
7426 }
7427 }
7428 }
7429
7430 if (hasDepthStencil && m_desc.depthResolveTexture()) {
7431 const QRhiTexture::Format dstFormat = m_desc.depthResolveTexture()->format();
7432 QRhiTexture::Format srcFormat = QRhiTexture::UnknownFormat;
7433 if (m_desc.depthStencilBuffer())
7434 srcFormat = QRhiTexture::D24S8;
7435 else if (!rhiD->caps.glesMultisampleRenderToTexture)
7436 srcFormat = m_desc.depthTexture()->format();
7437 // else resolving is automatic
7438 if (srcFormat != QRhiTexture::UnknownFormat && srcFormat != dstFormat) {
7439 qWarning("Multisample depth-stencil source format %d does not match the "
7440 "depthResolveTexture format %d. OpenGL cannot blit between different depth formats; "
7441 "the depth contents will not be resolved.",
7442 int(srcFormat), int(dstFormat));
7443 }
7444 }
7445
7446 if (hasDepthStencil) {
7447 if (m_desc.depthStencilBuffer()) {
7448 QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer());
7449 if (rhiD->caps.needsDepthStencilCombinedAttach) {
7450 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
7451 depthRbD->renderbuffer);
7452 } else {
7453 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
7454 depthRbD->renderbuffer);
7455 if (depthRbD->stencilRenderbuffer) {
7456 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
7457 depthRbD->stencilRenderbuffer);
7458 } else {
7459 // packed depth-stencil
7460 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
7461 depthRbD->renderbuffer);
7462 }
7463 }
7464 if (d.colorAttCount == 0) {
7465 d.pixelSize = depthRbD->pixelSize();
7466 d.sampleCount = depthRbD->samples;
7467 }
7468 } else {
7469 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
7470 if (multiViewCount < 2) {
7471 if (depthTexD->samples > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) {
7472 // Special path for GLES and
7473 // GL_EXT_multisampled_render_to_texture, for depth-stencil.
7474 // Relevant only when depthResolveTexture is set.
7475 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
7476 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target,
7477 depthResolveTexD->texture, 0, depthTexD->samples);
7478 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
7479 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target,
7480 depthResolveTexD->texture, 0, depthTexD->samples);
7481 }
7482 } else if (rhiD->caps.ctxMajor >= 3 && depthTexD->flags().testFlag(QRhiTexture::TextureArray) && m_desc.depthLayer() >= 0) {
7483 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
7484 /*level*/0, m_desc.depthLayer());
7485 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
7486 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
7487 /*level*/0, m_desc.depthLayer());
7488 }
7489 } else {
7490 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
7491 depthTexD->texture, 0);
7492 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
7493 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
7494 depthTexD->texture, 0);
7495 }
7496 }
7497 } else {
7498 if (depthTexD->samples > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) {
7499 // And so it turns out
7500 // https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt
7501 // does not work with multisample 2D texture arrays. (at least
7502 // that's what Issue 30 in the extension spec seems to imply)
7503 //
7504 // There is https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multiview_texture_multisample.txt
7505 // that seems to resolve that, but that does not seem to
7506 // work (or not available) on GLES devices such as the Quest 3.
7507 //
7508 // So instead, on GLES we can use the
7509 // multisample-multiview-auto-resolving version (which in
7510 // turn is not supported on desktop GL e.g. by NVIDIA), too
7511 // bad we have a multisample depth texture array here as
7512 // every other API out there requires that. So, in absence
7513 // of a depthResolveTexture, create a temporary one ignoring
7514 // what the user has already created.
7515 //
7516 if (!m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture()) {
7517 qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set."
7518 " This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create"
7519 " a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture.");
7520 }
7521 if (m_desc.depthResolveTexture()) {
7522 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
7523 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7524 GL_DEPTH_ATTACHMENT,
7525 depthResolveTexD->texture,
7526 0,
7527 depthTexD->samples,
7528 0,
7529 multiViewCount);
7530 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
7531 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7532 GL_STENCIL_ATTACHMENT,
7533 depthResolveTexD->texture,
7534 0,
7535 depthTexD->samples,
7536 0,
7537 multiViewCount);
7538 }
7539 } else {
7540 if (!nonMsaaThrowawayDepthTexture) {
7541 rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
7542 rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
7543 rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
7544 depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
7545 }
7546 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7547 GL_DEPTH_ATTACHMENT,
7548 nonMsaaThrowawayDepthTexture,
7549 0,
7550 depthTexD->samples,
7551 0,
7552 multiViewCount);
7553 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7554 GL_STENCIL_ATTACHMENT,
7555 nonMsaaThrowawayDepthTexture,
7556 0,
7557 depthTexD->samples,
7558 0,
7559 multiViewCount);
7560 }
7561 } else {
7562 // The depth texture here must be an array with at least
7563 // multiViewCount elements, and the format should be D24 or D32F
7564 // for depth only, or D24S8 for depth and stencil.
7565 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
7566 0, 0, multiViewCount);
7567 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
7568 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
7569 0, 0, multiViewCount);
7570 }
7571 }
7572 }
7573 if (d.colorAttCount == 0) {
7574 d.pixelSize = depthTexD->pixelSize();
7575 d.sampleCount = depthTexD->samples;
7576 }
7577 }
7578 d.dsAttCount = 1;
7579 } else {
7580 d.dsAttCount = 0;
7581 }
7582
7583 d.dpr = 1;
7584 d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
7585
7586 GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
7587 if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
7588 qWarning("Framebuffer incomplete: 0x%x", status);
7589 return false;
7590 }
7591
7592 if (rhiD->glObjectLabel)
7593 rhiD->glObjectLabel(GL_FRAMEBUFFER, framebuffer, -1, m_objectName.constData());
7594
7595 QRhiRenderTargetAttachmentTracker::updateResIdList<QGles2Texture, QGles2RenderBuffer>(m_desc, &d.currentResIdList);
7596
7597 rhiD->registerResource(this);
7598 return true;
7599}
7600
7601QSize QGles2TextureRenderTarget::pixelSize() const
7602{
7603 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(m_desc, d.currentResIdList))
7604 const_cast<QGles2TextureRenderTarget *>(this)->create();
7605
7606 return d.pixelSize;
7607}
7608
7609float QGles2TextureRenderTarget::devicePixelRatio() const
7610{
7611 return d.dpr;
7612}
7613
7614int QGles2TextureRenderTarget::sampleCount() const
7615{
7616 return d.sampleCount;
7617}
7618
7619QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi)
7620 : QRhiShaderResourceBindings(rhi)
7621{
7622}
7623
7624QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
7625{
7626 destroy();
7627}
7628
7629void QGles2ShaderResourceBindings::destroy()
7630{
7631 QRHI_RES_RHI(QRhiGles2);
7632 if (rhiD)
7633 rhiD->unregisterResource(this);
7634}
7635
7636bool QGles2ShaderResourceBindings::create()
7637{
7638 QRHI_RES_RHI(QRhiGles2);
7639 if (!rhiD->sanityCheckShaderResourceBindings(this))
7640 return false;
7641
7642 hasDynamicOffset = false;
7643 for (int i = 0, ie = m_bindings.size(); i != ie; ++i) {
7644 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(m_bindings.at(i));
7645 if (b->type == QRhiShaderResourceBinding::UniformBuffer) {
7646 if (b->u.ubuf.hasDynamicOffset) {
7647 hasDynamicOffset = true;
7648 break;
7649 }
7650 }
7651 }
7652
7653 rhiD->updateLayoutDesc(this);
7654
7655 generation += 1;
7656 rhiD->registerResource(this, false);
7657 return true;
7658}
7659
7660void QGles2ShaderResourceBindings::updateResources(UpdateFlags flags)
7661{
7662 Q_UNUSED(flags);
7663 generation += 1;
7664}
7665
7666QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi)
7667 : QRhiGraphicsPipeline(rhi)
7668{
7669}
7670
7671QGles2GraphicsPipeline::~QGles2GraphicsPipeline()
7672{
7673 destroy();
7674}
7675
7676void QGles2GraphicsPipeline::destroy()
7677{
7678 if (!program)
7679 return;
7680
7681 QRhiGles2::DeferredReleaseEntry e;
7682 e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
7683
7684 e.pipeline.program = program;
7685
7686 program = 0;
7687 uniforms.clear();
7688 pushConstantUniforms.clear();
7689 pushConstantSize = 0;
7690 samplers.clear();
7691 uniformState.clear();
7692
7693 QRHI_RES_RHI(QRhiGles2);
7694 if (rhiD) {
7695 rhiD->releaseQueue.append(e);
7696 rhiD->unregisterResource(this);
7697 }
7698}
7699
7700bool QGles2GraphicsPipeline::create()
7701{
7702 QRHI_RES_RHI(QRhiGles2);
7703
7704 if (program)
7705 destroy();
7706
7707 if (!rhiD->ensureContext())
7708 return false;
7709
7710 rhiD->pipelineCreationStart();
7711 if (!rhiD->sanityCheckGraphicsPipeline(this))
7712 return false;
7713
7714 drawMode = toGlTopology(m_topology);
7715
7716 program = rhiD->f->glCreateProgram();
7717
7718 enum {
7719 VtxIdx = 0,
7720 TCIdx,
7721 TEIdx,
7722 GeomIdx,
7723 FragIdx,
7724 LastIdx
7725 };
7726 const auto descIdxForStage = [](const QRhiShaderStage &shaderStage) {
7727 switch (shaderStage.type()) {
7728 case QRhiShaderStage::Vertex:
7729 return VtxIdx;
7730 case QRhiShaderStage::TessellationControl:
7731 return TCIdx;
7732 case QRhiShaderStage::TessellationEvaluation:
7733 return TEIdx;
7734 case QRhiShaderStage::Geometry:
7735 return GeomIdx;
7736 case QRhiShaderStage::Fragment:
7737 return FragIdx;
7738 default:
7739 break;
7740 }
7741 Q_UNREACHABLE_RETURN(VtxIdx);
7742 };
7743 const std::optional<QShaderVersion> commonVersion =
7744 rhiD->commonGlslEsVersion(m_shaderStages.constData(), m_shaderStages.size());
7745
7746 QShaderDescription desc[LastIdx];
7747 QShader::SeparateToCombinedImageSamplerMappingList samplerMappingList[LastIdx];
7748 bool vertexFragmentOnly = true;
7749 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7750 if (isGraphicsStage(shaderStage)) {
7751 const int idx = descIdxForStage(shaderStage);
7752 if (idx != VtxIdx && idx != FragIdx)
7753 vertexFragmentOnly = false;
7754 QShader shader = shaderStage.shader();
7755 QShaderVersion shaderVersion;
7756 desc[idx] = shader.description();
7757 if (!rhiD->shaderSource(shaderStage, &shaderVersion, commonVersion).isEmpty()) {
7758 samplerMappingList[idx] = shader.separateToCombinedImageSamplerMappingList(
7759 { QShader::GlslShader, shaderVersion, shaderStage.shaderVariant() });
7760 }
7761 }
7762 }
7763
7764 QByteArray cacheKey;
7765 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(m_shaderStages.constData(),
7766 m_shaderStages.size(),
7767 program,
7768 desc[VtxIdx].inputVariables(),
7769 &cacheKey,
7770 commonVersion);
7771 if (cacheResult == QRhiGles2::ProgramCacheError)
7772 return false;
7773
7774 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
7775 if (rhiD->caps.gles && !commonVersion) {
7776 qWarning("The shader stages of this pipeline have no GLSL ES version in common; "
7777 "linking the program will fail");
7778 }
7779 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7780 if (isGraphicsStage(shaderStage)) {
7781 if (!rhiD->compileShader(program, shaderStage, nullptr, commonVersion))
7782 return false;
7783 }
7784 }
7785
7786 // important when GLSL <= 150 is used that does not have location qualifiers
7787 const auto vtxInputVars = desc[VtxIdx].inputVariables();
7788 for (const QShaderDescription::InOutVariable &inVar : vtxInputVars)
7789 rhiD->f->glBindAttribLocation(program, GLuint(inVar.location), inVar.name);
7790
7791 if (vertexFragmentOnly)
7792 rhiD->sanityCheckVertexFragmentInterface(desc[VtxIdx], desc[FragIdx]);
7793
7794 if (!rhiD->linkProgram(program))
7795 return false;
7796
7797 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
7798 // force replacing existing cache entry (if there is one, then
7799 // something is wrong with it, as there was no hit)
7800 rhiD->trySaveToPipelineCache(program, cacheKey, true);
7801 } else {
7802 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
7803 // was not enabled, so instead store to the Qt 5 disk cache
7804 rhiD->trySaveToDiskCache(program, cacheKey);
7805 }
7806 } else {
7807 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
7808 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
7809 // just so that it ends up in the pipeline cache also when the hit was
7810 // from the disk cache
7811 rhiD->trySaveToPipelineCache(program, cacheKey);
7812 }
7813 }
7814
7815 // Use the same work area for the vertex & fragment stages, thus ensuring
7816 // that we will not do superfluous glUniform calls for uniforms that are
7817 // present in both shaders.
7818 QRhiGles2::ActiveUniformLocationTracker activeUniformLocations;
7819
7820 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7821 if (isGraphicsStage(shaderStage)) {
7822 const int idx = descIdxForStage(shaderStage);
7823 const auto uniformBlocks = desc[idx].uniformBlocks();
7824 for (const QShaderDescription::UniformBlock &ub : uniformBlocks)
7825 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
7826 const auto pushConstantBlocks = desc[idx].pushConstantBlocks();
7827 for (const QShaderDescription::PushConstantBlock &pcb : pushConstantBlocks) {
7828 rhiD->gatherPushConstantUniforms(program, pcb, &activeUniformLocations, &pushConstantUniforms);
7829 pushConstantSize = qMax(pushConstantSize, quint32(pcb.size));
7830 }
7831 const auto combinedImageSamplers = desc[idx].combinedImageSamplers();
7832 for (const QShaderDescription::InOutVariable &v : combinedImageSamplers)
7833 rhiD->gatherSamplers(program, v, &samplers);
7834 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : std::as_const(samplerMappingList[idx]))
7835 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
7836 }
7837 }
7838
7839 std::sort(uniforms.begin(), uniforms.end(),
7840 [](const QGles2UniformDescription &a, const QGles2UniformDescription &b)
7841 {
7842 return a.offset < b.offset;
7843 });
7844
7845 QRhiGles2::setupUniformStateTracking(&uniforms, &pushConstantUniforms, &uniformState);
7846
7847 currentSrb = nullptr;
7848 currentSrbGeneration = 0;
7849
7850 if (rhiD->glObjectLabel)
7851 rhiD->glObjectLabel(GL_PROGRAM, program, -1, m_objectName.constData());
7852
7853 rhiD->pipelineCreationEnd();
7854 generation += 1;
7855 rhiD->registerResource(this);
7856 return true;
7857}
7858
7859QGles2ComputePipeline::QGles2ComputePipeline(QRhiImplementation *rhi)
7860 : QRhiComputePipeline(rhi)
7861{
7862}
7863
7864QGles2ComputePipeline::~QGles2ComputePipeline()
7865{
7866 destroy();
7867}
7868
7869void QGles2ComputePipeline::destroy()
7870{
7871 if (!program)
7872 return;
7873
7874 QRhiGles2::DeferredReleaseEntry e;
7875 e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
7876
7877 e.pipeline.program = program;
7878
7879 program = 0;
7880 uniforms.clear();
7881 pushConstantUniforms.clear();
7882 pushConstantSize = 0;
7883 samplers.clear();
7884 uniformState.clear();
7885
7886 QRHI_RES_RHI(QRhiGles2);
7887 if (rhiD) {
7888 rhiD->releaseQueue.append(e);
7889 rhiD->unregisterResource(this);
7890 }
7891}
7892
7893bool QGles2ComputePipeline::create()
7894{
7895 QRHI_RES_RHI(QRhiGles2);
7896
7897 if (program)
7898 destroy();
7899
7900 if (!rhiD->ensureContext())
7901 return false;
7902
7903 rhiD->pipelineCreationStart();
7904
7905 const QShaderDescription csDesc = m_shaderStage.shader().description();
7906 QShader::SeparateToCombinedImageSamplerMappingList csSamplerMappingList;
7907 QShaderVersion shaderVersion;
7908 if (!rhiD->shaderSource(m_shaderStage, &shaderVersion).isEmpty()) {
7909 csSamplerMappingList = m_shaderStage.shader().separateToCombinedImageSamplerMappingList(
7910 { QShader::GlslShader, shaderVersion, m_shaderStage.shaderVariant() });
7911 }
7912
7913 program = rhiD->f->glCreateProgram();
7914
7915 QByteArray cacheKey;
7916 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(&m_shaderStage, 1, program, {}, &cacheKey);
7917 if (cacheResult == QRhiGles2::ProgramCacheError)
7918 return false;
7919
7920 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
7921 if (!rhiD->compileShader(program, m_shaderStage, nullptr))
7922 return false;
7923
7924 if (!rhiD->linkProgram(program))
7925 return false;
7926
7927 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
7928 // force replacing existing cache entry (if there is one, then
7929 // something is wrong with it, as there was no hit)
7930 rhiD->trySaveToPipelineCache(program, cacheKey, true);
7931 } else {
7932 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
7933 // was not enabled, so instead store to the Qt 5 disk cache
7934 rhiD->trySaveToDiskCache(program, cacheKey);
7935 }
7936 } else {
7937 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
7938 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
7939 // just so that it ends up in the pipeline cache also when the hit was
7940 // from the disk cache
7941 rhiD->trySaveToPipelineCache(program, cacheKey);
7942 }
7943 }
7944
7945 QRhiGles2::ActiveUniformLocationTracker activeUniformLocations;
7946 const auto csUniformBlocks = csDesc.uniformBlocks();
7947 for (const QShaderDescription::UniformBlock &ub : csUniformBlocks)
7948 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
7949 const auto csPushConstantBlocks = csDesc.pushConstantBlocks();
7950 for (const QShaderDescription::PushConstantBlock &pcb : csPushConstantBlocks) {
7951 rhiD->gatherPushConstantUniforms(program, pcb, &activeUniformLocations, &pushConstantUniforms);
7952 pushConstantSize = qMax(pushConstantSize, quint32(pcb.size));
7953 }
7954 const auto csCombinedImageSamplers = csDesc.combinedImageSamplers();
7955 for (const QShaderDescription::InOutVariable &v : csCombinedImageSamplers)
7956 rhiD->gatherSamplers(program, v, &samplers);
7957 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : std::as_const(csSamplerMappingList))
7958 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
7959
7960 // storage images and buffers need no special steps here
7961
7962 QRhiGles2::setupUniformStateTracking(&uniforms, &pushConstantUniforms, &uniformState);
7963
7964 currentSrb = nullptr;
7965 currentSrbGeneration = 0;
7966
7967 rhiD->pipelineCreationEnd();
7968 generation += 1;
7969 rhiD->registerResource(this);
7970 return true;
7971}
7972
7973QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
7974 : QRhiCommandBuffer(rhi)
7975{
7976 resetState();
7977}
7978
7979QGles2CommandBuffer::~QGles2CommandBuffer()
7980{
7981 destroy();
7982}
7983
7984void QGles2CommandBuffer::destroy()
7985{
7986 // nothing to do here
7987}
7988
7989QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
7990 : QRhiSwapChain(rhi),
7991 rt(rhi, this),
7992 rtLeft(rhi, this),
7993 rtRight(rhi, this),
7994 cb(rhi)
7995{
7996}
7997
7998QGles2SwapChain::~QGles2SwapChain()
7999{
8000 destroy();
8001}
8002
8003void QGles2SwapChain::destroy()
8004{
8005 QRHI_RES_RHI(QRhiGles2);
8006 if (rhiD) {
8007 // Don't leave the window surface bound, the platform may destroy it next.
8008 if (surface && currentSurfaceForCurrentContext(rhiD->ctx) == surface)
8009 rhiD->ctx->doneCurrent();
8010 rhiD->unregisterResource(this);
8011 }
8012}
8013
8014QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
8015{
8016 return &cb;
8017}
8018
8019QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
8020{
8021 return &rt;
8022}
8023
8024QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
8025{
8026 if (targetBuffer == LeftBuffer)
8027 return rtLeft.d.isValid() ? &rtLeft : &rt;
8028 else if (targetBuffer == RightBuffer)
8029 return rtRight.d.isValid() ? &rtRight : &rt;
8030 else
8031 Q_UNREACHABLE_RETURN(nullptr);
8032}
8033
8034QSize QGles2SwapChain::surfacePixelSize()
8035{
8036 Q_ASSERT(m_window);
8037 if (QPlatformWindow *platformWindow = m_window->handle())
8038 // Prefer using QPlatformWindow geometry and DPR in order to avoid
8039 // errors due to rounded QWindow geometry.
8040 return platformWindow->geometry().size() * platformWindow->devicePixelRatio();
8041 else
8042 return m_window->size() * m_window->devicePixelRatio();
8043}
8044
8045bool QGles2SwapChain::isFormatSupported(Format f)
8046{
8047 return f == SDR;
8048}
8049
8050QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
8051{
8052 QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
8053 QRHI_RES_RHI(QRhiGles2);
8054 rhiD->registerResource(rpD, false);
8055 return rpD;
8056}
8057
8058void QGles2SwapChain::initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt)
8059{
8060 rt->setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
8061 rt->d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
8062 rt->d.pixelSize = pixelSize;
8063 rt->d.dpr = float(m_window->devicePixelRatio());
8064 rt->d.sampleCount = qBound(1, m_sampleCount, 64);
8065 rt->d.colorAttCount = 1;
8066 rt->d.dsAttCount = m_depthStencil ? 1 : 0;
8067 rt->d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB);
8068}
8069
8070bool QGles2SwapChain::createOrResize()
8071{
8072 // can be called multiple times due to window resizes
8073 if (surface && surface != m_window)
8074 destroy();
8075
8076 surface = m_window;
8077 m_currentPixelSize = surfacePixelSize();
8078 pixelSize = m_currentPixelSize;
8079
8080 if (m_depthStencil && m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)
8081 && m_depthStencil->pixelSize() != pixelSize)
8082 {
8083 m_depthStencil->setPixelSize(pixelSize);
8084 m_depthStencil->create();
8085 }
8086
8087 initSwapChainRenderTarget(&rt);
8088
8089 if (m_window->format().stereo()) {
8090 initSwapChainRenderTarget(&rtLeft);
8091 rtLeft.d.stereoTarget = QRhiSwapChain::LeftBuffer;
8092 initSwapChainRenderTarget(&rtRight);
8093 rtRight.d.stereoTarget = QRhiSwapChain::RightBuffer;
8094 }
8095
8096 QRHI_RES_RHI(QRhiGles2);
8097 if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps) && rhiD->caps.timestamps)
8098 timestamps.prepare(rhiD);
8099
8100 // The only reason to register this fairly fake gl swapchain
8101 // object with no native resources underneath is to be able to
8102 // implement a safe destroy().
8103 rhiD->registerResource(this, false);
8104
8105 return true;
8106}
8107
8108void QGles2SwapChainTimestamps::prepare(QRhiGles2 *rhiD)
8109{
8110 if (!query[0])
8111 rhiD->f->glGenQueries(TIMESTAMP_PAIRS * 2, query);
8112}
8113
8114void QGles2SwapChainTimestamps::destroy(QRhiGles2 *rhiD)
8115{
8116 rhiD->f->glDeleteQueries(TIMESTAMP_PAIRS * 2, query);
8117 memset(active, 0, sizeof(active));
8118 memset(query, 0, sizeof(query));
8119}
8120
8121bool QGles2SwapChainTimestamps::tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
8122{
8123 if (!active[pairIndex])
8124 return false;
8125
8126 GLuint tsStart = query[pairIndex * 2];
8127 GLuint tsEnd = query[pairIndex * 2 + 1];
8128
8129 GLuint ready = GL_FALSE;
8130 rhiD->f->glGetQueryObjectuiv(tsEnd, GL_QUERY_RESULT_AVAILABLE, &ready);
8131
8132 if (!ready)
8133 return false;
8134
8135 bool result = false;
8136 quint64 timestamps[2];
8137 rhiD->glGetQueryObjectui64v(tsStart, GL_QUERY_RESULT, &timestamps[0]);
8138 rhiD->glGetQueryObjectui64v(tsEnd, GL_QUERY_RESULT, &timestamps[1]);
8139
8140 if (timestamps[1] >= timestamps[0]) {
8141 const quint64 nanoseconds = timestamps[1] - timestamps[0];
8142 *elapsedSec = nanoseconds / 1000000000.0;
8143 result = true;
8144 }
8145
8146 active[pairIndex] = false;
8147 return result;
8148}
8149
8150QT_END_NAMESPACE
const char * constData() const
Definition qrhi_p.h:477
const GLvoid const GLvoid GLenum
QRhiStats statistics() override
bool contextLost
const QRhiNativeHandles * nativeHandles(QRhiCommandBuffer *cb) override
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) override
void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
void gatherBlockMemberUniforms(GLuint program, const QByteArray &prefix, int binding, const QList< QShaderDescription::BlockVariable > &members, ActiveUniformLocationTracker *activeUniformLocations, QGles2UniformDescriptionVector *dst)
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override
const GLvoid GLenum
std::optional< QShaderVersion > commonGlslEsVersion(const QRhiShaderStage *stages, int stageCount) const
void enqueueBarriersForPass(QGles2CommandBuffer *cbD)
void drawIndirectCount(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, QRhiBuffer *countBuffer, quint32 countBufferOffset, quint32 maxDrawCount, quint32 stride) override
int resourceLimit(QRhi::ResourceLimit limit) const override
void setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) override
bool isFeatureSupported(QRhi::Feature feature) const override
QRhiGraphicsPipeline * createGraphicsPipeline() override
void bindShaderResources(QGles2CommandBuffer *cbD, QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs, QRhiShaderResourceBindings *srb, const uint *dynOfsPairs, int dynOfsCount)
bool create(QRhi::Flags flags) override
void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker, QGles2Texture *texD, QRhiPassResourceTracker::TextureAccess access, QRhiPassResourceTracker::TextureStage stage)
void executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2GraphicsPipeline *psD)
void dispatchIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset) override
QRhiDriverInfo driverInfo() const override
bool needsMakeCurrentDueToSwap
QRhiShadingRateMap * createShadingRateMap() override
void setUniformsFromBlock(const QGles2UniformDescriptionVector &uniforms, int binding, const char *blockData, qint64 blockSize, quint32 blockOffset, QGles2UniformState *uniformState)
void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access)
QOpenGLExtensions * f
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, QGles2Buffer *bufD, QRhiPassResourceTracker::BufferAccess access, QRhiPassResourceTracker::BufferStage stage)
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override
QRhiComputePipeline * createComputePipeline() override
QSurface * evaluateFallbackSurface() const
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QMatrix4x4 clipSpaceCorrMatrix() const override
QRhi::FrameOpResult finish() override
void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override
const void * void(QOPENGLF_APIENTRYP glGetQueryObjectui64v)(GLuint
void(QOPENGLF_APIENTRYP glMultiDrawArraysIndirect)(GLenum
QList< int > supportedSampleCounts() const override
void(QOPENGLF_APIENTRYP glMultiDrawElementsIndirectCount)(GLenum
QRhiSwapChain * createSwapChain() override
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
void destroy() override
void executeCommandBuffer(QRhiCommandBuffer *cb)
void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) override
QList< QShaderVersion > glslVersionsToTry() const
QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice=nullptr)
GLbitfield barriersForNextDispatch(QGles2CommandBuffer *cbD)
QList< QSize > supportedShadingRates(int sampleCount) const override
void drawIndexedIndirectCount(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, QRhiBuffer *countBuffer, quint32 countBufferOffset, quint32 maxDrawCount, quint32 stride) override
void(QOPENGLF_APIENTRYP glTexSubImage1D)(GLenum
const void GLuint
QRhiTexture * createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) override
void bindCombinedSampler(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Sampler *samplerD, void *ps, uint psGeneration, int glslLocation, int *texUnit, bool *activeTexUnitAltered)
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
QRhiTextureRenderTarget * createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) override
QByteArray shaderSource(const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion, std::optional< QShaderVersion > commonVersion=std::nullopt)
void(QOPENGLF_APIENTRYP glMultiDrawElementsIndirect)(GLenum
QRhiSampler * createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w) override
void beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
QGles2SwapChain * currentSwapChain
bool isDeviceLost() const override
QRhiShaderResourceBindings * createShaderResourceBindings() override
void drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override
void drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset, quint32 drawCount, quint32 stride) override
bool isYUpInNDC() const override
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
bool makeThreadLocalNativeContextCurrent() override
int ubufAlignment() const override
void beginExternal(QRhiCommandBuffer *cb) override
const GLvoid GLint
void endExternal(QRhiCommandBuffer *cb) override
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
void(QOPENGLF_APIENTRYP glCompressedTexImage1D)(GLenum
bool importedContext
void(QOPENGLF_APIENTRYP glDrawElementsInstancedBaseVertexBaseInstance)(GLenum
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override
void setUniformValue(const QGles2UniformDescription &uniform, const void *src, QGles2UniformState *uniformState)
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override
bool ensureContext(QSurface *surface=nullptr) const
void draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override
void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
void(QOPENGLF_APIENTRYP glQueryCounter)(GLuint
void(QOPENGLF_APIENTRYP glDrawArraysInstancedBaseInstance)(GLenum
void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
bool isYUpInFramebuffer() const override
bool linkProgram(GLuint program)
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override
void debugMarkEnd(QRhiCommandBuffer *cb) override
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
void registerUniformIfActive(const QShaderDescription::BlockVariable &var, const QByteArray &namePrefix, int binding, int baseOffset, GLuint program, ActiveUniformLocationTracker *activeUniformLocations, QGles2UniformDescriptionVector *dst)
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
void releaseCachedResources() override
bool isClipDepthZeroToOne() const override
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion, std::optional< QShaderVersion > commonVersion=std::nullopt)
void executeDeferredReleases()
QByteArray pipelineCacheData() override
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
void setPushConstants(QRhiCommandBuffer *cb, quint32 offset, quint32 size, const void *data) override
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
void setPipelineCacheData(const QByteArray &data) override
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
const QRhiNativeHandles * nativeHandles() override
void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage, const UsageState &state)
Definition qrhi.cpp:13673
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, const UsageState &state)
Definition qrhi.cpp:13713
const QRhiPassResourceMap< QRhiBuffer *, Buffer, 12 > & buffers() const
Definition qrhi_p.h:959
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:745
\inmodule QtGui
Definition qshader.h:28
Combined button and popup list for selecting options.
#define GL_CONTEXT_LOST
Definition qopengl.cpp:30
#define QOPENGLF_APIENTRYP
Definition qopengl.h:275
#define GL_MAP_READ_BIT
#define GL_TEXTURE_3D
#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY
#define GL_MAP_WRITE_BIT
#define GL_MIN
#define GL_TEXTURE_2D_MULTISAMPLE
#define GL_TEXTURE_2D_ARRAY
#define GL_MAX
#define GL_TEXTURE_EXTERNAL_OES
#define GL_PATCHES
#define GL_R32UI
#define GL_NUM_PROGRAM_BINARY_FORMATS
#define GL_TEXTURE_COMPARE_FUNC
Definition qopenglext.h:338
#define GL_DEPTH32F_STENCIL8
Definition qopenglext.h:995
#define GL_MAX_VARYING_VECTORS
#define GL_TEXTURE0
Definition qopenglext.h:129
#define GL_MAX_COMPUTE_WORK_GROUP_COUNT
#define GL_TEXTURE_WRAP_R
Definition qopenglext.h:87
#define GL_DEPTH_COMPONENT32F
Definition qopenglext.h:994
#define GL_GEOMETRY_SHADER
#define GL_DEPTH24_STENCIL8
#define GL_DEPTH_COMPONENT16
Definition qopenglext.h:328
#define GL_R16
#define GL_TEXTURE_CUBE_MAP
Definition qopenglext.h:170
#define GL_COPY_WRITE_BUFFER
#define GL_RG16
#define GL_R8
#define GL_PRIMITIVE_RESTART_FIXED_INDEX
#define GL_ONE_MINUS_CONSTANT_ALPHA
Definition qopenglext.h:367
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
#define GL_RG32UI
#define GL_RGBA16F
Definition qopenglext.h:913
#define GL_COMPRESSED_TEXTURE_FORMATS
Definition qopenglext.h:186
#define GL_RGBA32UI
Definition qopenglext.h:948
#define GL_RED_INTEGER
Definition qopenglext.h:960
#define GL_RG8
#define GL_CONSTANT_COLOR
Definition qopenglext.h:364
#define GL_FRAMEBUFFER_SRGB
#define GL_TESS_CONTROL_SHADER
#define GL_R8I
#define GL_MAX_VERTEX_OUTPUT_COMPONENTS
#define GL_DEPTH_STENCIL_ATTACHMENT
#define GL_R8UI
#define GL_TEXTURE_CUBE_MAP_SEAMLESS
#define GL_BGRA
Definition qopenglext.h:97
#define GL_UNIFORM_BARRIER_BIT
#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS
#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV
Definition qopenglext.h:996
#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS
#define GL_SRGB_ALPHA
Definition qopenglext.h:864
#define GL_TIMESTAMP
#define GL_QUERY_RESULT
Definition qopenglext.h:485
#define GL_COMPARE_REF_TO_TEXTURE
Definition qopenglext.h:894
#define GL_SHADER_STORAGE_BUFFER
#define GL_ALL_BARRIER_BITS
#define GL_R32F
#define GL_STENCIL_INDEX8
#define GL_ELEMENT_ARRAY_BARRIER_BIT
#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS
Definition qopenglext.h:611
#define GL_TEXTURE_FETCH_BARRIER_BIT
#define GL_UNSIGNED_INT_24_8
#define GL_COMMAND_BARRIER_BIT
#define GL_SRGB8_ALPHA8
Definition qopenglext.h:865
#define GL_RGBA32I
Definition qopenglext.h:954
#define GL_COMPUTE_SHADER
#define GL_MAX_COMPUTE_WORK_GROUP_SIZE
#define GL_VERTEX_PROGRAM_POINT_SIZE
Definition qopenglext.h:582
#define GL_DRAW_FRAMEBUFFER
#define GL_PROGRAM_BINARY_LENGTH
#define GL_RG
#define GL_MAX_SAMPLES
#define GL_FUNC_REVERSE_SUBTRACT
Definition qopenglext.h:369
#define GL_RGBA_INTEGER
Definition qopenglext.h:964
#define GL_NUM_COMPRESSED_TEXTURE_FORMATS
Definition qopenglext.h:185
#define GL_TEXTURE_RECTANGLE
#define GL_TEXTURE_1D_ARRAY
Definition qopenglext.h:922
#define GL_R16F
#define GL_MAX_DRAW_BUFFERS
Definition qopenglext.h:588
#define GL_RG_INTEGER
#define GL_QUERY_RESULT_AVAILABLE
Definition qopenglext.h:486
#define GL_HALF_FLOAT
#define GL_TESS_EVALUATION_SHADER
#define GL_MAX_ARRAY_TEXTURE_LAYERS
Definition qopenglext.h:916
#define GL_READ_WRITE
Definition qopenglext.h:494
#define GL_PIXEL_BUFFER_BARRIER_BIT
#define GL_CONSTANT_ALPHA
Definition qopenglext.h:366
#define GL_POINT_SPRITE
Definition qopenglext.h:657
#define GL_ONE_MINUS_CONSTANT_COLOR
Definition qopenglext.h:365
#define GL_MAX_VERTEX_UNIFORM_VECTORS
#define GL_DEPTH_STENCIL
#define GL_MAP_INVALIDATE_BUFFER_BIT
#define GL_TEXTURE_UPDATE_BARRIER_BIT
#define GL_WRITE_ONLY
Definition qopenglext.h:493
#define GL_READ_FRAMEBUFFER
#define GL_PARAMETER_BUFFER
#define GL_BUFFER_UPDATE_BARRIER_BIT
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X
Definition qopenglext.h:172
#define GL_BUFFER
#define GL_MAX_FRAGMENT_UNIFORM_VECTORS
#define GL_DISPATCH_INDIRECT_BUFFER
#define GL_SHADER_STORAGE_BARRIER_BIT
#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS
#define GL_RGBA32F
Definition qopenglext.h:911
#define GL_PROGRAM
#define GL_FUNC_SUBTRACT
Definition qopenglext.h:370
#define GL_RG32I
#define GL_DEPTH_COMPONENT24
Definition qopenglext.h:329
#define GL_PATCH_VERTICES
#define GL_MAX_VERTEX_UNIFORM_COMPONENTS
Definition qopenglext.h:612
#define GL_DEPTH_CLAMP
#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
#define GL_CLAMP_TO_EDGE
Definition qopenglext.h:100
#define GL_FRAMEBUFFER_BARRIER_BIT
#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS
#define GL_MAX_VARYING_COMPONENTS
Definition qopenglext.h:921
#define GL_R32I
#define GL_MAX_VARYING_FLOATS
Definition qopenglext.h:613
#define GL_FUNC_ADD
Definition qopenglext.h:368
#define GL_DRAW_INDIRECT_BUFFER
#define GL_TEXTURE_COMPARE_MODE
Definition qopenglext.h:337
#define GL_COPY_READ_BUFFER
#define GL_READ_ONLY
Definition qopenglext.h:492
#define GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_UNPACK_ROW_LENGTH
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:33
#define QRHI_RES(t, x)
Definition qrhi_p.h:32
static GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
static QGles2Buffer::Access toGlAccess(QRhiPassResourceTracker::BufferAccess access)
static GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
static GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
static GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
static bool qrhi_gl_isTrackedUniform(const QGles2UniformDescription &uniform)
static quint32 qrhi_uniform_stride(const QGles2UniformDescription &uniform)
static GLenum toGlWrapMode(QRhiSampler::AddressMode m)
static GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
#define GL_RGBA8
static GLbitfield barriersForTexture()
static GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
#define GL_BACK_LEFT
static void toGlTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiGles2::Caps &caps, GLenum *glintformat, GLenum *glsizedintformat, GLenum *glformat, GLenum *gltype)
static void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type, GLuint tsQuery=0)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
static bool bufferAccessIsWrite(QGles2Buffer::Access access)
static QGles2Texture::Access toGlAccess(QRhiPassResourceTracker::TextureAccess access)
#define GL_RED
static void qrhi_block_to_packed(T *dst, int vecSize, int elemCount, const void *src, quint32 srcStride)
static bool isGraphicsStage(const QRhiShaderStage &shaderStage)
#define GL_FILL
static GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
static const quint32 QRHI_GL_STD140_STRIDE
static qint64 qrhi_uniform_read_size(QShaderDescription::VariableType type, int arrayDim, quint32 stride)
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Texture::UsageState &texUsage)
static GLenum toGlShaderType(QRhiShaderStage::Type type)
static GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
#define GL_LINE
static GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
static GLbitfield barriersForBuffer()
static GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
static bool isDepthTextureFormat(QRhiTexture::Format format)
void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource, QRhiShaderResourceBinding::Type bindingType, int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
static void bindVertexIndexBufferWithStateReset(CommandBufferExecTrackedState *state, QOpenGLExtensions *f, GLenum target, GLuint buffer)
static bool textureAccessIsWrite(QGles2Texture::Access access)
#define GL_TEXTURE_1D
static GLenum toGlMagFilter(QRhiSampler::Filter f)
#define GL_STENCIL_INDEX
static GLenum toGlPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
static QSurface * currentSurfaceForCurrentContext(QOpenGLContext *ctx)
#define GL_BACK_RIGHT
bool operator!=(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a, const QGles2CommandBuffer::GraphicsPassState::ColorMask &b)
#define GL_FLOAT
#define GL_UNSIGNED_BYTE
#define GL_RGBA
bool enabledAttribArrays[TRACKED_ATTRIB_COUNT]
bool nonzeroAttribDivisor[TRACKED_ATTRIB_COUNT]
static const int TRACKED_ATTRIB_COUNT
QRhiGraphicsPipeline * ps
@ AccessStorageReadWrite
Definition qrhigles2_p.h:55
UsageState usageState
Definition qrhigles2_p.h:62
static const int TIMESTAMP_PAIRS
int currentTimestampPairIndex
bool create() override
Creates the corresponding native graphics resources.
UsageState usageState
uint needsDepthStencilCombinedAttach
uint perRenderTargetBlending
int maxThreadGroupsPerDimension
uint glesMultisampleRenderToTexture
uint srgbTextureRenderTarget
int maxFragmentStorageBuffers
uint fixedIndexPrimitiveRestart
uint screenSpaceDerivatives
uint nonBaseLevelFramebufferTexture
uint glesMultiviewMultisampleRenderToTexture
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1988