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