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