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