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