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