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
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 // Immutable texture storage. Needed to be able to allocate all mip levels
1165 // of a compressed texture without having data at hand.
1166 if (caps.gles)
1167 caps.textureStorage = caps.ctxMajor >= 3; // ES 3.0
1168 else
1169 caps.textureStorage = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 2) // 4.2
1170 || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_storage"));
1171
1172 if (caps.gles)
1173 caps.texture1D = false; // ES
1174 else
1175 caps.texture1D = glTexImage1D && (caps.ctxMajor >= 2); // 2.0
1176
1177 if (caps.gles)
1178 caps.tessellation = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
1179 else
1180 caps.tessellation = caps.ctxMajor >= 4; // 4.0
1181
1182 if (caps.gles)
1183 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
1184 else
1185 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2
1186
1187 if (caps.ctxMajor >= 3) { // 3.0 or ES 3.0
1188 GLint maxArraySize = 0;
1189 f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArraySize);
1190 caps.maxTextureArraySize = maxArraySize;
1191 } else {
1192 caps.maxTextureArraySize = 0;
1193 }
1194
1195 // The ES 2.0 spec only has MAX_xxxx_VECTORS. ES 3.0 and up has both
1196 // *VECTORS and *COMPONENTS. OpenGL 2.0-4.0 only has MAX_xxxx_COMPONENTS.
1197 // 4.1 and above has both. What a mess.
1198 if (caps.gles) {
1199 GLint maxVertexUniformVectors = 0;
1200 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniformVectors);
1201 GLint maxFragmentUniformVectors = 0;
1202 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniformVectors);
1203 caps.maxUniformVectors = qMin(maxVertexUniformVectors, maxFragmentUniformVectors);
1204 } else {
1205 GLint maxVertexUniformComponents = 0;
1206 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxVertexUniformComponents);
1207 GLint maxFragmentUniformComponents = 0;
1208 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &maxFragmentUniformComponents);
1209 caps.maxUniformVectors = qMin(maxVertexUniformComponents, maxFragmentUniformComponents) / 4;
1210 }
1211
1212 f->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.maxVertexInputs);
1213
1214 if (caps.gles) {
1215 f->glGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.maxVertexOutputs);
1216 } else if (caps.ctxMajor >= 3) {
1217 GLint components = 0;
1218 f->glGetIntegerv(caps.coreProfile ? GL_MAX_VERTEX_OUTPUT_COMPONENTS : GL_MAX_VARYING_COMPONENTS, &components);
1219 caps.maxVertexOutputs = components / 4;
1220 } else {
1221 // OpenGL before 3.0 only has this, and not the same as
1222 // MAX_VARYING_COMPONENTS strictly speaking, but will do.
1223 GLint components = 0;
1224 f->glGetIntegerv(GL_MAX_VARYING_FLOATS, &components);
1225 if (components > 0)
1226 caps.maxVertexOutputs = components / 4;
1227 }
1228
1229 if (!caps.gles) {
1231 if (!caps.coreProfile)
1232 f->glEnable(GL_POINT_SPRITE);
1233 } // else (with gles) these are always on
1234
1235 // Match D3D and others when it comes to seamless cubemap filtering.
1236 // ES 3.0+ has this always enabled. (hopefully)
1237 // ES 2.0 and GL < 3.2 will not have it.
1238 if (!caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)))
1240
1241 caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
1242
1243 // We always require GL_OVR_multiview2 for symmetry with other backends.
1244 caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView)
1245 && f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended);
1246 if (caps.multiView) {
1248 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>(
1249 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
1250 }
1251
1252 // Only do timestamp queries on OpenGL 3.3+.
1253 caps.timestamps = !caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3));
1254 if (caps.timestamps) {
1255 glQueryCounter = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum)>(
1256 ctx->getProcAddress(QByteArrayLiteral("glQueryCounter")));
1257 glGetQueryObjectui64v = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum, quint64 *)>(
1258 ctx->getProcAddress(QByteArrayLiteral("glGetQueryObjectui64v")));
1260 caps.timestamps = false;
1261 }
1262
1263 // glObjectLabel is available on OpenGL ES 3.2+ and OpenGL 4.3+
1264 if (caps.gles)
1265 caps.objectLabel = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1266 else
1267 caps.objectLabel = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
1268 if (caps.objectLabel) {
1269 glObjectLabel = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLuint, GLsizei, const GLchar *)>(
1270 ctx->getProcAddress(QByteArrayLiteral("glObjectLabel")));
1271 }
1272
1273 if (caps.gles) {
1274 // This is the third way to get multisample rendering with GLES. (1. is
1275 // multisample render buffer -> resolve to texture; 2. is multisample
1276 // texture with GLES 3.1; 3. is this, avoiding the explicit multisample
1277 // buffer and should be more efficient with tiled architectures.
1278 // Interesting also because 2. does not seem to work in practice on
1279 // devices such as the Quest 3)
1280 caps.glesMultisampleRenderToTexture = ctx->hasExtension("GL_EXT_multisampled_render_to_texture");
1282 glFramebufferTexture2DMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei)>(
1283 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture2DMultisampleEXT")));
1284 glRenderbufferStorageMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLsizei, GLenum, GLsizei, GLsizei)>(
1285 ctx->getProcAddress(QByteArrayLiteral("glRenderbufferStorageMultisampleEXT")));
1286 }
1287 caps.glesMultiviewMultisampleRenderToTexture = ctx->hasExtension("GL_OVR_multiview_multisampled_render_to_texture");
1289 glFramebufferTextureMultisampleMultiviewOVR = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei)>(
1290 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultisampleMultiviewOVR")));
1291 }
1292 } else {
1295 }
1296
1297 caps.unpackRowLength = !caps.gles || caps.ctxMajor >= 3;
1298
1299 if (caps.gles)
1300 caps.perRenderTargetBlending = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1301 else
1302 caps.perRenderTargetBlending = caps.ctxMajor >= 4;
1303
1304 if (caps.gles) {
1305 if (caps.ctxMajor == 3 && caps.ctxMinor < 2) {
1306 caps.sampleVariables = ctx->hasExtension("GL_OES_sample_variables");
1307 } else {
1308 caps.sampleVariables = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1309 }
1310 } else {
1311 caps.sampleVariables = caps.ctxMajor >= 4;
1312 }
1313
1314 if (caps.gles)
1315 caps.drawIndirect = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1);
1316 else
1317 caps.drawIndirect = caps.ctxMajor >= 4;
1318 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_draw_indirect")))
1319 caps.drawIndirect = true;
1320
1321 // glDraw*BaseInstance: core in 4.2+, ARB on older desktop. Not enabled on
1322 // GLES even when GL_EXT_base_instance is present, for lack of testing.
1323 // Requires instancing as well: the ARB extension can be advertised on
1324 // contexts below 3.3, where the instanced draw path, including the
1325 // attribute divisors, is not taken at all.
1326 if (caps.gles) {
1327 caps.baseInstance = false;
1328 } else {
1329 caps.baseInstance = caps.instancing
1330 && (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 2)
1331 || ctx->hasExtension(QByteArrayLiteral("GL_ARB_base_instance")));
1332 }
1333
1334 if (caps.baseInstance) {
1335 glDrawArraysInstancedBaseInstance = reinterpret_cast<decltype(glDrawArraysInstancedBaseInstance)>(
1336 ctx->getProcAddress(QByteArrayLiteral("glDrawArraysInstancedBaseInstance")));
1337 glDrawElementsInstancedBaseInstance = reinterpret_cast<decltype(glDrawElementsInstancedBaseInstance)>(
1338 ctx->getProcAddress(QByteArrayLiteral("glDrawElementsInstancedBaseInstance")));
1339 glDrawElementsInstancedBaseVertexBaseInstance = reinterpret_cast<decltype(glDrawElementsInstancedBaseVertexBaseInstance)>(
1340 ctx->getProcAddress(QByteArrayLiteral("glDrawElementsInstancedBaseVertexBaseInstance")));
1344 qWarning("Failed to resolve glDraw{Arrays,Elements}InstancedBaseInstance{,BaseVertex}; disabling base instance support.");
1345 caps.baseInstance = false;
1346 }
1347 }
1348
1349 if (caps.gles)
1350 caps.drawIndirectMulti = false;
1351 else
1352 caps.drawIndirectMulti = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
1353 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_multi_draw_indirect")) ||
1354 ctx->hasExtension(QByteArrayLiteral("GL_EXT_multi_draw_indirect")))
1355 caps.drawIndirectMulti = true;
1356
1357 if (caps.drawIndirectMulti) {
1358 glMultiDrawArraysIndirect = reinterpret_cast<decltype(glMultiDrawArraysIndirect)>(
1359 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirect")));
1361 glMultiDrawArraysIndirect = reinterpret_cast<decltype(glMultiDrawArraysIndirect)>(
1362 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectARB")));
1364 glMultiDrawArraysIndirect = reinterpret_cast<decltype(glMultiDrawArraysIndirect)>(
1365 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectEXT")));
1366 glMultiDrawElementsIndirect = reinterpret_cast<decltype(glMultiDrawElementsIndirect)>(
1367 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirect")));
1369 glMultiDrawElementsIndirect = reinterpret_cast<decltype(glMultiDrawElementsIndirect)>(
1370 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectARB")));
1372 glMultiDrawElementsIndirect = reinterpret_cast<decltype(glMultiDrawElementsIndirect)>(
1373 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectEXT")));
1375 qWarning("Failed to resolve glMultiDrawArraysIndirect or glMultiDrawElementsIndirect.");
1376 caps.drawIndirectMulti = false;
1377 }
1378 }
1379
1380 if (caps.gles)
1381 caps.shaderDrawParameters = false;
1382 else
1383 caps.shaderDrawParameters = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6);
1384 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_shader_draw_parameters")))
1385 caps.shaderDrawParameters = true;
1386 // glDispatchComputeIndirect was introduced together with the compute
1387 // pipeline in OpenGL 4.3 and OpenGL ES 3.1, so its availability matches
1388 // caps.compute. The function pointer is exposed by QOpenGLExtraFunctions.
1389 caps.dispatchIndirect = caps.compute;
1390
1391 // glCopyBufferSubData is core in OpenGL 3.1 and OpenGL ES 3.0, and is
1392 // available via GL_ARB_copy_buffer on older desktop versions. Either way
1393 // the function pointer is exposed by QOpenGLExtraFunctions, but only when
1394 // it can actually be resolved.
1395 if (caps.gles)
1396 caps.copyBuffer = caps.ctxMajor >= 3;
1397 else
1398 caps.copyBuffer = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1);
1399 if (!caps.copyBuffer && ctx->hasExtension(QByteArrayLiteral("GL_ARB_copy_buffer")))
1400 caps.copyBuffer = ctx->getProcAddress(QByteArrayLiteral("glCopyBufferSubData")) != nullptr;
1401
1402 if (!caps.gles
1403 && (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3)
1404 || ctx->hasExtension(QByteArrayLiteral("GL_ARB_clear_buffer_object"))))
1405 {
1406 glClearBufferSubData = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLintptr, GLsizeiptr,
1407 GLenum, GLenum, const void *)>(
1408 ctx->getProcAddress(QByteArrayLiteral("glClearBufferSubData")));
1409 caps.clearBuffer = glClearBufferSubData != nullptr;
1410 }
1411
1412 // glMultiDraw*IndirectCount: core in 4.6, ARB on older desktop.
1413 // No counterpart in OpenGL ES.
1414 if (caps.gles)
1415 caps.drawIndirectCount = false;
1416 else
1417 caps.drawIndirectCount = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6);
1418 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_indirect_parameters")))
1419 caps.drawIndirectCount = true;
1420
1421 if (caps.drawIndirectCount) {
1422 glMultiDrawArraysIndirectCount = reinterpret_cast<decltype(glMultiDrawArraysIndirectCount)>(
1423 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectCount")));
1425 glMultiDrawArraysIndirectCount = reinterpret_cast<decltype(glMultiDrawArraysIndirectCount)>(
1426 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawArraysIndirectCountARB")));
1427 glMultiDrawElementsIndirectCount = reinterpret_cast<decltype(glMultiDrawElementsIndirectCount)>(
1428 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectCount")));
1430 glMultiDrawElementsIndirectCount = reinterpret_cast<decltype(glMultiDrawElementsIndirectCount)>(
1431 ctx->getProcAddress(QByteArrayLiteral("glMultiDrawElementsIndirectCountARB")));
1433 qWarning("Failed to resolve glMultiDrawArraysIndirectCount or glMultiDrawElementsIndirectCount.");
1434 caps.drawIndirectCount = false;
1435 }
1436 }
1437
1438 nativeHandlesStruct.context = ctx;
1439
1440 contextLost = false;
1441
1442 return true;
1443}
1444
1446{
1447 if (!f)
1448 return;
1449
1450 if (ensureContext()) {
1452
1453 if (ofr.tsQueries[0]) {
1454 f->glDeleteQueries(2, ofr.tsQueries);
1455 ofr.tsQueries[0] = ofr.tsQueries[1] = 0;
1456 }
1457
1458 if (vao) {
1459 f->glDeleteVertexArrays(1, &vao);
1460 vao = 0;
1461 }
1462
1463 for (uint shader : std::as_const(m_shaderCache))
1464 f->glDeleteShader(shader);
1465 m_shaderCache.clear();
1466
1468 }
1469 bufferNameCacheCount = 0; // clear even when the context is lost
1470
1471 if (!importedContext) {
1472 delete ctx;
1473 ctx = nullptr;
1474 }
1475
1476 f = nullptr;
1477}
1478
1480{
1481 if (!bufferNameCacheCount) {
1482 bufferNameCache.fill(0);
1483 f->glGenBuffers(GLsizei(bufferNameCache.size()), bufferNameCache.data());
1484 bufferNameCacheCount = int(bufferNameCache.size());
1485 }
1486 return bufferNameCache[--bufferNameCacheCount];
1487}
1488
1490{
1492 f->glDeleteBuffers(GLsizei(bufferNameCacheCount), bufferNameCache.data());
1494 }
1495}
1496
1498{
1499 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
1500 const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
1501 switch (e.type) {
1503 f->glDeleteBuffers(1, &e.buffer.buffer);
1504 break;
1506 f->glDeleteProgram(e.pipeline.program);
1507 break;
1509 f->glDeleteTextures(1, &e.texture.texture);
1510 break;
1512 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
1513 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
1514 break;
1516 f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
1517 f->glDeleteTextures(1, &e.textureRenderTarget.nonMsaaThrowawayDepthTexture);
1518 break;
1519 default:
1520 Q_UNREACHABLE();
1521 break;
1522 }
1523 releaseQueue.removeAt(i);
1524 }
1525}
1526
1528{
1529 if (supportedSampleCountList.isEmpty()) {
1530 // 1, 2, 4, 8, ...
1531 for (int i = 1; i <= caps.maxSamples; i *= 2)
1532 supportedSampleCountList.append(i);
1533 }
1534 return supportedSampleCountList;
1535}
1536
1538{
1539 Q_UNUSED(sampleCount);
1540 return { QSize(1, 1) };
1541}
1542
1544{
1545 return new QGles2SwapChain(this);
1546}
1547
1548QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
1549{
1550 return new QGles2Buffer(this, type, usage, size);
1551}
1552
1554{
1555 // No real uniform buffers are used so no need to pretend there is any
1556 // alignment requirement.
1557 return 1;
1558}
1559
1561{
1562 return true;
1563}
1564
1566{
1567 return true;
1568}
1569
1571{
1572 return false;
1573}
1574
1576{
1577 return QMatrix4x4(); // identity
1578}
1579
1580static inline void toGlTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags,
1581 const QRhiGles2::Caps &caps,
1582 GLenum *glintformat, GLenum *glsizedintformat,
1583 GLenum *glformat, GLenum *gltype)
1584{
1585 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
1586 const bool legacyGlesSrgb = srgb && caps.gles && caps.ctxMajor < 3;
1587 switch (format) {
1588 case QRhiTexture::RGBA8:
1589 *glintformat = srgb ? (legacyGlesSrgb ? GL_SRGB_ALPHA : GL_SRGB8_ALPHA8) : GL_RGBA;
1590 *glsizedintformat = srgb ? GL_SRGB8_ALPHA8 : (caps.rgba8Format ? GL_RGBA8 : GL_RGBA);
1591 *glformat = legacyGlesSrgb ? GL_SRGB_ALPHA : GL_RGBA;
1592 *gltype = GL_UNSIGNED_BYTE;
1593 break;
1594 case QRhiTexture::BGRA8:
1595 *glintformat = srgb ? GL_SRGB8_ALPHA8 : (caps.bgraInternalFormat ? GL_BGRA : GL_RGBA);
1596 *glsizedintformat = srgb ? GL_SRGB8_ALPHA8 : (caps.rgba8Format ? GL_RGBA8 : GL_RGBA);
1597 *glformat = GL_BGRA;
1598 *gltype = GL_UNSIGNED_BYTE;
1599 break;
1600 case QRhiTexture::R16:
1601 *glintformat = GL_R16;
1602 *glsizedintformat = *glintformat;
1603 *glformat = GL_RED;
1604 *gltype = GL_UNSIGNED_SHORT;
1605 break;
1606 case QRhiTexture::RG16:
1607 *glintformat = GL_RG16;
1608 *glsizedintformat = *glintformat;
1609 *glformat = GL_RG;
1610 *gltype = GL_UNSIGNED_SHORT;
1611 break;
1612 case QRhiTexture::R8:
1613 // GL_EXT_texture_rg on ES 2.0 only accepts unsized GL_RED here; same split as RGBA8 above.
1614 *glintformat = caps.gles && caps.ctxMajor < 3 ? GL_RED : GL_R8;
1615 *glsizedintformat = GL_R8;
1616 *glformat = GL_RED;
1617 *gltype = GL_UNSIGNED_BYTE;
1618 break;
1619 case QRhiTexture::R8SI:
1620 *glintformat = GL_R8I;
1621 *glsizedintformat = *glintformat;
1622 *glformat = GL_RED_INTEGER;
1623 *gltype = GL_BYTE;
1624 break;
1625 case QRhiTexture::R8UI:
1626 *glintformat = GL_R8UI;
1627 *glsizedintformat = *glintformat;
1628 *glformat = GL_RED_INTEGER;
1629 *gltype = GL_UNSIGNED_BYTE;
1630 break;
1631 case QRhiTexture::RG8:
1632 *glintformat = caps.gles && caps.ctxMajor < 3 ? GL_RG : GL_RG8;
1633 *glsizedintformat = GL_RG8;
1634 *glformat = GL_RG;
1635 *gltype = GL_UNSIGNED_BYTE;
1636 break;
1637 case QRhiTexture::RED_OR_ALPHA8:
1638 *glintformat = caps.coreProfile ? GL_R8 : GL_ALPHA;
1639 *glsizedintformat = *glintformat;
1640 *glformat = caps.coreProfile ? GL_RED : GL_ALPHA;
1641 *gltype = GL_UNSIGNED_BYTE;
1642 break;
1643 case QRhiTexture::RGBA16F:
1644 *glintformat = GL_RGBA16F;
1645 *glsizedintformat = *glintformat;
1646 *glformat = GL_RGBA;
1647 *gltype = GL_HALF_FLOAT;
1648 break;
1649 case QRhiTexture::RGBA32F:
1650 *glintformat = GL_RGBA32F;
1651 *glsizedintformat = *glintformat;
1652 *glformat = GL_RGBA;
1653 *gltype = GL_FLOAT;
1654 break;
1655 case QRhiTexture::R16F:
1656 *glintformat = GL_R16F;
1657 *glsizedintformat = *glintformat;
1658 *glformat = GL_RED;
1659 *gltype = GL_HALF_FLOAT;
1660 break;
1661 case QRhiTexture::R32F:
1662 *glintformat = GL_R32F;
1663 *glsizedintformat = *glintformat;
1664 *glformat = GL_RED;
1665 *gltype = GL_FLOAT;
1666 break;
1667 case QRhiTexture::RGB10A2:
1668 *glintformat = GL_RGB10_A2;
1669 *glsizedintformat = *glintformat;
1670 *glformat = GL_RGBA;
1672 break;
1673 case QRhiTexture::R32SI:
1674 *glintformat = GL_R32I;
1675 *glsizedintformat = *glintformat;
1676 *glformat = GL_RED_INTEGER;
1677 *gltype = GL_INT;
1678 break;
1679 case QRhiTexture::R32UI:
1680 *glintformat = GL_R32UI;
1681 *glsizedintformat = *glintformat;
1682 *glformat = GL_RED_INTEGER;
1683 *gltype = GL_UNSIGNED_INT;
1684 break;
1685 case QRhiTexture::RG32SI:
1686 *glintformat = GL_RG32I;
1687 *glsizedintformat = *glintformat;
1688 *glformat = GL_RG_INTEGER;
1689 *gltype = GL_INT;
1690 break;
1691 case QRhiTexture::RG32UI:
1692 *glintformat = GL_RG32UI;
1693 *glsizedintformat = *glintformat;
1694 *glformat = GL_RG_INTEGER;
1695 *gltype = GL_UNSIGNED_INT;
1696 break;
1697 case QRhiTexture::RGBA32SI:
1698 *glintformat = GL_RGBA32I;
1699 *glsizedintformat = *glintformat;
1700 *glformat = GL_RGBA_INTEGER;
1701 *gltype = GL_INT;
1702 break;
1703 case QRhiTexture::RGBA32UI:
1704 *glintformat = GL_RGBA32UI;
1705 *glsizedintformat = *glintformat;
1706 *glformat = GL_RGBA_INTEGER;
1707 *gltype = GL_UNSIGNED_INT;
1708 break;
1709 case QRhiTexture::D16:
1710 *glintformat = GL_DEPTH_COMPONENT16;
1711 *glsizedintformat = *glintformat;
1712 *glformat = GL_DEPTH_COMPONENT;
1713 *gltype = GL_UNSIGNED_SHORT;
1714 break;
1715 case QRhiTexture::D24:
1716 *glintformat = GL_DEPTH_COMPONENT24;
1717 *glsizedintformat = *glintformat;
1718 *glformat = GL_DEPTH_COMPONENT;
1719 *gltype = GL_UNSIGNED_INT;
1720 break;
1721 case QRhiTexture::D24S8:
1722 *glintformat = GL_DEPTH24_STENCIL8;
1723 *glsizedintformat = *glintformat;
1724 *glformat = GL_DEPTH_STENCIL;
1725 *gltype = GL_UNSIGNED_INT_24_8;
1726 break;
1727 case QRhiTexture::D32F:
1728 *glintformat = GL_DEPTH_COMPONENT32F;
1729 *glsizedintformat = *glintformat;
1730 *glformat = GL_DEPTH_COMPONENT;
1731 *gltype = GL_FLOAT;
1732 break;
1733 case QRhiTexture::D32FS8:
1734 *glintformat = GL_DEPTH32F_STENCIL8;
1735 *glsizedintformat = *glintformat;
1736 *glformat = GL_DEPTH_STENCIL;
1738 break;
1739 default:
1740 Q_UNREACHABLE();
1741 *glintformat = GL_RGBA;
1742 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1743 *glformat = GL_RGBA;
1744 *gltype = GL_UNSIGNED_BYTE;
1745 break;
1746 }
1747}
1748
1749static inline bool isDepthTextureFormat(QRhiTexture::Format format)
1750{
1751 switch (format) {
1752 case QRhiTexture::D16:
1753 case QRhiTexture::D24:
1754 case QRhiTexture::D24S8:
1755 case QRhiTexture::D32F:
1756 case QRhiTexture::D32FS8:
1757 return true;
1758 default:
1759 return false;
1760 }
1761}
1762
1763bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
1764{
1765 if (isCompressedFormat(format))
1766 return supportedCompressedFormats.contains(GLint(toGlCompressedTextureFormat(format, flags)));
1767
1768 if ((flags & QRhiTexture::UsedWithLoadStore) && !caps.imageLoadStore)
1769 return false;
1770
1771 if (flags.testFlag(QRhiTexture::sRGB)) {
1772 if (format != QRhiTexture::RGBA8 && format != QRhiTexture::BGRA8)
1773 return false;
1774 if (!caps.srgbTextureFormat || flags.testFlag(QRhiTexture::UsedWithLoadStore))
1775 return false;
1776 if (format == QRhiTexture::BGRA8 && caps.gles)
1777 return false;
1778 if (flags.testFlag(QRhiTexture::UsedWithGenerateMips) && !caps.srgbGenerateMipmap)
1779 return false;
1780 if (flags.testFlag(QRhiTexture::RenderTarget) && !caps.srgbTextureRenderTarget)
1781 return false;
1782 }
1783
1784 switch (format) {
1785 case QRhiTexture::D16:
1786 case QRhiTexture::D32F:
1787 case QRhiTexture::D32FS8:
1788 return caps.depthTexture;
1789
1790 case QRhiTexture::D24:
1791 return caps.depthTexture && caps.depth24;
1792
1793 case QRhiTexture::D24S8:
1794 return caps.depthTexture && caps.depth24 && caps.packedDepthStencil;
1795
1796 case QRhiTexture::BGRA8:
1797 return caps.bgraExternalFormat;
1798
1799 case QRhiTexture::R8:
1800 case QRhiTexture::R8SI:
1801 case QRhiTexture::R8UI:
1802 return caps.r8Format;
1803
1804 case QRhiTexture::R32SI:
1805 case QRhiTexture::R32UI:
1806 case QRhiTexture::RG32SI:
1807 case QRhiTexture::RG32UI:
1808 case QRhiTexture::RGBA32SI:
1809 case QRhiTexture::RGBA32UI:
1810 return caps.r32uiFormat;
1811
1812 case QRhiTexture::RG8:
1813 return caps.r8Format;
1814
1815 case QRhiTexture::R16:
1816 return caps.r16Format;
1817
1818 case QRhiTexture::RG16:
1819 return caps.r16Format;
1820
1821 case QRhiTexture::RGBA16F:
1822 case QRhiTexture::RGBA32F:
1823 return caps.floatFormats;
1824
1825 case QRhiTexture::R16F:
1826 case QRhiTexture::R32F:
1827 return caps.floatFormats;
1828
1829 case QRhiTexture::RGB10A2:
1830 return caps.rgb10Formats;
1831
1832 default:
1833 break;
1834 }
1835
1836 return true;
1837}
1838
1839bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
1840{
1841 switch (feature) {
1842 case QRhi::MultisampleTexture:
1843 return caps.multisampledTexture;
1844 case QRhi::MultisampleRenderBuffer:
1845 return caps.msaaRenderBuffer;
1846 case QRhi::DebugMarkers:
1847 return false;
1848 case QRhi::Timestamps:
1849 return caps.timestamps;
1850 case QRhi::Instancing:
1851 return caps.instancing;
1852 case QRhi::CustomInstanceStepRate:
1853 return false;
1854 case QRhi::PrimitiveRestart:
1855 return caps.fixedIndexPrimitiveRestart;
1856 case QRhi::NonDynamicUniformBuffers:
1857 return true;
1858 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
1859 return true;
1860 case QRhi::NPOTTextureRepeat:
1861 return caps.npotTextureFull;
1862 case QRhi::RedOrAlpha8IsRed:
1863 return caps.coreProfile;
1864 case QRhi::ElementIndexUint:
1865 return caps.elementIndexUint;
1866 case QRhi::Compute:
1867 return caps.compute;
1868 case QRhi::FragmentStoresAndAtomics:
1869 return caps.compute && caps.maxFragmentStorageBuffers > 0;
1870 case QRhi::WideLines:
1871 return !caps.coreProfile;
1872 case QRhi::VertexShaderPointSize:
1873 return true;
1874 case QRhi::BaseVertex:
1875 return caps.baseVertex;
1876 case QRhi::BaseInstance:
1877 // gl_InstanceID on GL starts at 0 regardless of baseInstance, unlike
1878 // Vulkan's gl_InstanceIndex. QRhi::InstanceIndexIncludesBaseInstance
1879 // stays false on GL.
1880 return caps.baseInstance;
1881 case QRhi::TriangleFanTopology:
1882 return true;
1883 case QRhi::ReadBackNonUniformBuffer:
1884 return !caps.gles || caps.properMapBuffer;
1885 case QRhi::ReadBackNonBaseMipLevel:
1887 case QRhi::TexelFetch:
1888 return caps.texelFetch;
1889 case QRhi::RenderToNonBaseMipLevel:
1891 case QRhi::IntAttributes:
1892 return caps.intAttributes;
1893 case QRhi::ScreenSpaceDerivatives:
1894 return caps.screenSpaceDerivatives;
1895 case QRhi::ReadBackAnyTextureFormat:
1896 return false;
1897 case QRhi::PipelineCacheDataLoadSave:
1898 return caps.programBinary;
1899 case QRhi::ImageDataStride:
1900 return caps.unpackRowLength;
1901 case QRhi::RenderBufferImport:
1902 return true;
1903 case QRhi::ThreeDimensionalTextures:
1904 return caps.texture3D;
1905 case QRhi::RenderTo3DTextureSlice:
1906 return caps.texture3D;
1907 case QRhi::TextureArrays:
1908 return caps.maxTextureArraySize > 0;
1909 case QRhi::Tessellation:
1910 return caps.tessellation;
1911 case QRhi::GeometryShader:
1912 return caps.geometryShader;
1913 case QRhi::TextureArrayRange:
1914 return false;
1915 case QRhi::NonFillPolygonMode:
1916 return !caps.gles;
1917 case QRhi::OneDimensionalTextures:
1918 return caps.texture1D;
1919 case QRhi::OneDimensionalTextureMipmaps:
1920 return caps.texture1D;
1921 case QRhi::HalfAttributes:
1922 return caps.halfAttributes;
1923 case QRhi::RenderToOneDimensionalTexture:
1924 return caps.texture1D;
1925 case QRhi::ThreeDimensionalTextureMipmaps:
1926 return caps.texture3D;
1927 case QRhi::MultiView:
1928 return caps.multiView && caps.maxTextureArraySize > 0;
1929 case QRhi::TextureViewFormat:
1930 return false;
1931 case QRhi::ResolveDepthStencil:
1932 return true;
1933 case QRhi::VariableRateShading:
1934 return false;
1935 case QRhi::VariableRateShadingMap:
1936 case QRhi::VariableRateShadingMapWithTexture:
1937 return false;
1938 case QRhi::PerRenderTargetBlending:
1939 return caps.perRenderTargetBlending;
1940 case QRhi::SampleVariables:
1941 return caps.sampleVariables;
1942 case QRhi::InstanceIndexIncludesBaseInstance:
1943 return false; // gl_InstanceID always starts at 0 on GL
1944 case QRhi::DepthClamp:
1945 return caps.depthClamp;
1946 case QRhi::DrawIndirect:
1947 return caps.drawIndirect;
1948 case QRhi::DrawIndirectMulti:
1949 return caps.drawIndirectMulti;
1950 case QRhi::ShaderDrawParameters:
1951 return caps.shaderDrawParameters;
1952 case QRhi::DispatchIndirect:
1953 return caps.dispatchIndirect;
1954 case QRhi::PushConstants:
1955 return true;
1956 case QRhi::DrawIndirectCount:
1957 return caps.drawIndirectCount;
1958 case QRhi::BufferToBufferCopy:
1959 return caps.copyBuffer;
1960 case QRhi::StaticBuffersOnGpuTimeline:
1961 return true;
1962 default:
1963 Q_UNREACHABLE_RETURN(false);
1964 }
1965}
1966
1967int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
1968{
1969 switch (limit) {
1970 case QRhi::TextureSizeMin:
1971 return 1;
1972 case QRhi::TextureSizeMax:
1973 return caps.maxTextureSize;
1974 case QRhi::MaxColorAttachments:
1975 return caps.maxDrawBuffers;
1976 case QRhi::FramesInFlight:
1977 // From our perspective. What the GL impl does internally is another
1978 // question, but that's out of our hands and does not concern us here.
1979 return 1;
1980 case QRhi::MaxAsyncReadbackFrames:
1981 return 1;
1982 case QRhi::MaxThreadGroupsPerDimension:
1984 case QRhi::MaxThreadsPerThreadGroup:
1985 return caps.maxThreadsPerThreadGroup;
1986 case QRhi::MaxThreadGroupX:
1987 return caps.maxThreadGroupsX;
1988 case QRhi::MaxThreadGroupY:
1989 return caps.maxThreadGroupsY;
1990 case QRhi::MaxThreadGroupZ:
1991 return caps.maxThreadGroupsZ;
1992 case QRhi::TextureArraySizeMax:
1993 return 2048;
1994 case QRhi::MaxUniformBufferRange:
1995 return int(qMin<qint64>(INT_MAX, caps.maxUniformVectors * qint64(16)));
1996 case QRhi::MaxVertexInputs:
1997 return caps.maxVertexInputs;
1998 case QRhi::MaxVertexOutputs:
1999 return caps.maxVertexOutputs;
2000 case QRhi::MaxVertexStorageBuffers:
2001 return caps.maxVertexStorageBuffers;
2002 case QRhi::MaxPushConstantsSize:
2003 // Emulated with plain uniforms, so the real limit is the uniform
2004 // budget shared with everything else. Report the portable minimum.
2005 return int(MAX_PUSH_CONSTANTS_SIZE);
2006 case QRhi::MaxFragmentStorageBuffers:
2007 return caps.maxFragmentStorageBuffers;
2008 case QRhi::ShadingRateImageTileSize:
2009 return 0;
2010 default:
2011 Q_UNREACHABLE_RETURN(0);
2012 }
2013}
2014
2016{
2017 return &nativeHandlesStruct;
2018}
2019
2021{
2022 return driverInfoStruct;
2023}
2024
2026{
2027 QRhiStats result;
2028 result.totalPipelineCreationTime = totalPipelineCreationTime();
2029 return result;
2030}
2031
2033{
2034 if (inFrame && !ofr.active)
2035 return ensureContext(currentSwapChain->surface);
2036 else
2037 return ensureContext();
2038}
2039
2040void QRhiGles2::setQueueSubmitParams(QRhiNativeHandles *)
2041{
2042 // not applicable
2043}
2044
2046{
2047 if (!ensureContext())
2048 return;
2049
2050 for (uint shader : std::as_const(m_shaderCache))
2051 f->glDeleteShader(shader);
2052
2053 m_shaderCache.clear();
2054
2055 m_pipelineCache.clear();
2056
2058}
2059
2061{
2062 return contextLost;
2063}
2064
2073
2075{
2076 Q_STATIC_ASSERT(sizeof(QGles2PipelineCacheDataHeader) == 256);
2077
2078 if (m_pipelineCache.data.isEmpty())
2079 return QByteArray();
2080
2082 memset(&header, 0, sizeof(header));
2083 header.rhiId = pipelineCacheRhiId();
2084 header.arch = quint32(sizeof(void*));
2085 header.programBinaryCount = m_pipelineCache.data.size();
2086 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
2087 if (driverStrLen)
2088 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
2089 header.driver[driverStrLen] = '\0';
2090
2091 const size_t dataOffset = sizeof(header);
2092 size_t dataSize = 0;
2093 for (auto it = m_pipelineCache.data.cbegin(), end = m_pipelineCache.data.cend(); it != end; ++it) {
2094 dataSize += sizeof(quint32) + it.key().size()
2095 + sizeof(quint32) + it->data.size()
2096 + sizeof(quint32);
2097 }
2098
2099 QByteArray buf(dataOffset + dataSize, Qt::Uninitialized);
2100 char *p = buf.data() + dataOffset;
2101 for (auto it = m_pipelineCache.data.cbegin(), end = m_pipelineCache.data.cend(); it != end; ++it) {
2102 const QByteArray key = it.key();
2103 const QByteArray data = it->data;
2104 const quint32 format = it->format;
2105
2106 quint32 i = key.size();
2107 memcpy(p, &i, 4);
2108 p += 4;
2109 memcpy(p, key.constData(), key.size());
2110 p += key.size();
2111
2112 i = data.size();
2113 memcpy(p, &i, 4);
2114 p += 4;
2115 memcpy(p, data.constData(), data.size());
2116 p += data.size();
2117
2118 memcpy(p, &format, 4);
2119 p += 4;
2120 }
2121 Q_ASSERT(p == buf.data() + dataOffset + dataSize);
2122
2123 header.dataSize = quint32(dataSize);
2124 memcpy(buf.data(), &header, sizeof(header));
2125
2126 return buf;
2127}
2128
2129void QRhiGles2::setPipelineCacheData(const QByteArray &data)
2130{
2131 if (data.isEmpty())
2132 return;
2133
2134 const size_t headerSize = sizeof(QGles2PipelineCacheDataHeader);
2135 if (data.size() < qsizetype(headerSize)) {
2136 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
2137 return;
2138 }
2139 const size_t dataOffset = headerSize;
2141 memcpy(&header, data.constData(), headerSize);
2142
2143 const quint32 rhiId = pipelineCacheRhiId();
2144 if (header.rhiId != rhiId) {
2145 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
2146 rhiId, header.rhiId);
2147 return;
2148 }
2149 const quint32 arch = quint32(sizeof(void*));
2150 if (header.arch != arch) {
2151 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
2152 arch, header.arch);
2153 return;
2154 }
2155 if (header.programBinaryCount == 0)
2156 return;
2157
2158 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
2159 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
2160 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OpenGL vendor/renderer/version does not match");
2161 return;
2162 }
2163
2164 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
2165 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
2166 return;
2167 }
2168
2169 m_pipelineCache.clear();
2170
2171 QRhiPipelineCacheDataReader reader(data.constData() + dataOffset, header.dataSize);
2172 for (quint32 i = 0; i < header.programBinaryCount; ++i) {
2173 QByteArray key;
2174 QByteArray binary;
2175 quint32 format = 0;
2176 if (!reader.readByteArray(&key) || !reader.readByteArray(&binary) || !reader.readUInt32(&format)) {
2177 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob (truncated or corrupt program binary data)");
2178 m_pipelineCache.clear();
2179 return;
2180 }
2181 m_pipelineCache.insertWithCapacityLimit(key, { format, binary });
2182 }
2183
2184 qCDebug(QRHI_LOG_INFO, "Seeded pipeline cache with %d program binaries", int(m_pipelineCache.data.size()));
2185}
2186
2187QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
2188 int sampleCount, QRhiRenderBuffer::Flags flags,
2189 QRhiTexture::Format backingFormatHint)
2190{
2191 return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
2192}
2193
2194QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format,
2195 const QSize &pixelSize, int depth, int arraySize,
2196 int sampleCount, QRhiTexture::Flags flags)
2197{
2198 return new QGles2Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
2199}
2200
2201QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
2202 QRhiSampler::Filter mipmapMode,
2203 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
2204{
2205 return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
2206}
2207
2208QRhiShadingRateMap *QRhiGles2::createShadingRateMap()
2209{
2210 return nullptr;
2211}
2212
2213QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
2214 QRhiTextureRenderTarget::Flags flags)
2215{
2216 return new QGles2TextureRenderTarget(this, desc, flags);
2217}
2218
2220{
2221 return new QGles2GraphicsPipeline(this);
2222}
2223
2225{
2226 return new QGles2ShaderResourceBindings(this);
2227}
2228
2230{
2231 return new QGles2ComputePipeline(this);
2232}
2233
2234void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
2235{
2236 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2239 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
2240
2241 if (pipelineChanged) {
2242 cbD->currentGraphicsPipeline = ps;
2243 cbD->currentComputePipeline = nullptr;
2245 if (psD->lastUsedInFrameNo != frameNo) {
2247 psD->currentSrb = nullptr;
2249 }
2250
2251 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2253 cmd.args.bindGraphicsPipeline.ps = ps;
2254 }
2255}
2256
2257void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
2258 int dynamicOffsetCount,
2259 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
2260{
2261 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2263 QGles2GraphicsPipeline *gfxPsD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
2264 QGles2ComputePipeline *compPsD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
2265
2266 if (!srb) {
2267 if (gfxPsD)
2268 srb = gfxPsD->m_shaderResourceBindings;
2269 else
2270 srb = compPsD->m_shaderResourceBindings;
2271 }
2272
2275 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2276 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
2277 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
2278 switch (b->type()) {
2279 case QRhiShaderResourceBinding::UniformBuffer:
2280 // no BufUniformRead / AccessUniform because no real uniform buffers are used
2281 break;
2282 case QRhiShaderResourceBinding::SampledTexture:
2283 case QRhiShaderResourceBinding::Texture:
2284 for (const auto &texSampler : b->stex().texSamplers) {
2285 QGles2Texture *texD = QRHI_RES(QGles2Texture, texSampler.tex);
2286 sanityCheckResourceOwnership(texD);
2287 trackedRegisterTexture(&passResTracker,
2288 texD,
2289 QRhiPassResourceTracker::TexSample,
2290 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
2291 }
2292 break;
2293 case QRhiShaderResourceBinding::ImageLoad:
2294 case QRhiShaderResourceBinding::ImageStore:
2295 case QRhiShaderResourceBinding::ImageLoadStore:
2296 {
2297 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->simage().tex);
2298 sanityCheckResourceOwnership(texD);
2300 if (b->type() == QRhiShaderResourceBinding::ImageLoad)
2302 else if (b->type() == QRhiShaderResourceBinding::ImageStore)
2304 else
2306 trackedRegisterTexture(&passResTracker, texD, access,
2307 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
2308 }
2309 break;
2310 case QRhiShaderResourceBinding::BufferLoad:
2311 case QRhiShaderResourceBinding::BufferStore:
2312 case QRhiShaderResourceBinding::BufferLoadStore:
2313 {
2314 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->sbuf().buf);
2315 sanityCheckResourceOwnership(bufD);
2317 if (b->type() == QRhiShaderResourceBinding::BufferLoad)
2319 else if (b->type() == QRhiShaderResourceBinding::BufferStore)
2321 else
2323 trackedRegisterBuffer(&passResTracker, bufD, access,
2324 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
2325 }
2326 break;
2327 default:
2328 break;
2329 }
2330 }
2331 }
2332
2333 bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
2334
2335 // The Command::BindShaderResources command generated below is what will
2336 // cause uniforms to be set (glUniformNxx). This needs some special
2337 // handling here in this backend without real uniform buffers, because,
2338 // like in other backends, we optimize out the setShaderResources when the
2339 // srb that was set before is attempted to be set again on the command
2340 // buffer, but that is incorrect if the same srb is now used with another
2341 // pipeline. (because that could mean a glUseProgram not followed by
2342 // up-to-date glUniform calls, i.e. with GL we have a strong dependency
2343 // between the pipeline (== program) and the srb, unlike other APIs) This
2344 // is the reason there is a second level of srb(+generation) tracking in
2345 // the pipeline objects.
2346 if (gfxPsD && (gfxPsD->currentSrb != srb || gfxPsD->currentSrbGeneration != srbD->generation)) {
2347 srbChanged = true;
2348 gfxPsD->currentSrb = srb;
2350 } else if (compPsD && (compPsD->currentSrb != srb || compPsD->currentSrbGeneration != srbD->generation)) {
2351 srbChanged = true;
2352 compPsD->currentSrb = srb;
2354 }
2355
2356 if (srbChanged || cbD->currentSrbGeneration != srbD->generation || srbD->hasDynamicOffset) {
2357 if (gfxPsD) {
2358 cbD->currentGraphicsSrb = srb;
2359 cbD->currentComputeSrb = nullptr;
2360 } else {
2361 cbD->currentGraphicsSrb = nullptr;
2362 cbD->currentComputeSrb = srb;
2363 }
2365
2366 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2368 cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
2369 cmd.args.bindShaderResources.maybeComputePs = compPsD;
2370 cmd.args.bindShaderResources.srb = srb;
2371 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
2372 cmd.args.bindShaderResources.dynamicOffsetPoolOffset = 0;
2373 if (srbD->hasDynamicOffset && dynamicOffsetCount > 0) {
2374 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
2375 cmd.args.bindShaderResources.dynamicOffsetPoolOffset = quint32(cbD->dynamicOffsetPool.size());
2376 for (int i = 0; i < dynamicOffsetCount; ++i) {
2377 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
2378 cbD->dynamicOffsetPool.append(uint(dynOfs.first));
2379 cbD->dynamicOffsetPool.append(dynOfs.second);
2380 }
2381 }
2382 }
2383}
2384
2385void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
2386 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
2387 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
2388{
2389 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2391 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2392
2393 for (int i = 0; i < bindingCount; ++i) {
2394 QRhiBuffer *buf = bindings[i].first;
2395 quint32 ofs = bindings[i].second;
2396 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
2397 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
2398
2399 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2401 cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
2402 cmd.args.bindVertexBuffer.buffer = bufD->buffer;
2403 cmd.args.bindVertexBuffer.offset = ofs;
2404 cmd.args.bindVertexBuffer.binding = startBinding + i;
2405
2409 }
2410 }
2411
2412 if (indexBuf) {
2413 QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
2414 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
2415
2416 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2418 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
2419 cmd.args.bindIndexBuffer.offset = indexOffset;
2420 cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
2421
2425 }
2426 }
2427}
2428
2429void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
2430{
2431 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2433
2434 const std::array<float, 4> r = viewport.viewport();
2435 // A negative width or height is an error. A negative x or y is not.
2436 if (r[2] < 0.0f || r[3] < 0.0f)
2437 return;
2438
2439 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2441 cmd.args.viewport.x = r[0];
2442 cmd.args.viewport.y = r[1];
2443 cmd.args.viewport.w = r[2];
2444 cmd.args.viewport.h = r[3];
2445 cmd.args.viewport.d0 = viewport.minDepth();
2446 cmd.args.viewport.d1 = viewport.maxDepth();
2447}
2448
2449void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
2450{
2451 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2453
2454 const std::array<int, 4> r = scissor.scissor();
2455 // A negative width or height is an error. A negative x or y is not.
2456 if (r[2] < 0 || r[3] < 0)
2457 return;
2458
2459 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2461 cmd.args.scissor.x = r[0];
2462 cmd.args.scissor.y = r[1];
2463 cmd.args.scissor.w = r[2];
2464 cmd.args.scissor.h = r[3];
2465}
2466
2467void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
2468{
2469 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2471
2472 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2474 cmd.args.blendConstants.r = c.redF();
2475 cmd.args.blendConstants.g = c.greenF();
2476 cmd.args.blendConstants.b = c.blueF();
2477 cmd.args.blendConstants.a = c.alphaF();
2478}
2479
2480void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
2481{
2482 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2484
2485 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2487 cmd.args.stencilRef.ref = refValue;
2488 cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
2489}
2490
2491void QRhiGles2::setPushConstants(QRhiCommandBuffer *cb, quint32 offset, quint32 size, const void *data)
2492{
2493 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2495
2496 QRhiGraphicsPipeline *maybeGraphicsPs = nullptr;
2497 QRhiComputePipeline *maybeComputePs = nullptr;
2498 quint32 blockSize = 0;
2500 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
2501 if (!psD)
2502 return;
2503 maybeComputePs = cbD->currentComputePipeline;
2504 blockSize = psD->pushConstantSize;
2505 } else {
2506 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
2507 if (!psD)
2508 return;
2509 maybeGraphicsPs = cbD->currentGraphicsPipeline;
2510 blockSize = psD->pushConstantSize;
2511 }
2512 if (!blockSize) {
2513 qWarning("No pipeline with a push constant block is active; setPushConstants ignored");
2514 return;
2515 }
2516
2517 if (offset + size > MAX_PUSH_CONSTANTS_SIZE) {
2518 qWarning("Push constant data of %u bytes at offset %u exceeds the %u byte maximum; "
2519 "setPushConstants ignored", size, offset, MAX_PUSH_CONSTANTS_SIZE);
2520 return;
2521 }
2522
2523 // A partial update has to be honored, so the block is kept on the command
2524 // buffer and the whole of it is submitted every time.
2525 const quint32 total = qMin(qMax(blockSize, offset + size), MAX_PUSH_CONSTANTS_SIZE);
2526 if (quint32(cbD->pushConstantData.size()) < total)
2527 cbD->pushConstantData.resize(int(total), 0);
2528 memcpy(cbD->pushConstantData.data() + offset, data, size);
2529
2530 const quint32 dataOffset = quint32(cbD->pushConstantPool.size());
2531 cbD->pushConstantPool.append(cbD->pushConstantData.constData(), total);
2532
2533 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2535 cmd.args.setPushConstants.maybeGraphicsPs = maybeGraphicsPs;
2536 cmd.args.setPushConstants.maybeComputePs = maybeComputePs;
2537 cmd.args.setPushConstants.dataOffset = dataOffset;
2538 cmd.args.setPushConstants.size = total;
2539}
2540
2541void QRhiGles2::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
2542{
2543 Q_UNUSED(cb);
2544 Q_UNUSED(coarsePixelSize);
2545}
2546
2547void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
2548 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2549{
2550 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2552
2553 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2555 cmd.args.draw.ps = cbD->currentGraphicsPipeline;
2556 cmd.args.draw.vertexCount = vertexCount;
2557 cmd.args.draw.firstVertex = firstVertex;
2558 cmd.args.draw.instanceCount = instanceCount;
2559 cmd.args.draw.baseInstance = firstInstance;
2560}
2561
2562void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
2563 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2564{
2565 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2567
2568 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2570 cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
2571 cmd.args.drawIndexed.indexCount = indexCount;
2572 cmd.args.drawIndexed.firstIndex = firstIndex;
2573 cmd.args.drawIndexed.instanceCount = instanceCount;
2574 cmd.args.drawIndexed.baseInstance = firstInstance;
2575 cmd.args.drawIndexed.baseVertex = vertexOffset;
2576}
2577
2578void QRhiGles2::drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
2579 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2580{
2581 if (!caps.drawIndirect)
2582 return;
2583
2584 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2586
2587 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2588
2590 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2591 trackedRegisterBuffer(&passResTracker, indirectBufD,
2594 }
2595
2596 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2598 cmd.args.drawIndirect.ps = cbD->currentGraphicsPipeline;
2599 cmd.args.drawIndirect.buffer = indirectBufD->buffer;
2600 cmd.args.drawIndirect.offset = indirectBufferOffset;
2601 cmd.args.drawIndirect.drawCount = drawCount;
2602 cmd.args.drawIndirect.stride = stride;
2603}
2604
2605void QRhiGles2::drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
2606 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2607{
2608 if (!caps.drawIndirect)
2609 return;
2610
2611 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2613
2614 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2615
2617 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2618 trackedRegisterBuffer(&passResTracker, indirectBufD,
2621 }
2622
2623 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2625 cmd.args.drawIndexedIndirect.ps = cbD->currentGraphicsPipeline;
2626 cmd.args.drawIndexedIndirect.buffer = indirectBufD->buffer;
2627 cmd.args.drawIndexedIndirect.offset = indirectBufferOffset;
2628 cmd.args.drawIndexedIndirect.drawCount = drawCount;
2629 cmd.args.drawIndexedIndirect.stride = stride;
2630}
2631
2632void QRhiGles2::drawIndirectCount(QRhiCommandBuffer *cb,
2633 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
2634 QRhiBuffer *countBuffer, quint32 countBufferOffset,
2635 quint32 maxDrawCount, quint32 stride)
2636{
2637 if (!caps.drawIndirectCount) {
2638 qWarning("drawIndirectCount called but the DrawIndirectCount feature is not supported");
2639 return;
2640 }
2641
2642 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2644
2645 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2646 QGles2Buffer *countBufD = QRHI_RES(QGles2Buffer, countBuffer);
2647
2649 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2650 trackedRegisterBuffer(&passResTracker, indirectBufD,
2653 trackedRegisterBuffer(&passResTracker, countBufD,
2656 }
2657
2658 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2660 cmd.args.drawIndirectCount.ps = cbD->currentGraphicsPipeline;
2661 cmd.args.drawIndirectCount.buffer = indirectBufD->buffer;
2662 cmd.args.drawIndirectCount.offset = indirectBufferOffset;
2663 cmd.args.drawIndirectCount.countBuffer = countBufD->buffer;
2664 cmd.args.drawIndirectCount.countOffset = countBufferOffset;
2665 cmd.args.drawIndirectCount.maxDrawCount = maxDrawCount;
2666 cmd.args.drawIndirectCount.stride = stride;
2667}
2668
2669void QRhiGles2::drawIndexedIndirectCount(QRhiCommandBuffer *cb,
2670 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
2671 QRhiBuffer *countBuffer, quint32 countBufferOffset,
2672 quint32 maxDrawCount, quint32 stride)
2673{
2674 if (!caps.drawIndirectCount) {
2675 qWarning("drawIndexedIndirectCount called but the DrawIndirectCount feature is not supported");
2676 return;
2677 }
2678
2679 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2681
2682 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
2683 QGles2Buffer *countBufD = QRHI_RES(QGles2Buffer, countBuffer);
2684
2686 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
2687 trackedRegisterBuffer(&passResTracker, indirectBufD,
2690 trackedRegisterBuffer(&passResTracker, countBufD,
2693 }
2694
2695 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2697 cmd.args.drawIndexedIndirectCount.ps = cbD->currentGraphicsPipeline;
2698 cmd.args.drawIndexedIndirectCount.buffer = indirectBufD->buffer;
2699 cmd.args.drawIndexedIndirectCount.offset = indirectBufferOffset;
2700 cmd.args.drawIndexedIndirectCount.countBuffer = countBufD->buffer;
2701 cmd.args.drawIndexedIndirectCount.countOffset = countBufferOffset;
2702 cmd.args.drawIndexedIndirectCount.maxDrawCount = maxDrawCount;
2703 cmd.args.drawIndexedIndirectCount.stride = stride;
2704}
2705
2706void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
2707{
2708 if (!debugMarkers)
2709 return;
2710
2711 Q_UNUSED(cb);
2712 Q_UNUSED(name);
2713}
2714
2715void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
2716{
2717 if (!debugMarkers)
2718 return;
2719
2720 Q_UNUSED(cb);
2721}
2722
2723void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
2724{
2725 if (!debugMarkers)
2726 return;
2727
2728 Q_UNUSED(cb);
2729 Q_UNUSED(msg);
2730}
2731
2732const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
2733{
2734 Q_UNUSED(cb);
2735 return nullptr;
2736}
2737
2738static inline void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type, GLuint tsQuery = 0)
2739{
2740 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2741 cmd.cmd = type;
2743 cmd.args.beginFrame.timestampQuery = tsQuery;
2745 cmd.args.endFrame.timestampQuery = tsQuery;
2746}
2747
2748void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
2749{
2750 if (ofr.active) {
2751 Q_ASSERT(!currentSwapChain);
2752 if (!ensureContext())
2753 return;
2754 } else {
2755 Q_ASSERT(currentSwapChain);
2756 if (!ensureContext(currentSwapChain->surface))
2757 return;
2758 }
2759
2760 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2761
2763 && !cbD->computePassState.writtenResources.isEmpty())
2764 {
2765 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2767 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
2768 }
2769
2771
2773
2774 // ARRAY_BUFFER and SHADER_STORAGE_BUFFER are context state, not vertex
2775 // array object state, so going back to object 0 does not clear them.
2776 if (vao)
2777 f->glBindVertexArray(0);
2778 f->glBindBuffer(GL_ARRAY_BUFFER, 0);
2779 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2780 if (caps.compute)
2781 f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
2782}
2783
2784void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
2785{
2786 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2787 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
2788
2790
2792 // Commands that come after this point need a resource tracker and also
2793 // a BarriersForPass command enqueued. (the ones we had from
2794 // beginPass() are now gone since beginExternal() processed all that
2795 // due to calling executeCommandBuffer()).
2797 }
2798
2800
2801 if (cbD->currentTarget)
2802 enqueueBindFramebuffer(cbD->currentTarget, cbD);
2803}
2804
2805double QRhiGles2::lastCompletedGpuTime(QRhiCommandBuffer *cb)
2806{
2807 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
2808 return cbD->lastGpuTime;
2809}
2810
2812{
2813 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2814 if (!ensureContext(swapChainD->surface))
2815 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2816
2817 ctx->handle()->beginFrame();
2818
2819 currentSwapChain = swapChainD;
2820
2822 swapChainD->cb.resetState();
2823 frameNo += 1;
2824
2825 if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
2826 double elapsedSec = 0;
2827 if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, this, &elapsedSec))
2828 swapChainD->cb.lastGpuTime = elapsedSec;
2829 }
2830
2831 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2832 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2833 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2834
2835 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame, recordTimestamps ? tsStart : 0);
2836
2837 return QRhi::FrameOpSuccess;
2838}
2839
2840QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
2841{
2842 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2843 Q_ASSERT(currentSwapChain == swapChainD);
2844
2845 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2846 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2847 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2848 if (recordTimestamps) {
2849 swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
2851 }
2852
2853 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame, recordTimestamps ? tsEnd : 0);
2854
2855 if (!ensureContext(swapChainD->surface))
2856 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2857
2858 executeCommandBuffer(&swapChainD->cb);
2859
2860 if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
2861 ctx->swapBuffers(swapChainD->surface);
2863 } else {
2864 f->glFlush();
2865 }
2866
2867 currentSwapChain = nullptr;
2868
2869 ctx->handle()->endFrame();
2870
2871 return QRhi::FrameOpSuccess;
2872}
2873
2875{
2876 if (!ensureContext())
2877 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2878
2879 ofr.active = true;
2880
2882 ofr.cbWrapper.resetState();
2883
2884 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && caps.timestamps) {
2885 if (!ofr.tsQueries[0])
2886 f->glGenQueries(2, ofr.tsQueries);
2887 }
2888
2889 addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame, ofr.tsQueries[0]);
2890 *cb = &ofr.cbWrapper;
2891
2892 return QRhi::FrameOpSuccess;
2893}
2894
2895QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
2896{
2897 Q_UNUSED(flags);
2898 Q_ASSERT(ofr.active);
2899 ofr.active = false;
2900
2901 addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame, ofr.tsQueries[1]);
2902
2903 if (!ensureContext())
2904 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2905
2906 executeCommandBuffer(&ofr.cbWrapper);
2907
2908 // Just as endFrame() does a flush when skipping the swapBuffers(), do it
2909 // here as well. This has the added benefit of playing nice when rendering
2910 // to a texture from a context and then consuming that texture from
2911 // another, sharing context.
2912 f->glFlush();
2913
2914 if (ofr.tsQueries[0]) {
2915 quint64 timestamps[2];
2916 glGetQueryObjectui64v(ofr.tsQueries[1], GL_QUERY_RESULT, &timestamps[1]);
2917 glGetQueryObjectui64v(ofr.tsQueries[0], GL_QUERY_RESULT, &timestamps[0]);
2918 if (timestamps[1] >= timestamps[0]) {
2919 const quint64 nanoseconds = timestamps[1] - timestamps[0];
2920 ofr.cbWrapper.lastGpuTime = nanoseconds / 1000000000.0; // seconds
2921 }
2922 }
2923
2924 return QRhi::FrameOpSuccess;
2925}
2926
2928{
2929 if (inFrame) {
2930 if (ofr.active) {
2931 Q_ASSERT(!currentSwapChain);
2932 Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
2933 if (!ensureContext())
2934 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2935 executeCommandBuffer(&ofr.cbWrapper);
2936 ofr.cbWrapper.resetCommands();
2937 } else {
2938 Q_ASSERT(currentSwapChain);
2939 Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
2940 if (!ensureContext(currentSwapChain->surface))
2941 return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
2943 currentSwapChain->cb.resetCommands();
2944 }
2945 // Do an actual glFinish(). May seem superfluous, but this is what
2946 // matches most other backends e.g. Vulkan/Metal that do a heavyweight
2947 // wait-for-idle blocking in their finish(). More importantly, this
2948 // allows clients simply call finish() in threaded or shared context
2949 // situations where one explicitly needs to do a glFlush or Finish.
2950 f->glFinish();
2951 }
2952 return QRhi::FrameOpSuccess;
2953}
2954
2956{
2957 return access == QGles2Buffer::AccessStorageWrite
2959 || access == QGles2Buffer::AccessUpdate;
2960}
2961
2963{
2964 return access == QGles2Texture::AccessStorageWrite
2966 || access == QGles2Texture::AccessUpdate
2968}
2969
2979
2988
2990{
2991 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2992 if (!bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer))
2993 return;
2994
2995 const QGles2Buffer::Access prevAccess = bufD->usageState.access;
2996 if (access == prevAccess)
2997 return;
2998
2999 if (bufferAccessIsWrite(prevAccess)) {
3000 // Generating the minimal barrier set is way too complicated to do
3001 // correctly (prevAccess is overwritten so we won't have proper
3002 // tracking across multiple passes) so setting all barrier bits will do
3003 // for now.
3004 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3006 cmd.args.barrier.barriers = barriersForBuffer();
3007 }
3008
3009 bufD->usageState.access = access;
3010}
3011
3013{
3014 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
3015 if (!texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
3016 return;
3017
3018 const QGles2Texture::Access prevAccess = texD->usageState.access;
3019 if (access == prevAccess)
3020 return;
3021
3022 if (textureAccessIsWrite(prevAccess)) {
3023 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3025 cmd.args.barrier.barriers = barriersForTexture();
3026 }
3027
3028 texD->usageState.access = access;
3029}
3030
3032 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
3033{
3035 const bool isCompressed = isCompressedFormat(texD->m_format);
3036 const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
3037 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3038 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
3039 const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
3040 const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
3041 const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
3042 const QPoint dp = subresDesc.destinationTopLeft();
3043 const QByteArray rawData = subresDesc.data();
3044
3045 auto setCmdByNotCompressedData = [&](const void* data, QSize size, quint32 dataStride)
3046 {
3047 quint32 bytesPerLine = 0;
3048 quint32 bytesPerPixel = 0;
3049 textureFormatInfo(texD->m_format, size, &bytesPerLine, nullptr, &bytesPerPixel);
3050
3051 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3053 cmd.args.subImage.target = texD->target;
3054 cmd.args.subImage.texture = texD->texture;
3055 cmd.args.subImage.faceTarget = effectiveTarget;
3056 cmd.args.subImage.level = level;
3057 cmd.args.subImage.dx = dp.x();
3058 cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
3059 cmd.args.subImage.dz = is3D || isArray ? layer : 0;
3060 cmd.args.subImage.w = size.width();
3061 cmd.args.subImage.h = size.height();
3062 cmd.args.subImage.glformat = texD->glformat;
3063 cmd.args.subImage.gltype = texD->gltype;
3064
3065 if (dataStride == 0)
3066 dataStride = bytesPerLine;
3067
3068 cmd.args.subImage.rowStartAlign = (dataStride & 3) ? 1 : 4;
3069 cmd.args.subImage.rowLength = caps.unpackRowLength ? (bytesPerPixel ? dataStride / bytesPerPixel : 0) : 0;
3070
3071 cmd.args.subImage.data = data;
3072 };
3073
3074 if (!subresDesc.image().isNull()) {
3075 QImage img = subresDesc.image();
3076 QSize size = img.size();
3077 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3078 const QPoint sp = subresDesc.sourceTopLeft();
3079 if (!subresDesc.sourceSize().isEmpty())
3080 size = subresDesc.sourceSize();
3081 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3082 if (caps.unpackRowLength) {
3083 cbD->retainImage(img);
3084 // create a non-owning wrapper for the subimage
3085 const uchar *data = img.constBits() + sp.y() * img.bytesPerLine() + sp.x() * (qMax(1, img.depth() / 8));
3086 img = QImage(data, size.width(), size.height(), img.bytesPerLine(), img.format());
3087 } else {
3088 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
3089 }
3090 } else {
3091 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3092 }
3093
3094 setCmdByNotCompressedData(cbD->retainImage(img), size, img.bytesPerLine());
3095 } else if (!rawData.isEmpty() && isCompressed) {
3096 const int depth = qMax(1, texD->m_depth);
3097 const int arraySize = qMax(0, texD->m_arraySize);
3098 // When create() could not allocate the storage up front, each mip
3099 // level is allocated upon the first upload touching it. For cubemaps
3100 // every face is a separate image, hence tracking per face as well.
3101 const int face = isCubeMap ? qBound(0, layer, 5) : 0;
3102 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3103 QSize size = subresDesc.sourceSize().isEmpty() ? subresSize : subresDesc.sourceSize();
3104 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3105 if (size.isEmpty())
3106 return;
3107
3108 // A single subresource upload for a 3D or array texture covers one
3109 // slice or layer only, so it can never allocate the mip level on its
3110 // own. Neither can an upload that does not cover the entire level.
3111 const bool coversWholeSubres = !is3D && !isArray && dp.isNull() && size == subresSize;
3112
3113 if (!texD->isLevelAllocated(face, level) && !coversWholeSubres) {
3114 // glCompressedTexImage* cannot take nullptr data, so allocate the
3115 // level with a zero-filled buffer. Any data is accepted here, but
3116 // what the areas not overwritten later decode to depends on the
3117 // format (e.g. an all-zero ASTC block is an illegal encoding, so
3118 // it gives the error color). The real data follows below via
3119 // glCompressedTexSubImage*.
3120 const int mipDepth = is3D ? q->sizeForMipLevel(level, QSize(depth, depth)).width() : 1;
3121 quint32 levelByteSize = 0;
3122 if (!compressedFormatInfo(texD->m_format, subresSize, nullptr, &levelByteSize, nullptr))
3123 return;
3124 quint64 byteSize = levelByteSize;
3125 if (is3D)
3126 byteSize *= quint64(mipDepth);
3127 if (isArray)
3128 byteSize *= quint64(arraySize);
3129 if (byteSize > quint64(std::numeric_limits<int>::max())) {
3130 qWarning("Compressed texture zero-initialization would need %llu bytes "
3131 "which is too large; skipping", byteSize);
3132 return;
3133 }
3134 QByteArray zeroBuf(qsizetype(byteSize), 0);
3135 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3137 cmd.args.compressedImage.target = texD->target;
3138 cmd.args.compressedImage.texture = texD->texture;
3139 cmd.args.compressedImage.faceTarget = effectiveTarget;
3140 cmd.args.compressedImage.level = level;
3141 cmd.args.compressedImage.glintformat = texD->glintformat;
3142 cmd.args.compressedImage.w = subresSize.width();
3143 cmd.args.compressedImage.h = is1D && isArray ? arraySize : subresSize.height();
3144 cmd.args.compressedImage.depth = is3D ? mipDepth : (isArray ? arraySize : 0);
3145 cmd.args.compressedImage.size = int(byteSize);
3146 cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
3147 texD->allocatedLevels[face] |= 1u << uint(level);
3148 }
3149
3150 if (texD->isLevelAllocated(face, level)) {
3151 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3153 cmd.args.compressedSubImage.target = texD->target;
3154 cmd.args.compressedSubImage.texture = texD->texture;
3155 cmd.args.compressedSubImage.faceTarget = effectiveTarget;
3156 cmd.args.compressedSubImage.level = level;
3157 cmd.args.compressedSubImage.dx = dp.x();
3158 cmd.args.compressedSubImage.dy = is1D && isArray ? layer : dp.y();
3159 cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
3160 cmd.args.compressedSubImage.w = size.width();
3161 cmd.args.compressedSubImage.h = size.height();
3162 cmd.args.compressedSubImage.glintformat = texD->glintformat;
3163 cmd.args.compressedSubImage.size = rawData.size();
3164 cmd.args.compressedSubImage.data = cbD->retainData(rawData);
3165 } else {
3166 // Allocate the level and upload the real data in one go.
3167 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3169 cmd.args.compressedImage.target = texD->target;
3170 cmd.args.compressedImage.texture = texD->texture;
3171 cmd.args.compressedImage.faceTarget = effectiveTarget;
3172 cmd.args.compressedImage.level = level;
3173 cmd.args.compressedImage.glintformat = texD->glintformat;
3174 cmd.args.compressedImage.w = size.width();
3175 cmd.args.compressedImage.h = size.height();
3176 cmd.args.compressedImage.depth = 0;
3177 cmd.args.compressedImage.size = rawData.size();
3178 cmd.args.compressedImage.data = cbD->retainData(rawData);
3179 texD->allocatedLevels[face] |= 1u << uint(level);
3180 }
3181 } else if (!rawData.isEmpty()) {
3182 QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
3183 : subresDesc.sourceSize();
3184 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3185 quint32 bytesPerPixel = 0;
3186 textureFormatInfo(texD->m_format, size, nullptr, nullptr, &bytesPerPixel);
3187 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3188 bytesPerPixel, rawData.size());
3189 if (size.isEmpty())
3190 return;
3191
3192 setCmdByNotCompressedData(cbD->retainData(rawData), size, subresDesc.dataStride());
3193 } else {
3194 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3195 }
3196}
3197
3198void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
3199{
3200 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
3202
3203 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
3204 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
3206 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3207 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3208 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
3209 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
3210 } else {
3212 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3214 cmd.args.bufferSubData.target = bufD->targetForDataOps;
3215 cmd.args.bufferSubData.buffer = bufD->buffer;
3216 cmd.args.bufferSubData.offset = u.offset;
3217 cmd.args.bufferSubData.size = u.data.size();
3218 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
3219 }
3221 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3222 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3223 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
3224 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
3225 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
3226 } else {
3228 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3230 cmd.args.bufferSubData.target = bufD->targetForDataOps;
3231 cmd.args.bufferSubData.buffer = bufD->buffer;
3232 cmd.args.bufferSubData.offset = u.offset;
3233 cmd.args.bufferSubData.size = u.data.size();
3234 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
3235 }
3237 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3238 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
3239 u.result->data.resize(u.readSize);
3240 memcpy(u.result->data.data(), bufD->data.constData() + u.offset, size_t(u.readSize));
3241 if (u.result->completed)
3242 u.result->completed();
3243 } else {
3244 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3246 cmd.args.getBufferSubData.result = u.result;
3247 cmd.args.getBufferSubData.target = bufD->targetForDataOps;
3248 cmd.args.getBufferSubData.buffer = bufD->buffer;
3249 cmd.args.getBufferSubData.offset = u.offset;
3250 cmd.args.getBufferSubData.size = u.readSize;
3251 }
3253 if (!caps.copyBuffer)
3254 continue;
3255
3256 QGles2Buffer *dstD = QRHI_RES(QGles2Buffer, u.buf);
3257 QGles2Buffer *srcD = QRHI_RES(QGles2Buffer, u.src);
3258 Q_ASSERT(dstD->buffer && srcD->buffer);
3259
3262
3263 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3265 cmd.args.copyBuf.srcBuffer = srcD->buffer;
3266 cmd.args.copyBuf.dstBuffer = dstD->buffer;
3267 cmd.args.copyBuf.srcOffset = u.srcOffset;
3268 cmd.args.copyBuf.dstOffset = u.offset;
3269 cmd.args.copyBuf.size = u.readSize;
3271 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
3272 Q_ASSERT(bufD->buffer);
3273
3275
3276 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3277 if (caps.clearBuffer) {
3279 cmd.args.clearBufferSubData.target = bufD->targetForDataOps;
3280 cmd.args.clearBufferSubData.buffer = bufD->buffer;
3281 cmd.args.clearBufferSubData.offset = u.offset;
3282 cmd.args.clearBufferSubData.size = u.readSize;
3283 cmd.args.clearBufferSubData.value = u.fillValue;
3284 } else {
3285 QRhiBufferData fillData;
3286 fillData.assign(QByteArray(u.readSize, char(u.fillValue)));
3288 cmd.args.bufferSubData.target = bufD->targetForDataOps;
3289 cmd.args.bufferSubData.buffer = bufD->buffer;
3290 cmd.args.bufferSubData.offset = u.offset;
3291 cmd.args.bufferSubData.size = u.readSize;
3292 cmd.args.bufferSubData.data = cbD->retainBufferData(fillData);
3293 }
3294 }
3295 }
3296
3297 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
3298 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
3300 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
3301 for (const auto &subres : u.subresDesc)
3302 enqueueSubresUpload(texD, cbD, subres.layer, subres.level, subres.desc);
3304 Q_ASSERT(u.src && u.dst);
3305 QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.src);
3306 QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.dst);
3307
3310
3311 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3312 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3313 // do not translate coordinates, even if sp is bottom-left from gl's pov
3314 const QPoint sp = u.desc.sourceTopLeft();
3315 const QPoint dp = u.desc.destinationTopLeft();
3316
3317 const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
3318 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
3319 const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
3320 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
3321
3322 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3324
3325 const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
3326 const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
3327 const bool dstIs1dArray = dstD->m_flags.testFlag(QRhiTexture::OneDimensional)
3328 && dstD->m_flags.testFlag(QRhiTexture::TextureArray);
3329
3330 cmd.args.copyTex.srcTarget = srcD->target;
3331 cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
3332 cmd.args.copyTex.srcTexture = srcD->texture;
3333 cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
3334 cmd.args.copyTex.srcX = sp.x();
3335 cmd.args.copyTex.srcY = sp.y();
3336 cmd.args.copyTex.srcZ = srcHasZ ? u.desc.sourceLayer() : 0;
3337
3338 cmd.args.copyTex.dstTarget = dstD->target;
3339 cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + (dstHasZ ? 0u : uint(u.desc.destinationLayer()));
3340 cmd.args.copyTex.dstTexture = dstD->texture;
3341 cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
3342 cmd.args.copyTex.dstX = dp.x();
3343 cmd.args.copyTex.dstY = dstIs1dArray ? u.desc.destinationLayer() : dp.y();
3344 cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
3345
3346 cmd.args.copyTex.w = copySize.width();
3347 cmd.args.copyTex.h = copySize.height();
3349 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3351 cmd.args.readPixels.result = u.result;
3352 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.rb.texture());
3353 if (texD)
3355 cmd.args.readPixels.texture = texD ? texD->texture : 0;
3356 cmd.args.readPixels.slice3D = -1;
3357 if (texD) {
3358 if (u.rb.rect().isValid()) {
3359 cmd.args.readPixels.x = u.rb.rect().x();
3360 cmd.args.readPixels.y = u.rb.rect().y();
3361 cmd.args.readPixels.w = u.rb.rect().width();
3362 cmd.args.readPixels.h = u.rb.rect().height();
3363 }
3364 else {
3365 const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
3366 cmd.args.readPixels.x = 0;
3367 cmd.args.readPixels.y = 0;
3368 cmd.args.readPixels.w = readImageSize.width();
3369 cmd.args.readPixels.h = readImageSize.height();
3370 }
3371 cmd.args.readPixels.format = texD->m_format;
3372 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
3373 || texD->m_flags.testFlag(QRhiTexture::TextureArray))
3374 {
3375 cmd.args.readPixels.readTarget = texD->target;
3376 cmd.args.readPixels.slice3D = u.rb.layer();
3377 } else {
3378 const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
3379 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
3380 cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer());
3381 }
3382 cmd.args.readPixels.level = u.rb.level();
3383 }
3384 else { // swapchain
3385 if (u.rb.rect().isValid()) {
3386 cmd.args.readPixels.x = u.rb.rect().x();
3387 cmd.args.readPixels.y = u.rb.rect().y();
3388 cmd.args.readPixels.w = u.rb.rect().width();
3389 cmd.args.readPixels.h = u.rb.rect().height();
3390 }
3391 else {
3392 cmd.args.readPixels.x = 0;
3393 cmd.args.readPixels.y = 0;
3394 cmd.args.readPixels.w = currentSwapChain->pixelSize.width();
3395 cmd.args.readPixels.h = currentSwapChain->pixelSize.height();
3396 }
3397 }
3399 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
3401 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
3403 cmd.args.genMip.target = texD->target;
3404 cmd.args.genMip.texture = texD->texture;
3405 }
3406 }
3407
3408 ud->free();
3409}
3410
3411static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
3412{
3413 switch (t) {
3414 case QRhiGraphicsPipeline::Triangles:
3415 return GL_TRIANGLES;
3416 case QRhiGraphicsPipeline::TriangleStrip:
3417 return GL_TRIANGLE_STRIP;
3418 case QRhiGraphicsPipeline::TriangleFan:
3419 return GL_TRIANGLE_FAN;
3420 case QRhiGraphicsPipeline::Lines:
3421 return GL_LINES;
3422 case QRhiGraphicsPipeline::LineStrip:
3423 return GL_LINE_STRIP;
3424 case QRhiGraphicsPipeline::Points:
3425 return GL_POINTS;
3426 case QRhiGraphicsPipeline::Patches:
3427 return GL_PATCHES;
3428 default:
3429 Q_UNREACHABLE_RETURN(GL_TRIANGLES);
3430 }
3431}
3432
3433static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
3434{
3435 switch (c) {
3436 case QRhiGraphicsPipeline::Front:
3437 return GL_FRONT;
3438 case QRhiGraphicsPipeline::Back:
3439 return GL_BACK;
3440 default:
3441 Q_UNREACHABLE_RETURN(GL_BACK);
3442 }
3443}
3444
3445static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
3446{
3447 switch (f) {
3448 case QRhiGraphicsPipeline::CCW:
3449 return GL_CCW;
3450 case QRhiGraphicsPipeline::CW:
3451 return GL_CW;
3452 default:
3453 Q_UNREACHABLE_RETURN(GL_CCW);
3454 }
3455}
3456
3457static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
3458{
3459 switch (f) {
3460 case QRhiGraphicsPipeline::Zero:
3461 return GL_ZERO;
3462 case QRhiGraphicsPipeline::One:
3463 return GL_ONE;
3464 case QRhiGraphicsPipeline::SrcColor:
3465 return GL_SRC_COLOR;
3466 case QRhiGraphicsPipeline::OneMinusSrcColor:
3467 return GL_ONE_MINUS_SRC_COLOR;
3468 case QRhiGraphicsPipeline::DstColor:
3469 return GL_DST_COLOR;
3470 case QRhiGraphicsPipeline::OneMinusDstColor:
3471 return GL_ONE_MINUS_DST_COLOR;
3472 case QRhiGraphicsPipeline::SrcAlpha:
3473 return GL_SRC_ALPHA;
3474 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
3475 return GL_ONE_MINUS_SRC_ALPHA;
3476 case QRhiGraphicsPipeline::DstAlpha:
3477 return GL_DST_ALPHA;
3478 case QRhiGraphicsPipeline::OneMinusDstAlpha:
3479 return GL_ONE_MINUS_DST_ALPHA;
3480 case QRhiGraphicsPipeline::ConstantColor:
3481 return GL_CONSTANT_COLOR;
3482 case QRhiGraphicsPipeline::OneMinusConstantColor:
3484 case QRhiGraphicsPipeline::ConstantAlpha:
3485 return GL_CONSTANT_ALPHA;
3486 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
3488 case QRhiGraphicsPipeline::SrcAlphaSaturate:
3489 return GL_SRC_ALPHA_SATURATE;
3490 case QRhiGraphicsPipeline::Src1Color:
3491 case QRhiGraphicsPipeline::OneMinusSrc1Color:
3492 case QRhiGraphicsPipeline::Src1Alpha:
3493 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
3494 qWarning("Unsupported blend factor %d", f);
3495 return GL_ZERO;
3496 default:
3497 Q_UNREACHABLE_RETURN(GL_ZERO);
3498 }
3499}
3500
3501static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
3502{
3503 switch (op) {
3504 case QRhiGraphicsPipeline::Add:
3505 return GL_FUNC_ADD;
3506 case QRhiGraphicsPipeline::Subtract:
3507 return GL_FUNC_SUBTRACT;
3508 case QRhiGraphicsPipeline::ReverseSubtract:
3510 case QRhiGraphicsPipeline::Min:
3511 return GL_MIN;
3512 case QRhiGraphicsPipeline::Max:
3513 return GL_MAX;
3514 default:
3515 Q_UNREACHABLE_RETURN(GL_FUNC_ADD);
3516 }
3517}
3518
3519static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
3520{
3521 switch (op) {
3522 case QRhiGraphicsPipeline::Never:
3523 return GL_NEVER;
3524 case QRhiGraphicsPipeline::Less:
3525 return GL_LESS;
3526 case QRhiGraphicsPipeline::Equal:
3527 return GL_EQUAL;
3528 case QRhiGraphicsPipeline::LessOrEqual:
3529 return GL_LEQUAL;
3530 case QRhiGraphicsPipeline::Greater:
3531 return GL_GREATER;
3532 case QRhiGraphicsPipeline::NotEqual:
3533 return GL_NOTEQUAL;
3534 case QRhiGraphicsPipeline::GreaterOrEqual:
3535 return GL_GEQUAL;
3536 case QRhiGraphicsPipeline::Always:
3537 return GL_ALWAYS;
3538 default:
3539 Q_UNREACHABLE_RETURN(GL_ALWAYS);
3540 }
3541}
3542
3543static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
3544{
3545 switch (op) {
3546 case QRhiGraphicsPipeline::StencilZero:
3547 return GL_ZERO;
3548 case QRhiGraphicsPipeline::Keep:
3549 return GL_KEEP;
3550 case QRhiGraphicsPipeline::Replace:
3551 return GL_REPLACE;
3552 case QRhiGraphicsPipeline::IncrementAndClamp:
3553 return GL_INCR;
3554 case QRhiGraphicsPipeline::DecrementAndClamp:
3555 return GL_DECR;
3556 case QRhiGraphicsPipeline::Invert:
3557 return GL_INVERT;
3558 case QRhiGraphicsPipeline::IncrementAndWrap:
3559 return GL_INCR_WRAP;
3560 case QRhiGraphicsPipeline::DecrementAndWrap:
3561 return GL_DECR_WRAP;
3562 default:
3563 Q_UNREACHABLE_RETURN(GL_KEEP);
3564 }
3565}
3566
3567static inline GLenum toGlPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
3568{
3569 switch (mode) {
3570 case QRhiGraphicsPipeline::PolygonMode::Fill:
3571 return GL_FILL;
3572 case QRhiGraphicsPipeline::PolygonMode::Line:
3573 return GL_LINE;
3574 default:
3575 Q_UNREACHABLE_RETURN(GL_FILL);
3576 }
3577}
3578
3579static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
3580{
3581 switch (f) {
3582 case QRhiSampler::Nearest:
3583 if (m == QRhiSampler::None)
3584 return GL_NEAREST;
3585 else
3586 return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
3587 case QRhiSampler::Linear:
3588 if (m == QRhiSampler::None)
3589 return GL_LINEAR;
3590 else
3591 return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
3592 default:
3593 Q_UNREACHABLE_RETURN(GL_LINEAR);
3594 }
3595}
3596
3597static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
3598{
3599 switch (f) {
3600 case QRhiSampler::Nearest:
3601 return GL_NEAREST;
3602 case QRhiSampler::Linear:
3603 return GL_LINEAR;
3604 default:
3605 Q_UNREACHABLE_RETURN(GL_LINEAR);
3606 }
3607}
3608
3609static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
3610{
3611 switch (m) {
3612 case QRhiSampler::Repeat:
3613 return GL_REPEAT;
3614 case QRhiSampler::ClampToEdge:
3615 return GL_CLAMP_TO_EDGE;
3616 case QRhiSampler::Mirror:
3617 return GL_MIRRORED_REPEAT;
3618 default:
3619 Q_UNREACHABLE_RETURN(GL_CLAMP_TO_EDGE);
3620 }
3621}
3622
3623static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
3624{
3625 switch (op) {
3626 case QRhiSampler::Never:
3627 return GL_NEVER;
3628 case QRhiSampler::Less:
3629 return GL_LESS;
3630 case QRhiSampler::Equal:
3631 return GL_EQUAL;
3632 case QRhiSampler::LessOrEqual:
3633 return GL_LEQUAL;
3634 case QRhiSampler::Greater:
3635 return GL_GREATER;
3636 case QRhiSampler::NotEqual:
3637 return GL_NOTEQUAL;
3638 case QRhiSampler::GreaterOrEqual:
3639 return GL_GEQUAL;
3640 case QRhiSampler::Always:
3641 return GL_ALWAYS;
3642 default:
3643 Q_UNREACHABLE_RETURN(GL_NEVER);
3644 }
3645}
3646
3670
3672{
3674 u.layout = 0; // N/A
3675 u.access = bufUsage.access;
3676 u.stage = 0; // N/A
3677 return u;
3678}
3679
3702
3704{
3706 u.layout = 0; // N/A
3707 u.access = texUsage.access;
3708 u.stage = 0; // N/A
3709 return u;
3710}
3711
3713 QGles2Buffer *bufD,
3716{
3718 passResTracker->registerBuffer(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
3719 u.access = toGlAccess(access);
3720}
3721
3723 QGles2Texture *texD,
3726{
3728 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
3729 u.access = toGlAccess(access);
3730}
3731
3733{
3734 GLenum indexType = GL_UNSIGNED_SHORT;
3739 struct {
3740 QRhiGraphicsPipeline *ps = nullptr;
3741 GLuint buffer = 0;
3742 quint32 offset = 0;
3743 int binding = 0;
3744 } lastBindVertexBuffer;
3745 static const int TRACKED_ATTRIB_COUNT = 16;
3750};
3751
3752// Helper that must be used in executeCommandBuffer() whenever changing the
3753// ARRAY or ELEMENT_ARRAY buffer binding outside of Command::BindVertexBuffer
3754// and Command::BindIndexBuffer.
3756 QOpenGLExtensions *f,
3757 GLenum target,
3758 GLuint buffer)
3759{
3760 state->currentArrayBuffer = 0;
3761 state->currentElementArrayBuffer = 0;
3762 state->lastBindVertexBuffer.buffer = 0;
3763 f->glBindBuffer(target, buffer);
3764}
3765
3766void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
3767{
3769 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
3770
3771 for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
3772 const QGles2CommandBuffer::Command &cmd(*it);
3773 switch (cmd.cmd) {
3775 if (cmd.args.beginFrame.timestampQuery)
3776 glQueryCounter(cmd.args.beginFrame.timestampQuery, GL_TIMESTAMP);
3777 if (caps.vertexArrayObject) {
3778 if (!vao)
3779 f->glGenVertexArrays(1, &vao);
3780 f->glBindVertexArray(vao);
3781 }
3782 break;
3784 if (state.instancedAttributesUsed) {
3785 for (int i = 0; i < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; ++i) {
3786 if (state.nonzeroAttribDivisor[i])
3787 f->glVertexAttribDivisor(GLuint(i), 0);
3788 }
3790 f->glVertexAttribDivisor(GLuint(i), 0);
3791 state.instancedAttributesUsed = false;
3792 }
3793 // The enables are vertex array object state, so without this they
3794 // would persist across frames, pointing into deleted buffers.
3795 for (int i = 0; i < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; ++i) {
3796 if (state.enabledAttribArrays[i]) {
3797 f->glDisableVertexAttribArray(GLuint(i));
3798 state.enabledAttribArrays[i] = false;
3799 }
3800 }
3801 if (vao)
3802 f->glBindVertexArray(0);
3803 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3804 if (cmd.args.endFrame.timestampQuery)
3805 glQueryCounter(cmd.args.endFrame.timestampQuery, GL_TIMESTAMP);
3806 break;
3808 if (vao)
3809 f->glBindVertexArray(vao);
3810 break;
3812 f->glViewport(GLint(cmd.args.viewport.x), GLint(cmd.args.viewport.y), GLsizei(cmd.args.viewport.w), GLsizei(cmd.args.viewport.h));
3813 f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
3814 break;
3816 f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
3817 break;
3819 f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
3820 break;
3822 {
3823 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps);
3824 if (psD) {
3825 const GLint ref = GLint(cmd.args.stencilRef.ref);
3826 f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), ref, psD->m_stencilReadMask);
3827 f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), ref, psD->m_stencilReadMask);
3828 cbD->graphicsPassState.dynamic.stencilRef = ref;
3829 cbD->graphicsPassState.stencilRef = ref;
3830 } else {
3831 qWarning("No graphics pipeline active for setStencilRef; ignored");
3832 }
3833 }
3834 break;
3836 {
3837 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
3838 if (psD) {
3839 if (state.lastBindVertexBuffer.ps == psD
3840 && state.lastBindVertexBuffer.buffer == cmd.args.bindVertexBuffer.buffer
3841 && state.lastBindVertexBuffer.offset == cmd.args.bindVertexBuffer.offset
3842 && state.lastBindVertexBuffer.binding == cmd.args.bindVertexBuffer.binding)
3843 {
3844 // The pipeline and so the vertex input layout is
3845 // immutable, no point in issuing the exact same set of
3846 // glVertexAttribPointer again and again for the same buffer.
3847 break;
3848 }
3849 state.lastBindVertexBuffer.ps = psD;
3850 state.lastBindVertexBuffer.buffer = cmd.args.bindVertexBuffer.buffer;
3851 state.lastBindVertexBuffer.offset = cmd.args.bindVertexBuffer.offset;
3852 state.lastBindVertexBuffer.binding = cmd.args.bindVertexBuffer.binding;
3853
3854 if (cmd.args.bindVertexBuffer.buffer != state.currentArrayBuffer) {
3855 state.currentArrayBuffer = cmd.args.bindVertexBuffer.buffer;
3856 // we do not support more than one vertex buffer
3857 f->glBindBuffer(GL_ARRAY_BUFFER, state.currentArrayBuffer);
3858 }
3859 for (auto it = psD->m_vertexInputLayout.cbeginAttributes(), itEnd = psD->m_vertexInputLayout.cendAttributes();
3860 it != itEnd; ++it)
3861 {
3862 const int bindingIdx = it->binding();
3863 if (bindingIdx != cmd.args.bindVertexBuffer.binding)
3864 continue;
3865
3866 const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(bindingIdx);
3867 const int stride = int(inputBinding->stride());
3868 int size = 1;
3869 GLenum type = GL_FLOAT;
3870 bool normalize = false;
3871 switch (it->format()) {
3872 case QRhiVertexInputAttribute::Float4:
3873 type = GL_FLOAT;
3874 size = 4;
3875 break;
3876 case QRhiVertexInputAttribute::Float3:
3877 type = GL_FLOAT;
3878 size = 3;
3879 break;
3880 case QRhiVertexInputAttribute::Float2:
3881 type = GL_FLOAT;
3882 size = 2;
3883 break;
3884 case QRhiVertexInputAttribute::Float:
3885 type = GL_FLOAT;
3886 size = 1;
3887 break;
3888 case QRhiVertexInputAttribute::UNormByte4:
3889 type = GL_UNSIGNED_BYTE;
3890 normalize = true;
3891 size = 4;
3892 break;
3893 case QRhiVertexInputAttribute::UNormByte2:
3894 type = GL_UNSIGNED_BYTE;
3895 normalize = true;
3896 size = 2;
3897 break;
3898 case QRhiVertexInputAttribute::UNormByte:
3899 type = GL_UNSIGNED_BYTE;
3900 normalize = true;
3901 size = 1;
3902 break;
3903 case QRhiVertexInputAttribute::UInt4:
3904 type = GL_UNSIGNED_INT;
3905 size = 4;
3906 break;
3907 case QRhiVertexInputAttribute::UInt3:
3908 type = GL_UNSIGNED_INT;
3909 size = 3;
3910 break;
3911 case QRhiVertexInputAttribute::UInt2:
3912 type = GL_UNSIGNED_INT;
3913 size = 2;
3914 break;
3915 case QRhiVertexInputAttribute::UInt:
3916 type = GL_UNSIGNED_INT;
3917 size = 1;
3918 break;
3919 case QRhiVertexInputAttribute::SInt4:
3920 type = GL_INT;
3921 size = 4;
3922 break;
3923 case QRhiVertexInputAttribute::SInt3:
3924 type = GL_INT;
3925 size = 3;
3926 break;
3927 case QRhiVertexInputAttribute::SInt2:
3928 type = GL_INT;
3929 size = 2;
3930 break;
3931 case QRhiVertexInputAttribute::SInt:
3932 type = GL_INT;
3933 size = 1;
3934 break;
3935 case QRhiVertexInputAttribute::Half4:
3936 type = GL_HALF_FLOAT;
3937 size = 4;
3938 break;
3939 case QRhiVertexInputAttribute::Half3:
3940 type = GL_HALF_FLOAT;
3941 size = 3;
3942 break;
3943 case QRhiVertexInputAttribute::Half2:
3944 type = GL_HALF_FLOAT;
3945 size = 2;
3946 break;
3947 case QRhiVertexInputAttribute::Half:
3948 type = GL_HALF_FLOAT;
3949 size = 1;
3950 break;
3951 case QRhiVertexInputAttribute::UShort4:
3952 type = GL_UNSIGNED_SHORT;
3953 size = 4;
3954 break;
3955 case QRhiVertexInputAttribute::UShort3:
3956 type = GL_UNSIGNED_SHORT;
3957 size = 3;
3958 break;
3959 case QRhiVertexInputAttribute::UShort2:
3960 type = GL_UNSIGNED_SHORT;
3961 size = 2;
3962 break;
3963 case QRhiVertexInputAttribute::UShort:
3964 type = GL_UNSIGNED_SHORT;
3965 size = 1;
3966 break;
3967 case QRhiVertexInputAttribute::SShort4:
3968 type = GL_SHORT;
3969 size = 4;
3970 break;
3971 case QRhiVertexInputAttribute::SShort3:
3972 type = GL_SHORT;
3973 size = 3;
3974 break;
3975 case QRhiVertexInputAttribute::SShort2:
3976 type = GL_SHORT;
3977 size = 2;
3978 break;
3979 case QRhiVertexInputAttribute::SShort:
3980 type = GL_SHORT;
3981 size = 1;
3982 break;
3983 default:
3984 break;
3985 }
3986
3987 const int locationIdx = it->location();
3988 quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset;
3989 if (type == GL_UNSIGNED_INT || type == GL_INT) {
3990 if (caps.intAttributes) {
3991 f->glVertexAttribIPointer(GLuint(locationIdx), size, type, stride,
3992 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3993 } else {
3994 qWarning("Current RHI backend does not support IntAttributes. Check supported features.");
3995 // This is a trick to disable this attribute
3997 state.enabledAttribArrays[locationIdx] = true;
3998 }
3999 } else {
4000 f->glVertexAttribPointer(GLuint(locationIdx), size, type, normalize, stride,
4001 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
4002 }
4003 if (locationIdx >= CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT || !state.enabledAttribArrays[locationIdx]) {
4005 state.enabledAttribArrays[locationIdx] = true;
4006 f->glEnableVertexAttribArray(GLuint(locationIdx));
4007 }
4008 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing) {
4009 f->glVertexAttribDivisor(GLuint(locationIdx), inputBinding->instanceStepRate());
4010 if (Q_LIKELY(locationIdx < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT))
4011 state.nonzeroAttribDivisor[locationIdx] = true;
4012 else
4014 state.instancedAttributesUsed = true;
4016 && state.nonzeroAttribDivisor[locationIdx])
4017 || Q_UNLIKELY(locationIdx >= CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT
4018 && locationIdx <= state.maxUntrackedInstancedAttribute))
4019 {
4020 f->glVertexAttribDivisor(GLuint(locationIdx), 0);
4022 state.nonzeroAttribDivisor[locationIdx] = false;
4023 }
4024 }
4025 } else {
4026 qWarning("No graphics pipeline active for setVertexInput; ignored");
4027 }
4028 }
4029 break;
4031 state.indexType = cmd.args.bindIndexBuffer.type;
4032 state.indexStride = state.indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
4033 state.indexOffset = cmd.args.bindIndexBuffer.offset;
4034 if (state.currentElementArrayBuffer != cmd.args.bindIndexBuffer.buffer) {
4035 state.currentElementArrayBuffer = cmd.args.bindIndexBuffer.buffer;
4036 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.currentElementArrayBuffer);
4037 }
4038 break;
4040 {
4041 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps);
4042 if (psD) {
4043 const bool useBaseInstance = (cmd.args.draw.baseInstance != 0 && caps.baseInstance);
4044 // Same rationale as DrawIndexed below.
4045 const bool needsInstancedPath = caps.instancing
4046 && (cmd.args.draw.instanceCount != 1 || useBaseInstance);
4047
4048 if (!needsInstancedPath) {
4049 f->glDrawArrays(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount));
4050 } else if (useBaseInstance) {
4052 GLint(cmd.args.draw.firstVertex),
4053 GLsizei(cmd.args.draw.vertexCount),
4054 GLsizei(cmd.args.draw.instanceCount),
4055 cmd.args.draw.baseInstance);
4056 } else {
4057 f->glDrawArraysInstanced(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount),
4058 GLsizei(cmd.args.draw.instanceCount));
4059 }
4060 } else {
4061 qWarning("No graphics pipeline active for draw; ignored");
4062 }
4063 }
4064 break;
4066 {
4067 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
4068 if (psD) {
4069 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4070 quintptr(cmd.args.drawIndexed.firstIndex * state.indexStride + state.indexOffset));
4071 const bool useBaseVertex = (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex);
4072 const bool useBaseInstance = (cmd.args.drawIndexed.baseInstance != 0 && caps.baseInstance);
4073 // Take the *Instanced path with a non-zero baseInstance even
4074 // when instanceCount is 1: only those calls fetch per-instance
4075 // attributes at the base instance offset.
4076 const bool needsInstancedPath = caps.instancing
4077 && (cmd.args.drawIndexed.instanceCount != 1 || useBaseInstance);
4078
4079 if (!needsInstancedPath) {
4080 if (useBaseVertex) {
4081 f->glDrawElementsBaseVertex(psD->drawMode,
4082 GLsizei(cmd.args.drawIndexed.indexCount),
4083 state.indexType,
4084 ofs,
4085 cmd.args.drawIndexed.baseVertex);
4086 } else {
4087 f->glDrawElements(psD->drawMode,
4088 GLsizei(cmd.args.drawIndexed.indexCount),
4089 state.indexType,
4090 ofs);
4091 }
4092 } else if (useBaseInstance && useBaseVertex) {
4094 GLsizei(cmd.args.drawIndexed.indexCount),
4095 state.indexType,
4096 ofs,
4097 GLsizei(cmd.args.drawIndexed.instanceCount),
4098 cmd.args.drawIndexed.baseVertex,
4099 cmd.args.drawIndexed.baseInstance);
4100 } else if (useBaseInstance) {
4102 GLsizei(cmd.args.drawIndexed.indexCount),
4103 state.indexType,
4104 ofs,
4105 GLsizei(cmd.args.drawIndexed.instanceCount),
4106 cmd.args.drawIndexed.baseInstance);
4107 } else if (useBaseVertex) {
4108 f->glDrawElementsInstancedBaseVertex(psD->drawMode,
4109 GLsizei(cmd.args.drawIndexed.indexCount),
4110 state.indexType,
4111 ofs,
4112 GLsizei(cmd.args.drawIndexed.instanceCount),
4113 cmd.args.drawIndexed.baseVertex);
4114 } else {
4115 f->glDrawElementsInstanced(psD->drawMode,
4116 GLsizei(cmd.args.drawIndexed.indexCount),
4117 state.indexType,
4118 ofs,
4119 GLsizei(cmd.args.drawIndexed.instanceCount));
4120 }
4121 } else {
4122 qWarning("No graphics pipeline active for drawIndexed; ignored");
4123 }
4124 }
4125 break;
4127 {
4128 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndirect.ps);
4129 if (psD) {
4130 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndirect.buffer);
4132 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4133 quintptr(cmd.args.drawIndirect.offset));
4134 glMultiDrawArraysIndirect(psD->drawMode,
4135 ofs,
4136 cmd.args.drawIndirect.drawCount,
4137 cmd.args.drawIndirect.stride);
4138 } else { // Fallback to issuing multiple single indirect draws
4139 for (quint32 i = 0; i < cmd.args.drawIndirect.drawCount; ++i) {
4140 const quintptr indirectOffset = quintptr(cmd.args.drawIndirect.offset)
4141 + quintptr(i) * cmd.args.drawIndirect.stride;
4142 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(indirectOffset);
4143 f->glDrawArraysIndirect(psD->drawMode,
4144 ofs);
4145 }
4146 }
4147 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4148 } else {
4149 qWarning("No graphics pipeline active for drawIndirect; ignored");
4150 }
4151 }
4152 break;
4154 {
4155 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexedIndirect.ps);
4156 if (psD) {
4157 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndexedIndirect.buffer);
4159 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4160 quintptr(cmd.args.drawIndexedIndirect.offset));
4162 state.indexType,
4163 ofs,
4164 cmd.args.drawIndexedIndirect.drawCount,
4165 cmd.args.drawIndexedIndirect.stride);
4166 } else { // Fallback to issuing multiple single indirect draws
4167 for (quint32 i = 0; i < cmd.args.drawIndexedIndirect.drawCount; ++i) {
4168 const quintptr indirectOffset = quintptr(cmd.args.drawIndexedIndirect.offset)
4169 + quintptr(i) * cmd.args.drawIndexedIndirect.stride;
4170 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(indirectOffset);
4171 f->glDrawElementsIndirect(psD->drawMode,
4172 state.indexType,
4173 ofs);
4174 }
4175 }
4176 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4177 } else {
4178 qWarning("No graphics pipeline active for drawIndexedIndirect; ignored");
4179 }
4180 }
4181 break;
4183 {
4184 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndirectCount.ps);
4185 if (psD && glMultiDrawArraysIndirectCount) {
4186 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndirectCount.buffer);
4187 f->glBindBuffer(GL_PARAMETER_BUFFER, cmd.args.drawIndirectCount.countBuffer);
4188 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4189 quintptr(cmd.args.drawIndirectCount.offset));
4191 ofs,
4192 GLintptr(cmd.args.drawIndirectCount.countOffset),
4193 cmd.args.drawIndirectCount.maxDrawCount,
4194 cmd.args.drawIndirectCount.stride);
4195 f->glBindBuffer(GL_PARAMETER_BUFFER, 0);
4196 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4197 } else {
4198 qWarning("No graphics pipeline active or glMultiDrawArraysIndirectCount unavailable; ignored");
4199 }
4200 }
4201 break;
4203 {
4204 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexedIndirectCount.ps);
4206 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, cmd.args.drawIndexedIndirectCount.buffer);
4207 f->glBindBuffer(GL_PARAMETER_BUFFER, cmd.args.drawIndexedIndirectCount.countBuffer);
4208 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
4209 quintptr(cmd.args.drawIndexedIndirectCount.offset));
4211 state.indexType,
4212 ofs,
4213 GLintptr(cmd.args.drawIndexedIndirectCount.countOffset),
4214 cmd.args.drawIndexedIndirectCount.maxDrawCount,
4215 cmd.args.drawIndexedIndirectCount.stride);
4216 f->glBindBuffer(GL_PARAMETER_BUFFER, 0);
4217 f->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
4218 } else {
4219 qWarning("No graphics pipeline active or glMultiDrawElementsIndirectCount unavailable; ignored");
4220 }
4221 }
4222 break;
4224 executeBindGraphicsPipeline(cbD, QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindGraphicsPipeline.ps));
4225 break;
4226 case QGles2CommandBuffer::Command::BindShaderResources:
4227 bindShaderResources(cbD,
4228 cmd.args.bindShaderResources.maybeGraphicsPs,
4229 cmd.args.bindShaderResources.maybeComputePs,
4230 cmd.args.bindShaderResources.srb,
4231 cbD->dynamicOffsetPool.constData() + cmd.args.bindShaderResources.dynamicOffsetPoolOffset,
4232 cmd.args.bindShaderResources.dynamicOffsetCount);
4233 break;
4235 {
4236 QRhiGraphicsPipeline *maybeGraphicsPs = cmd.args.setPushConstants.maybeGraphicsPs;
4237 QRhiComputePipeline *maybeComputePs = cmd.args.setPushConstants.maybeComputePs;
4238 const QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs
4239 ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->pushConstantUniforms
4240 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->pushConstantUniforms);
4241 QGles2UniformState *uniformState = maybeGraphicsPs
4242 ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState.data()
4243 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniformState.data();
4244 setUniformsFromBlock(uniforms, -1,
4245 cbD->pushConstantPool.constData() + cmd.args.setPushConstants.dataOffset,
4246 cmd.args.setPushConstants.size, 0, uniformState);
4247 }
4248 break;
4250 {
4251 // Like with the other backends, each pass starts with a stencil
4252 // reference value of 0, regardless of what earlier passes set.
4253 cbD->graphicsPassState.dynamic.stencilRef = 0;
4254 QVarLengthArray<GLenum, 8> bufs;
4255 GLuint fbo = cmd.args.bindFramebuffer.fbo;
4256 if (!fbo)
4257 fbo = ctx->defaultFramebufferObject();
4258 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4259 if (fbo) {
4260 const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
4261 bufs.append(colorAttCount > 0 ? GL_COLOR_ATTACHMENT0 : GL_NONE);
4262 if (caps.maxDrawBuffers > 1) {
4263 for (int i = 1; i < colorAttCount; ++i)
4264 bufs.append(GL_COLOR_ATTACHMENT0 + uint(i));
4265 }
4266 } else {
4267 if (cmd.args.bindFramebuffer.stereo && cmd.args.bindFramebuffer.stereoTarget == QRhiSwapChain::RightBuffer)
4268 bufs.append(GL_BACK_RIGHT);
4269 else
4270 bufs.append(caps.gles ? GL_BACK : GL_BACK_LEFT);
4271 }
4272 if (caps.hasDrawBuffersFunc)
4273 f->glDrawBuffers(bufs.count(), bufs.constData());
4274 if (caps.srgbWriteControl) {
4275 if (cmd.args.bindFramebuffer.srgb)
4276 f->glEnable(GL_FRAMEBUFFER_SRGB);
4277 else
4278 f->glDisable(GL_FRAMEBUFFER_SRGB);
4279 }
4280 }
4281 break;
4283 f->glDisable(GL_SCISSOR_TEST);
4284 if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
4285 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4286 f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
4287 }
4288 if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
4289 f->glDepthMask(GL_TRUE);
4290 f->glClearDepthf(cmd.args.clear.d);
4291 }
4292 if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) {
4293 f->glStencilMask(0xFF);
4294 f->glClearStencil(GLint(cmd.args.clear.s));
4295 }
4296 f->glClear(cmd.args.clear.mask);
4297 cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking
4298 break;
4300 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
4301 f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
4302 cmd.args.bufferSubData.data);
4303 break;
4305 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.clearBufferSubData.target, cmd.args.clearBufferSubData.buffer);
4306 glClearBufferSubData(cmd.args.clearBufferSubData.target, GL_R8UI,
4307 cmd.args.clearBufferSubData.offset, cmd.args.clearBufferSubData.size,
4308 GL_RED_INTEGER, GL_UNSIGNED_BYTE, &cmd.args.clearBufferSubData.value);
4309 break;
4311 {
4312 QRhiReadbackResult *result = cmd.args.getBufferSubData.result;
4313 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
4314 if (caps.gles) {
4315 if (caps.properMapBuffer) {
4316 void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target,
4317 cmd.args.getBufferSubData.offset,
4318 cmd.args.getBufferSubData.size,
4320 if (p) {
4321 result->data.resize(cmd.args.getBufferSubData.size);
4322 memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size));
4323 f->glUnmapBuffer(cmd.args.getBufferSubData.target);
4324 }
4325 }
4326 } else {
4327 result->data.resize(cmd.args.getBufferSubData.size);
4328 f->glGetBufferSubData(cmd.args.getBufferSubData.target,
4329 cmd.args.getBufferSubData.offset,
4330 cmd.args.getBufferSubData.size,
4331 result->data.data());
4332 }
4333 if (result->completed)
4334 result->completed();
4335 }
4336 break;
4338 f->glBindBuffer(GL_COPY_READ_BUFFER, cmd.args.copyBuf.srcBuffer);
4339 f->glBindBuffer(GL_COPY_WRITE_BUFFER, cmd.args.copyBuf.dstBuffer);
4340 f->glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
4341 cmd.args.copyBuf.srcOffset, cmd.args.copyBuf.dstOffset,
4342 cmd.args.copyBuf.size);
4343 f->glBindBuffer(GL_COPY_READ_BUFFER, 0);
4344 f->glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
4345 break;
4347 {
4348 GLuint fbo;
4349 f->glGenFramebuffers(1, &fbo);
4350 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4351 if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D
4352 || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY
4353 || cmd.args.copyTex.srcTarget == GL_TEXTURE_1D_ARRAY) {
4354 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
4355 cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
4356 } else if (cmd.args.copyTex.srcTarget == GL_TEXTURE_1D) {
4357 glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4358 cmd.args.copyTex.srcTarget, cmd.args.copyTex.srcTexture,
4359 cmd.args.copyTex.srcLevel);
4360 } else {
4361 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4362 cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
4363 }
4364 f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
4365 if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D || cmd.args.copyTex.dstTarget == GL_TEXTURE_2D_ARRAY) {
4366 f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
4367 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
4368 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
4369 cmd.args.copyTex.w, cmd.args.copyTex.h);
4370 } else if (cmd.args.copyTex.dstTarget == GL_TEXTURE_1D) {
4371 glCopyTexSubImage1D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
4372 cmd.args.copyTex.dstX, cmd.args.copyTex.srcX,
4373 cmd.args.copyTex.srcY, cmd.args.copyTex.w);
4374 } else {
4375 f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
4376 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
4377 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
4378 cmd.args.copyTex.w, cmd.args.copyTex.h);
4379 }
4380 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4381 f->glDeleteFramebuffers(1, &fbo);
4382 }
4383 break;
4385 {
4386 QRhiReadbackResult *result = cmd.args.readPixels.result;
4387 GLuint tex = cmd.args.readPixels.texture;
4388 GLuint fbo = 0;
4389 int mipLevel = 0;
4390 result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
4391 if (tex) {
4392 result->format = cmd.args.readPixels.format;
4393 mipLevel = cmd.args.readPixels.level;
4394 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
4395 f->glGenFramebuffers(1, &fbo);
4396 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4397 const auto attach = [&](GLenum attachment) {
4398 if (cmd.args.readPixels.slice3D >= 0) {
4399 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
4400 tex, mipLevel, cmd.args.readPixels.slice3D);
4401 } else if (cmd.args.readPixels.readTarget == GL_TEXTURE_1D) {
4402 glFramebufferTexture1D(GL_FRAMEBUFFER, attachment,
4403 cmd.args.readPixels.readTarget, tex, mipLevel);
4404 } else {
4405 f->glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
4406 cmd.args.readPixels.readTarget, tex, mipLevel);
4407 }
4408 };
4409 // A depth(-stencil) texture cannot be attached as a color attachment.
4410 if (isDepthTextureFormat(result->format)) {
4411 attach(GL_DEPTH_ATTACHMENT);
4412 if (isStencilSupportingFormat(result->format))
4413 attach(GL_STENCIL_ATTACHMENT);
4414 // There is no color attachment, which would make the FBO incomplete
4415 // on desktop GL < 4.1 unless the read buffer is set to none.
4416 if (!caps.gles)
4417 f->glReadBuffer(GL_NONE);
4418 } else {
4419 attach(GL_COLOR_ATTACHMENT0);
4420 }
4421 }
4422 } else {
4423 result->format = QRhiTexture::RGBA8;
4424 // readPixels handles multisample resolving implicitly
4425 }
4426 const int x = cmd.args.readPixels.x;
4427 const int y = cmd.args.readPixels.y;
4428 const int w = cmd.args.readPixels.w;
4429 const int h = cmd.args.readPixels.h;
4430 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
4431 // With GLES, GL_RGBA is the only mandated readback format, so stick with it.
4432 // (and that's why we return false for the ReadBackAnyTextureFormat feature)
4433 if (result->format == QRhiTexture::R8 || result->format == QRhiTexture::RED_OR_ALPHA8) {
4434 result->data.resizeForOverwrite(w * h);
4435 QByteArray tmpBuf;
4436 tmpBuf.resizeForOverwrite(w * h * 4);
4437 f->glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tmpBuf.data());
4438 const quint8 *srcBase = reinterpret_cast<const quint8 *>(tmpBuf.constData());
4439 quint8 *dstBase = reinterpret_cast<quint8 *>(result->data.data());
4440 const int componentIndex = result->format == QRhiTexture::R8
4441 || isFeatureSupported(QRhi::RedOrAlpha8IsRed)
4442 ? 0
4443 : 3;
4444 for (int y = 0; y < h; ++y) {
4445 const quint8 *src = srcBase + y * w * 4;
4446 quint8 *dst = dstBase + y * w;
4447 int count = w;
4448 while (count-- > 0) {
4449 *dst++ = src[componentIndex];
4450 src += 4;
4451 }
4452 }
4453 } else {
4454 // For other formats try it because this can be relevant for some use cases;
4455 // if it works, then fine, if not, there's nothing we can do.
4456 [[maybe_unused]] GLenum glintformat;
4457 [[maybe_unused]] GLenum glsizedintformat;
4458 GLenum glformat;
4459 GLenum gltype;
4460 toGlTextureFormat(result->format, {}, caps,
4461 &glintformat, &glsizedintformat, &glformat, &gltype);
4462 quint32 byteSize;
4463 textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize, nullptr);
4464 result->data.resizeForOverwrite(byteSize);
4465 // The result is tightly packed, unlike with the default alignment of 4.
4466 f->glPixelStorei(GL_PACK_ALIGNMENT, 1);
4467 f->glReadPixels(x, y, w, h, glformat, gltype, result->data.data());
4468 f->glPixelStorei(GL_PACK_ALIGNMENT, 4);
4469 // QRhi guarantees the unused bits of a depth readback to be zero. With D24
4470 // the depth is normalized to 32 bits, but only the top 24 bits are
4471 // meaningful, with D32FS8 only the lowest 8 bits of the second word are.
4472 if (result->format == QRhiTexture::D24 || result->format == QRhiTexture::D32FS8) {
4473 const bool isD24 = result->format == QRhiTexture::D24;
4474 const quint32 mask = isD24 ? 0xFFFFFF00u : 0xFFu;
4475 char *p = result->data.data();
4476 for (qsizetype i = isD24 ? 0 : 1, count = result->data.size() / 4; i < count; i += isD24 ? 1 : 2)
4477 qToUnaligned(qFromUnaligned<quint32>(p + i * 4) & mask, p + i * 4);
4478 }
4479 }
4480 } else {
4481 // this level can't be read back; return a zero-filled image sized for the format
4482 quint32 byteSize = 0;
4483 textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize, nullptr);
4484 result->data.fill('\0', byteSize);
4485 }
4486 if (fbo) {
4487 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4488 f->glDeleteFramebuffers(1, &fbo);
4489 }
4490 if (result->completed)
4491 result->completed();
4492 }
4493 break;
4495 f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
4496 if (cmd.args.subImage.rowStartAlign != 4)
4497 f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
4498 if (cmd.args.subImage.rowLength != 0)
4499 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
4500 if (cmd.args.subImage.target == GL_TEXTURE_3D || cmd.args.subImage.target == GL_TEXTURE_2D_ARRAY) {
4501 f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level,
4502 cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
4503 cmd.args.subImage.w, cmd.args.subImage.h, 1,
4504 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
4505 cmd.args.subImage.data);
4506 } else if (cmd.args.subImage.target == GL_TEXTURE_1D) {
4507 glTexSubImage1D(cmd.args.subImage.target, cmd.args.subImage.level,
4508 cmd.args.subImage.dx, cmd.args.subImage.w,
4509 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
4510 cmd.args.subImage.data);
4511 } else {
4512 f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
4513 cmd.args.subImage.dx, cmd.args.subImage.dy,
4514 cmd.args.subImage.w, cmd.args.subImage.h,
4515 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
4516 cmd.args.subImage.data);
4517 }
4518 if (cmd.args.subImage.rowStartAlign != 4)
4519 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
4520 if (cmd.args.subImage.rowLength != 0)
4521 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4522 break;
4524 f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
4525 if (cmd.args.compressedImage.target == GL_TEXTURE_3D || cmd.args.compressedImage.target == GL_TEXTURE_2D_ARRAY) {
4526 f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
4527 cmd.args.compressedImage.glintformat,
4528 cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
4529 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
4530 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
4532 cmd.args.compressedImage.target, cmd.args.compressedImage.level,
4533 cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, 0,
4534 cmd.args.compressedImage.size, cmd.args.compressedImage.data);
4535 } else {
4536 f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
4537 cmd.args.compressedImage.glintformat,
4538 cmd.args.compressedImage.w, cmd.args.compressedImage.h,
4539 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
4540 }
4541 break;
4543 f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
4544 if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D || cmd.args.compressedSubImage.target == GL_TEXTURE_2D_ARRAY) {
4545 f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
4546 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
4547 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
4548 cmd.args.compressedSubImage.glintformat,
4549 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
4550 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
4552 cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
4553 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.w,
4554 cmd.args.compressedSubImage.glintformat, cmd.args.compressedSubImage.size,
4555 cmd.args.compressedSubImage.data);
4556 } else {
4557 f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
4558 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
4559 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
4560 cmd.args.compressedSubImage.glintformat,
4561 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
4562 }
4563 break;
4565 {
4566 // Altering the scissor state, so reset the stored state, although
4567 // not strictly required as long as blit is done in endPass() only.
4568 cbD->graphicsPassState.reset();
4569 f->glDisable(GL_SCISSOR_TEST);
4570 GLuint fbo[2];
4571 f->glGenFramebuffers(2, fbo);
4572 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
4573 const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil;
4574 const bool hasStencil = ds && cmd.args.blitFromRenderbuffer.hasStencil;
4575 if (ds) {
4576 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4577 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
4578 if (hasStencil) {
4579 // With a packed depth-stencil renderbuffer stencilRenderbuffer is 0.
4580 const GLuint srcStencilRb = cmd.args.blitFromRenderbuffer.stencilRenderbuffer
4581 ? cmd.args.blitFromRenderbuffer.stencilRenderbuffer
4582 : cmd.args.blitFromRenderbuffer.renderbuffer;
4583 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4584 GL_RENDERBUFFER, srcStencilRb);
4585 }
4586 } else {
4587 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4588 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
4589 }
4590 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
4591 if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) {
4592 if (ds) {
4593 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4594 cmd.args.blitFromRenderbuffer.dstTexture,
4595 cmd.args.blitFromRenderbuffer.dstLevel,
4596 cmd.args.blitFromRenderbuffer.dstLayer);
4597 if (hasStencil) {
4598 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4599 cmd.args.blitFromRenderbuffer.dstTexture,
4600 cmd.args.blitFromRenderbuffer.dstLevel,
4601 cmd.args.blitFromRenderbuffer.dstLayer);
4602 }
4603 } else {
4604 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4605 cmd.args.blitFromRenderbuffer.dstTexture,
4606 cmd.args.blitFromRenderbuffer.dstLevel,
4607 cmd.args.blitFromRenderbuffer.dstLayer);
4608 }
4609 } else {
4610 if (ds) {
4611 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
4612 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
4613 if (hasStencil) {
4614 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
4615 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
4616 }
4617 } else {
4618 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
4619 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
4620 }
4621 }
4622 if (ds && !caps.gles) {
4623 f->glReadBuffer(GL_NONE);
4624 const GLenum noBuf = GL_NONE;
4625 f->glDrawBuffers(1, &noBuf);
4626 }
4627 const GLbitfield rbBlitMask = ds ? (GL_DEPTH_BUFFER_BIT | (hasStencil ? GL_STENCIL_BUFFER_BIT : 0))
4628 : GL_COLOR_BUFFER_BIT;
4629 f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
4630 0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
4631 rbBlitMask,
4632 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
4633 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4634 f->glDeleteFramebuffers(2, fbo);
4635 }
4636 break;
4638 {
4639 // Altering the scissor state, so reset the stored state, although
4640 // not strictly required as long as blit is done in endPass() only.
4641 cbD->graphicsPassState.reset();
4642 f->glDisable(GL_SCISSOR_TEST);
4643 GLuint fbo[2];
4644 f->glGenFramebuffers(2, fbo);
4645 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
4646 const bool ds = cmd.args.blitFromTexture.isDepthStencil;
4647 const bool hasStencil = ds && cmd.args.blitFromTexture.hasStencil;
4648 if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
4649 if (ds) {
4650 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4651 cmd.args.blitFromTexture.srcTexture,
4652 cmd.args.blitFromTexture.srcLevel,
4653 cmd.args.blitFromTexture.srcLayer);
4654 if (hasStencil) {
4655 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4656 cmd.args.blitFromTexture.srcTexture,
4657 cmd.args.blitFromTexture.srcLevel,
4658 cmd.args.blitFromTexture.srcLayer);
4659 }
4660 } else {
4661 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4662 cmd.args.blitFromTexture.srcTexture,
4663 cmd.args.blitFromTexture.srcLevel,
4664 cmd.args.blitFromTexture.srcLayer);
4665 }
4666 } else {
4667 if (ds) {
4668 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
4669 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
4670 if (hasStencil) {
4671 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
4672 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
4673 }
4674 } else {
4675 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
4676 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
4677 }
4678 }
4679 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
4680 if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) {
4681 if (ds) {
4682 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
4683 cmd.args.blitFromTexture.dstTexture,
4684 cmd.args.blitFromTexture.dstLevel,
4685 cmd.args.blitFromTexture.dstLayer);
4686 if (hasStencil) {
4687 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
4688 cmd.args.blitFromTexture.dstTexture,
4689 cmd.args.blitFromTexture.dstLevel,
4690 cmd.args.blitFromTexture.dstLayer);
4691 }
4692 } else {
4693 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4694 cmd.args.blitFromTexture.dstTexture,
4695 cmd.args.blitFromTexture.dstLevel,
4696 cmd.args.blitFromTexture.dstLayer);
4697 }
4698 } else {
4699 if (ds) {
4700 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
4701 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
4702 if (hasStencil) {
4703 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
4704 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
4705 }
4706 } else {
4707 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
4708 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
4709 }
4710 }
4711 if (ds && !caps.gles) {
4712 f->glReadBuffer(GL_NONE);
4713 const GLenum noBuf = GL_NONE;
4714 f->glDrawBuffers(1, &noBuf);
4715 }
4716 const GLbitfield texBlitMask = ds ? (GL_DEPTH_BUFFER_BIT | (hasStencil ? GL_STENCIL_BUFFER_BIT : 0))
4717 : GL_COLOR_BUFFER_BIT;
4718 f->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
4719 0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
4720 texBlitMask,
4721 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
4722 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
4723 f->glDeleteFramebuffers(2, fbo);
4724 }
4725 break;
4727 f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
4728 f->glGenerateMipmap(cmd.args.genMip.target);
4729 break;
4731 {
4732 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cmd.args.bindComputePipeline.ps);
4733 f->glUseProgram(psD->program);
4734 }
4735 break;
4737 f->glDispatchCompute(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
4738 break;
4740 {
4741 f->glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, cmd.args.dispatchIndirect.buffer);
4742 f->glDispatchComputeIndirect(GLintptr(cmd.args.dispatchIndirect.offset));
4743 f->glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0);
4744 }
4745 break;
4747 {
4748 if (!caps.compute)
4749 break;
4750 GLbitfield barriers = 0;
4751 QRhiPassResourceTracker &tracker(cbD->passResTrackers[cmd.args.barriersForPass.trackerIndex]);
4752 // we only care about after-write, not any other accesses, and
4753 // cannot tell if something was written in a shader several passes
4754 // ago: now the previously written resource may be used with an
4755 // access that was not in the previous passes, result in a missing
4756 // barrier in theory. Hence setting all barrier bits whenever
4757 // something previously written is used for the first time in a
4758 // subsequent pass.
4759 for (const auto &[rhiB, trackedB]: tracker.buffers()) {
4760 Q_UNUSED(rhiB)
4761 QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(trackedB.stateAtPassBegin.access);
4762 if (bufferAccessIsWrite(accessBeforePass))
4763 barriers |= barriersForBuffer();
4764 }
4765 for (const auto &[rhiT, trackedT]: tracker.textures()) {
4766 Q_UNUSED(rhiT)
4767 QGles2Texture::Access accessBeforePass = QGles2Texture::Access(trackedT.stateAtPassBegin.access);
4768 if (textureAccessIsWrite(accessBeforePass))
4769 barriers |= barriersForTexture();
4770 }
4771 if (barriers)
4772 f->glMemoryBarrier(barriers);
4773 }
4774 break;
4776 if (caps.compute)
4777 f->glMemoryBarrier(cmd.args.barrier.barriers);
4778 break;
4780 if (caps.gles && caps.ctxMajor >= 3) {
4781 f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.invalidateFramebuffer.fbo);
4782 f->glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
4783 cmd.args.invalidateFramebuffer.attCount,
4784 cmd.args.invalidateFramebuffer.att);
4785 }
4786 break;
4787 default:
4788 break;
4789 }
4790 }
4791 if (state.instancedAttributesUsed) {
4792 for (int i = 0; i < CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; ++i) {
4793 if (state.nonzeroAttribDivisor[i])
4794 f->glVertexAttribDivisor(GLuint(i), 0);
4795 }
4797 f->glVertexAttribDivisor(GLuint(i), 0);
4798 }
4799}
4800
4802{
4803 QGles2CommandBuffer::GraphicsPassState &state(cbD->graphicsPassState);
4804 const bool forceUpdate = !state.valid;
4805 state.valid = true;
4806
4807 const bool scissor = psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor);
4808 if (forceUpdate || scissor != state.scissor) {
4809 state.scissor = scissor;
4810 if (scissor)
4811 f->glEnable(GL_SCISSOR_TEST);
4812 else
4813 f->glDisable(GL_SCISSOR_TEST);
4814 }
4815
4816 const bool cullFace = psD->m_cullMode != QRhiGraphicsPipeline::None;
4817 const GLenum cullMode = cullFace ? toGlCullMode(psD->m_cullMode) : GL_NONE;
4818 if (forceUpdate || cullFace != state.cullFace || cullMode != state.cullMode) {
4819 state.cullFace = cullFace;
4820 state.cullMode = cullMode;
4821 if (cullFace) {
4822 f->glEnable(GL_CULL_FACE);
4823 f->glCullFace(cullMode);
4824 } else {
4825 f->glDisable(GL_CULL_FACE);
4826 }
4827 }
4828
4829 const GLenum frontFace = toGlFrontFace(psD->m_frontFace);
4830 if (forceUpdate || frontFace != state.frontFace) {
4831 state.frontFace = frontFace;
4832 f->glFrontFace(frontFace);
4833 }
4834
4835 const GLenum polygonMode = toGlPolygonMode(psD->m_polygonMode);
4836 if (glPolygonMode && (forceUpdate || polygonMode != state.polygonMode)) {
4837 state.polygonMode = polygonMode;
4838 glPolygonMode(GL_FRONT_AND_BACK, polygonMode);
4839 }
4840
4841 if (!psD->m_targetBlends.isEmpty()) {
4842 GLint buffer = 0;
4843 bool anyBlendEnabled = false;
4844 for (const auto targetBlend : psD->m_targetBlends) {
4845 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = {
4846 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::R),
4847 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::G),
4848 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::B),
4849 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::A)
4850 };
4851 if (forceUpdate || colorMask != state.colorMask[buffer]) {
4852 state.colorMask[buffer] = colorMask;
4853 if (caps.perRenderTargetBlending)
4854 f->glColorMaski(buffer, colorMask.r, colorMask.g, colorMask.b, colorMask.a);
4855 else
4856 f->glColorMask(colorMask.r, colorMask.g, colorMask.b, colorMask.a);
4857 }
4858
4859 const bool blendEnabled = targetBlend.enable;
4860 const QGles2CommandBuffer::GraphicsPassState::Blend blend = {
4861 toGlBlendFactor(targetBlend.srcColor),
4862 toGlBlendFactor(targetBlend.dstColor),
4863 toGlBlendFactor(targetBlend.srcAlpha),
4864 toGlBlendFactor(targetBlend.dstAlpha),
4865 toGlBlendOp(targetBlend.opColor),
4866 toGlBlendOp(targetBlend.opAlpha)
4867 };
4868 anyBlendEnabled |= blendEnabled;
4869 if (forceUpdate || blendEnabled != state.blendEnabled[buffer] || (blendEnabled && blend != state.blend[buffer])) {
4870 state.blendEnabled[buffer] = blendEnabled;
4871 if (blendEnabled) {
4872 state.blend[buffer] = blend;
4873 if (caps.perRenderTargetBlending) {
4874 f->glBlendFuncSeparatei(buffer, blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
4875 f->glBlendEquationSeparatei(buffer, blend.opColor, blend.opAlpha);
4876 } else {
4877 f->glBlendFuncSeparate(blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
4878 f->glBlendEquationSeparate(blend.opColor, blend.opAlpha);
4879 }
4880 }
4881 }
4882 buffer++;
4883 if (!caps.perRenderTargetBlending)
4884 break;
4885 }
4886 if (anyBlendEnabled)
4887 f->glEnable(GL_BLEND);
4888 else
4889 f->glDisable(GL_BLEND);
4890 } else {
4891 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { true, true, true, true };
4892 if (forceUpdate || colorMask != state.colorMask[0]) {
4893 state.colorMask[0] = colorMask;
4894 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4895 }
4896 const bool blendEnabled = false;
4897 if (forceUpdate || blendEnabled != state.blendEnabled[0]) {
4898 state.blendEnabled[0] = blendEnabled;
4899 f->glDisable(GL_BLEND);
4900 }
4901 }
4902
4903 const bool depthTest = psD->m_depthTest;
4904 if (forceUpdate || depthTest != state.depthTest) {
4905 state.depthTest = depthTest;
4906 if (depthTest)
4907 f->glEnable(GL_DEPTH_TEST);
4908 else
4909 f->glDisable(GL_DEPTH_TEST);
4910 }
4911
4912 const bool depthWrite = psD->m_depthWrite;
4913 if (forceUpdate || depthWrite != state.depthWrite) {
4914 state.depthWrite = depthWrite;
4915 f->glDepthMask(depthWrite);
4916 }
4917
4918 const bool depthClamp = psD->m_depthClamp;
4919 if (caps.depthClamp && (forceUpdate || depthClamp != state.depthClamp)) {
4920 state.depthClamp = depthClamp;
4921 if (depthClamp)
4922 f->glEnable(GL_DEPTH_CLAMP);
4923 else
4924 f->glDisable(GL_DEPTH_CLAMP);
4925 }
4926
4927 const GLenum depthFunc = toGlCompareOp(psD->m_depthOp);
4928 if (forceUpdate || depthFunc != state.depthFunc) {
4929 state.depthFunc = depthFunc;
4930 f->glDepthFunc(depthFunc);
4931 }
4932
4933 const bool stencilTest = psD->m_stencilTest;
4934 const GLuint stencilReadMask = psD->m_stencilReadMask;
4935 const GLuint stencilWriteMask = psD->m_stencilWriteMask;
4936 // The stencil reference value is global state in OpenGL, but in QRhi it is
4937 // only dynamic for pipelines with UsesStencilRef.
4938 const GLint stencilRef = psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef)
4939 ? state.dynamic.stencilRef : 0;
4940 const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilFront = {
4941 toGlCompareOp(psD->m_stencilFront.compareOp),
4942 toGlStencilOp(psD->m_stencilFront.failOp),
4943 toGlStencilOp(psD->m_stencilFront.depthFailOp),
4944 toGlStencilOp(psD->m_stencilFront.passOp)
4945 };
4946 const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilBack = {
4947 toGlCompareOp(psD->m_stencilBack.compareOp),
4948 toGlStencilOp(psD->m_stencilBack.failOp),
4949 toGlStencilOp(psD->m_stencilBack.depthFailOp),
4950 toGlStencilOp(psD->m_stencilBack.passOp)
4951 };
4952 if (forceUpdate || stencilTest != state.stencilTest
4953 || (stencilTest
4954 && (stencilReadMask != state.stencilReadMask || stencilWriteMask != state.stencilWriteMask
4955 || stencilRef != state.stencilRef
4956 || stencilFront != state.stencil[0] || stencilBack != state.stencil[1])))
4957 {
4958 state.stencilTest = stencilTest;
4959 if (stencilTest) {
4960 state.stencilReadMask = stencilReadMask;
4961 state.stencilWriteMask = stencilWriteMask;
4962 state.stencilRef = stencilRef;
4963 state.stencil[0] = stencilFront;
4964 state.stencil[1] = stencilBack;
4965
4966 f->glEnable(GL_STENCIL_TEST);
4967
4968 f->glStencilFuncSeparate(GL_FRONT, stencilFront.func, stencilRef, stencilReadMask);
4969 f->glStencilOpSeparate(GL_FRONT, stencilFront.failOp, stencilFront.zfailOp, stencilFront.zpassOp);
4970 f->glStencilMaskSeparate(GL_FRONT, stencilWriteMask);
4971
4972 f->glStencilFuncSeparate(GL_BACK, stencilBack.func, stencilRef, stencilReadMask);
4973 f->glStencilOpSeparate(GL_BACK, stencilBack.failOp, stencilBack.zfailOp, stencilBack.zpassOp);
4974 f->glStencilMaskSeparate(GL_BACK, stencilWriteMask);
4975 } else {
4976 f->glDisable(GL_STENCIL_TEST);
4977 }
4978 }
4979
4980 const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(psD->m_slopeScaledDepthBias);
4981 const float polyOffsetFactor = psD->m_slopeScaledDepthBias;
4982 const float polyOffsetUnits = psD->m_depthBias;
4983 if (forceUpdate || state.polyOffsetFill != polyOffsetFill
4984 || polyOffsetFactor != state.polyOffsetFactor || polyOffsetUnits != state.polyOffsetUnits)
4985 {
4986 state.polyOffsetFill = polyOffsetFill;
4987 state.polyOffsetFactor = polyOffsetFactor;
4988 state.polyOffsetUnits = polyOffsetUnits;
4989 if (polyOffsetFill) {
4990 f->glPolygonOffset(polyOffsetFactor, polyOffsetUnits);
4991 f->glEnable(GL_POLYGON_OFFSET_FILL);
4992 } else {
4993 f->glDisable(GL_POLYGON_OFFSET_FILL);
4994 }
4995 }
4996
4997 if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) {
4998 const float lineWidth = psD->m_lineWidth;
4999 if (forceUpdate || lineWidth != state.lineWidth) {
5000 state.lineWidth = lineWidth;
5001 f->glLineWidth(lineWidth);
5002 }
5003 }
5004
5005 if (psD->m_topology == QRhiGraphicsPipeline::Patches) {
5006 const int cpCount = psD->m_patchControlPointCount;
5007 if (forceUpdate || cpCount != state.cpCount) {
5008 state.cpCount = cpCount;
5009 f->glPatchParameteri(GL_PATCH_VERTICES, qMax(1, cpCount));
5010 }
5011 }
5012
5013 f->glUseProgram(psD->program);
5014}
5015
5016// The source is a uniform block (std140) or a push constant block (std430)
5017// laid out for the GPU, which is not what glUniform* wants: there the values
5018// are tightly packed. srcStride is the distance in bytes between the array
5019// elements, or between the matrix columns for matrix types.
5020template <typename T>
5021static inline void qrhi_block_to_packed(T *dst, int vecSize, int elemCount, const void *src, quint32 srcStride)
5022{
5023 const char *p = reinterpret_cast<const char *>(src);
5024 for (int i = 0; i < elemCount; ++i) {
5025 const T *e = reinterpret_cast<const T *>(p);
5026 for (int j = 0; j < vecSize; ++j)
5027 dst[vecSize * i + j] = e[j];
5028 p += srcStride;
5029 }
5030}
5031
5032// The default stride for both the arrays and the matrix columns in an std140
5033// uniform block. Push constant blocks are std430, where an array of scalars or
5034// vec2 is tightly packed, and so is a mat2; there the reflection-provided
5035// stride is used instead.
5037
5039{
5040 return uniform.elemStride ? uniform.elemStride : QRHI_GL_STD140_STRIDE;
5041}
5042
5043static inline qint64 qrhi_uniform_read_size(QShaderDescription::VariableType type, int arrayDim, quint32 stride)
5044{
5045 if (!stride)
5046 stride = QRHI_GL_STD140_STRIDE;
5047
5048 qint64 elemSize;
5049 qint64 elemStride = stride;
5050
5051 switch (type) {
5052 case QShaderDescription::Float:
5053 case QShaderDescription::Int:
5054 case QShaderDescription::Uint:
5055 elemSize = 4;
5056 break;
5057 case QShaderDescription::Vec2:
5058 case QShaderDescription::Int2:
5059 case QShaderDescription::Uint2:
5060 elemSize = 8;
5061 break;
5062 case QShaderDescription::Vec3:
5063 case QShaderDescription::Int3:
5064 case QShaderDescription::Uint3:
5065 elemSize = 12;
5066 break;
5067 case QShaderDescription::Vec4:
5068 case QShaderDescription::Int4:
5069 case QShaderDescription::Uint4:
5070 elemSize = 16;
5071 break;
5072 case QShaderDescription::Mat2:
5073 // two columns, stride is the column stride (16 with std140)
5074 elemSize = stride + 8;
5075 elemStride = 2 * stride;
5076 break;
5077 case QShaderDescription::Mat3:
5078 // three columns
5079 elemSize = 2 * stride + 12;
5080 elemStride = 3 * stride;
5081 break;
5082 case QShaderDescription::Mat4:
5083 elemSize = 3 * stride + 16;
5084 elemStride = 4 * stride;
5085 break;
5086 case QShaderDescription::Bool:
5087 return 4;
5088 case QShaderDescription::Bool2:
5089 return 8;
5090 case QShaderDescription::Bool3:
5091 return 12;
5092 case QShaderDescription::Bool4:
5093 return 16;
5094 default:
5095 return 0;
5096 }
5097
5098 return arrayDim < 1 ? elemSize : (arrayDim - 1) * elemStride + elemSize;
5099}
5100
5102 void *ps, uint psGeneration, int glslLocation,
5103 int *texUnit, bool *activeTexUnitAltered)
5104{
5105 const bool samplerStateValid = texD->samplerState == samplerD->d;
5106 const bool cachedStateInRange = *texUnit < 16;
5107 bool updateTextureBinding = true;
5108 if (samplerStateValid && cachedStateInRange) {
5109 // If we already encountered the same texture with
5110 // the same pipeline for this texture unit in the
5111 // current pass, then the shader program already
5112 // has the uniform set. As in a 3D scene one model
5113 // often has more than one associated texture map,
5114 // the savings here can become significant,
5115 // depending on the scene.
5116 if (cbD->textureUnitState[*texUnit].ps == ps
5117 && cbD->textureUnitState[*texUnit].psGeneration == psGeneration
5118 && cbD->textureUnitState[*texUnit].texture == texD->texture)
5119 {
5120 updateTextureBinding = false;
5121 }
5122 }
5123 if (updateTextureBinding) {
5124 f->glActiveTexture(GL_TEXTURE0 + uint(*texUnit));
5125 *activeTexUnitAltered = true;
5126 f->glBindTexture(texD->target, texD->texture);
5127 f->glUniform1i(glslLocation, *texUnit);
5128 if (cachedStateInRange) {
5129 cbD->textureUnitState[*texUnit].ps = ps;
5130 cbD->textureUnitState[*texUnit].psGeneration = psGeneration;
5131 cbD->textureUnitState[*texUnit].texture = texD->texture;
5132 }
5133 }
5134 ++(*texUnit);
5135 if (!samplerStateValid) {
5136 f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
5137 f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
5138 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
5139 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
5140 if (caps.texture3D && texD->target == GL_TEXTURE_3D)
5141 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, GLint(samplerD->d.glwrapr));
5142 if (caps.textureCompareMode) {
5143 if (samplerD->d.gltexcomparefunc != GL_NEVER) {
5144 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
5145 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
5146 } else {
5147 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
5148 }
5149 }
5150 texD->samplerState = samplerD->d;
5151 }
5152}
5153
5154void QRhiGles2::setUniformValue(const QGles2UniformDescription &uniform, const void *src, QGles2UniformState *uniformState)
5155{
5156 const quint32 stride = qrhi_uniform_stride(uniform);
5157
5158 switch (uniform.type) {
5159 case QShaderDescription::Float:
5160 {
5161 const int elemCount = uniform.arrayDim;
5162 if (elemCount < 1) {
5163 const float v = *reinterpret_cast<const float *>(src);
5164 if (uniform.stateIndex >= 0) {
5165 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5166 if (thisUniformState.componentCount != 1 || thisUniformState.v[0] != v) {
5167 thisUniformState.componentCount = 1;
5168 thisUniformState.v[0] = v;
5169 f->glUniform1f(uniform.glslLocation, v);
5170 }
5171 } else {
5172 f->glUniform1f(uniform.glslLocation, v);
5173 }
5174 } else {
5175 m_scratch.packedArray.resize(elemCount);
5176 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 1, elemCount, src, stride);
5177 f->glUniform1fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5178 }
5179 }
5180 break;
5181 case QShaderDescription::Vec2:
5182 {
5183 const int elemCount = uniform.arrayDim;
5184 if (elemCount < 1) {
5185 const float *v = reinterpret_cast<const float *>(src);
5186 if (uniform.stateIndex >= 0) {
5187 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5188 if (thisUniformState.componentCount != 2
5189 || thisUniformState.v[0] != v[0]
5190 || thisUniformState.v[1] != v[1])
5191 {
5192 thisUniformState.componentCount = 2;
5193 thisUniformState.v[0] = v[0];
5194 thisUniformState.v[1] = v[1];
5195 f->glUniform2fv(uniform.glslLocation, 1, v);
5196 }
5197 } else {
5198 f->glUniform2fv(uniform.glslLocation, 1, v);
5199 }
5200 } else {
5201 m_scratch.packedArray.resize(elemCount * 2);
5202 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 2, elemCount, src, stride);
5203 f->glUniform2fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5204 }
5205 }
5206 break;
5207 case QShaderDescription::Vec3:
5208 {
5209 const int elemCount = uniform.arrayDim;
5210 if (elemCount < 1) {
5211 const float *v = reinterpret_cast<const float *>(src);
5212 if (uniform.stateIndex >= 0) {
5213 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5214 if (thisUniformState.componentCount != 3
5215 || thisUniformState.v[0] != v[0]
5216 || thisUniformState.v[1] != v[1]
5217 || thisUniformState.v[2] != v[2])
5218 {
5219 thisUniformState.componentCount = 3;
5220 thisUniformState.v[0] = v[0];
5221 thisUniformState.v[1] = v[1];
5222 thisUniformState.v[2] = v[2];
5223 f->glUniform3fv(uniform.glslLocation, 1, v);
5224 }
5225 } else {
5226 f->glUniform3fv(uniform.glslLocation, 1, v);
5227 }
5228 } else {
5229 m_scratch.packedArray.resize(elemCount * 3);
5230 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 3, elemCount, src, stride);
5231 f->glUniform3fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5232 }
5233 }
5234 break;
5235 case QShaderDescription::Vec4:
5236 {
5237 const int elemCount = uniform.arrayDim;
5238 if (elemCount < 1) {
5239 const float *v = reinterpret_cast<const float *>(src);
5240 if (uniform.stateIndex >= 0) {
5241 QGles2UniformState &thisUniformState(uniformState[uniform.stateIndex]);
5242 if (thisUniformState.componentCount != 4
5243 || thisUniformState.v[0] != v[0]
5244 || thisUniformState.v[1] != v[1]
5245 || thisUniformState.v[2] != v[2]
5246 || thisUniformState.v[3] != v[3])
5247 {
5248 thisUniformState.componentCount = 4;
5249 thisUniformState.v[0] = v[0];
5250 thisUniformState.v[1] = v[1];
5251 thisUniformState.v[2] = v[2];
5252 thisUniformState.v[3] = v[3];
5253 f->glUniform4fv(uniform.glslLocation, 1, v);
5254 }
5255 } else {
5256 f->glUniform4fv(uniform.glslLocation, 1, v);
5257 }
5258 } else if (stride == 16) {
5259 f->glUniform4fv(uniform.glslLocation, elemCount, reinterpret_cast<const float *>(src));
5260 } else {
5261 m_scratch.packedArray.resize(elemCount * 4);
5262 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 4, elemCount, src, stride);
5263 f->glUniform4fv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->f);
5264 }
5265 }
5266 break;
5267 case QShaderDescription::Mat2:
5268 {
5269 const int elemCount = uniform.arrayDim;
5270 m_scratch.packedArray.resize(qMax(1, elemCount) * 4);
5271 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 2, qMax(1, elemCount) * 2, src, stride);
5272 f->glUniformMatrix2fv(uniform.glslLocation, qMax(1, elemCount), GL_FALSE, &m_scratch.packedArray.constData()->f);
5273 }
5274 break;
5275 case QShaderDescription::Mat3:
5276 {
5277 const int elemCount = uniform.arrayDim;
5278 m_scratch.packedArray.resize(qMax(1, elemCount) * 9);
5279 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 3, qMax(1, elemCount) * 3, src, stride);
5280 f->glUniformMatrix3fv(uniform.glslLocation, qMax(1, elemCount), GL_FALSE, &m_scratch.packedArray.constData()->f);
5281 }
5282 break;
5283 case QShaderDescription::Mat4:
5284 if (stride == 16) {
5285 f->glUniformMatrix4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), GL_FALSE, reinterpret_cast<const float *>(src));
5286 } else {
5287 const int elemCount = qMax(1, uniform.arrayDim);
5288 m_scratch.packedArray.resize(elemCount * 16);
5289 qrhi_block_to_packed(&m_scratch.packedArray.data()->f, 4, elemCount * 4, src, stride);
5290 f->glUniformMatrix4fv(uniform.glslLocation, elemCount, GL_FALSE, &m_scratch.packedArray.constData()->f);
5291 }
5292 break;
5293 case QShaderDescription::Int:
5294 {
5295 const int elemCount = uniform.arrayDim;
5296 if (elemCount < 1) {
5297 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
5298 } else {
5299 m_scratch.packedArray.resize(elemCount);
5300 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 1, elemCount, src, stride);
5301 f->glUniform1iv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->i);
5302 }
5303 }
5304 break;
5305 case QShaderDescription::Int2:
5306 {
5307 const int elemCount = uniform.arrayDim;
5308 if (elemCount < 1) {
5309 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5310 } else {
5311 m_scratch.packedArray.resize(elemCount * 2);
5312 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 2, elemCount, src, stride);
5313 f->glUniform2iv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->i);
5314 }
5315 }
5316 break;
5317 case QShaderDescription::Int3:
5318 {
5319 const int elemCount = uniform.arrayDim;
5320 if (elemCount < 1) {
5321 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5322 } else {
5323 m_scratch.packedArray.resize(elemCount * 3);
5324 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 3, elemCount, src, stride);
5325 f->glUniform3iv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->i);
5326 }
5327 }
5328 break;
5329 case QShaderDescription::Int4:
5330 if (uniform.arrayDim < 1 || stride == 16) {
5331 f->glUniform4iv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const qint32 *>(src));
5332 } else {
5333 m_scratch.packedArray.resize(uniform.arrayDim * 4);
5334 qrhi_block_to_packed(&m_scratch.packedArray.data()->i, 4, uniform.arrayDim, src, stride);
5335 f->glUniform4iv(uniform.glslLocation, uniform.arrayDim, &m_scratch.packedArray.constData()->i);
5336 }
5337 break;
5338 case QShaderDescription::Uint:
5339 {
5340 const int elemCount = uniform.arrayDim;
5341 if (elemCount < 1) {
5342 f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
5343 } else {
5344 m_scratch.packedArray.resize(elemCount);
5345 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 1, elemCount, src, stride);
5346 f->glUniform1uiv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->u);
5347 }
5348 }
5349 break;
5350 case QShaderDescription::Uint2:
5351 {
5352 const int elemCount = uniform.arrayDim;
5353 if (elemCount < 1) {
5354 f->glUniform2uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
5355 } else {
5356 m_scratch.packedArray.resize(elemCount * 2);
5357 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 2, elemCount, src, stride);
5358 f->glUniform2uiv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->u);
5359 }
5360 }
5361 break;
5362 case QShaderDescription::Uint3:
5363 {
5364 const int elemCount = uniform.arrayDim;
5365 if (elemCount < 1) {
5366 f->glUniform3uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
5367 } else {
5368 m_scratch.packedArray.resize(elemCount * 3);
5369 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 3, elemCount, src, stride);
5370 f->glUniform3uiv(uniform.glslLocation, elemCount, &m_scratch.packedArray.constData()->u);
5371 }
5372 }
5373 break;
5374 case QShaderDescription::Uint4:
5375 if (uniform.arrayDim < 1 || stride == 16) {
5376 f->glUniform4uiv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const quint32 *>(src));
5377 } else {
5378 m_scratch.packedArray.resize(uniform.arrayDim * 4);
5379 qrhi_block_to_packed(&m_scratch.packedArray.data()->u, 4, uniform.arrayDim, src, stride);
5380 f->glUniform4uiv(uniform.glslLocation, uniform.arrayDim, &m_scratch.packedArray.constData()->u);
5381 }
5382 break;
5383 case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
5384 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
5385 break;
5386 case QShaderDescription::Bool2:
5387 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5388 break;
5389 case QShaderDescription::Bool3:
5390 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5391 break;
5392 case QShaderDescription::Bool4:
5393 f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
5394 break;
5395 default:
5396 qWarning("Uniform with binding %d, offset %u has unsupported type %d",
5397 uniform.binding, uniform.offset, uniform.type);
5398 break;
5399 }
5400}
5401
5402static inline bool qrhi_gl_isTrackedUniform(const QGles2UniformDescription &uniform)
5403{
5404 // The logic here must match setUniformValue().
5405
5406 if (uniform.arrayDim > 0)
5407 return false;
5408
5409 switch (uniform.type) {
5410 case QShaderDescription::Float:
5411 case QShaderDescription::Vec2:
5412 case QShaderDescription::Vec3:
5413 case QShaderDescription::Vec4:
5414 return true;
5415 default:
5416 return false;
5417 }
5418}
5419
5420void QRhiGles2::setupUniformStateTracking(QGles2UniformDescriptionVector *uniforms,
5421 QGles2UniformDescriptionVector *pushConstantUniforms,
5422 QGles2UniformStateVector *uniformState)
5423{
5424 int count = 0;
5425 for (QGles2UniformDescriptionVector *list : { uniforms, pushConstantUniforms }) {
5426 for (QGles2UniformDescription &uniform : *list)
5427 uniform.stateIndex = qrhi_gl_isTrackedUniform(uniform) ? count++ : -1;
5428 }
5429 uniformState->resize(count);
5430 if (count)
5431 memset(uniformState->data(), 0, size_t(count) * sizeof(QGles2UniformState));
5432}
5433
5434// Sets all uniforms belonging to one block. blockData is the start of the
5435// source data (the uniform buffer's contents, or the push constant block),
5436// blockOffset the offset the block starts at within it. Pass a binding of -1
5437// to take all entries of uniforms, which is what push constant blocks, having
5438// no binding, do.
5439void QRhiGles2::setUniformsFromBlock(const QGles2UniformDescriptionVector &uniforms, int binding,
5440 const char *blockData, qint64 blockSize, quint32 blockOffset,
5441 QGles2UniformState *uniformState)
5442{
5443 for (const QGles2UniformDescription &uniform : uniforms) {
5444 if (binding >= 0 && uniform.binding != binding)
5445 continue;
5446
5447 const qint64 readOffset = qint64(blockOffset) + uniform.offset;
5448 const qint64 readSize = qrhi_uniform_read_size(uniform.type, uniform.arrayDim, uniform.elemStride);
5449 if (readOffset < 0 || readOffset + readSize > blockSize) {
5450 qWarning("Uniform with binding %d, offset %u, type %d, array dimension %d would read "
5451 "outside of the %lld bytes of source data. Skipping.",
5452 uniform.binding, uniform.offset, uniform.type, uniform.arrayDim, blockSize);
5453 continue;
5454 }
5455
5456#ifndef QT_NO_DEBUG
5457 if (uniform.arrayDim > 0
5458 && uniform.type != QShaderDescription::Float
5459 && uniform.type != QShaderDescription::Vec2
5460 && uniform.type != QShaderDescription::Vec3
5461 && uniform.type != QShaderDescription::Vec4
5462 && uniform.type != QShaderDescription::Int
5463 && uniform.type != QShaderDescription::Int2
5464 && uniform.type != QShaderDescription::Int3
5465 && uniform.type != QShaderDescription::Int4
5466 && uniform.type != QShaderDescription::Uint
5467 && uniform.type != QShaderDescription::Uint2
5468 && uniform.type != QShaderDescription::Uint3
5469 && uniform.type != QShaderDescription::Uint4
5470 && uniform.type != QShaderDescription::Mat2
5471 && uniform.type != QShaderDescription::Mat3
5472 && uniform.type != QShaderDescription::Mat4)
5473 {
5474 qWarning("Uniform with binding %d, offset %u, type %d is an array, "
5475 "but arrays are only supported for float, vec2, vec3, vec4, int, "
5476 "ivec2, ivec3, ivec4, uint, uvec2, uvec3, uvec4, mat2, mat3 "
5477 "and mat4. "
5478 "Only the first element will be set.",
5479 uniform.binding, uniform.offset, uniform.type);
5480 }
5481#endif
5482
5483 // in both a uniform block and a push constant block everything is at
5484 // least 4 byte aligned, so this should not cause unaligned reads
5485 setUniformValue(uniform, blockData + readOffset, uniformState);
5486 }
5487}
5488
5490 QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
5491 QRhiShaderResourceBindings *srb,
5492 const uint *dynOfsPairs, int dynOfsCount)
5493{
5495 int texUnit = 1; // start from unit 1, keep 0 for resource mgmt stuff to avoid clashes
5496 bool activeTexUnitAltered = false;
5497 QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
5498 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
5499 QGles2UniformState *uniformState = maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState.data()
5500 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniformState.data();
5501 m_scratch.separateTextureBindings.clear();
5502 m_scratch.separateSamplerBindings.clear();
5503
5504 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
5505 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
5506
5507 switch (b->type()) {
5508 case QRhiShaderResourceBinding::UniformBuffer:
5509 {
5510 int viewOffset = b->ubuf().offset;
5511 for (int j = 0; j < dynOfsCount; ++j) {
5512 if (dynOfsPairs[2 * j] == uint(b->binding)) {
5513 viewOffset = int(dynOfsPairs[2 * j + 1]);
5514 break;
5515 }
5516 }
5517 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->ubuf().buf);
5518 setUniformsFromBlock(uniforms, b->binding, bufD->data.constData(), bufD->data.size(),
5519 quint32(viewOffset), uniformState);
5520 }
5521 break;
5522 case QRhiShaderResourceBinding::SampledTexture:
5523 {
5524 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
5525 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
5526 void *ps;
5527 uint psGeneration;
5528 if (maybeGraphicsPs) {
5529 ps = maybeGraphicsPs;
5530 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
5531 } else {
5532 ps = maybeComputePs;
5533 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
5534 }
5535 const auto &texSamplers = b->stex().texSamplers;
5536 for (int elem = 0; elem < texSamplers.count(); ++elem) {
5537 QGles2Texture *texD = QRHI_RES(QGles2Texture, texSamplers[elem].tex);
5538 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, texSamplers[elem].sampler);
5539 for (const QGles2SamplerDescription &shaderSampler : samplers) {
5540 if (shaderSampler.combinedBinding == b->binding) {
5541 const int loc = shaderSampler.glslLocation + elem;
5542 bindCombinedSampler(cbD, texD, samplerD, ps, psGeneration, loc, &texUnit, &activeTexUnitAltered);
5543 break;
5544 }
5545 }
5546 }
5547 }
5548 break;
5549 case QRhiShaderResourceBinding::Texture:
5550 {
5551 const auto &texSamplers = b->stex().texSamplers;
5552 for (int elem = 0; elem < texSamplers.count(); ++elem) {
5553 QGles2Texture *texD = QRHI_RES(QGles2Texture, texSamplers[elem].tex);
5554 m_scratch.separateTextureBindings.append({ texD, b->binding, elem });
5555 }
5556 }
5557 break;
5558 case QRhiShaderResourceBinding::Sampler:
5559 {
5560 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->stex().texSamplers[0].sampler);
5561 m_scratch.separateSamplerBindings.append({ samplerD, b->binding });
5562 }
5563 break;
5564 case QRhiShaderResourceBinding::ImageLoad:
5565 case QRhiShaderResourceBinding::ImageStore:
5566 case QRhiShaderResourceBinding::ImageLoadStore:
5567 {
5568 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->simage().tex);
5569 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
5570 // arrays, cubemaps, and 3D textures expose the whole texture with all layers/slices
5571 const bool layered = texD->m_flags.testFlag(QRhiTexture::CubeMap)
5572 || texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
5573 || texD->m_flags.testFlag(QRhiTexture::TextureArray);
5574 GLenum access = GL_READ_WRITE;
5575 if (b->type() == QRhiShaderResourceBinding::ImageLoad)
5576 access = GL_READ_ONLY;
5577 else if (b->type() == QRhiShaderResourceBinding::ImageStore)
5578 access = GL_WRITE_ONLY;
5579 f->glBindImageTexture(GLuint(b->binding), texD->texture,
5580 b->simage().level, layered, 0,
5581 access, texD->glsizedintformat);
5582 }
5583 break;
5584 case QRhiShaderResourceBinding::BufferLoad:
5585 case QRhiShaderResourceBinding::BufferStore:
5586 case QRhiShaderResourceBinding::BufferLoadStore:
5587 {
5588 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->sbuf().buf);
5589 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
5590 if (b->sbuf().offset == 0 && b->sbuf().maybeSize == 0)
5591 f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer);
5592 else
5593 f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer,
5594 b->sbuf().offset, b->sbuf().maybeSize ? b->sbuf().maybeSize : bufD->m_size);
5595 }
5596 break;
5597 default:
5598 Q_UNREACHABLE();
5599 break;
5600 }
5601 }
5602
5603 if (!m_scratch.separateTextureBindings.isEmpty() || !m_scratch.separateSamplerBindings.isEmpty()) {
5604 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
5605 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
5606 void *ps;
5607 uint psGeneration;
5608 if (maybeGraphicsPs) {
5609 ps = maybeGraphicsPs;
5610 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
5611 } else {
5612 ps = maybeComputePs;
5613 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
5614 }
5615 for (const QGles2SamplerDescription &shaderSampler : samplers) {
5616 if (shaderSampler.combinedBinding >= 0)
5617 continue;
5618 for (const Scratch::SeparateSampler &sepSampler : std::as_const(m_scratch.separateSamplerBindings)) {
5619 if (sepSampler.binding != shaderSampler.sbinding)
5620 continue;
5621 for (const Scratch::SeparateTexture &sepTex : std::as_const(m_scratch.separateTextureBindings)) {
5622 if (sepTex.binding != shaderSampler.tbinding)
5623 continue;
5624 const int loc = shaderSampler.glslLocation + sepTex.elem;
5625 bindCombinedSampler(cbD, sepTex.texture, sepSampler.sampler, ps, psGeneration,
5626 loc, &texUnit, &activeTexUnitAltered);
5627 }
5628 }
5629 }
5630 }
5631
5632 if (activeTexUnitAltered)
5633 f->glActiveTexture(GL_TEXTURE0);
5634}
5635
5636void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
5637{
5638 Q_ASSERT(QRHI_RES(QGles2CommandBuffer, cb)->recordingPass == QGles2CommandBuffer::NoPass);
5639
5640 enqueueResourceUpdates(cb, resourceUpdates);
5641}
5642
5643QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
5644 bool *wantsColorClear, bool *wantsDsClear)
5645{
5646 QGles2RenderTargetData *rtD = nullptr;
5647 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
5648
5649 QGles2CommandBuffer::Command &fbCmd(cbD->commands.get());
5650 fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer;
5651
5652 static const bool doClearBuffers = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_BUFFERS") == 0;
5653 static const bool doClearColorBuffer = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_COLOR_BUFFER") == 0;
5654
5655 switch (rt->resourceType()) {
5656 case QRhiResource::SwapChainRenderTarget:
5657 rtD = &QRHI_RES(QGles2SwapChainRenderTarget, rt)->d;
5658 if (wantsColorClear)
5659 *wantsColorClear = doClearBuffers && doClearColorBuffer;
5660 if (wantsDsClear)
5661 *wantsDsClear = doClearBuffers;
5662 fbCmd.args.bindFramebuffer.fbo = 0;
5663 fbCmd.args.bindFramebuffer.colorAttCount = 1;
5664 fbCmd.args.bindFramebuffer.stereo = rtD->stereoTarget.has_value();
5665 if (fbCmd.args.bindFramebuffer.stereo)
5666 fbCmd.args.bindFramebuffer.stereoTarget = rtD->stereoTarget.value();
5667 break;
5668 case QRhiResource::TextureRenderTarget:
5669 {
5670 QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, rt);
5671 rtD = &rtTex->d;
5672 if (wantsColorClear)
5673 *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
5674 if (wantsDsClear)
5675 *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
5676 fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
5677 fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
5678 fbCmd.args.bindFramebuffer.stereo = false;
5679
5680 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
5681 it != itEnd; ++it)
5682 {
5683 const QRhiColorAttachment &colorAtt(*it);
5684 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
5685 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5686 if (texD && cbD->passNeedsResourceTracking) {
5687 trackedRegisterTexture(&passResTracker, texD,
5688 QRhiPassResourceTracker::TexColorOutput,
5689 QRhiPassResourceTracker::TexColorOutputStage);
5690 }
5691 if (resolveTexD && cbD->passNeedsResourceTracking) {
5692 trackedRegisterTexture(&passResTracker, resolveTexD,
5693 QRhiPassResourceTracker::TexColorOutput,
5694 QRhiPassResourceTracker::TexColorOutputStage);
5695 }
5696 // renderbuffers cannot be written in shaders (no image store) so
5697 // they do not matter here
5698 }
5699 if (rtTex->m_desc.depthTexture() && cbD->passNeedsResourceTracking) {
5700 trackedRegisterTexture(&passResTracker, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
5701 QRhiPassResourceTracker::TexDepthOutput,
5702 QRhiPassResourceTracker::TexDepthOutputStage);
5703 }
5704 }
5705 break;
5706 default:
5707 Q_UNREACHABLE();
5708 break;
5709 }
5710
5711 fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
5712
5713 return rtD;
5714}
5715
5717{
5718 cbD->passResTrackers.emplace_back();
5719 cbD->currentPassResTrackerIndex = cbD->passResTrackers.size() - 1;
5720 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5722 cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
5723}
5724
5725void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
5726 QRhiRenderTarget *rt,
5727 const QColor &colorClearValue,
5728 const QRhiDepthStencilClearValue &depthStencilClearValue,
5729 QRhiResourceUpdateBatch *resourceUpdates,
5730 QRhiCommandBuffer::BeginPassFlags flags)
5731{
5732 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5734
5735 if (resourceUpdates)
5736 enqueueResourceUpdates(cb, resourceUpdates);
5737
5738 // Get a new resource tracker. Then add a command that will generate
5739 // glMemoryBarrier() calls based on that tracker when submitted.
5741
5742 if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
5744 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(rtTex->description(), rtTex->d.currentResIdList))
5745 rtTex->create();
5746 }
5747
5748 bool wantsColorClear, wantsDsClear;
5749 QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
5750
5751 QGles2CommandBuffer::Command &clearCmd(cbD->commands.get());
5753 clearCmd.args.clear.mask = 0;
5754 if (rtD->colorAttCount && wantsColorClear)
5755 clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
5756 if (rtD->dsAttCount && wantsDsClear)
5757 clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
5758 clearCmd.args.clear.c[0] = colorClearValue.redF();
5759 clearCmd.args.clear.c[1] = colorClearValue.greenF();
5760 clearCmd.args.clear.c[2] = colorClearValue.blueF();
5761 clearCmd.args.clear.c[3] = colorClearValue.alphaF();
5762 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
5763 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
5764
5766 cbD->passNeedsResourceTracking = !flags.testFlag(QRhiCommandBuffer::DoNotTrackResourcesForCompute) && caps.compute;
5767 cbD->currentTarget = rt;
5768
5770}
5771
5772void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
5773{
5774 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5776
5777 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
5778 QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
5779 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
5780 it != itEnd; ++it)
5781 {
5782 const QRhiColorAttachment &colorAtt(*it);
5783 if (!colorAtt.resolveTexture())
5784 continue;
5785
5786 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5787 const QSize size = resolveTexD->pixelSize();
5788 if (colorAtt.renderBuffer()) {
5789 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer());
5790 if (rbD->pixelSize() != size) {
5791 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
5792 rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
5793 }
5795 // colorAtt.renderBuffer() is not actually used for anything if OpenGL ES'
5796 // auto-resolving GL_EXT_multisampled_render_to_texture is used.
5797 } else {
5798 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5800 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
5801 cmd.args.blitFromRenderbuffer.stencilRenderbuffer = 0;
5802 cmd.args.blitFromRenderbuffer.w = size.width();
5803 cmd.args.blitFromRenderbuffer.h = size.height();
5804 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
5805 cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
5806 else
5807 cmd.args.blitFromRenderbuffer.target = resolveTexD->target;
5808 cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture;
5809 cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel();
5810 const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
5811 || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray);
5812 cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
5813 cmd.args.blitFromRenderbuffer.isDepthStencil = false;
5814 cmd.args.blitFromRenderbuffer.hasStencil = false;
5815 }
5816 } else if (caps.glesMultisampleRenderToTexture) {
5817 // Nothing to do, resolving into colorAtt.resolveTexture() is automatic,
5818 // colorAtt.texture() is in fact not used for anything.
5819 } else {
5820 Q_ASSERT(colorAtt.texture());
5821 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
5822 if (texD->pixelSize() != size) {
5823 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
5824 texD->pixelSize().width(), texD->pixelSize().height(), size.width(), size.height());
5825 }
5826 const int resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
5827 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
5828 const int srcLayer = colorAtt.layer() + resolveIdx;
5829 const int dstLayer = colorAtt.resolveLayer() + resolveIdx;
5830 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5832 if (texD->m_flags.testFlag(QRhiTexture::CubeMap))
5833 cmd.args.blitFromTexture.srcTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(srcLayer);
5834 else
5835 cmd.args.blitFromTexture.srcTarget = texD->target;
5836 cmd.args.blitFromTexture.srcTexture = texD->texture;
5837 cmd.args.blitFromTexture.srcLevel = colorAtt.level();
5838 cmd.args.blitFromTexture.srcLayer = 0;
5839 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || texD->m_flags.testFlag(QRhiTexture::TextureArray))
5840 cmd.args.blitFromTexture.srcLayer = srcLayer;
5841 cmd.args.blitFromTexture.w = size.width();
5842 cmd.args.blitFromTexture.h = size.height();
5843 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
5844 cmd.args.blitFromTexture.dstTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(dstLayer);
5845 else
5846 cmd.args.blitFromTexture.dstTarget = resolveTexD->target;
5847 cmd.args.blitFromTexture.dstTexture = resolveTexD->texture;
5848 cmd.args.blitFromTexture.dstLevel = colorAtt.resolveLevel();
5849 cmd.args.blitFromTexture.dstLayer = 0;
5850 if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray))
5851 cmd.args.blitFromTexture.dstLayer = dstLayer;
5852 cmd.args.blitFromTexture.isDepthStencil = false;
5853 cmd.args.blitFromTexture.hasStencil = false;
5854 }
5855 }
5856 }
5857
5858 if (rtTex->m_desc.depthResolveTexture()) {
5859 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture());
5860 const QSize size = depthResolveTexD->pixelSize();
5861 if (rtTex->m_desc.depthStencilBuffer()) {
5862 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer());
5863 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5865 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
5866 cmd.args.blitFromRenderbuffer.stencilRenderbuffer = rbD->stencilRenderbuffer;
5867 cmd.args.blitFromRenderbuffer.w = size.width();
5868 cmd.args.blitFromRenderbuffer.h = size.height();
5869 cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target;
5870 cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture;
5871 cmd.args.blitFromRenderbuffer.dstLevel = 0;
5872 cmd.args.blitFromRenderbuffer.dstLayer = 0;
5873 cmd.args.blitFromRenderbuffer.isDepthStencil = true;
5874 // The renderbuffer always has stencil, but the destination may
5875 // be depth-only, in which case attaching it to
5876 // GL_STENCIL_ATTACHMENT would make the framebuffer incomplete.
5877 cmd.args.blitFromRenderbuffer.hasStencil = isStencilSupportingFormat(depthResolveTexD->format());
5878 } else if (caps.glesMultisampleRenderToTexture) {
5879 // Nothing to do, resolving into depthResolveTexture() is automatic.
5880 } else {
5881 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture());
5882 // Only involve the stencil when both sides actually have it.
5883 const bool hasStencil = isStencilSupportingFormat(depthTexD->format()) && isStencilSupportingFormat(depthResolveTexD->format());
5884 const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1;
5885 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
5886 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5888 cmd.args.blitFromTexture.srcTarget = depthTexD->target;
5889 cmd.args.blitFromTexture.srcTexture = depthTexD->texture;
5890 cmd.args.blitFromTexture.srcLevel = 0;
5891 cmd.args.blitFromTexture.srcLayer = resolveIdx;
5892 cmd.args.blitFromTexture.w = size.width();
5893 cmd.args.blitFromTexture.h = size.height();
5894 cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target;
5895 cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture;
5896 cmd.args.blitFromTexture.dstLevel = 0;
5897 cmd.args.blitFromTexture.dstLayer = resolveIdx;
5898 cmd.args.blitFromTexture.isDepthStencil = true;
5899 cmd.args.blitFromTexture.hasStencil = hasStencil;
5900 }
5901 }
5902 }
5903
5904 const bool mayDiscardDepthStencil =
5905 (rtTex->m_desc.depthStencilBuffer()
5906 || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)))
5907 && !rtTex->m_desc.depthResolveTexture();
5908 if (mayDiscardDepthStencil) {
5909 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5911 cmd.args.invalidateFramebuffer.fbo = rtTex->framebuffer;
5913 cmd.args.invalidateFramebuffer.attCount = 1;
5914 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_STENCIL_ATTACHMENT;
5915 } else {
5916 cmd.args.invalidateFramebuffer.attCount = 2;
5917 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_ATTACHMENT;
5918 cmd.args.invalidateFramebuffer.att[1] = GL_STENCIL_ATTACHMENT;
5919 }
5920 }
5921 }
5922
5924 cbD->currentTarget = nullptr;
5925
5926 if (resourceUpdates)
5927 enqueueResourceUpdates(cb, resourceUpdates);
5928}
5929
5930void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb,
5931 QRhiResourceUpdateBatch *resourceUpdates,
5933{
5934 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5936
5937 if (resourceUpdates)
5938 enqueueResourceUpdates(cb, resourceUpdates);
5939
5941
5943
5945}
5946
5947void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
5948{
5949 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5951
5953
5954 if (resourceUpdates)
5955 enqueueResourceUpdates(cb, resourceUpdates);
5956}
5957
5958void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
5959{
5960 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
5963 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
5964
5965 if (pipelineChanged) {
5966 cbD->currentGraphicsPipeline = nullptr;
5967 cbD->currentComputePipeline = ps;
5969 if (psD->lastUsedInFrameNo != frameNo) {
5971 psD->currentSrb = nullptr;
5973 }
5974
5975 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
5977 cmd.args.bindComputePipeline.ps = ps;
5978 }
5979}
5980
5981template<typename T>
5982inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
5983 QRhiShaderResourceBinding::Type bindingType,
5984 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
5985{
5986 int access = 0;
5987 if (bindingType == loadTypeVal) {
5989 } else {
5991 if (bindingType == loadStoreTypeVal)
5993 }
5994 auto it = writtenResources->find(resource);
5995 if (it != writtenResources->end())
5996 it->first |= access;
5997 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
5998 writtenResources->insert(resource, { access, true });
5999}
6000
6001// The glMemoryBarrier() bits needed based on what previous dispatches in the
6002// pass wrote. Updates writtenResources, so call once per dispatch.
6004{
6005 if (!cbD->currentComputeSrb)
6006 return 0;
6007
6008 GLbitfield barriers = 0;
6009
6010 // The key in the writtenResources map indicates that the resource was
6011 // written in a previous dispatch, whereas the value accumulates the
6012 // access mask in the current one.
6013 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
6014 accessAndIsNewFlag = { 0, false };
6015
6017 const int bindingCount = srbD->m_bindings.size();
6018 for (int i = 0; i < bindingCount; ++i) {
6019 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
6020 switch (b->type()) {
6021 case QRhiShaderResourceBinding::ImageLoad:
6022 case QRhiShaderResourceBinding::ImageStore:
6023 case QRhiShaderResourceBinding::ImageLoadStore:
6024 qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
6025 b->simage().tex,
6026 b->type(),
6027 QRhiShaderResourceBinding::ImageLoad,
6028 QRhiShaderResourceBinding::ImageStore,
6029 QRhiShaderResourceBinding::ImageLoadStore);
6030 break;
6031 case QRhiShaderResourceBinding::BufferLoad:
6032 case QRhiShaderResourceBinding::BufferStore:
6033 case QRhiShaderResourceBinding::BufferLoadStore:
6034 qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
6035 b->sbuf().buf,
6036 b->type(),
6037 QRhiShaderResourceBinding::BufferLoad,
6038 QRhiShaderResourceBinding::BufferStore,
6039 QRhiShaderResourceBinding::BufferLoadStore);
6040 break;
6041 default:
6042 break;
6043 }
6044 }
6045
6046 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
6047 const int accessInThisDispatch = it->first;
6048 const bool isNewInThisDispatch = it->second;
6049 if (accessInThisDispatch && !isNewInThisDispatch) {
6050 if (it.key()->resourceType() == QRhiResource::Texture)
6052 else
6054 }
6055 // Anything that was previously written, but is only read now, can be
6056 // removed from the written list (because that previous write got a
6057 // corresponding barrier now).
6058 if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
6059 it = cbD->computePassState.writtenResources.erase(it);
6060 else
6061 ++it;
6062 }
6063
6064 return barriers;
6065}
6066
6067void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
6068{
6069 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
6071
6072 if (const GLbitfield barriers = barriersForNextDispatch(cbD)) {
6073 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6075 cmd.args.barrier.barriers = barriers;
6076 }
6077
6078 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6080 cmd.args.dispatch.x = GLuint(x);
6081 cmd.args.dispatch.y = GLuint(y);
6082 cmd.args.dispatch.z = GLuint(z);
6083}
6084
6085void QRhiGles2::dispatchIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
6086 quint32 indirectBufferOffset)
6087{
6088 if (!caps.dispatchIndirect) {
6089 qWarning("dispatchIndirect called but the DispatchIndirect feature is not supported");
6090 return;
6091 }
6092
6093 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
6095
6096 // Sample before barriersForNextDispatch(), which drops entries for anything
6097 // the upcoming dispatch only reads - including this buffer, if the consuming
6098 // shader also binds it as a storage buffer.
6099 const bool indirectBufWrittenInThisPass =
6100 cbD->computePassState.writtenResources.contains(indirectBuffer);
6101
6102 GLbitfield barriers = barriersForNextDispatch(cbD);
6103
6104 // The work group counts are read via the command processor, not as a shader
6105 // storage read, hence GL_COMMAND_BARRIER_BIT.
6106 if (indirectBufWrittenInThisPass)
6107 barriers |= GL_COMMAND_BARRIER_BIT;
6108
6109 if (barriers) {
6110 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6112 cmd.args.barrier.barriers = barriers;
6113 }
6114
6115 QGles2Buffer *indirectBufD = QRHI_RES(QGles2Buffer, indirectBuffer);
6116
6118 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
6119 // When already registered - typically as the storage buffer the
6120 // arguments were generated into - registerBuffer() would reject the
6121 // differing access with a warning. Skipping it is fine because
6122 // BarriersForPass sets all buffer barrier bits anyway.
6123 if (!passResTracker.buffers().contains(indirectBufD)) {
6124 trackedRegisterBuffer(&passResTracker, indirectBufD,
6127 }
6128 }
6129
6130 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
6132 cmd.args.dispatchIndirect.buffer = indirectBufD->buffer;
6133 cmd.args.dispatchIndirect.offset = indirectBufferOffset;
6134}
6135
6136static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
6137{
6138 switch (type) {
6139 case QRhiShaderStage::Vertex:
6140 return GL_VERTEX_SHADER;
6141 case QRhiShaderStage::TessellationControl:
6143 case QRhiShaderStage::TessellationEvaluation:
6145 case QRhiShaderStage::Geometry:
6146 return GL_GEOMETRY_SHADER;
6147 case QRhiShaderStage::Fragment:
6148 return GL_FRAGMENT_SHADER;
6149 case QRhiShaderStage::Compute:
6150 return GL_COMPUTE_SHADER;
6151 default:
6152 Q_UNREACHABLE_RETURN(GL_VERTEX_SHADER);
6153 }
6154}
6155
6156static inline bool isGraphicsStage(const QRhiShaderStage &shaderStage)
6157{
6158 const QRhiShaderStage::Type t = shaderStage.type();
6159 return t == QRhiShaderStage::Vertex
6160 || t == QRhiShaderStage::TessellationControl
6161 || t == QRhiShaderStage::TessellationEvaluation
6162 || t == QRhiShaderStage::Geometry
6163 || t == QRhiShaderStage::Fragment;
6164}
6165
6167{
6168 QList<int> versionsToTry;
6169 if (caps.gles) {
6170 if (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)) {
6171 versionsToTry << 320 << 310 << 300 << 100;
6172 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
6173 versionsToTry << 310 << 300 << 100;
6174 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
6175 versionsToTry << 300 << 100;
6176 } else {
6177 versionsToTry << 100;
6178 }
6179 } else {
6180 if (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6)) {
6181 versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6182 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 5) {
6183 versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6184 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 4) {
6185 versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6186 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 3) {
6187 versionsToTry << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6188 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 2) {
6189 versionsToTry << 420 << 410 << 400 << 330 << 150 << 140 << 130;
6190 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 1) {
6191 versionsToTry << 410 << 400 << 330 << 150 << 140 << 130;
6192 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 0) {
6193 versionsToTry << 400 << 330 << 150 << 140 << 130;
6194 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 3) {
6195 versionsToTry << 330 << 150 << 140 << 130;
6196 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 2) {
6197 versionsToTry << 150 << 140 << 130;
6198 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
6199 versionsToTry << 140 << 130;
6200 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
6201 versionsToTry << 130;
6202 }
6203 if (!caps.coreProfile)
6204 versionsToTry << 120;
6205 }
6206
6207 const QShaderVersion::Flags flags = caps.gles ? QShaderVersion::GlslEs : QShaderVersion::Flags();
6208 QList<QShaderVersion> versions;
6209 versions.reserve(versionsToTry.size());
6210 for (int v : std::as_const(versionsToTry))
6211 versions.append(QShaderVersion(v, flags));
6212 return versions;
6213}
6214
6215/*
6216 Returns the highest GLSL ES version all graphics stages in stages have code
6217 for, or nothing when they share none. GLSL ES cannot link shaders of
6218 different versions; non-es GLSL can, hence the caps.gles check.
6219*/
6221 int stageCount) const
6222{
6223 if (!caps.gles)
6224 return std::nullopt;
6225
6226 for (const QShaderVersion &ver : glslVersionsToTry()) {
6227 bool allStagesHaveIt = true;
6228 for (int i = 0; i < stageCount; ++i) {
6229 const QRhiShaderStage &stage(stages[i]);
6230 if (!isGraphicsStage(stage))
6231 continue;
6232 const QShaderKey key(QShader::GlslShader, ver, stage.shaderVariant());
6233 if (stage.shader().shader(key).shader().isEmpty()) {
6234 allStagesHaveIt = false;
6235 break;
6236 }
6237 }
6238 if (allStagesHaveIt)
6239 return ver;
6240 }
6241 return std::nullopt;
6242}
6243
6244QByteArray QRhiGles2::shaderSource(const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion,
6245 std::optional<QShaderVersion> commonVersion)
6246{
6247 const QShader bakedShader = shaderStage.shader();
6248 const QList<QShaderVersion> versionsToTry = commonVersion
6249 ? QList<QShaderVersion>{ *commonVersion }
6250 : glslVersionsToTry();
6251
6252 QByteArray source;
6253 for (const QShaderVersion &ver : versionsToTry) {
6254 source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
6255 if (!source.isEmpty()) {
6256 if (shaderVersion)
6257 *shaderVersion = ver;
6258 break;
6259 }
6260 }
6261 if (source.isEmpty()) {
6262 qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
6263 << ") in baked shader" << bakedShader;
6264 }
6265 return source;
6266}
6267
6268bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion,
6269 std::optional<QShaderVersion> commonVersion)
6270{
6271 QShaderVersion actualVersion;
6272 const QByteArray source = shaderSource(shaderStage, &actualVersion, commonVersion);
6273 if (source.isEmpty())
6274 return false;
6275 if (shaderVersion)
6276 *shaderVersion = actualVersion;
6277
6278 GLuint shader;
6279 const auto cacheKey = std::make_pair(shaderStage, actualVersion);
6280 auto cacheIt = m_shaderCache.constFind(cacheKey);
6281 if (cacheIt != m_shaderCache.constEnd()) {
6282 shader = *cacheIt;
6283 } else {
6284 shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
6285 const char *srcStr = source.constData();
6286 const GLint srcLength = source.size();
6287 f->glShaderSource(shader, 1, &srcStr, &srcLength);
6288 f->glCompileShader(shader);
6289 GLint compiled = 0;
6290 f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
6291 if (!compiled) {
6292 GLint infoLogLength = 0;
6293 f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
6294 QByteArray log;
6295 if (infoLogLength > 1) {
6296 GLsizei length = 0;
6297 log.resize(infoLogLength);
6298 f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
6299 }
6300 qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
6301 f->glDeleteShader(shader);
6302 return false;
6303 }
6304 if (m_shaderCache.size() >= MAX_SHADER_CACHE_ENTRIES) {
6305 // Use the simplest strategy: too many cached shaders -> drop them all.
6306 for (uint shader : std::as_const(m_shaderCache))
6307 f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
6308 m_shaderCache.clear();
6309 }
6310 m_shaderCache.insert(cacheKey, shader);
6311 }
6312
6313 f->glAttachShader(program, shader);
6314
6315 return true;
6316}
6317
6318bool QRhiGles2::linkProgram(GLuint program)
6319{
6320 f->glLinkProgram(program);
6321 GLint linked = 0;
6322 f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
6323 if (!linked) {
6324 GLint infoLogLength = 0;
6325 f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
6326 QByteArray log;
6327 if (infoLogLength > 1) {
6328 GLsizei length = 0;
6329 log.resize(infoLogLength);
6330 f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
6331 }
6332 qWarning("Failed to link shader program: %s", log.constData());
6333 return false;
6334 }
6335 return true;
6336}
6337
6338void QRhiGles2::registerUniformIfActive(const QShaderDescription::BlockVariable &var,
6339 const QByteArray &namePrefix,
6340 int binding,
6341 int baseOffset,
6342 GLuint program,
6343 ActiveUniformLocationTracker *activeUniformLocations,
6344 QGles2UniformDescriptionVector *dst)
6345{
6346 if (var.type == QShaderDescription::Struct) {
6347 qWarning("Nested structs are not supported at the moment. '%s' ignored.",
6348 var.name.constData());
6349 return;
6350 }
6352 uniform.type = var.type;
6353 const QByteArray name = namePrefix + var.name;
6354 // Here we expect that the OpenGL implementation has proper active uniform
6355 // handling, meaning that a uniform that is declared but not accessed
6356 // elsewhere in the code is reported as -1 when querying the location. If
6357 // that is not the case, it won't break anything, but we'll generate
6358 // unnecessary glUniform* calls then.
6359 uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
6360 uniform.stateIndex = -1; // assigned later
6361 if (uniform.glslLocation >= 0 && !activeUniformLocations->hasSeen(uniform.glslLocation)) {
6362 if (var.arrayDims.size() > 1) {
6363 qWarning("Array '%s' has more than one dimension. This is not supported.",
6364 var.name.constData());
6365 return;
6366 }
6367 uniform.binding = binding;
6368 uniform.offset = uint(baseOffset + var.offset);
6369 uniform.size = var.size;
6370 uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
6371 // For matrices the distance between the columns is known from the
6372 // reflection. For arrays it has to be derived from the total size.
6373 // (std140 and std430 differ here: an array of scalars or vec2 is
6374 // tightly packed in the latter, which is what push constants use)
6375 if (var.matrixStride)
6376 uniform.elemStride = quint32(var.matrixStride);
6377 else if (uniform.arrayDim > 0 && var.size > 0)
6378 uniform.elemStride = quint32(var.size / uniform.arrayDim);
6379 else
6380 uniform.elemStride = 0;
6381 dst->append(uniform);
6382 }
6383}
6384
6386 const QByteArray &prefix,
6387 int binding,
6388 const QList<QShaderDescription::BlockVariable> &members,
6389 ActiveUniformLocationTracker *activeUniformLocations,
6390 QGles2UniformDescriptionVector *dst)
6391{
6392 for (const QShaderDescription::BlockVariable &blockMember : members) {
6393 if (blockMember.type == QShaderDescription::Struct) {
6394 QByteArray structPrefix = prefix + blockMember.name;
6395
6396 const int baseOffset = blockMember.offset;
6397 if (blockMember.arrayDims.isEmpty()) {
6398 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
6399 registerUniformIfActive(structMember, structPrefix + ".", binding,
6400 baseOffset, program, activeUniformLocations, dst);
6401 } else {
6402 if (blockMember.arrayDims.size() > 1) {
6403 qWarning("Array of struct '%s' has more than one dimension. Only the first "
6404 "dimension is used.",
6405 blockMember.name.constData());
6406 }
6407 const int dim = blockMember.arrayDims.first();
6408 if (dim < 1) {
6409 qWarning("Array of struct '%s' has an invalid first dimension (%d). Ignored.",
6410 blockMember.name.constData(), dim);
6411 continue;
6412 }
6413 const int elemSize = blockMember.size / dim;
6414 int elemOffset = baseOffset;
6415 for (int di = 0; di < dim; ++di) {
6416 const QByteArray arrayPrefix = structPrefix + '[' + QByteArray::number(di) + ']' + '.';
6417 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
6418 registerUniformIfActive(structMember, arrayPrefix, binding, elemOffset, program, activeUniformLocations, dst);
6419 elemOffset += elemSize;
6420 }
6421 }
6422 } else {
6423 registerUniformIfActive(blockMember, prefix, binding, 0, program, activeUniformLocations, dst);
6424 }
6425 }
6426}
6427
6428void QRhiGles2::gatherUniforms(GLuint program,
6429 const QShaderDescription::UniformBlock &ub,
6430 ActiveUniformLocationTracker *activeUniformLocations,
6431 QGles2UniformDescriptionVector *dst)
6432{
6433 gatherBlockMemberUniforms(program, ub.structName + '.', ub.binding, ub.members,
6434 activeUniformLocations, dst);
6435}
6436
6437void QRhiGles2::gatherPushConstantUniforms(GLuint program,
6438 const QShaderDescription::PushConstantBlock &pcb,
6439 ActiveUniformLocationTracker *activeUniformLocations,
6440 QGles2UniformDescriptionVector *dst)
6441{
6442 gatherBlockMemberUniforms(program, pcb.name + '.', -1, pcb.members, activeUniformLocations, dst);
6443}
6444
6445void QRhiGles2::gatherSamplers(GLuint program,
6446 const QShaderDescription::InOutVariable &v,
6447 QGles2SamplerDescriptionVector *dst)
6448{
6449 QGles2SamplerDescription sampler;
6450 sampler.glslLocation = f->glGetUniformLocation(program, v.name.constData());
6451 if (sampler.glslLocation >= 0) {
6452 sampler.combinedBinding = v.binding;
6453 sampler.tbinding = -1;
6454 sampler.sbinding = -1;
6455 dst->append(sampler);
6456 }
6457}
6458
6459void QRhiGles2::gatherGeneratedSamplers(GLuint program,
6460 const QShader::SeparateToCombinedImageSamplerMapping &mapping,
6461 QGles2SamplerDescriptionVector *dst)
6462{
6463 QGles2SamplerDescription sampler;
6464 sampler.glslLocation = f->glGetUniformLocation(program, mapping.combinedSamplerName.constData());
6465 if (sampler.glslLocation >= 0) {
6466 sampler.combinedBinding = -1;
6467 sampler.tbinding = mapping.textureBinding;
6468 sampler.sbinding = mapping.samplerBinding;
6469 dst->append(sampler);
6470 }
6471}
6472
6473void QRhiGles2::sanityCheckVertexFragmentInterface(const QShaderDescription &vsDesc, const QShaderDescription &fsDesc)
6474{
6475 if (!vsDesc.isValid() || !fsDesc.isValid())
6476 return;
6477
6478 // Print a warning if the fragment shader input for a given location uses a
6479 // name that does not match the vertex shader output at the same location.
6480 // This is not an error with any other API and not with GLSL >= 330 either,
6481 // but matters for older GLSL code that has no location qualifiers.
6482 const auto vsOutputs = vsDesc.outputVariables();
6483 const auto fsInputs = fsDesc.inputVariables();
6484 for (const QShaderDescription::InOutVariable &outVar : vsOutputs) {
6485 for (const QShaderDescription::InOutVariable &inVar : fsInputs) {
6486 if (inVar.location == outVar.location) {
6487 if (inVar.name != outVar.name) {
6488 qWarning("Vertex output name '%s' does not match fragment input '%s'. "
6489 "This should be avoided because it causes problems with older GLSL versions.",
6490 outVar.name.constData(), inVar.name.constData());
6491 }
6492 break;
6493 }
6494 }
6495 }
6496}
6497
6498bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
6499{
6500 static QOpenGLProgramBinarySupportCheckWrapper checker;
6501 return checker.get(ctx)->isSupported();
6502}
6503
6504Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
6505
6506static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
6507{
6508 switch (type) {
6509 case QRhiShaderStage::Vertex:
6510 return QShader::VertexStage;
6511 case QRhiShaderStage::TessellationControl:
6512 return QShader::TessellationControlStage;
6513 case QRhiShaderStage::TessellationEvaluation:
6514 return QShader::TessellationEvaluationStage;
6515 case QRhiShaderStage::Geometry:
6516 return QShader::GeometryStage;
6517 case QRhiShaderStage::Fragment:
6518 return QShader::FragmentStage;
6519 case QRhiShaderStage::Compute:
6520 return QShader::ComputeStage;
6521 default:
6522 Q_UNREACHABLE_RETURN(QShader::VertexStage);
6523 }
6524}
6525
6526QRhiGles2::ProgramCacheResult QRhiGles2::tryLoadFromDiskOrPipelineCache(const QRhiShaderStage *stages,
6527 int stageCount,
6528 GLuint program,
6529 const QVector<QShaderDescription::InOutVariable> &inputVars,
6530 QByteArray *cacheKey,
6531 std::optional<QShaderVersion> commonVersion)
6532{
6533 Q_ASSERT(cacheKey);
6534
6535 // the traditional QOpenGL disk cache since Qt 5.9
6536 const bool legacyDiskCacheEnabled = isProgramBinaryDiskCacheEnabled();
6537
6538 // QRhi's own (set)PipelineCacheData()
6539 const bool pipelineCacheEnabled = caps.programBinary && !m_pipelineCache.data.isEmpty();
6540
6541 // calculating the cache key based on the source code is common for both types of caches
6542 if (legacyDiskCacheEnabled || pipelineCacheEnabled) {
6543 QOpenGLProgramBinaryCache::ProgramDesc binaryProgram;
6544 for (int i = 0; i < stageCount; ++i) {
6545 const QRhiShaderStage &stage(stages[i]);
6546 // commonVersion covers the graphics stages only.
6547 QByteArray source = shaderSource(stage, nullptr,
6548 isGraphicsStage(stage) ? commonVersion : std::nullopt);
6549 if (source.isEmpty())
6550 return QRhiGles2::ProgramCacheError;
6551
6552 if (stage.type() == QRhiShaderStage::Vertex) {
6553 // Now add something to the key that indicates the vertex input locations.
6554 // A GLSL shader lower than 330 (150, 140, ...) will not have location
6555 // qualifiers. This means that the shader source code is the same
6556 // regardless of what locations inputVars contains. This becomes a problem
6557 // because we'll glBindAttribLocation the shader based on inputVars, but
6558 // that's only when compiling/linking when there was no cache hit. Picking
6559 // from the cache afterwards should take the input locations into account
6560 // since if inputVars has now different locations for the attributes, then
6561 // it is not ok to reuse a program binary that used different attribute
6562 // locations. For a lot of clients this would not be an issue since they
6563 // typically hardcode and use the same vertex locations on every run. Some
6564 // systems that dynamically generate shaders may end up with a non-stable
6565 // order (and so location numbers), however. This is sub-optimal because
6566 // it makes caching inefficient, and said clients should be fixed, but in
6567 // any case this should not break rendering. Hence including the locations
6568 // in the cache key.
6569 QMap<QByteArray, int> inputLocations; // sorted by key when iterating
6570 for (const QShaderDescription::InOutVariable &var : inputVars)
6571 inputLocations.insert(var.name, var.location);
6572 source += QByteArrayLiteral("\n // "); // just to be nice; treated as an arbitrary string regardless
6573 for (auto it = inputLocations.cbegin(), end = inputLocations.cend(); it != end; ++it) {
6574 source += it.key();
6575 source += QByteArray::number(it.value());
6576 }
6577 source += QByteArrayLiteral("\n");
6578 }
6579
6580 binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(toShaderStage(stage.type()), source));
6581 }
6582
6583 *cacheKey = binaryProgram.cacheKey();
6584
6585 // Try our pipeline cache simulation first, if it got seeded with
6586 // setPipelineCacheData and there's a hit, then no need to go to the
6587 // filesystem at all.
6588 if (pipelineCacheEnabled) {
6589 auto it = m_pipelineCache.data.constFind(*cacheKey);
6590 if (it != m_pipelineCache.data.constEnd()) {
6591 GLenum err;
6592 for ( ; ; ) {
6593 err = f->glGetError();
6594 if (err == GL_NO_ERROR || err == GL_CONTEXT_LOST)
6595 break;
6596 }
6597 f->glProgramBinary(program, it->format, it->data.constData(), it->data.size());
6598 err = f->glGetError();
6599 if (err == GL_NO_ERROR) {
6600 GLint linkStatus = 0;
6601 f->glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
6602 if (linkStatus == GL_TRUE)
6603 return QRhiGles2::ProgramCacheHit;
6604 }
6605 }
6606 }
6607
6608 if (legacyDiskCacheEnabled && qrhi_programBinaryCache()->load(*cacheKey, program)) {
6609 // use the logging category QOpenGLShaderProgram would
6610 qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
6611 program, cacheKey->constData());
6612 return QRhiGles2::ProgramCacheHit;
6613 }
6614 }
6615
6616 return QRhiGles2::ProgramCacheMiss;
6617}
6618
6619void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
6620{
6621 // This is only for the traditional QOpenGL disk cache since Qt 5.9.
6622
6623 if (isProgramBinaryDiskCacheEnabled()) {
6624 // use the logging category QOpenGLShaderProgram would
6625 qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
6626 program, cacheKey.constData());
6627 qrhi_programBinaryCache()->save(cacheKey, program);
6628 }
6629}
6630
6631void QRhiGles2::trySaveToPipelineCache(GLuint program, const QByteArray &cacheKey, bool force)
6632{
6633 // This handles our own simulated "pipeline cache". (specific to QRhi, not
6634 // shared with legacy QOpenGL* stuff)
6635
6636 if (caps.programBinary && (force || !m_pipelineCache.data.contains(cacheKey))) {
6637 GLint blobSize = 0;
6638 f->glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &blobSize);
6639 QByteArray blob(blobSize, Qt::Uninitialized);
6640 GLint outSize = 0;
6641 GLenum binaryFormat = 0;
6642 f->glGetProgramBinary(program, blobSize, &outSize, &binaryFormat, blob.data());
6643 if (blobSize == outSize)
6644 m_pipelineCache.insertWithCapacityLimit(cacheKey, { binaryFormat, blob });
6645 }
6646}
6647
6648QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
6649 : QRhiBuffer(rhi, type, usage, size)
6650{
6651}
6652
6653QGles2Buffer::~QGles2Buffer()
6654{
6655 destroy();
6656}
6657
6658void QGles2Buffer::destroy()
6659{
6660 data.clear();
6661 if (!buffer)
6662 return;
6663
6664 QRhiGles2::DeferredReleaseEntry e;
6665 e.type = QRhiGles2::DeferredReleaseEntry::Buffer;
6666
6667 e.buffer.buffer = buffer;
6668 buffer = 0;
6669
6670 QRHI_RES_RHI(QRhiGles2);
6671 if (rhiD) {
6672 rhiD->releaseQueue.append(e);
6673 rhiD->unregisterResource(this);
6674 }
6675}
6676
6677bool QGles2Buffer::create()
6678{
6679 if (buffer)
6680 destroy();
6681
6682 QRHI_RES_RHI(QRhiGles2);
6683
6684 nonZeroSize = m_size <= 0 ? 256 : m_size;
6685
6686 if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
6687 if (int(m_usage) != QRhiBuffer::UniformBuffer) {
6688 qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
6689 return false;
6690 }
6691 data.resize(nonZeroSize);
6692 return true;
6693 }
6694
6695 if (!rhiD->ensureContext())
6696 return false;
6697
6698 targetForDataOps = GL_ARRAY_BUFFER;
6699 if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
6700 targetForDataOps = GL_ELEMENT_ARRAY_BUFFER;
6701 else if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
6702 targetForDataOps = GL_SHADER_STORAGE_BUFFER;
6703 else if (m_usage.testFlag(QRhiBuffer::IndirectBuffer))
6704 targetForDataOps = GL_DRAW_INDIRECT_BUFFER;
6705
6706 buffer = rhiD->genBuffer();
6707 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6708 rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
6709
6710 if (rhiD->glObjectLabel)
6711 rhiD->glObjectLabel(GL_BUFFER, buffer, -1, m_objectName.constData());
6712
6713 usageState.access = AccessNone;
6714
6715 rhiD->registerResource(this);
6716 return true;
6717}
6718
6719QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer()
6720{
6721 if (m_usage.testFlag(QRhiBuffer::UniformBuffer))
6722 return { {}, 0 };
6723
6724 return { { &buffer }, 1 };
6725}
6726
6727char *QGles2Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
6728{
6729 Q_ASSERT(m_type == Dynamic);
6730 if (!m_usage.testFlag(UniformBuffer)) {
6731 QRHI_RES_RHI(QRhiGles2);
6732 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6733 if (rhiD->caps.properMapBuffer) {
6734 return static_cast<char *>(rhiD->f->glMapBufferRange(targetForDataOps, 0, nonZeroSize,
6736 } else {
6737 // Need some storage for the data, use the otherwise unused 'data' member.
6738 if (data.isEmpty())
6739 data.resize(nonZeroSize);
6740 }
6741 }
6742 return data.data();
6743}
6744
6745void QGles2Buffer::endFullDynamicBufferUpdateForCurrentFrame()
6746{
6747 if (!m_usage.testFlag(UniformBuffer)) {
6748 QRHI_RES_RHI(QRhiGles2);
6749 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6750 if (rhiD->caps.properMapBuffer)
6751 rhiD->f->glUnmapBuffer(targetForDataOps);
6752 else
6753 rhiD->f->glBufferSubData(targetForDataOps, 0, nonZeroSize, data.data());
6754 }
6755}
6756
6757void QGles2Buffer::fullDynamicBufferUpdateForCurrentFrame(const void *bufferData, quint32 size)
6758{
6759 const quint32 copySize = size > 0 ? size : m_size;
6760 if (!m_usage.testFlag(UniformBuffer)) {
6761 QRHI_RES_RHI(QRhiGles2);
6762 rhiD->f->glBindBuffer(targetForDataOps, buffer);
6763 rhiD->f->glBufferSubData(targetForDataOps, 0, copySize, bufferData);
6764 } else {
6765 memcpy(data.data(), bufferData, copySize);
6766 }
6767}
6768
6769QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
6770 int sampleCount, QRhiRenderBuffer::Flags flags,
6771 QRhiTexture::Format backingFormatHint)
6772 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
6773{
6774}
6775
6776QGles2RenderBuffer::~QGles2RenderBuffer()
6777{
6778 destroy();
6779}
6780
6781void QGles2RenderBuffer::destroy()
6782{
6783 if (!renderbuffer)
6784 return;
6785
6786 QRhiGles2::DeferredReleaseEntry e;
6787 e.type = QRhiGles2::DeferredReleaseEntry::RenderBuffer;
6788
6789 e.renderbuffer.renderbuffer = renderbuffer;
6790 e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
6791
6792 renderbuffer = 0;
6793 stencilRenderbuffer = 0;
6794
6795 QRHI_RES_RHI(QRhiGles2);
6796 if (rhiD) {
6797 if (owns)
6798 rhiD->releaseQueue.append(e);
6799 rhiD->unregisterResource(this);
6800 }
6801}
6802
6803bool QGles2RenderBuffer::create()
6804{
6805 if (renderbuffer)
6806 destroy();
6807
6808 QRHI_RES_RHI(QRhiGles2);
6809 samples = rhiD->effectiveSampleCount(m_sampleCount);
6810
6811 if (m_flags.testFlag(UsedWithSwapChainOnly)) {
6812 if (m_type == DepthStencil)
6813 return true;
6814
6815 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
6816 }
6817
6818 if (!rhiD->ensureContext())
6819 return false;
6820
6821 rhiD->f->glGenRenderbuffers(1, &renderbuffer);
6822 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
6823
6824 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
6825
6826 switch (m_type) {
6827 case QRhiRenderBuffer::DepthStencil:
6828 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
6829 if (rhiD->caps.glesMultisampleRenderToTexture) {
6830 // Must match the logic in QGles2TextureRenderTarget::create().
6831 // EXT and non-EXT are not the same thing.
6832 rhiD->glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
6833 size.width(), size.height());
6834 } else {
6835 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
6836 size.width(), size.height());
6837 }
6838 stencilRenderbuffer = 0;
6839 } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
6840 const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
6841 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage,
6842 size.width(), size.height());
6843 stencilRenderbuffer = 0;
6844 } else {
6845 GLenum depthStorage = GL_DEPTH_COMPONENT;
6846 if (rhiD->caps.gles) {
6847 if (rhiD->caps.depth24)
6848 depthStorage = GL_DEPTH_COMPONENT24;
6849 else
6850 depthStorage = GL_DEPTH_COMPONENT16; // plain ES 2.0 only has this
6851 }
6852 const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
6853 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage,
6854 size.width(), size.height());
6855 rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
6856 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
6857 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
6858 size.width(), size.height());
6859 }
6860 break;
6861 case QRhiRenderBuffer::Color:
6862 {
6863 GLenum internalFormat = GL_RGBA4; // ES 2.0
6864 if (rhiD->caps.rgba8Format) {
6865 internalFormat = GL_RGBA8;
6866 if (m_backingFormatHint != QRhiTexture::UnknownFormat) {
6867 GLenum glintformat, glformat, gltype;
6868 // only care about the sized internal format, the rest is not used here
6869 toGlTextureFormat(m_backingFormatHint, {}, rhiD->caps,
6870 &glintformat, &internalFormat, &glformat, &gltype);
6871 }
6872 }
6873 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
6874 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalFormat,
6875 size.width(), size.height());
6876 } else {
6877 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat,
6878 size.width(), size.height());
6879 }
6880 }
6881 break;
6882 default:
6883 Q_UNREACHABLE();
6884 break;
6885 }
6886
6887 if (rhiD->glObjectLabel)
6888 rhiD->glObjectLabel(GL_RENDERBUFFER, renderbuffer, -1, m_objectName.constData());
6889
6890 owns = true;
6891 generation += 1;
6892 rhiD->registerResource(this);
6893 return true;
6894}
6895
6896bool QGles2RenderBuffer::createFrom(NativeRenderBuffer src)
6897{
6898 if (!src.object)
6899 return false;
6900
6901 if (renderbuffer)
6902 destroy();
6903
6904 QRHI_RES_RHI(QRhiGles2);
6905 samples = rhiD->effectiveSampleCount(m_sampleCount);
6906
6907 if (m_flags.testFlag(UsedWithSwapChainOnly))
6908 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless when importing an existing native object");
6909
6910 if (!rhiD->ensureContext())
6911 return false;
6912
6913 renderbuffer = src.object;
6914
6915 owns = false;
6916 generation += 1;
6917 rhiD->registerResource(this);
6918 return true;
6919}
6920
6921QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
6922{
6923 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
6924 return m_backingFormatHint;
6925 else
6926 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
6927}
6928
6929QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
6930 int arraySize, int sampleCount, Flags flags)
6931 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
6932{
6933}
6934
6935QGles2Texture::~QGles2Texture()
6936{
6937 destroy();
6938}
6939
6940void QGles2Texture::destroy()
6941{
6942 if (!texture)
6943 return;
6944
6945 QRhiGles2::DeferredReleaseEntry e;
6946 e.type = QRhiGles2::DeferredReleaseEntry::Texture;
6947
6948 e.texture.texture = texture;
6949
6950 texture = 0;
6951 specified = false;
6952 for (quint32 &levelMask : allocatedLevels)
6953 levelMask = 0;
6954
6955 QRHI_RES_RHI(QRhiGles2);
6956 if (rhiD) {
6957 if (owns)
6958 rhiD->releaseQueue.append(e);
6959 rhiD->unregisterResource(this);
6960 }
6961}
6962
6963bool QGles2Texture::prepareCreate(QSize *adjustedSize)
6964{
6965 if (texture)
6966 destroy();
6967
6968 QRHI_RES_RHI(QRhiGles2);
6969 if (!rhiD->ensureContext())
6970 return false;
6971 if (!rhiD->isTextureFormatSupported(m_format, m_flags))
6972 return false;
6973
6974 const bool isCube = m_flags.testFlag(CubeMap);
6975 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
6976 const bool is3D = m_flags.testFlag(ThreeDimensional);
6977 const bool hasMipMaps = m_flags.testFlag(MipMapped);
6978 const bool isCompressed = rhiD->isCompressedFormat(m_format);
6979 const bool is1D = m_flags.testFlag(OneDimensional);
6980
6981 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
6982 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
6983
6984 samples = rhiD->effectiveSampleCount(m_sampleCount);
6985
6986 if (is3D && !rhiD->caps.texture3D) {
6987 qWarning("3D textures are not supported");
6988 return false;
6989 }
6990 if (isCube && is3D) {
6991 qWarning("Texture cannot be both cube and 3D");
6992 return false;
6993 }
6994 if (isArray && is3D) {
6995 qWarning("Texture cannot be both array and 3D");
6996 return false;
6997 }
6998 if (is1D && !rhiD->caps.texture1D) {
6999 qWarning("1D textures are not supported");
7000 return false;
7001 }
7002 if (is1D && is3D) {
7003 qWarning("Texture cannot be both 1D and 3D");
7004 return false;
7005 }
7006 if (is1D && isCube) {
7007 qWarning("Texture cannot be both 1D and cube");
7008 return false;
7009 }
7010
7011 if (m_depth > 1 && !is3D) {
7012 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
7013 return false;
7014 }
7015 if (m_arraySize > 0 && !isArray) {
7016 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
7017 return false;
7018 }
7019 if (m_arraySize < 1 && isArray) {
7020 qWarning("Texture is an array but array size is %d", m_arraySize);
7021 return false;
7022 }
7023
7024 target = isCube ? GL_TEXTURE_CUBE_MAP
7026 : (is3D ? GL_TEXTURE_3D
7027 : (is1D ? (isArray ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D)
7028 : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)));
7029
7030 if (m_flags.testFlag(ExternalOES))
7031 target = GL_TEXTURE_EXTERNAL_OES;
7032 else if (m_flags.testFlag(TextureRectangleGL))
7033 target = GL_TEXTURE_RECTANGLE;
7034
7035 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
7036 gltype = GL_UNSIGNED_BYTE;
7037
7038 if (isCompressed) {
7039 if (m_flags.testFlag(UsedWithLoadStore)) {
7040 qWarning("Compressed texture cannot be used with image load/store");
7041 return false;
7042 }
7043 glintformat = toGlCompressedTextureFormat(m_format, m_flags);
7044 if (!glintformat) {
7045 qWarning("Compressed format %d not mappable to GL compressed format", m_format);
7046 return false;
7047 }
7048 glsizedintformat = glintformat;
7049 glformat = GL_RGBA;
7050 } else {
7051 toGlTextureFormat(m_format, m_flags, rhiD->caps,
7052 &glintformat, &glsizedintformat, &glformat, &gltype);
7053 }
7054
7055 samplerState = QGles2SamplerData();
7056
7057 usageState.access = AccessNone;
7058
7059 if (!rhiD->textureFormatInfo(m_format, size, nullptr, nullptr, nullptr))
7060 return false;
7061
7062 if (adjustedSize)
7063 *adjustedSize = size;
7064
7065 return true;
7066}
7067
7068bool QGles2Texture::create()
7069{
7070 QSize size;
7071 if (!prepareCreate(&size))
7072 return false;
7073
7074 QRHI_RES_RHI(QRhiGles2);
7075 rhiD->f->glGenTextures(1, &texture);
7076
7077 const bool isCube = m_flags.testFlag(CubeMap);
7078 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
7079 const bool is3D = m_flags.testFlag(ThreeDimensional);
7080 const bool hasMipMaps = m_flags.testFlag(MipMapped);
7081 const bool isCompressed = rhiD->isCompressedFormat(m_format);
7082 const bool is1D = m_flags.testFlag(OneDimensional);
7083
7084 if (!isCompressed) {
7085 rhiD->f->glBindTexture(target, texture);
7086 if (!m_flags.testFlag(UsedWithLoadStore)) {
7087 if (is1D) {
7088 for (int level = 0; level < mipLevelCount; ++level) {
7089 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7090 if (isArray)
7091 rhiD->f->glTexImage2D(target, level, GLint(glintformat), mipSize.width(),
7092 qMax(0, m_arraySize), 0, glformat, gltype, nullptr);
7093 else
7094 rhiD->glTexImage1D(target, level, GLint(glintformat), mipSize.width(), 0,
7095 glformat, gltype, nullptr);
7096 }
7097 } else if (isArray) {
7098 const int layerCount = qMax(0, m_arraySize);
7099 if (hasMipMaps) {
7100 for (int level = 0; level != mipLevelCount; ++level) {
7101 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7102 rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), layerCount,
7103 0, glformat, gltype, nullptr);
7104 }
7105 } else {
7106 rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), layerCount,
7107 0, glformat, gltype, nullptr);
7108 }
7109 } else if (is3D) {
7110 if (hasMipMaps) {
7111 const int depth = qMax(1, m_depth);
7112 for (int level = 0; level != mipLevelCount; ++level) {
7113 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7114 const int mipDepth = rhiD->q->sizeForMipLevel(level, QSize(depth, depth)).width();
7115 rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), mipDepth,
7116 0, glformat, gltype, nullptr);
7117 }
7118 } else {
7119 rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), qMax(1, m_depth),
7120 0, glformat, gltype, nullptr);
7121 }
7122 } else if (hasMipMaps || isCube) {
7123 const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
7124 for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
7125 for (int level = 0; level != mipLevelCount; ++level) {
7126 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
7127 rhiD->f->glTexImage2D(faceTargetBase + uint(layer), level, GLint(glintformat),
7128 mipSize.width(), mipSize.height(), 0,
7129 glformat, gltype, nullptr);
7130 }
7131 }
7132 } else {
7133 // 2D texture. For multisample textures the GLES 3.1
7134 // glStorage2DMultisample must be used for portability.
7135 if (samples > 1 && rhiD->caps.multisampledTexture) {
7136 // internal format must be sized
7137 rhiD->f->glTexStorage2DMultisample(target, samples, glsizedintformat,
7138 size.width(), size.height(), GL_TRUE);
7139 } else {
7140 rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
7141 0, glformat, gltype, nullptr);
7142 }
7143 }
7144 } else {
7145 // Must be specified with immutable storage functions otherwise
7146 // bindImageTexture may fail. Also, the internal format must be a
7147 // sized format here.
7148 if (is1D && !isArray)
7149 rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
7150 else if (!is1D && (is3D || isArray))
7151 rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(),
7152 is3D ? qMax(1, m_depth) : qMax(0, m_arraySize));
7153 else if (samples > 1)
7154 rhiD->f->glTexStorage2DMultisample(target, samples, glsizedintformat,
7155 size.width(), size.height(), GL_TRUE);
7156 else
7157 rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
7158 is1D ? qMax(0, m_arraySize) : size.height());
7159 }
7160 // Make sure the min filter is set to something non-mipmap-based already
7161 // here, given the ridiculous default of GL. It is changed based on
7162 // the sampler later, but there could be cases when one pulls the native
7163 // object out via nativeTexture() right away.
7164 rhiD->f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
7165 specified = true;
7166 } else if (rhiD->caps.textureStorage && !m_flags.testFlag(ExternalOES)
7167 && (!is1D || isArray || rhiD->glTexStorage1D))
7168 {
7169 // A compressed format cannot be specified with glCompressedTexImage2D(nullptr),
7170 // but the immutable storage functions do not need data, so allocate all mip
7171 // levels up front. Some implementations reject certain compressed formats
7172 // here however (e.g. ASTC with the Android emulator), so check and fall back
7173 // to allocating each level upon the first upload touching it.
7174 rhiD->f->glBindTexture(target, texture);
7175 GLenum err;
7176 for ( ; ; ) {
7177 err = rhiD->f->glGetError();
7178 if (err == GL_NO_ERROR || err == GL_CONTEXT_LOST)
7179 break;
7180 }
7181 if (is1D && !isArray) {
7182 rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
7183 } else if (!is1D && (is3D || isArray)) {
7184 rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(),
7185 is3D ? qMax(1, m_depth) : qMax(0, m_arraySize));
7186 } else {
7187 rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
7188 is1D ? qMax(0, m_arraySize) : size.height());
7189 }
7190 specified = rhiD->f->glGetError() == GL_NO_ERROR;
7191 if (specified) // min filter must not be mipmap-based, see above
7192 rhiD->f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
7193 } else {
7194 specified = false;
7195 }
7196
7197 if (rhiD->glObjectLabel)
7198 rhiD->glObjectLabel(GL_TEXTURE, texture, -1, m_objectName.constData());
7199
7200 owns = true;
7201
7202 generation += 1;
7203 rhiD->registerResource(this);
7204 return true;
7205}
7206
7207bool QGles2Texture::createFrom(QRhiTexture::NativeTexture src)
7208{
7209 const uint textureId = uint(src.object);
7210 if (textureId == 0)
7211 return false;
7212
7213 if (!prepareCreate())
7214 return false;
7215
7216 texture = textureId;
7217 specified = true;
7218
7219 owns = false;
7220
7221 generation += 1;
7222 QRHI_RES_RHI(QRhiGles2);
7223 rhiD->registerResource(this);
7224 return true;
7225}
7226
7227QRhiTexture::NativeTexture QGles2Texture::nativeTexture()
7228{
7229 return {texture, 0};
7230}
7231
7232QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
7233 AddressMode u, AddressMode v, AddressMode w)
7234 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
7235{
7236}
7237
7238QGles2Sampler::~QGles2Sampler()
7239{
7240 destroy();
7241}
7242
7243void QGles2Sampler::destroy()
7244{
7245 QRHI_RES_RHI(QRhiGles2);
7246 if (rhiD)
7247 rhiD->unregisterResource(this);
7248}
7249
7250bool QGles2Sampler::create()
7251{
7252 d.glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode);
7253 d.glmagfilter = toGlMagFilter(m_magFilter);
7254 d.glwraps = toGlWrapMode(m_addressU);
7255 d.glwrapt = toGlWrapMode(m_addressV);
7256 d.glwrapr = toGlWrapMode(m_addressW);
7257 d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
7258
7259 generation += 1;
7260 QRHI_RES_RHI(QRhiGles2);
7261 rhiD->registerResource(this, false);
7262 return true;
7263}
7264
7265// dummy, no Vulkan-style RenderPass+Framebuffer concept here
7266QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi)
7267 : QRhiRenderPassDescriptor(rhi)
7268{
7269}
7270
7271QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
7272{
7273 destroy();
7274}
7275
7276void QGles2RenderPassDescriptor::destroy()
7277{
7278 QRHI_RES_RHI(QRhiGles2);
7279 if (rhiD)
7280 rhiD->unregisterResource(this);
7281}
7282
7283bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
7284{
7285 Q_UNUSED(other);
7286 return true;
7287}
7288
7289QRhiRenderPassDescriptor *QGles2RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
7290{
7291 QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
7292 QRHI_RES_RHI(QRhiGles2);
7293 rhiD->registerResource(rpD, false);
7294 return rpD;
7295}
7296
7297QVector<quint32> QGles2RenderPassDescriptor::serializedFormat() const
7298{
7299 return {};
7300}
7301
7302QGles2SwapChainRenderTarget::QGles2SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
7303 : QRhiSwapChainRenderTarget(rhi, swapchain),
7304 d(rhi)
7305{
7306}
7307
7308QGles2SwapChainRenderTarget::~QGles2SwapChainRenderTarget()
7309{
7310 destroy();
7311}
7312
7313void QGles2SwapChainRenderTarget::destroy()
7314{
7315 // nothing to do here
7316}
7317
7318QSize QGles2SwapChainRenderTarget::pixelSize() const
7319{
7320 return d.pixelSize;
7321}
7322
7323float QGles2SwapChainRenderTarget::devicePixelRatio() const
7324{
7325 return d.dpr;
7326}
7327
7328int QGles2SwapChainRenderTarget::sampleCount() const
7329{
7330 return d.sampleCount;
7331}
7332
7333QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi,
7334 const QRhiTextureRenderTargetDescription &desc,
7335 Flags flags)
7336 : QRhiTextureRenderTarget(rhi, desc, flags),
7337 d(rhi)
7338{
7339}
7340
7341QGles2TextureRenderTarget::~QGles2TextureRenderTarget()
7342{
7343 destroy();
7344}
7345
7346void QGles2TextureRenderTarget::destroy()
7347{
7348 if (!framebuffer)
7349 return;
7350
7351 QRhiGles2::DeferredReleaseEntry e;
7352 e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget;
7353
7354 e.textureRenderTarget.framebuffer = framebuffer;
7355 e.textureRenderTarget.nonMsaaThrowawayDepthTexture = nonMsaaThrowawayDepthTexture;
7356
7357 framebuffer = 0;
7358 nonMsaaThrowawayDepthTexture = 0;
7359
7360 QRHI_RES_RHI(QRhiGles2);
7361 if (rhiD) {
7362 rhiD->releaseQueue.append(e);
7363 rhiD->unregisterResource(this);
7364 }
7365}
7366
7367QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
7368{
7369 QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
7370 QRHI_RES_RHI(QRhiGles2);
7371 rhiD->registerResource(rpD, false);
7372 return rpD;
7373}
7374
7375bool QGles2TextureRenderTarget::create()
7376{
7377 QRHI_RES_RHI(QRhiGles2);
7378
7379 if (framebuffer)
7380 destroy();
7381
7382 const bool hasColorAttachments = m_desc.colorAttachmentCount() > 0;
7383 Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
7384 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
7385 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
7386
7387 if (hasColorAttachments) {
7388 const int count = int(m_desc.colorAttachmentCount());
7389 if (count > rhiD->caps.maxDrawBuffers) {
7390 qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
7391 count, rhiD->caps.maxDrawBuffers);
7392 }
7393 }
7394 if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
7395 qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
7396
7397 if (!rhiD->ensureContext())
7398 return false;
7399
7400 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments();
7401 it != itEnd; ++it) {
7402 const QRhiColorAttachment &colorAtt(*it);
7403 const QRhiTexture *resolveTexture = colorAtt.resolveTexture();
7404 if (!resolveTexture)
7405 continue;
7406
7407 const QRhiTexture *texture = colorAtt.texture();
7408 const QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
7409 Q_ASSERT(texture || renderBuffer);
7410 const QRhiTexture::Format sourceFormat = texture ? texture->format()
7411 : renderBuffer->backingFormat();
7412 const bool sourceIsSrgb = texture && texture->flags().testFlag(QRhiTexture::sRGB);
7413 const bool resolveIsSrgb = resolveTexture->flags().testFlag(QRhiTexture::sRGB);
7414 if (sourceFormat != resolveTexture->format() || sourceIsSrgb != resolveIsSrgb) {
7415 qWarning("Multisample resolve between different formats "
7416 "(%d, sRGB %d) and (%d, sRGB %d) is not supported.",
7417 int(sourceFormat), int(sourceIsSrgb),
7418 int(resolveTexture->format()), int(resolveIsSrgb));
7419 return false;
7420 }
7421 }
7422
7423 rhiD->f->glGenFramebuffers(1, &framebuffer);
7424 rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
7425
7426 d.colorAttCount = 0;
7427 d.srgbUpdateAndBlend = false;
7428 int attIndex = 0;
7429 int multiViewCount = 0;
7430 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
7431 d.colorAttCount += 1;
7432 const QRhiColorAttachment &colorAtt(*it);
7433 QRhiTexture *texture = colorAtt.texture();
7434 QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
7435 Q_ASSERT(texture || renderBuffer);
7436 if (texture) {
7437 QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
7438 Q_ASSERT(texD->texture && texD->specified);
7439 d.srgbUpdateAndBlend |= texD->flags().testFlag(QRhiTexture::sRGB);
7440 if (colorAtt.resolveTexture())
7441 d.srgbUpdateAndBlend |= colorAtt.resolveTexture()->flags().testFlag(QRhiTexture::sRGB);
7442 if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
7443 if (colorAtt.multiViewCount() < 2) {
7444 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
7445 colorAtt.level(), colorAtt.layer());
7446 } else {
7447 multiViewCount = colorAtt.multiViewCount();
7448 if (texD->samples > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture && colorAtt.resolveTexture()) {
7449 // Special path for GLES and GL_OVR_multiview_multisampled_render_to_texture:
7450 // ignore the color attachment's (multisample) texture
7451 // array and give the resolve texture array to GL. (no
7452 // explicit resolving is needed by us later on)
7453 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
7454 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7455 GL_COLOR_ATTACHMENT0 + uint(attIndex),
7456 resolveTexD->texture,
7457 colorAtt.resolveLevel(),
7458 texD->samples,
7459 colorAtt.resolveLayer(),
7460 multiViewCount);
7461 } else {
7462 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER,
7463 GL_COLOR_ATTACHMENT0 + uint(attIndex),
7464 texD->texture,
7465 colorAtt.level(),
7466 colorAtt.layer(),
7467 multiViewCount);
7468 }
7469 }
7470 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
7471 rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
7472 texD->target + uint(colorAtt.layer()), texD->texture,
7473 colorAtt.level());
7474 } else {
7475 if (texD->samples > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
7476 // Special path for GLES and GL_EXT_multisampled_render_to_texture:
7477 // ignore the color attachment's (multisample) texture and
7478 // give the resolve texture to GL. (no explicit resolving is
7479 // needed by us later on)
7480 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
7481 const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
7482 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
7483 resolveTexD->texture, colorAtt.level(), texD->samples);
7484 } else {
7485 const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
7486 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
7487 texD->texture, colorAtt.level());
7488 }
7489 }
7490 if (attIndex == 0) {
7491 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
7492 d.sampleCount = texD->samples;
7493 }
7494 } else if (renderBuffer) {
7495 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
7496 if (colorAtt.resolveTexture())
7497 d.srgbUpdateAndBlend |= colorAtt.resolveTexture()->flags().testFlag(QRhiTexture::sRGB);
7498 if (rbD->samples > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
7499 // Special path for GLES and GL_EXT_multisampled_render_to_texture: ignore
7500 // the (multisample) renderbuffer and give the resolve texture to GL. (so
7501 // no explicit resolve; depending on GL implementation internals, this may
7502 // play nicer with tiled architectures)
7503 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
7504 const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
7505 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
7506 resolveTexD->texture, colorAtt.level(), rbD->samples);
7507 } else {
7508 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer);
7509 }
7510 if (attIndex == 0) {
7511 d.pixelSize = rbD->pixelSize();
7512 d.sampleCount = rbD->samples;
7513 }
7514 }
7515 }
7516
7517 if (hasDepthStencil && m_desc.depthResolveTexture()) {
7518 const QRhiTexture::Format dstFormat = m_desc.depthResolveTexture()->format();
7519 QRhiTexture::Format srcFormat = QRhiTexture::UnknownFormat;
7520 if (m_desc.depthStencilBuffer())
7521 srcFormat = QRhiTexture::D24S8;
7522 else if (!rhiD->caps.glesMultisampleRenderToTexture)
7523 srcFormat = m_desc.depthTexture()->format();
7524 // else resolving is automatic
7525 if (srcFormat != QRhiTexture::UnknownFormat && srcFormat != dstFormat) {
7526 qWarning("Multisample depth-stencil source format %d does not match the "
7527 "depthResolveTexture format %d. OpenGL cannot blit between different depth formats; "
7528 "the depth contents will not be resolved.",
7529 int(srcFormat), int(dstFormat));
7530 }
7531 }
7532
7533 if (hasDepthStencil) {
7534 if (m_desc.depthStencilBuffer()) {
7535 QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer());
7536 if (rhiD->caps.needsDepthStencilCombinedAttach) {
7537 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
7538 depthRbD->renderbuffer);
7539 } else {
7540 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
7541 depthRbD->renderbuffer);
7542 if (depthRbD->stencilRenderbuffer) {
7543 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
7544 depthRbD->stencilRenderbuffer);
7545 } else {
7546 // packed depth-stencil
7547 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
7548 depthRbD->renderbuffer);
7549 }
7550 }
7551 if (d.colorAttCount == 0) {
7552 d.pixelSize = depthRbD->pixelSize();
7553 d.sampleCount = depthRbD->samples;
7554 }
7555 } else {
7556 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
7557 if (multiViewCount < 2) {
7558 if (depthTexD->samples > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) {
7559 // Special path for GLES and
7560 // GL_EXT_multisampled_render_to_texture, for depth-stencil.
7561 // Relevant only when depthResolveTexture is set.
7562 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
7563 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target,
7564 depthResolveTexD->texture, 0, depthTexD->samples);
7565 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
7566 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target,
7567 depthResolveTexD->texture, 0, depthTexD->samples);
7568 }
7569 } else if (rhiD->caps.ctxMajor >= 3 && depthTexD->flags().testFlag(QRhiTexture::TextureArray) && m_desc.depthLayer() >= 0) {
7570 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
7571 /*level*/0, m_desc.depthLayer());
7572 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
7573 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
7574 /*level*/0, m_desc.depthLayer());
7575 }
7576 } else {
7577 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
7578 depthTexD->texture, 0);
7579 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
7580 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
7581 depthTexD->texture, 0);
7582 }
7583 }
7584 } else {
7585 if (depthTexD->samples > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) {
7586 // And so it turns out
7587 // https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt
7588 // does not work with multisample 2D texture arrays. (at least
7589 // that's what Issue 30 in the extension spec seems to imply)
7590 //
7591 // There is https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multiview_texture_multisample.txt
7592 // that seems to resolve that, but that does not seem to
7593 // work (or not available) on GLES devices such as the Quest 3.
7594 //
7595 // So instead, on GLES we can use the
7596 // multisample-multiview-auto-resolving version (which in
7597 // turn is not supported on desktop GL e.g. by NVIDIA), too
7598 // bad we have a multisample depth texture array here as
7599 // every other API out there requires that. So, in absence
7600 // of a depthResolveTexture, create a temporary one ignoring
7601 // what the user has already created.
7602 //
7603 if (!m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture()) {
7604 qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set."
7605 " This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create"
7606 " a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture.");
7607 }
7608 if (m_desc.depthResolveTexture()) {
7609 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
7610 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7611 GL_DEPTH_ATTACHMENT,
7612 depthResolveTexD->texture,
7613 0,
7614 depthTexD->samples,
7615 0,
7616 multiViewCount);
7617 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
7618 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7619 GL_STENCIL_ATTACHMENT,
7620 depthResolveTexD->texture,
7621 0,
7622 depthTexD->samples,
7623 0,
7624 multiViewCount);
7625 }
7626 } else {
7627 if (!nonMsaaThrowawayDepthTexture) {
7628 rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
7629 rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
7630 rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
7631 depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
7632 }
7633 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7634 GL_DEPTH_ATTACHMENT,
7635 nonMsaaThrowawayDepthTexture,
7636 0,
7637 depthTexD->samples,
7638 0,
7639 multiViewCount);
7640 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
7641 GL_STENCIL_ATTACHMENT,
7642 nonMsaaThrowawayDepthTexture,
7643 0,
7644 depthTexD->samples,
7645 0,
7646 multiViewCount);
7647 }
7648 } else {
7649 // The depth texture here must be an array with at least
7650 // multiViewCount elements, and the format should be D24 or D32F
7651 // for depth only, or D24S8 for depth and stencil.
7652 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
7653 0, 0, multiViewCount);
7654 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
7655 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
7656 0, 0, multiViewCount);
7657 }
7658 }
7659 }
7660 if (d.colorAttCount == 0) {
7661 d.pixelSize = depthTexD->pixelSize();
7662 d.sampleCount = depthTexD->samples;
7663 }
7664 }
7665 d.dsAttCount = 1;
7666 } else {
7667 d.dsAttCount = 0;
7668 }
7669
7670 d.dpr = 1;
7671 d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
7672
7673 GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
7674 if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
7675 qWarning("Framebuffer incomplete: 0x%x", status);
7676 return false;
7677 }
7678
7679 if (rhiD->glObjectLabel)
7680 rhiD->glObjectLabel(GL_FRAMEBUFFER, framebuffer, -1, m_objectName.constData());
7681
7682 QRhiRenderTargetAttachmentTracker::updateResIdList<QGles2Texture, QGles2RenderBuffer>(m_desc, &d.currentResIdList);
7683
7684 rhiD->registerResource(this);
7685 return true;
7686}
7687
7688QSize QGles2TextureRenderTarget::pixelSize() const
7689{
7690 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(m_desc, d.currentResIdList))
7691 const_cast<QGles2TextureRenderTarget *>(this)->create();
7692
7693 return d.pixelSize;
7694}
7695
7696float QGles2TextureRenderTarget::devicePixelRatio() const
7697{
7698 return d.dpr;
7699}
7700
7701int QGles2TextureRenderTarget::sampleCount() const
7702{
7703 return d.sampleCount;
7704}
7705
7706QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi)
7707 : QRhiShaderResourceBindings(rhi)
7708{
7709}
7710
7711QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
7712{
7713 destroy();
7714}
7715
7716void QGles2ShaderResourceBindings::destroy()
7717{
7718 QRHI_RES_RHI(QRhiGles2);
7719 if (rhiD)
7720 rhiD->unregisterResource(this);
7721}
7722
7723bool QGles2ShaderResourceBindings::create()
7724{
7725 QRHI_RES_RHI(QRhiGles2);
7726 if (!rhiD->sanityCheckShaderResourceBindings(this))
7727 return false;
7728
7729 hasDynamicOffset = false;
7730 for (int i = 0, ie = m_bindings.size(); i != ie; ++i) {
7731 const QRhiShaderResourceBinding::Data *b = QRhiImplementation::shaderResourceBindingData(m_bindings.at(i));
7732 if (b->type() == QRhiShaderResourceBinding::UniformBuffer) {
7733 if (b->ubuf().hasDynamicOffset) {
7734 hasDynamicOffset = true;
7735 break;
7736 }
7737 }
7738 }
7739
7740 rhiD->updateLayoutDesc(this);
7741
7742 generation += 1;
7743 rhiD->registerResource(this, false);
7744 return true;
7745}
7746
7747void QGles2ShaderResourceBindings::updateResources(UpdateFlags flags)
7748{
7749 Q_UNUSED(flags);
7750 generation += 1;
7751}
7752
7753QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi)
7754 : QRhiGraphicsPipeline(rhi)
7755{
7756}
7757
7758QGles2GraphicsPipeline::~QGles2GraphicsPipeline()
7759{
7760 destroy();
7761}
7762
7763void QGles2GraphicsPipeline::destroy()
7764{
7765 if (!program)
7766 return;
7767
7768 QRhiGles2::DeferredReleaseEntry e;
7769 e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
7770
7771 e.pipeline.program = program;
7772
7773 program = 0;
7774 uniforms.clear();
7775 pushConstantUniforms.clear();
7776 pushConstantSize = 0;
7777 samplers.clear();
7778 uniformState.clear();
7779
7780 QRHI_RES_RHI(QRhiGles2);
7781 if (rhiD) {
7782 rhiD->releaseQueue.append(e);
7783 rhiD->unregisterResource(this);
7784 }
7785}
7786
7787bool QGles2GraphicsPipeline::create()
7788{
7789 QRHI_RES_RHI(QRhiGles2);
7790
7791 if (program)
7792 destroy();
7793
7794 if (!rhiD->ensureContext())
7795 return false;
7796
7797 rhiD->pipelineCreationStart();
7798 if (!rhiD->sanityCheckGraphicsPipeline(this))
7799 return false;
7800
7801 drawMode = toGlTopology(m_topology);
7802
7803 program = rhiD->f->glCreateProgram();
7804
7805 enum {
7806 VtxIdx = 0,
7807 TCIdx,
7808 TEIdx,
7809 GeomIdx,
7810 FragIdx,
7811 LastIdx
7812 };
7813 const auto descIdxForStage = [](const QRhiShaderStage &shaderStage) {
7814 switch (shaderStage.type()) {
7815 case QRhiShaderStage::Vertex:
7816 return VtxIdx;
7817 case QRhiShaderStage::TessellationControl:
7818 return TCIdx;
7819 case QRhiShaderStage::TessellationEvaluation:
7820 return TEIdx;
7821 case QRhiShaderStage::Geometry:
7822 return GeomIdx;
7823 case QRhiShaderStage::Fragment:
7824 return FragIdx;
7825 default:
7826 break;
7827 }
7828 Q_UNREACHABLE_RETURN(VtxIdx);
7829 };
7830 const std::optional<QShaderVersion> commonVersion =
7831 rhiD->commonGlslEsVersion(m_shaderStages.constData(), m_shaderStages.size());
7832
7833 QShaderDescription desc[LastIdx];
7834 QShader::SeparateToCombinedImageSamplerMappingList samplerMappingList[LastIdx];
7835 bool vertexFragmentOnly = true;
7836 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7837 if (isGraphicsStage(shaderStage)) {
7838 const int idx = descIdxForStage(shaderStage);
7839 if (idx != VtxIdx && idx != FragIdx)
7840 vertexFragmentOnly = false;
7841 QShader shader = shaderStage.shader();
7842 QShaderVersion shaderVersion;
7843 desc[idx] = shader.description();
7844 if (!rhiD->shaderSource(shaderStage, &shaderVersion, commonVersion).isEmpty()) {
7845 samplerMappingList[idx] = shader.separateToCombinedImageSamplerMappingList(
7846 { QShader::GlslShader, shaderVersion, shaderStage.shaderVariant() });
7847 }
7848 }
7849 }
7850
7851 QByteArray cacheKey;
7852 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(m_shaderStages.constData(),
7853 m_shaderStages.size(),
7854 program,
7855 desc[VtxIdx].inputVariables(),
7856 &cacheKey,
7857 commonVersion);
7858 if (cacheResult == QRhiGles2::ProgramCacheError)
7859 return false;
7860
7861 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
7862 if (rhiD->caps.gles && !commonVersion) {
7863 qWarning("The shader stages of this pipeline have no GLSL ES version in common; "
7864 "linking the program will fail");
7865 }
7866 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7867 if (isGraphicsStage(shaderStage)) {
7868 if (!rhiD->compileShader(program, shaderStage, nullptr, commonVersion))
7869 return false;
7870 }
7871 }
7872
7873 // important when GLSL <= 150 is used that does not have location qualifiers
7874 const auto vtxInputVars = desc[VtxIdx].inputVariables();
7875 for (const QShaderDescription::InOutVariable &inVar : vtxInputVars)
7876 rhiD->f->glBindAttribLocation(program, GLuint(inVar.location), inVar.name);
7877
7878 if (vertexFragmentOnly)
7879 rhiD->sanityCheckVertexFragmentInterface(desc[VtxIdx], desc[FragIdx]);
7880
7881 if (!rhiD->linkProgram(program))
7882 return false;
7883
7884 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
7885 // force replacing existing cache entry (if there is one, then
7886 // something is wrong with it, as there was no hit)
7887 rhiD->trySaveToPipelineCache(program, cacheKey, true);
7888 } else {
7889 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
7890 // was not enabled, so instead store to the Qt 5 disk cache
7891 rhiD->trySaveToDiskCache(program, cacheKey);
7892 }
7893 } else {
7894 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
7895 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
7896 // just so that it ends up in the pipeline cache also when the hit was
7897 // from the disk cache
7898 rhiD->trySaveToPipelineCache(program, cacheKey);
7899 }
7900 }
7901
7902 // Use the same work area for the vertex & fragment stages, thus ensuring
7903 // that we will not do superfluous glUniform calls for uniforms that are
7904 // present in both shaders.
7905 QRhiGles2::ActiveUniformLocationTracker activeUniformLocations;
7906
7907 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7908 if (isGraphicsStage(shaderStage)) {
7909 const int idx = descIdxForStage(shaderStage);
7910 const auto uniformBlocks = desc[idx].uniformBlocks();
7911 for (const QShaderDescription::UniformBlock &ub : uniformBlocks)
7912 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
7913 const auto pushConstantBlocks = desc[idx].pushConstantBlocks();
7914 for (const QShaderDescription::PushConstantBlock &pcb : pushConstantBlocks) {
7915 rhiD->gatherPushConstantUniforms(program, pcb, &activeUniformLocations, &pushConstantUniforms);
7916 pushConstantSize = qMax(pushConstantSize, quint32(pcb.size));
7917 }
7918 const auto combinedImageSamplers = desc[idx].combinedImageSamplers();
7919 for (const QShaderDescription::InOutVariable &v : combinedImageSamplers)
7920 rhiD->gatherSamplers(program, v, &samplers);
7921 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : std::as_const(samplerMappingList[idx]))
7922 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
7923 }
7924 }
7925
7926 std::sort(uniforms.begin(), uniforms.end(),
7927 [](const QGles2UniformDescription &a, const QGles2UniformDescription &b)
7928 {
7929 return a.offset < b.offset;
7930 });
7931
7932 QRhiGles2::setupUniformStateTracking(&uniforms, &pushConstantUniforms, &uniformState);
7933
7934 currentSrb = nullptr;
7935 currentSrbGeneration = 0;
7936
7937 if (rhiD->glObjectLabel)
7938 rhiD->glObjectLabel(GL_PROGRAM, program, -1, m_objectName.constData());
7939
7940 rhiD->pipelineCreationEnd();
7941 generation += 1;
7942 rhiD->registerResource(this);
7943 return true;
7944}
7945
7946QGles2ComputePipeline::QGles2ComputePipeline(QRhiImplementation *rhi)
7947 : QRhiComputePipeline(rhi)
7948{
7949}
7950
7951QGles2ComputePipeline::~QGles2ComputePipeline()
7952{
7953 destroy();
7954}
7955
7956void QGles2ComputePipeline::destroy()
7957{
7958 if (!program)
7959 return;
7960
7961 QRhiGles2::DeferredReleaseEntry e;
7962 e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
7963
7964 e.pipeline.program = program;
7965
7966 program = 0;
7967 uniforms.clear();
7968 pushConstantUniforms.clear();
7969 pushConstantSize = 0;
7970 samplers.clear();
7971 uniformState.clear();
7972
7973 QRHI_RES_RHI(QRhiGles2);
7974 if (rhiD) {
7975 rhiD->releaseQueue.append(e);
7976 rhiD->unregisterResource(this);
7977 }
7978}
7979
7980bool QGles2ComputePipeline::create()
7981{
7982 QRHI_RES_RHI(QRhiGles2);
7983
7984 if (program)
7985 destroy();
7986
7987 if (!rhiD->ensureContext())
7988 return false;
7989
7990 rhiD->pipelineCreationStart();
7991
7992 const QShaderDescription csDesc = m_shaderStage.shader().description();
7993 QShader::SeparateToCombinedImageSamplerMappingList csSamplerMappingList;
7994 QShaderVersion shaderVersion;
7995 if (!rhiD->shaderSource(m_shaderStage, &shaderVersion).isEmpty()) {
7996 csSamplerMappingList = m_shaderStage.shader().separateToCombinedImageSamplerMappingList(
7997 { QShader::GlslShader, shaderVersion, m_shaderStage.shaderVariant() });
7998 }
7999
8000 program = rhiD->f->glCreateProgram();
8001
8002 QByteArray cacheKey;
8003 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(&m_shaderStage, 1, program, {}, &cacheKey);
8004 if (cacheResult == QRhiGles2::ProgramCacheError)
8005 return false;
8006
8007 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
8008 if (!rhiD->compileShader(program, m_shaderStage, nullptr))
8009 return false;
8010
8011 if (!rhiD->linkProgram(program))
8012 return false;
8013
8014 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
8015 // force replacing existing cache entry (if there is one, then
8016 // something is wrong with it, as there was no hit)
8017 rhiD->trySaveToPipelineCache(program, cacheKey, true);
8018 } else {
8019 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
8020 // was not enabled, so instead store to the Qt 5 disk cache
8021 rhiD->trySaveToDiskCache(program, cacheKey);
8022 }
8023 } else {
8024 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
8025 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
8026 // just so that it ends up in the pipeline cache also when the hit was
8027 // from the disk cache
8028 rhiD->trySaveToPipelineCache(program, cacheKey);
8029 }
8030 }
8031
8032 QRhiGles2::ActiveUniformLocationTracker activeUniformLocations;
8033 const auto csUniformBlocks = csDesc.uniformBlocks();
8034 for (const QShaderDescription::UniformBlock &ub : csUniformBlocks)
8035 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
8036 const auto csPushConstantBlocks = csDesc.pushConstantBlocks();
8037 for (const QShaderDescription::PushConstantBlock &pcb : csPushConstantBlocks) {
8038 rhiD->gatherPushConstantUniforms(program, pcb, &activeUniformLocations, &pushConstantUniforms);
8039 pushConstantSize = qMax(pushConstantSize, quint32(pcb.size));
8040 }
8041 const auto csCombinedImageSamplers = csDesc.combinedImageSamplers();
8042 for (const QShaderDescription::InOutVariable &v : csCombinedImageSamplers)
8043 rhiD->gatherSamplers(program, v, &samplers);
8044 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : std::as_const(csSamplerMappingList))
8045 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
8046
8047 // storage images and buffers need no special steps here
8048
8049 QRhiGles2::setupUniformStateTracking(&uniforms, &pushConstantUniforms, &uniformState);
8050
8051 currentSrb = nullptr;
8052 currentSrbGeneration = 0;
8053
8054 rhiD->pipelineCreationEnd();
8055 generation += 1;
8056 rhiD->registerResource(this);
8057 return true;
8058}
8059
8060QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
8061 : QRhiCommandBuffer(rhi)
8062{
8063 resetState();
8064}
8065
8066QGles2CommandBuffer::~QGles2CommandBuffer()
8067{
8068 destroy();
8069}
8070
8071void QGles2CommandBuffer::destroy()
8072{
8073 // nothing to do here
8074}
8075
8076QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
8077 : QRhiSwapChain(rhi),
8078 rt(rhi, this),
8079 rtLeft(rhi, this),
8080 rtRight(rhi, this),
8081 cb(rhi)
8082{
8083}
8084
8085QGles2SwapChain::~QGles2SwapChain()
8086{
8087 destroy();
8088}
8089
8090void QGles2SwapChain::destroy()
8091{
8092 QRHI_RES_RHI(QRhiGles2);
8093 if (rhiD) {
8094 // Don't leave the window surface bound, the platform may destroy it next.
8095 if (surface && currentSurfaceForCurrentContext(rhiD->ctx) == surface)
8096 rhiD->ctx->doneCurrent();
8097 rhiD->unregisterResource(this);
8098 }
8099}
8100
8101QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
8102{
8103 return &cb;
8104}
8105
8106QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
8107{
8108 return &rt;
8109}
8110
8111QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer)
8112{
8113 if (targetBuffer == LeftBuffer)
8114 return rtLeft.d.isValid() ? &rtLeft : &rt;
8115 else if (targetBuffer == RightBuffer)
8116 return rtRight.d.isValid() ? &rtRight : &rt;
8117 else
8118 Q_UNREACHABLE_RETURN(nullptr);
8119}
8120
8121QSize QGles2SwapChain::surfacePixelSize()
8122{
8123 Q_ASSERT(m_window);
8124 if (QPlatformWindow *platformWindow = m_window->handle())
8125 // Prefer using QPlatformWindow geometry and DPR in order to avoid
8126 // errors due to rounded QWindow geometry.
8127 return platformWindow->geometry().size() * platformWindow->devicePixelRatio();
8128 else
8129 return m_window->size() * m_window->devicePixelRatio();
8130}
8131
8132bool QGles2SwapChain::isFormatSupported(Format f)
8133{
8134 return f == SDR;
8135}
8136
8137QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
8138{
8139 QGles2RenderPassDescriptor *rpD = new QGles2RenderPassDescriptor(m_rhi);
8140 QRHI_RES_RHI(QRhiGles2);
8141 rhiD->registerResource(rpD, false);
8142 return rpD;
8143}
8144
8145void QGles2SwapChain::initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt)
8146{
8147 rt->setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
8148 rt->d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
8149 rt->d.pixelSize = pixelSize;
8150 rt->d.dpr = float(m_window->devicePixelRatio());
8151 rt->d.sampleCount = qBound(1, m_sampleCount, 64);
8152 rt->d.colorAttCount = 1;
8153 rt->d.dsAttCount = m_depthStencil ? 1 : 0;
8154 rt->d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB);
8155}
8156
8157bool QGles2SwapChain::createOrResize()
8158{
8159 // can be called multiple times due to window resizes
8160 if (surface && surface != m_window)
8161 destroy();
8162
8163 surface = m_window;
8164 m_currentPixelSize = surfacePixelSize();
8165 pixelSize = m_currentPixelSize;
8166
8167 if (m_depthStencil && m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)
8168 && m_depthStencil->pixelSize() != pixelSize)
8169 {
8170 m_depthStencil->setPixelSize(pixelSize);
8171 m_depthStencil->create();
8172 }
8173
8174 initSwapChainRenderTarget(&rt);
8175
8176 if (m_window->format().stereo()) {
8177 initSwapChainRenderTarget(&rtLeft);
8178 rtLeft.d.stereoTarget = QRhiSwapChain::LeftBuffer;
8179 initSwapChainRenderTarget(&rtRight);
8180 rtRight.d.stereoTarget = QRhiSwapChain::RightBuffer;
8181 }
8182
8183 QRHI_RES_RHI(QRhiGles2);
8184 if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps) && rhiD->caps.timestamps)
8185 timestamps.prepare(rhiD);
8186
8187 // The only reason to register this fairly fake gl swapchain
8188 // object with no native resources underneath is to be able to
8189 // implement a safe destroy().
8190 rhiD->registerResource(this, false);
8191
8192 return true;
8193}
8194
8195void QGles2SwapChainTimestamps::prepare(QRhiGles2 *rhiD)
8196{
8197 if (!query[0])
8198 rhiD->f->glGenQueries(TIMESTAMP_PAIRS * 2, query);
8199}
8200
8201void QGles2SwapChainTimestamps::destroy(QRhiGles2 *rhiD)
8202{
8203 rhiD->f->glDeleteQueries(TIMESTAMP_PAIRS * 2, query);
8204 memset(active, 0, sizeof(active));
8205 memset(query, 0, sizeof(query));
8206}
8207
8208bool QGles2SwapChainTimestamps::tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
8209{
8210 if (!active[pairIndex])
8211 return false;
8212
8213 GLuint tsStart = query[pairIndex * 2];
8214 GLuint tsEnd = query[pairIndex * 2 + 1];
8215
8216 GLuint ready = GL_FALSE;
8217 rhiD->f->glGetQueryObjectuiv(tsEnd, GL_QUERY_RESULT_AVAILABLE, &ready);
8218
8219 if (!ready)
8220 return false;
8221
8222 bool result = false;
8223 quint64 timestamps[2];
8224 rhiD->glGetQueryObjectui64v(tsStart, GL_QUERY_RESULT, &timestamps[0]);
8225 rhiD->glGetQueryObjectui64v(tsEnd, GL_QUERY_RESULT, &timestamps[1]);
8226
8227 if (timestamps[1] >= timestamps[0]) {
8228 const quint64 nanoseconds = timestamps[1] - timestamps[0];
8229 *elapsedSec = nanoseconds / 1000000000.0;
8230 result = true;
8231 }
8232
8233 active[pairIndex] = false;
8234 return result;
8235}
8236
8237QT_END_NAMESPACE
const GLvoid const GLvoid GLenum
QRhiStats statistics() override
void releaseBufferNameCache()
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
void(QOPENGLF_APIENTRYP glRenderbufferStorageMultisampleEXT)(GLenum
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
int bufferNameCacheCount
QRhiDriverInfo driverInfo() const override
void(QOPENGLF_APIENTRYP glFramebufferTextureMultisampleMultiviewOVR)(GLenum
bool needsMakeCurrentDueToSwap
QRhiShadingRateMap * createShadingRateMap() override
const void quint64 GLuint
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
GLuint genBuffer()
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:13674
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, const UsageState &state)
Definition qrhi.cpp:13714
const QRhiPassResourceMap< QRhiBuffer *, Buffer, 12 > & buffers() const
Definition qrhi_p.h:968
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:754
\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
@ 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.
bool isLevelAllocated(int face, int level) const
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:2016