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