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