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