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