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