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