Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
155#ifndef GL_BGRA
156#define GL_BGRA 0x80E1
157#endif
158
159#ifndef GL_R8
160#define GL_R8 0x8229
161#endif
162
163#ifndef GL_RG8
164#define GL_RG8 0x822B
165#endif
166
167#ifndef GL_RG
168#define GL_RG 0x8227
169#endif
170
171#ifndef GL_R16
172#define GL_R16 0x822A
173#endif
174
175#ifndef GL_RG16
176#define GL_RG16 0x822C
177#endif
178
179#ifndef GL_RED
180#define GL_RED 0x1903
181#endif
182
183#ifndef GL_RGBA8
184#define GL_RGBA8 0x8058
185#endif
186
187#ifndef GL_RGBA32F
188#define GL_RGBA32F 0x8814
189#endif
190
191#ifndef GL_RGBA16F
192#define GL_RGBA16F 0x881A
193#endif
194
195#ifndef GL_R16F
196#define GL_R16F 0x822D
197#endif
198
199#ifndef GL_R32F
200#define GL_R32F 0x822E
201#endif
202
203#ifndef GL_HALF_FLOAT
204#define GL_HALF_FLOAT 0x140B
205#endif
206
207#ifndef GL_DEPTH_COMPONENT16
208#define GL_DEPTH_COMPONENT16 0x81A5
209#endif
210
211#ifndef GL_DEPTH_COMPONENT24
212#define GL_DEPTH_COMPONENT24 0x81A6
213#endif
214
215#ifndef GL_DEPTH_COMPONENT32F
216#define GL_DEPTH_COMPONENT32F 0x8CAC
217#endif
218
219#ifndef GL_UNSIGNED_INT_24_8
220#define GL_UNSIGNED_INT_24_8 0x84FA
221#endif
222
223#ifndef GL_STENCIL_INDEX
224#define GL_STENCIL_INDEX 0x1901
225#endif
226
227#ifndef GL_STENCIL_INDEX8
228#define GL_STENCIL_INDEX8 0x8D48
229#endif
230
231#ifndef GL_DEPTH24_STENCIL8
232#define GL_DEPTH24_STENCIL8 0x88F0
233#endif
234
235#ifndef GL_DEPTH_STENCIL_ATTACHMENT
236#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
237#endif
238
239#ifndef GL_DEPTH_STENCIL
240#define GL_DEPTH_STENCIL 0x84F9
241#endif
242
243#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
244#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
245#endif
246
247#ifndef GL_FRAMEBUFFER_SRGB
248#define GL_FRAMEBUFFER_SRGB 0x8DB9
249#endif
250
251#ifndef GL_READ_FRAMEBUFFER
252#define GL_READ_FRAMEBUFFER 0x8CA8
253#endif
254
255#ifndef GL_DRAW_FRAMEBUFFER
256#define GL_DRAW_FRAMEBUFFER 0x8CA9
257#endif
258
259#ifndef GL_MAX_DRAW_BUFFERS
260#define GL_MAX_DRAW_BUFFERS 0x8824
261#endif
262
263#ifndef GL_TEXTURE_COMPARE_MODE
264#define GL_TEXTURE_COMPARE_MODE 0x884C
265#endif
266
267#ifndef GL_COMPARE_REF_TO_TEXTURE
268#define GL_COMPARE_REF_TO_TEXTURE 0x884E
269#endif
270
271#ifndef GL_TEXTURE_COMPARE_FUNC
272#define GL_TEXTURE_COMPARE_FUNC 0x884D
273#endif
274
275#ifndef GL_MAX_SAMPLES
276#define GL_MAX_SAMPLES 0x8D57
277#endif
278
279#ifndef GL_SHADER_STORAGE_BUFFER
280#define GL_SHADER_STORAGE_BUFFER 0x90D2
281#endif
282
283#ifndef GL_READ_ONLY
284#define GL_READ_ONLY 0x88B8
285#endif
286
287#ifndef GL_WRITE_ONLY
288#define GL_WRITE_ONLY 0x88B9
289#endif
290
291#ifndef GL_READ_WRITE
292#define GL_READ_WRITE 0x88BA
293#endif
294
295#ifndef GL_COMPUTE_SHADER
296#define GL_COMPUTE_SHADER 0x91B9
297#endif
298
299#ifndef GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
300#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
301#endif
302
303#ifndef GL_ELEMENT_ARRAY_BARRIER_BIT
304#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
305#endif
306
307#ifndef GL_UNIFORM_BARRIER_BIT
308#define GL_UNIFORM_BARRIER_BIT 0x00000004
309#endif
310
311#ifndef GL_BUFFER_UPDATE_BARRIER_BIT
312#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200
313#endif
314
315#ifndef GL_SHADER_STORAGE_BARRIER_BIT
316#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
317#endif
318
319#ifndef GL_TEXTURE_FETCH_BARRIER_BIT
320#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
321#endif
322
323#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
324#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
325#endif
326
327#ifndef GL_PIXEL_BUFFER_BARRIER_BIT
328#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080
329#endif
330
331#ifndef GL_TEXTURE_UPDATE_BARRIER_BIT
332#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100
333#endif
334
335#ifndef GL_FRAMEBUFFER_BARRIER_BIT
336#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
337#endif
338
339#ifndef GL_ALL_BARRIER_BITS
340#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
341#endif
342
343#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
344#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
345#endif
346
347#ifndef GL_POINT_SPRITE
348#define GL_POINT_SPRITE 0x8861
349#endif
350
351#ifndef GL_MAP_READ_BIT
352#define GL_MAP_READ_BIT 0x0001
353#endif
354
355#ifndef GL_MAP_WRITE_BIT
356#define GL_MAP_WRITE_BIT 0x0002
357#endif
358
359#ifndef GL_TEXTURE_2D_MULTISAMPLE
360#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
361#endif
362
363#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY
364#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
365#endif
366
367#ifndef GL_TEXTURE_EXTERNAL_OES
368#define GL_TEXTURE_EXTERNAL_OES 0x8D65
369#endif
370
371#ifndef GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS
372#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB
373#endif
374
375#ifndef GL_MAX_COMPUTE_WORK_GROUP_COUNT
376#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE
377#endif
378
379#ifndef GL_MAX_COMPUTE_WORK_GROUP_SIZE
380#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF
381#endif
382
383#ifndef GL_TEXTURE_CUBE_MAP_SEAMLESS
384#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
385#endif
386
387#ifndef GL_CONTEXT_LOST
388#define GL_CONTEXT_LOST 0x0507
389#endif
390
391#ifndef GL_PROGRAM_BINARY_LENGTH
392#define GL_PROGRAM_BINARY_LENGTH 0x8741
393#endif
394
395#ifndef GL_NUM_PROGRAM_BINARY_FORMATS
396#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
397#endif
398
399#ifndef GL_UNPACK_ROW_LENGTH
400#define GL_UNPACK_ROW_LENGTH 0x0CF2
401#endif
402
403#ifndef GL_TEXTURE_3D
404#define GL_TEXTURE_3D 0x806F
405#endif
406
407#ifndef GL_TEXTURE_WRAP_R
408#define GL_TEXTURE_WRAP_R 0x8072
409#endif
410
411#ifndef GL_TEXTURE_RECTANGLE
412#define GL_TEXTURE_RECTANGLE 0x84F5
413#endif
414
415#ifndef GL_TEXTURE_2D_ARRAY
416#define GL_TEXTURE_2D_ARRAY 0x8C1A
417#endif
418
419#ifndef GL_MAX_ARRAY_TEXTURE_LAYERS
420#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
421#endif
422
423#ifndef GL_MAX_VERTEX_UNIFORM_COMPONENTS
424#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
425#endif
426
427#ifndef GL_MAX_FRAGMENT_UNIFORM_COMPONENTS
428#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
429#endif
430
431#ifndef GL_MAX_VERTEX_UNIFORM_VECTORS
432#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
433#endif
434
435#ifndef GL_MAX_FRAGMENT_UNIFORM_VECTORS
436#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
437#endif
438
439#ifndef GL_RGB10_A2
440#define GL_RGB10_A2 0x8059
441#endif
442
443#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
444#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
445#endif
446
447#ifndef GL_MAX_VARYING_COMPONENTS
448#define GL_MAX_VARYING_COMPONENTS 0x8B4B
449#endif
450
451#ifndef GL_MAX_VARYING_FLOATS
452#define GL_MAX_VARYING_FLOATS 0x8B4B
453#endif
454
455#ifndef GL_MAX_VARYING_VECTORS
456#define GL_MAX_VARYING_VECTORS 0x8DFC
457#endif
458
459#ifndef GL_TESS_CONTROL_SHADER
460#define GL_TESS_CONTROL_SHADER 0x8E88
461#endif
462
463#ifndef GL_TESS_EVALUATION_SHADER
464#define GL_TESS_EVALUATION_SHADER 0x8E87
465#endif
466
467#ifndef GL_PATCH_VERTICES
468#define GL_PATCH_VERTICES 0x8E72
469#endif
470
471#ifndef GL_LINE
472#define GL_LINE 0x1B01
473#endif
474
475#ifndef GL_FILL
476#define GL_FILL 0x1B02
477#endif
478
479#ifndef GL_PATCHES
480#define GL_PATCHES 0x000E
481#endif
482
483#ifndef GL_GEOMETRY_SHADER
484#define GL_GEOMETRY_SHADER 0x8DD9
485#endif
486
487#ifndef GL_BACK_LEFT
488#define GL_BACK_LEFT 0x0402
489#endif
490
491#ifndef GL_BACK_RIGHT
492#define GL_BACK_RIGHT 0x0403
493#endif
494
495#ifndef GL_TEXTURE_1D
496# define GL_TEXTURE_1D 0x0DE0
497#endif
498
499#ifndef GL_TEXTURE_1D_ARRAY
500# define GL_TEXTURE_1D_ARRAY 0x8C18
501#endif
502
503#ifndef GL_HALF_FLOAT
504#define GL_HALF_FLOAT 0x140B
505#endif
506
507#ifndef GL_MAX_VERTEX_OUTPUT_COMPONENTS
508#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
509#endif
510
511#ifndef GL_TIMESTAMP
512#define GL_TIMESTAMP 0x8E28
513#endif
514
515#ifndef GL_QUERY_RESULT
516#define GL_QUERY_RESULT 0x8866
517#endif
518
519#ifndef GL_QUERY_RESULT_AVAILABLE
520#define GL_QUERY_RESULT_AVAILABLE 0x8867
521#endif
522
523#ifndef GL_BUFFER
524#define GL_BUFFER 0x82E0
525#endif
526
527#ifndef GL_PROGRAM
528#define GL_PROGRAM 0x82E2
529#endif
530
536QRhiGles2InitParams::QRhiGles2InitParams()
537{
539}
540
557QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
558{
560
561 // To resolve all fields in the format as much as possible, create a context.
562 // This may be heavy, but allows avoiding BAD_MATCH on some systems.
563 QOpenGLContext tempContext;
564 tempContext.setFormat(fmt);
565 if (tempContext.create())
566 fmt = tempContext.format();
567 else
568 qWarning("QRhiGles2: Failed to create temporary context");
569
571 s->setFormat(fmt);
572 s->create();
573
574 return s;
575}
576
578 : ofr(this)
579{
580 requestedFormat = params->format;
581 fallbackSurface = params->fallbackSurface;
582 maybeWindow = params->window; // may be null
583 maybeShareContext = params->shareContext; // may be null
584
585 importedContext = importDevice != nullptr;
586 if (importedContext) {
587 ctx = importDevice->context;
588 if (!ctx) {
589 qWarning("No OpenGL context given, cannot import");
590 importedContext = false;
591 }
592 }
593}
594
596{
598 return nullptr;
599
600 QSurface *currentSurface = ctx->surface();
601 if (!currentSurface)
602 return nullptr;
603
604 if (currentSurface->surfaceClass() == QSurface::Window && !currentSurface->surfaceHandle())
605 return nullptr;
606
607 return currentSurface;
608}
609
611{
612 // With Apple's deprecated OpenGL support we need to minimize the usage of
613 // QOffscreenSurface since delicate problems can pop up with
614 // NSOpenGLContext and drawables.
615#if defined(Q_OS_MACOS)
616 return maybeWindow && maybeWindow->handle() ? static_cast<QSurface *>(maybeWindow) : fallbackSurface;
617#else
618 return fallbackSurface;
619#endif
620}
621
623{
624 if (!surface) {
625 // null means any surface is good because not going to render
627 return true;
628 // if the context is not already current with a valid surface, use our
629 // fallback surface, but platform specific quirks may apply
630 surface = evaluateFallbackSurface();
631 } else if (surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
632 // the window is not usable anymore (no native window underneath), behave as if offscreen
633 surface = evaluateFallbackSurface();
635 // bail out if the makeCurrent is not necessary
636 return true;
637 }
639
640 if (!ctx->makeCurrent(surface)) {
641 if (ctx->isValid()) {
642 qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen.");
643 } else {
644 qWarning("QRhiGles2: Context is lost.");
645 contextLost = true;
646 }
647 return false;
648 }
649
650 return true;
651}
652
654{
655 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
656 switch (format) {
657 case QRhiTexture::BC1:
658 return srgb ? 0x8C4C : 0x83F0;
659 case QRhiTexture::BC2:
660 return srgb ? 0x8C4E : 0x83F2;
661 case QRhiTexture::BC3:
662 return srgb ? 0x8C4F : 0x83F3;
663
665 return srgb ? 0x9275 : 0x9274;
667 return srgb ? 0x9277 : 0x9276;
669 return srgb ? 0x9279 : 0x9278;
670
672 return srgb ? 0x93D0 : 0x93B0;
674 return srgb ? 0x93D1 : 0x93B1;
676 return srgb ? 0x93D2 : 0x93B2;
678 return srgb ? 0x93D3 : 0x93B3;
680 return srgb ? 0x93D4 : 0x93B4;
682 return srgb ? 0x93D5 : 0x93B5;
684 return srgb ? 0x93D6 : 0x93B6;
686 return srgb ? 0x93D7 : 0x93B7;
688 return srgb ? 0x93D8 : 0x93B8;
690 return srgb ? 0x93D9 : 0x93B9;
692 return srgb ? 0x93DA : 0x93BA;
694 return srgb ? 0x93DB : 0x93BB;
696 return srgb ? 0x93DC : 0x93BC;
698 return srgb ? 0x93DD : 0x93BD;
699
700 default:
701 return 0; // this is reachable, just return an invalid format
702 }
703}
704
705bool QRhiGles2::create(QRhi::Flags flags)
706{
708 rhiFlags = flags;
709
710 if (!importedContext) {
711 ctx = new QOpenGLContext;
713 if (maybeShareContext) {
714 ctx->setShareContext(maybeShareContext);
715 ctx->setScreen(maybeShareContext->screen());
716 } else if (QOpenGLContext *shareContext = qt_gl_global_share_context()) {
717 ctx->setShareContext(shareContext);
718 ctx->setScreen(shareContext->screen());
719 } else if (maybeWindow) {
720 ctx->setScreen(maybeWindow->screen());
721 }
722 if (!ctx->create()) {
723 qWarning("QRhiGles2: Failed to create context");
724 delete ctx;
725 ctx = nullptr;
726 return false;
727 }
728 qCDebug(QRHI_LOG_INFO) << "Created OpenGL context" << ctx->format();
729 }
730
731 if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
732 return false;
733
734 f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
735 const QSurfaceFormat actualFormat = ctx->format();
736 caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
737
738 if (!caps.gles) {
739 glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
740 ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
741
742 glTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
743 GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void *)>(
744 ctx->getProcAddress(QByteArrayLiteral("glTexImage1D")));
745
746 glTexStorage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei)>(
747 ctx->getProcAddress(QByteArrayLiteral("glTexStorage1D")));
748
749 glTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
750 GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *)>(
751 ctx->getProcAddress(QByteArrayLiteral("glTexSubImage1D")));
752
753 glCopyTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint,
754 GLint, GLsizei)>(
755 ctx->getProcAddress(QByteArrayLiteral("glCopyTexSubImage1D")));
756
757 glCompressedTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
758 GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *)>(
759 ctx->getProcAddress(QByteArrayLiteral("glCompressedTexImage1D")));
760
761 glCompressedTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
762 GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *)>(
763 ctx->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage1D")));
764
765 glFramebufferTexture1D =
766 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint)>(
767 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture1D")));
768 }
769
770 const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
771 const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
772 const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
773 if (vendor && renderer && version)
774 qCDebug(QRHI_LOG_INFO, "OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
775
776 if (vendor) {
779 }
780 if (renderer) {
783 }
784 if (version)
786
787 caps.ctxMajor = actualFormat.majorVersion();
788 caps.ctxMinor = actualFormat.minorVersion();
789
790 GLint n = 0;
791 f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
792 if (n > 0) {
793 QVarLengthArray<GLint, 16> compressedTextureFormats(n);
794 f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compressedTextureFormats.data());
795 for (GLint format : compressedTextureFormats)
797
798 }
799 // The above looks nice, if only it worked always. With GLES the list we
800 // query is likely the full list of compressed formats (mostly anything
801 // that can be decoded). With OpenGL however the list is not required to
802 // include all formats due to the way the spec is worded. For instance, we
803 // cannot rely on ASTC formats being present in the list on non-ES. Some
804 // drivers do include them (Intel, NVIDIA), some don't (Mesa). On the other
805 // hand, relying on extension strings only is not ok: for example, Intel
806 // reports GL_KHR_texture_compression_astc_ldr whereas NVIDIA doesn't. So
807 // the only reasonable thing to do is to query the list always and then see
808 // if there is something we can add - if not already in there.
809 std::array<QRhiTexture::Flags, 2> textureVariantFlags;
810 textureVariantFlags[0] = {};
811 textureVariantFlags[1] = QRhiTexture::sRGB;
812 if (f->hasOpenGLExtension(QOpenGLExtensions::DDSTextureCompression)) {
813 for (QRhiTexture::Flags f : textureVariantFlags) {
817 }
818 }
819 if (f->hasOpenGLExtension(QOpenGLExtensions::ETC2TextureCompression)) {
820 for (QRhiTexture::Flags f : textureVariantFlags) {
824 }
825 }
826 if (f->hasOpenGLExtension(QOpenGLExtensions::ASTCTextureCompression)) {
827 for (QRhiTexture::Flags f : textureVariantFlags) {
841 }
842 }
843
844 f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
845
846 if (!caps.gles || caps.ctxMajor >= 3) {
847 // non-ES or ES 3.0+
848 f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
849 caps.hasDrawBuffersFunc = true;
850 f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
851 caps.maxSamples = qMax(1, caps.maxSamples);
852 } else {
853 // ES 2.0 / WebGL 1
854 caps.maxDrawBuffers = 1;
855 caps.hasDrawBuffersFunc = false;
856 // This does not mean MSAA is not supported, just that we cannot query
857 // the supported sample counts. Assume that 4x is always supported.
858 caps.maxSamples = 4;
859 }
860
861 caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
862 && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
863
864 caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
865 && f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
866
867 if (caps.gles)
868 caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
869 else
870 caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
871
872 if (caps.fixedIndexPrimitiveRestart) {
873#ifdef Q_OS_WASM
874 // WebGL 2 behaves as if GL_PRIMITIVE_RESTART_FIXED_INDEX was always
875 // enabled (i.e. matching D3D/Metal), and the value cannot be passed to
876 // glEnable, so skip the call.
877#else
879#endif
880 }
881
882 caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
883 caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
884 caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
885 caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
886 caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
887 caps.rgb10Formats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
888 caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
889 caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
890#ifdef Q_OS_WASM
891 caps.needsDepthStencilCombinedAttach = true;
892#else
893 caps.needsDepthStencilCombinedAttach = false;
894#endif
895
896 // QOpenGLExtensions::SRGBFrameBuffer is not useful here. We need to know if
897 // controlling the sRGB-on-shader-write state is supported, not that if the
898 // default framebuffer is sRGB-capable. And there are two different
899 // extensions for desktop and ES.
900 caps.srgbWriteControl = ctx->hasExtension("GL_EXT_framebuffer_sRGB") || ctx->hasExtension("GL_EXT_sRGB_write_control");
901
902 caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
903
904 if (caps.gles)
905 caps.uniformBuffers = caps.ctxMajor >= 3; // ES 3.0
906 else
907 caps.uniformBuffers = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // 3.1
908
909 caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
910 caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
911 caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats);
912
913 if (caps.gles)
914 caps.instancing = caps.ctxMajor >= 3; // ES 3.0
915 else
916 caps.instancing = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3); // 3.3
917
918 caps.baseVertex = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2 or ES 3.2
919
920 if (caps.gles)
921 caps.compute = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
922 else
923 caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
924
925 if (caps.compute) {
926 f->glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &caps.maxThreadsPerThreadGroup);
927 GLint tgPerDim[3];
928 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &tgPerDim[0]);
929 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &tgPerDim[1]);
930 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &tgPerDim[2]);
931 caps.maxThreadGroupsPerDimension = qMin(tgPerDim[0], qMin(tgPerDim[1], tgPerDim[2]));
932 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &caps.maxThreadGroupsX);
933 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &caps.maxThreadGroupsY);
934 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &caps.maxThreadGroupsZ);
935 }
936
937 if (caps.gles)
938 caps.textureCompareMode = caps.ctxMajor >= 3; // ES 3.0
939 else
940 caps.textureCompareMode = true;
941
942 // proper as in ES 3.0 (glMapBufferRange), not the old glMapBuffer
943 // extension(s) (which is not in ES 3.0...messy)
944 caps.properMapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange);
945
946 if (caps.gles)
947 caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0
948 else
949 caps.nonBaseLevelFramebufferTexture = true;
950
951 caps.texelFetch = caps.ctxMajor >= 3; // 3.0 or ES 3.0
952 caps.intAttributes = caps.ctxMajor >= 3; // 3.0 or ES 3.0
953 caps.screenSpaceDerivatives = f->hasOpenGLExtension(QOpenGLExtensions::StandardDerivatives);
954
955 if (caps.gles)
956 caps.multisampledTexture = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
957 else
958 caps.multisampledTexture = caps.ctxMajor >= 3; // 3.0
959
960 // Program binary support: only the core stuff, do not bother with the old
961 // extensions like GL_OES_get_program_binary
962 if (caps.gles)
963 caps.programBinary = caps.ctxMajor >= 3; // ES 3.0
964 else
965 caps.programBinary = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 1); // 4.1
966
967 if (caps.programBinary) {
968 GLint fmtCount = 0;
969 f->glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &fmtCount);
970 if (fmtCount < 1)
971 caps.programBinary = false;
972 }
973
974 caps.texture3D = caps.ctxMajor >= 3; // 3.0
975
976 if (caps.gles)
977 caps.texture1D = false; // ES
978 else
979 caps.texture1D = glTexImage1D && (caps.ctxMajor >= 2); // 2.0
980
981 if (caps.gles)
982 caps.tessellation = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
983 else
984 caps.tessellation = caps.ctxMajor >= 4; // 4.0
985
986 if (caps.gles)
987 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
988 else
989 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2
990
991 if (caps.ctxMajor >= 3) { // 3.0 or ES 3.0
992 GLint maxArraySize = 0;
993 f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArraySize);
994 caps.maxTextureArraySize = maxArraySize;
995 } else {
996 caps.maxTextureArraySize = 0;
997 }
998
999 // The ES 2.0 spec only has MAX_xxxx_VECTORS. ES 3.0 and up has both
1000 // *VECTORS and *COMPONENTS. OpenGL 2.0-4.0 only has MAX_xxxx_COMPONENTS.
1001 // 4.1 and above has both. What a mess.
1002 if (caps.gles) {
1003 GLint maxVertexUniformVectors = 0;
1004 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniformVectors);
1005 GLint maxFragmentUniformVectors = 0;
1006 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniformVectors);
1007 caps.maxUniformVectors = qMin(maxVertexUniformVectors, maxFragmentUniformVectors);
1008 } else {
1009 GLint maxVertexUniformComponents = 0;
1010 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxVertexUniformComponents);
1011 GLint maxFragmentUniformComponents = 0;
1012 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &maxFragmentUniformComponents);
1013 caps.maxUniformVectors = qMin(maxVertexUniformComponents, maxFragmentUniformComponents) / 4;
1014 }
1015
1016 f->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.maxVertexInputs);
1017
1018 if (caps.gles) {
1019 f->glGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.maxVertexOutputs);
1020 } else if (caps.ctxMajor >= 3) {
1021 GLint components = 0;
1023 caps.maxVertexOutputs = components / 4;
1024 } else {
1025 // OpenGL before 3.0 only has this, and not the same as
1026 // MAX_VARYING_COMPONENTS strictly speaking, but will do.
1027 GLint components = 0;
1028 f->glGetIntegerv(GL_MAX_VARYING_FLOATS, &components);
1029 if (components > 0)
1030 caps.maxVertexOutputs = components / 4;
1031 }
1032
1033 if (!caps.gles) {
1035 if (!caps.coreProfile)
1036 f->glEnable(GL_POINT_SPRITE);
1037 } // else (with gles) these are always on
1038
1039 // Match D3D and others when it comes to seamless cubemap filtering.
1040 // ES 3.0+ has this always enabled. (hopefully)
1041 // ES 2.0 and GL < 3.2 will not have it.
1042 if (!caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)))
1044
1045 caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
1046
1047 // We always require GL_OVR_multiview2 for symmetry with other backends.
1048 caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView)
1049 && f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended);
1050 if (caps.multiView) {
1051 glFramebufferTextureMultiviewOVR =
1052 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>(
1053 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
1054 }
1055
1056 // Only do timestamp queries on OpenGL 3.3+.
1057 caps.timestamps = !caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3));
1058 if (caps.timestamps) {
1059 glQueryCounter = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum)>(
1060 ctx->getProcAddress(QByteArrayLiteral("glQueryCounter")));
1061 glGetQueryObjectui64v = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum, quint64 *)>(
1062 ctx->getProcAddress(QByteArrayLiteral("glGetQueryObjectui64v")));
1063 if (!glQueryCounter || !glGetQueryObjectui64v)
1064 caps.timestamps = false;
1065 }
1066
1067 // glObjectLabel is available on OpenGL ES 3.2+ and OpenGL 4.3+
1068 if (caps.gles)
1069 caps.objectLabel = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1070 else
1071 caps.objectLabel = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
1072 if (caps.objectLabel) {
1073 glObjectLabel = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLuint, GLsizei, const GLchar *)>(
1074 ctx->getProcAddress(QByteArrayLiteral("glObjectLabel")));
1075 }
1076
1077 if (caps.gles) {
1078 // This is the third way to get multisample rendering with GLES. (1. is
1079 // multisample render buffer -> resolve to texture; 2. is multisample
1080 // texture with GLES 3.1; 3. is this, avoiding the explicit multisample
1081 // buffer and should be more efficient with tiled architectures.
1082 // Interesting also because 2. does not seem to work in practice on
1083 // devices such as the Quest 3)
1084 caps.glesMultisampleRenderToTexture = ctx->hasExtension("GL_EXT_multisampled_render_to_texture");
1085 if (caps.glesMultisampleRenderToTexture) {
1086 glFramebufferTexture2DMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei)>(
1087 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture2DMultisampleEXT")));
1088 }
1089 caps.glesMultiviewMultisampleRenderToTexture = ctx->hasExtension("GL_OVR_multiview_multisampled_render_to_texture");
1090 if (caps.glesMultiviewMultisampleRenderToTexture) {
1091 glFramebufferTextureMultisampleMultiviewOVR = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei)>(
1092 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultisampleMultiviewOVR")));
1093 }
1094 } else {
1095 caps.glesMultisampleRenderToTexture = false;
1096 caps.glesMultiviewMultisampleRenderToTexture = false;
1097 }
1098
1099 caps.unpackRowLength = !caps.gles || caps.ctxMajor >= 3;
1100
1101 nativeHandlesStruct.context = ctx;
1102
1103 contextLost = false;
1104
1105 return true;
1106}
1107
1109{
1110 if (!f)
1111 return;
1112
1113 ensureContext();
1115
1116 if (ofr.tsQueries[0]) {
1117 f->glDeleteQueries(2, ofr.tsQueries);
1118 ofr.tsQueries[0] = ofr.tsQueries[1] = 0;
1119 }
1120
1121 if (vao) {
1122 f->glDeleteVertexArrays(1, &vao);
1123 vao = 0;
1124 }
1125
1126 for (uint shader : m_shaderCache)
1127 f->glDeleteShader(shader);
1129
1130 if (!importedContext) {
1131 delete ctx;
1132 ctx = nullptr;
1133 }
1134
1135 f = nullptr;
1136}
1137
1139{
1140 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
1142 switch (e.type) {
1144 f->glDeleteBuffers(1, &e.buffer.buffer);
1145 break;
1147 f->glDeleteProgram(e.pipeline.program);
1148 break;
1150 f->glDeleteTextures(1, &e.texture.texture);
1151 break;
1153 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
1154 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
1155 break;
1157 f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
1158 f->glDeleteTextures(1, &e.textureRenderTarget.nonMsaaThrowawayDepthTexture);
1159 break;
1160 default:
1161 Q_UNREACHABLE();
1162 break;
1163 }
1164 releaseQueue.removeAt(i);
1165 }
1166}
1167
1169{
1171 // 1, 2, 4, 8, ...
1172 for (int i = 1; i <= caps.maxSamples; i *= 2)
1174 }
1176}
1177
1179{
1180 return new QGles2SwapChain(this);
1181}
1182
1184{
1185 return new QGles2Buffer(this, type, usage, size);
1186}
1187
1189{
1190 // No real uniform buffers are used so no need to pretend there is any
1191 // alignment requirement.
1192 return 1;
1193}
1194
1196{
1197 return true;
1198}
1199
1201{
1202 return true;
1203}
1204
1206{
1207 return false;
1208}
1209
1211{
1212 return QMatrix4x4(); // identity
1213}
1214
1216 GLenum *glintformat, GLenum *glsizedintformat,
1217 GLenum *glformat, GLenum *gltype)
1218{
1219 switch (format) {
1220 case QRhiTexture::RGBA8:
1221 *glintformat = GL_RGBA;
1222 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1223 *glformat = GL_RGBA;
1224 *gltype = GL_UNSIGNED_BYTE;
1225 break;
1226 case QRhiTexture::BGRA8:
1227 *glintformat = caps.bgraInternalFormat ? GL_BGRA : GL_RGBA;
1228 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1229 *glformat = GL_BGRA;
1230 *gltype = GL_UNSIGNED_BYTE;
1231 break;
1232 case QRhiTexture::R16:
1233 *glintformat = GL_R16;
1234 *glsizedintformat = *glintformat;
1235 *glformat = GL_RED;
1236 *gltype = GL_UNSIGNED_SHORT;
1237 break;
1238 case QRhiTexture::RG16:
1239 *glintformat = GL_RG16;
1240 *glsizedintformat = *glintformat;
1241 *glformat = GL_RG;
1242 *gltype = GL_UNSIGNED_SHORT;
1243 break;
1244 case QRhiTexture::R8:
1245 *glintformat = GL_R8;
1246 *glsizedintformat = *glintformat;
1247 *glformat = GL_RED;
1248 *gltype = GL_UNSIGNED_BYTE;
1249 break;
1250 case QRhiTexture::RG8:
1251 *glintformat = GL_RG8;
1252 *glsizedintformat = *glintformat;
1253 *glformat = GL_RG;
1254 *gltype = GL_UNSIGNED_BYTE;
1255 break;
1257 *glintformat = caps.coreProfile ? GL_R8 : GL_ALPHA;
1258 *glsizedintformat = *glintformat;
1259 *glformat = caps.coreProfile ? GL_RED : GL_ALPHA;
1260 *gltype = GL_UNSIGNED_BYTE;
1261 break;
1263 *glintformat = GL_RGBA16F;
1264 *glsizedintformat = *glintformat;
1265 *glformat = GL_RGBA;
1266 *gltype = GL_HALF_FLOAT;
1267 break;
1269 *glintformat = GL_RGBA32F;
1270 *glsizedintformat = *glintformat;
1271 *glformat = GL_RGBA;
1272 *gltype = GL_FLOAT;
1273 break;
1274 case QRhiTexture::R16F:
1275 *glintformat = GL_R16F;
1276 *glsizedintformat = *glintformat;
1277 *glformat = GL_RED;
1278 *gltype = GL_HALF_FLOAT;
1279 break;
1280 case QRhiTexture::R32F:
1281 *glintformat = GL_R32F;
1282 *glsizedintformat = *glintformat;
1283 *glformat = GL_RED;
1284 *gltype = GL_FLOAT;
1285 break;
1287 *glintformat = GL_RGB10_A2;
1288 *glsizedintformat = *glintformat;
1289 *glformat = GL_RGBA;
1291 break;
1292 case QRhiTexture::D16:
1293 *glintformat = GL_DEPTH_COMPONENT16;
1294 *glsizedintformat = *glintformat;
1295 *glformat = GL_DEPTH_COMPONENT;
1296 *gltype = GL_UNSIGNED_SHORT;
1297 break;
1298 case QRhiTexture::D24:
1299 *glintformat = GL_DEPTH_COMPONENT24;
1300 *glsizedintformat = *glintformat;
1301 *glformat = GL_DEPTH_COMPONENT;
1302 *gltype = GL_UNSIGNED_INT;
1303 break;
1304 case QRhiTexture::D24S8:
1305 *glintformat = GL_DEPTH24_STENCIL8;
1306 *glsizedintformat = *glintformat;
1307 *glformat = GL_DEPTH_STENCIL;
1308 *gltype = GL_UNSIGNED_INT_24_8;
1309 break;
1310 case QRhiTexture::D32F:
1311 *glintformat = GL_DEPTH_COMPONENT32F;
1312 *glsizedintformat = *glintformat;
1313 *glformat = GL_DEPTH_COMPONENT;
1314 *gltype = GL_FLOAT;
1315 break;
1316 default:
1317 Q_UNREACHABLE();
1318 *glintformat = GL_RGBA;
1319 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1320 *glformat = GL_RGBA;
1321 *gltype = GL_UNSIGNED_BYTE;
1322 break;
1323 }
1324}
1325
1327{
1330
1331 switch (format) {
1332 case QRhiTexture::D16:
1333 case QRhiTexture::D32F:
1334 return caps.depthTexture;
1335
1336 case QRhiTexture::D24:
1337 return caps.depth24;
1338
1339 case QRhiTexture::D24S8:
1340 return caps.depth24 && caps.packedDepthStencil;
1341
1342 case QRhiTexture::BGRA8:
1343 return caps.bgraExternalFormat;
1344
1345 case QRhiTexture::R8:
1346 return caps.r8Format;
1347
1348 case QRhiTexture::RG8:
1349 return caps.r8Format;
1350
1351 case QRhiTexture::R16:
1352 return caps.r16Format;
1353
1354 case QRhiTexture::RG16:
1355 return caps.r16Format;
1356
1359 return caps.floatFormats;
1360
1361 case QRhiTexture::R16F:
1362 case QRhiTexture::R32F:
1363 return caps.floatFormats;
1364
1366 return caps.rgb10Formats;
1367
1368 default:
1369 break;
1370 }
1371
1372 return true;
1373}
1374
1376{
1377 switch (feature) {
1379 return caps.multisampledTexture;
1381 return caps.msaaRenderBuffer;
1382 case QRhi::DebugMarkers:
1383 return false;
1384 case QRhi::Timestamps:
1385 return caps.timestamps;
1386 case QRhi::Instancing:
1387 return caps.instancing;
1389 return false;
1391 return caps.fixedIndexPrimitiveRestart;
1393 return true;
1395 return true;
1397 return caps.npotTextureFull;
1399 return caps.coreProfile;
1401 return caps.elementIndexUint;
1402 case QRhi::Compute:
1403 return caps.compute;
1404 case QRhi::WideLines:
1405 return !caps.coreProfile;
1407 return true;
1408 case QRhi::BaseVertex:
1409 return caps.baseVertex;
1410 case QRhi::BaseInstance:
1411 return false; // not in ES 3.2, so won't bother
1413 return true;
1415 return !caps.gles || caps.properMapBuffer;
1417 return caps.nonBaseLevelFramebufferTexture;
1418 case QRhi::TexelFetch:
1419 return caps.texelFetch;
1421 return caps.nonBaseLevelFramebufferTexture;
1423 return caps.intAttributes;
1425 return caps.screenSpaceDerivatives;
1427 return false;
1429 return caps.programBinary;
1431 return caps.unpackRowLength;
1433 return true;
1435 return caps.texture3D;
1437 return caps.texture3D;
1439 return caps.maxTextureArraySize > 0;
1440 case QRhi::Tessellation:
1441 return caps.tessellation;
1443 return caps.geometryShader;
1445 return false;
1447 return !caps.gles;
1449 return caps.texture1D;
1451 return caps.texture1D;
1453 return caps.halfAttributes;
1455 return caps.texture1D;
1457 return caps.texture3D;
1458 case QRhi::MultiView:
1459 return caps.multiView && caps.maxTextureArraySize > 0;
1461 return false;
1463 return true;
1464 default:
1465 Q_UNREACHABLE_RETURN(false);
1466 }
1467}
1468
1470{
1471 switch (limit) {
1473 return 1;
1475 return caps.maxTextureSize;
1477 return caps.maxDrawBuffers;
1479 // From our perspective. What the GL impl does internally is another
1480 // question, but that's out of our hands and does not concern us here.
1481 return 1;
1483 return 1;
1485 return caps.maxThreadGroupsPerDimension;
1487 return caps.maxThreadsPerThreadGroup;
1489 return caps.maxThreadGroupsX;
1491 return caps.maxThreadGroupsY;
1493 return caps.maxThreadGroupsZ;
1495 return 2048;
1497 return int(qMin<qint64>(INT_MAX, caps.maxUniformVectors * qint64(16)));
1499 return caps.maxVertexInputs;
1501 return caps.maxVertexOutputs;
1502 default:
1503 Q_UNREACHABLE_RETURN(0);
1504 }
1505}
1506
1511
1516
1523
1525{
1526 if (inFrame && !ofr.active)
1528 else
1529 return ensureContext();
1530}
1531
1533{
1534 if (!ensureContext())
1535 return;
1536
1537 for (uint shader : m_shaderCache)
1538 f->glDeleteShader(shader);
1539
1541
1542 m_pipelineCache.clear();
1543}
1544
1546{
1547 return contextLost;
1548}
1549
1558
1560{
1562
1563 if (m_pipelineCache.isEmpty())
1564 return QByteArray();
1565
1567 memset(&header, 0, sizeof(header));
1568 header.rhiId = pipelineCacheRhiId();
1569 header.arch = quint32(sizeof(void*));
1570 header.programBinaryCount = m_pipelineCache.size();
1571 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
1572 if (driverStrLen)
1573 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1574 header.driver[driverStrLen] = '\0';
1575
1576 const size_t dataOffset = sizeof(header);
1577 size_t dataSize = 0;
1578 for (auto it = m_pipelineCache.cbegin(), end = m_pipelineCache.cend(); it != end; ++it) {
1579 dataSize += sizeof(quint32) + it.key().size()
1580 + sizeof(quint32) + it->data.size()
1581 + sizeof(quint32);
1582 }
1583
1585 char *p = buf.data() + dataOffset;
1586 for (auto it = m_pipelineCache.cbegin(), end = m_pipelineCache.cend(); it != end; ++it) {
1587 const QByteArray key = it.key();
1588 const QByteArray data = it->data;
1589 const quint32 format = it->format;
1590
1591 quint32 i = key.size();
1592 memcpy(p, &i, 4);
1593 p += 4;
1594 memcpy(p, key.constData(), key.size());
1595 p += key.size();
1596
1597 i = data.size();
1598 memcpy(p, &i, 4);
1599 p += 4;
1600 memcpy(p, data.constData(), data.size());
1601 p += data.size();
1602
1603 memcpy(p, &format, 4);
1604 p += 4;
1605 }
1606 Q_ASSERT(p == buf.data() + dataOffset + dataSize);
1607
1608 header.dataSize = quint32(dataSize);
1609 memcpy(buf.data(), &header, sizeof(header));
1610
1611 return buf;
1612}
1613
1615{
1616 if (data.isEmpty())
1617 return;
1618
1619 const size_t headerSize = sizeof(QGles2PipelineCacheDataHeader);
1620 if (data.size() < qsizetype(headerSize)) {
1621 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
1622 return;
1623 }
1624 const size_t dataOffset = headerSize;
1626 memcpy(&header, data.constData(), headerSize);
1627
1628 const quint32 rhiId = pipelineCacheRhiId();
1629 if (header.rhiId != rhiId) {
1630 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1631 rhiId, header.rhiId);
1632 return;
1633 }
1634 const quint32 arch = quint32(sizeof(void*));
1635 if (header.arch != arch) {
1636 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
1637 arch, header.arch);
1638 return;
1639 }
1640 if (header.programBinaryCount == 0)
1641 return;
1642
1643 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
1644 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1645 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OpenGL vendor/renderer/version does not match");
1646 return;
1647 }
1648
1649 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1650 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
1651 return;
1652 }
1653
1654 m_pipelineCache.clear();
1655
1656 const char *p = data.constData() + dataOffset;
1657 for (quint32 i = 0; i < header.programBinaryCount; ++i) {
1658 quint32 len = 0;
1659 memcpy(&len, p, 4);
1660 p += 4;
1662 memcpy(key.data(), p, len);
1663 p += len;
1664
1665 memcpy(&len, p, 4);
1666 p += 4;
1668 memcpy(data.data(), p, len);
1669 p += len;
1670
1672 memcpy(&format, p, 4);
1673 p += 4;
1674
1675 m_pipelineCache.insert(key, { format, data });
1676 }
1677
1678 qCDebug(QRHI_LOG_INFO, "Seeded pipeline cache with %d program binaries", int(m_pipelineCache.size()));
1679}
1680
1682 int sampleCount, QRhiRenderBuffer::Flags flags,
1683 QRhiTexture::Format backingFormatHint)
1684{
1685 return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
1686}
1687
1689 const QSize &pixelSize, int depth, int arraySize,
1690 int sampleCount, QRhiTexture::Flags flags)
1691{
1692 return new QGles2Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
1693}
1694
1696 QRhiSampler::Filter mipmapMode,
1698{
1699 return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
1700}
1701
1703 QRhiTextureRenderTarget::Flags flags)
1704{
1705 return new QGles2TextureRenderTarget(this, desc, flags);
1706}
1707
1712
1717
1722
1724{
1728 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
1729
1730 if (pipelineChanged) {
1731 cbD->currentGraphicsPipeline = ps;
1732 cbD->currentComputePipeline = nullptr;
1733 cbD->currentPipelineGeneration = psD->generation;
1734
1735 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1737 cmd.args.bindGraphicsPipeline.ps = ps;
1738 }
1739}
1740
1742 int dynamicOffsetCount,
1743 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1744{
1749
1750 if (!srb) {
1751 if (gfxPsD)
1752 srb = gfxPsD->m_shaderResourceBindings;
1753 else
1754 srb = compPsD->m_shaderResourceBindings;
1755 }
1756
1758 if (cbD->passNeedsResourceTracking) {
1760 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
1761 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
1762 switch (b->type) {
1764 // no BufUniformRead / AccessUniform because no real uniform buffers are used
1765 break;
1768 for (int elem = 0; elem < b->u.stex.count; ++elem) {
1769 trackedRegisterTexture(&passResTracker,
1770 QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
1773 }
1774 break;
1778 {
1779 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
1783 else if (b->type == QRhiShaderResourceBinding::ImageStore)
1785 else
1787 trackedRegisterTexture(&passResTracker, texD, access,
1789 }
1790 break;
1794 {
1795 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
1799 else if (b->type == QRhiShaderResourceBinding::BufferStore)
1801 else
1803 trackedRegisterBuffer(&passResTracker, bufD, access,
1805 }
1806 break;
1807 default:
1808 break;
1809 }
1810 }
1811 }
1812
1813 bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
1814
1815 // The Command::BindShaderResources command generated below is what will
1816 // cause uniforms to be set (glUniformNxx). This needs some special
1817 // handling here in this backend without real uniform buffers, because,
1818 // like in other backends, we optimize out the setShaderResources when the
1819 // srb that was set before is attempted to be set again on the command
1820 // buffer, but that is incorrect if the same srb is now used with another
1821 // pipeline. (because that could mean a glUseProgram not followed by
1822 // up-to-date glUniform calls, i.e. with GL we have a strong dependency
1823 // between the pipeline (== program) and the srb, unlike other APIs) This
1824 // is the reason there is a second level of srb(+generation) tracking in
1825 // the pipeline objects.
1826 if (gfxPsD && (gfxPsD->currentSrb != srb || gfxPsD->currentSrbGeneration != srbD->generation)) {
1827 srbChanged = true;
1828 gfxPsD->currentSrb = srb;
1829 gfxPsD->currentSrbGeneration = srbD->generation;
1830 } else if (compPsD && (compPsD->currentSrb != srb || compPsD->currentSrbGeneration != srbD->generation)) {
1831 srbChanged = true;
1832 compPsD->currentSrb = srb;
1833 compPsD->currentSrbGeneration = srbD->generation;
1834 }
1835
1836 if (srbChanged || cbD->currentSrbGeneration != srbD->generation || srbD->hasDynamicOffset) {
1837 if (gfxPsD) {
1838 cbD->currentGraphicsSrb = srb;
1839 cbD->currentComputeSrb = nullptr;
1840 } else {
1841 cbD->currentGraphicsSrb = nullptr;
1842 cbD->currentComputeSrb = srb;
1843 }
1844 cbD->currentSrbGeneration = srbD->generation;
1845
1846 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1848 cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
1849 cmd.args.bindShaderResources.maybeComputePs = compPsD;
1850 cmd.args.bindShaderResources.srb = srb;
1851 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
1852 if (srbD->hasDynamicOffset) {
1853 if (dynamicOffsetCount < QGles2CommandBuffer::MAX_DYNAMIC_OFFSET_COUNT) {
1854 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
1855 uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
1856 for (int i = 0; i < dynamicOffsetCount; ++i) {
1857 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1858 *p++ = uint(dynOfs.first);
1859 *p++ = dynOfs.second;
1860 }
1861 } else {
1862 qWarning("Too many dynamic offsets (%d, max is %d)",
1864 }
1865 }
1866 }
1867}
1868
1870 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1871 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1872{
1876
1877 for (int i = 0; i < bindingCount; ++i) {
1878 QRhiBuffer *buf = bindings[i].first;
1879 quint32 ofs = bindings[i].second;
1881 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
1882
1883 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1885 cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
1886 cmd.args.bindVertexBuffer.buffer = bufD->buffer;
1887 cmd.args.bindVertexBuffer.offset = ofs;
1888 cmd.args.bindVertexBuffer.binding = startBinding + i;
1889
1890 if (cbD->passNeedsResourceTracking) {
1893 }
1894 }
1895
1896 if (indexBuf) {
1897 QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
1898 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
1899
1900 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1902 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
1903 cmd.args.bindIndexBuffer.offset = indexOffset;
1904 cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
1905
1906 if (cbD->passNeedsResourceTracking) {
1909 }
1910 }
1911}
1912
1914{
1917
1918 const std::array<float, 4> r = viewport.viewport();
1919 // A negative width or height is an error. A negative x or y is not.
1920 if (r[2] < 0.0f || r[3] < 0.0f)
1921 return;
1922
1923 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1925 cmd.args.viewport.x = r[0];
1926 cmd.args.viewport.y = r[1];
1927 cmd.args.viewport.w = r[2];
1928 cmd.args.viewport.h = r[3];
1929 cmd.args.viewport.d0 = viewport.minDepth();
1930 cmd.args.viewport.d1 = viewport.maxDepth();
1931}
1932
1934{
1937
1938 const std::array<int, 4> r = scissor.scissor();
1939 // A negative width or height is an error. A negative x or y is not.
1940 if (r[2] < 0 || r[3] < 0)
1941 return;
1942
1943 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1945 cmd.args.scissor.x = r[0];
1946 cmd.args.scissor.y = r[1];
1947 cmd.args.scissor.w = r[2];
1948 cmd.args.scissor.h = r[3];
1949}
1950
1952{
1955
1956 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1958 cmd.args.blendConstants.r = float(c.redF());
1959 cmd.args.blendConstants.g = float(c.greenF());
1960 cmd.args.blendConstants.b = float(c.blueF());
1961 cmd.args.blendConstants.a = float(c.alphaF());
1962}
1963
1965{
1968
1969 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1971 cmd.args.stencilRef.ref = refValue;
1972 cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
1973}
1974
1976 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
1977{
1980
1981 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1983 cmd.args.draw.ps = cbD->currentGraphicsPipeline;
1984 cmd.args.draw.vertexCount = vertexCount;
1985 cmd.args.draw.firstVertex = firstVertex;
1986 cmd.args.draw.instanceCount = instanceCount;
1987 cmd.args.draw.baseInstance = firstInstance;
1988}
1989
1991 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
1992{
1995
1996 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1998 cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
1999 cmd.args.drawIndexed.indexCount = indexCount;
2000 cmd.args.drawIndexed.firstIndex = firstIndex;
2001 cmd.args.drawIndexed.instanceCount = instanceCount;
2002 cmd.args.drawIndexed.baseInstance = firstInstance;
2003 cmd.args.drawIndexed.baseVertex = vertexOffset;
2004}
2005
2007{
2008 if (!debugMarkers)
2009 return;
2010
2011 Q_UNUSED(cb);
2012 Q_UNUSED(name);
2013}
2014
2016{
2017 if (!debugMarkers)
2018 return;
2019
2020 Q_UNUSED(cb);
2021}
2022
2024{
2025 if (!debugMarkers)
2026 return;
2027
2028 Q_UNUSED(cb);
2029 Q_UNUSED(msg);
2030}
2031
2033{
2034 Q_UNUSED(cb);
2035 return nullptr;
2036}
2037
2039{
2040 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2041 cmd.cmd = type;
2043 cmd.args.beginFrame.timestampQuery = tsQuery;
2045 cmd.args.endFrame.timestampQuery = tsQuery;
2046}
2047
2049{
2050 if (ofr.active) {
2052 if (!ensureContext())
2053 return;
2054 } else {
2057 return;
2058 }
2059
2061
2063 && !cbD->computePassState.writtenResources.isEmpty())
2064 {
2065 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2067 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
2068 }
2069
2071
2072 cbD->resetCommands();
2073
2074 if (vao) {
2075 f->glBindVertexArray(0);
2076 } else {
2077 f->glBindBuffer(GL_ARRAY_BUFFER, 0);
2078 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2079 if (caps.compute)
2080 f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
2081 }
2082}
2083
2085{
2087 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
2088
2089 cbD->resetCachedState();
2090
2092 // Commands that come after this point need a resource tracker and also
2093 // a BarriersForPass command enqueued. (the ones we had from
2094 // beginPass() are now gone since beginExternal() processed all that
2095 // due to calling executeCommandBuffer()).
2097 }
2098
2100
2101 if (cbD->currentTarget)
2103}
2104
2110
2112{
2113 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2114 if (!ensureContext(swapChainD->surface))
2116
2117 ctx->handle()->beginFrame();
2118
2119 currentSwapChain = swapChainD;
2120
2122 swapChainD->cb.resetState();
2123
2124 if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
2125 double elapsedSec = 0;
2126 if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, this, &elapsedSec))
2127 swapChainD->cb.lastGpuTime = elapsedSec;
2128 }
2129
2130 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2131 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2132 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2133
2134 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame, recordTimestamps ? tsStart : 0);
2135
2136 return QRhi::FrameOpSuccess;
2137}
2138
2140{
2141 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2142 Q_ASSERT(currentSwapChain == swapChainD);
2143
2144 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2145 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2146 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2147 if (recordTimestamps) {
2148 swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
2149 swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QGles2SwapChainTimestamps::TIMESTAMP_PAIRS;
2150 }
2151
2152 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame, recordTimestamps ? tsEnd : 0);
2153
2154 if (!ensureContext(swapChainD->surface))
2156
2157 executeCommandBuffer(&swapChainD->cb);
2158
2159 if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
2160 ctx->swapBuffers(swapChainD->surface);
2162 } else {
2163 f->glFlush();
2164 }
2165
2166 swapChainD->frameCount += 1;
2167 currentSwapChain = nullptr;
2168
2169 ctx->handle()->endFrame();
2170
2171 return QRhi::FrameOpSuccess;
2172}
2173
2175{
2176 if (!ensureContext())
2178
2179 ofr.active = true;
2180
2182 ofr.cbWrapper.resetState();
2183
2184 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && caps.timestamps) {
2185 if (!ofr.tsQueries[0])
2186 f->glGenQueries(2, ofr.tsQueries);
2187 }
2188
2190 *cb = &ofr.cbWrapper;
2191
2192 return QRhi::FrameOpSuccess;
2193}
2194
2196{
2197 Q_UNUSED(flags);
2198 Q_ASSERT(ofr.active);
2199 ofr.active = false;
2200
2202
2203 if (!ensureContext())
2205
2206 executeCommandBuffer(&ofr.cbWrapper);
2207
2208 // Just as endFrame() does a flush when skipping the swapBuffers(), do it
2209 // here as well. This has the added benefit of playing nice when rendering
2210 // to a texture from a context and then consuming that texture from
2211 // another, sharing context.
2212 f->glFlush();
2213
2214 if (ofr.tsQueries[0]) {
2215 quint64 timestamps[2];
2216 glGetQueryObjectui64v(ofr.tsQueries[1], GL_QUERY_RESULT, &timestamps[1]);
2217 glGetQueryObjectui64v(ofr.tsQueries[0], GL_QUERY_RESULT, &timestamps[0]);
2218 if (timestamps[1] >= timestamps[0]) {
2219 const quint64 nanoseconds = timestamps[1] - timestamps[0];
2220 ofr.cbWrapper.lastGpuTime = nanoseconds / 1000000000.0; // seconds
2221 }
2222 }
2223
2224 return QRhi::FrameOpSuccess;
2225}
2226
2228{
2229 if (inFrame) {
2230 if (ofr.active) {
2232 Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
2233 if (!ensureContext())
2235 executeCommandBuffer(&ofr.cbWrapper);
2236 ofr.cbWrapper.resetCommands();
2237 } else {
2244 }
2245 // Do an actual glFinish(). May seem superfluous, but this is what
2246 // matches most other backends e.g. Vulkan/Metal that do a heavyweight
2247 // wait-for-idle blocking in their finish(). More importantly, this
2248 // allows clients simply call finish() in threaded or shared context
2249 // situations where one explicitly needs to do a glFlush or Finish.
2250 f->glFinish();
2251 }
2252 return QRhi::FrameOpSuccess;
2253}
2254
2261
2269
2278
2287
2289{
2290 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2291 if (!bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer))
2292 return;
2293
2294 const QGles2Buffer::Access prevAccess = bufD->usageState.access;
2295 if (access == prevAccess)
2296 return;
2297
2298 if (bufferAccessIsWrite(prevAccess)) {
2299 // Generating the minimal barrier set is way too complicated to do
2300 // correctly (prevAccess is overwritten so we won't have proper
2301 // tracking across multiple passes) so setting all barrier bits will do
2302 // for now.
2303 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2305 cmd.args.barrier.barriers = barriersForBuffer();
2306 }
2307
2308 bufD->usageState.access = access;
2309}
2310
2312{
2313 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2314 if (!texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
2315 return;
2316
2317 const QGles2Texture::Access prevAccess = texD->usageState.access;
2318 if (access == prevAccess)
2319 return;
2320
2321 if (textureAccessIsWrite(prevAccess)) {
2322 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2324 cmd.args.barrier.barriers = barriersForTexture();
2325 }
2326
2327 texD->usageState.access = access;
2328}
2329
2331 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
2332{
2334 const bool isCompressed = isCompressedFormat(texD->m_format);
2335 const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
2336 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2337 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
2338 const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
2339 const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
2340 const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
2341 const QPoint dp = subresDesc.destinationTopLeft();
2342 const QByteArray rawData = subresDesc.data();
2343
2344 auto setCmdByNotCompressedData = [&](const void* data, QSize size, quint32 dataStride)
2345 {
2346 quint32 bytesPerLine = 0;
2347 quint32 bytesPerPixel = 0;
2348 textureFormatInfo(texD->m_format, size, &bytesPerLine, nullptr, &bytesPerPixel);
2349
2350 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2352 cmd.args.subImage.target = texD->target;
2353 cmd.args.subImage.texture = texD->texture;
2354 cmd.args.subImage.faceTarget = effectiveTarget;
2355 cmd.args.subImage.level = level;
2356 cmd.args.subImage.dx = dp.x();
2357 cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
2358 cmd.args.subImage.dz = is3D || isArray ? layer : 0;
2359 cmd.args.subImage.w = size.width();
2360 cmd.args.subImage.h = size.height();
2361 cmd.args.subImage.glformat = texD->glformat;
2362 cmd.args.subImage.gltype = texD->gltype;
2363
2364 if (dataStride == 0)
2365 dataStride = bytesPerLine;
2366
2367 cmd.args.subImage.rowStartAlign = (dataStride & 3) ? 1 : 4;
2368 cmd.args.subImage.rowLength = caps.unpackRowLength ? (bytesPerPixel ? dataStride / bytesPerPixel : 0) : 0;
2369
2370 cmd.args.subImage.data = data;
2371 };
2372
2373 if (!subresDesc.image().isNull()) {
2374 QImage img = subresDesc.image();
2375 QSize size = img.size();
2376 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2377 const QPoint sp = subresDesc.sourceTopLeft();
2378 if (!subresDesc.sourceSize().isEmpty())
2379 size = subresDesc.sourceSize();
2380
2381 if (caps.unpackRowLength) {
2382 cbD->retainImage(img);
2383 // create a non-owning wrapper for the subimage
2384 const uchar *data = img.constBits() + sp.y() * img.bytesPerLine() + sp.x() * (qMax(1, img.depth() / 8));
2385 img = QImage(data, size.width(), size.height(), img.bytesPerLine(), img.format());
2386 } else {
2387 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
2388 }
2389 }
2390
2391 setCmdByNotCompressedData(cbD->retainImage(img), size, img.bytesPerLine());
2392 } else if (!rawData.isEmpty() && isCompressed) {
2393 const int depth = qMax(1, texD->m_depth);
2394 const int arraySize = qMax(0, texD->m_arraySize);
2395 if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray)
2396 && !texD->zeroInitialized)
2397 {
2398 // Create on first upload since glCompressedTexImage2D cannot take
2399 // nullptr data. We have a rule in the QRhi docs that the first
2400 // upload for a compressed texture must cover the entire image, but
2401 // that is clearly not ideal when building a texture atlas, or when
2402 // having a 3D texture with per-slice data.
2403 quint32 byteSize = 0;
2404 compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
2405 if (is3D)
2406 byteSize *= depth;
2407 if (isArray)
2408 byteSize *= arraySize;
2409 QByteArray zeroBuf(byteSize, 0);
2410 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2412 cmd.args.compressedImage.target = texD->target;
2413 cmd.args.compressedImage.texture = texD->texture;
2414 cmd.args.compressedImage.faceTarget = effectiveTarget;
2415 cmd.args.compressedImage.level = level;
2416 cmd.args.compressedImage.glintformat = texD->glintformat;
2417 cmd.args.compressedImage.w = texD->m_pixelSize.width();
2418 cmd.args.compressedImage.h = is1D && isArray ? arraySize : texD->m_pixelSize.height();
2419 cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
2420 cmd.args.compressedImage.size = byteSize;
2421 cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
2422 texD->zeroInitialized = true;
2423 }
2424
2425 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
2426 : subresDesc.sourceSize();
2427 if (texD->specified || texD->zeroInitialized) {
2428 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2430 cmd.args.compressedSubImage.target = texD->target;
2431 cmd.args.compressedSubImage.texture = texD->texture;
2432 cmd.args.compressedSubImage.faceTarget = effectiveTarget;
2433 cmd.args.compressedSubImage.level = level;
2434 cmd.args.compressedSubImage.dx = dp.x();
2435 cmd.args.compressedSubImage.dy = is1D && isArray ? layer : dp.y();
2436 cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
2437 cmd.args.compressedSubImage.w = size.width();
2438 cmd.args.compressedSubImage.h = size.height();
2439 cmd.args.compressedSubImage.glintformat = texD->glintformat;
2440 cmd.args.compressedSubImage.size = rawData.size();
2441 cmd.args.compressedSubImage.data = cbD->retainData(rawData);
2442 } else {
2443 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2445 cmd.args.compressedImage.target = texD->target;
2446 cmd.args.compressedImage.texture = texD->texture;
2447 cmd.args.compressedImage.faceTarget = effectiveTarget;
2448 cmd.args.compressedImage.level = level;
2449 cmd.args.compressedImage.glintformat = texD->glintformat;
2450 cmd.args.compressedImage.w = size.width();
2451 cmd.args.compressedImage.h = is1D && isArray ? arraySize : size.height();
2452 cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
2453 cmd.args.compressedImage.size = rawData.size();
2454 cmd.args.compressedImage.data = cbD->retainData(rawData);
2455 }
2456 } else if (!rawData.isEmpty()) {
2457 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
2458 : subresDesc.sourceSize();
2459
2460 setCmdByNotCompressedData(cbD->retainData(rawData), size, subresDesc.dataStride());
2461 } else {
2462 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2463 }
2464}
2465
2467{
2470
2471 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
2472 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
2475 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2476 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
2477 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
2478 } else {
2480 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2482 cmd.args.bufferSubData.target = bufD->targetForDataOps;
2483 cmd.args.bufferSubData.buffer = bufD->buffer;
2484 cmd.args.bufferSubData.offset = u.offset;
2485 cmd.args.bufferSubData.size = u.data.size();
2486 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
2487 }
2490 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2491 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
2492 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
2493 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
2494 } else {
2496 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2498 cmd.args.bufferSubData.target = bufD->targetForDataOps;
2499 cmd.args.bufferSubData.buffer = bufD->buffer;
2500 cmd.args.bufferSubData.offset = u.offset;
2501 cmd.args.bufferSubData.size = u.data.size();
2502 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
2503 }
2506 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
2507 u.result->data.resize(u.readSize);
2508 memcpy(u.result->data.data(), bufD->data.constData() + u.offset, size_t(u.readSize));
2509 if (u.result->completed)
2510 u.result->completed();
2511 } else {
2512 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2514 cmd.args.getBufferSubData.result = u.result;
2515 cmd.args.getBufferSubData.target = bufD->targetForDataOps;
2516 cmd.args.getBufferSubData.buffer = bufD->buffer;
2517 cmd.args.getBufferSubData.offset = u.offset;
2518 cmd.args.getBufferSubData.size = u.readSize;
2519 }
2520 }
2521 }
2522
2523 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
2524 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
2527 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
2528 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2529 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2530 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
2531 }
2532 }
2533 texD->specified = true;
2535 Q_ASSERT(u.src && u.dst);
2538
2541
2542 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2543 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2544 // do not translate coordinates, even if sp is bottom-left from gl's pov
2545 const QPoint sp = u.desc.sourceTopLeft();
2546 const QPoint dp = u.desc.destinationTopLeft();
2547
2548 const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
2549 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
2550 const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
2551 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
2552
2553 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2555
2556 const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
2557 const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
2558 const bool dstIs1dArray = dstD->m_flags.testFlag(QRhiTexture::OneDimensional)
2559 && dstD->m_flags.testFlag(QRhiTexture::TextureArray);
2560
2561 cmd.args.copyTex.srcTarget = srcD->target;
2562 cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
2563 cmd.args.copyTex.srcTexture = srcD->texture;
2564 cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
2565 cmd.args.copyTex.srcX = sp.x();
2566 cmd.args.copyTex.srcY = sp.y();
2567 cmd.args.copyTex.srcZ = srcHasZ ? u.desc.sourceLayer() : 0;
2568
2569 cmd.args.copyTex.dstTarget = dstD->target;
2570 cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + (dstHasZ ? 0u : uint(u.desc.destinationLayer()));
2571 cmd.args.copyTex.dstTexture = dstD->texture;
2572 cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
2573 cmd.args.copyTex.dstX = dp.x();
2574 cmd.args.copyTex.dstY = dstIs1dArray ? u.desc.destinationLayer() : dp.y();
2575 cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
2576
2577 cmd.args.copyTex.w = copySize.width();
2578 cmd.args.copyTex.h = copySize.height();
2580 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2582 cmd.args.readPixels.result = u.result;
2584 if (texD)
2586 cmd.args.readPixels.texture = texD ? texD->texture : 0;
2587 cmd.args.readPixels.slice3D = -1;
2588 if (texD) {
2589 const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
2590 cmd.args.readPixels.w = readImageSize.width();
2591 cmd.args.readPixels.h = readImageSize.height();
2592 cmd.args.readPixels.format = texD->m_format;
2593 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
2594 || texD->m_flags.testFlag(QRhiTexture::TextureArray))
2595 {
2596 cmd.args.readPixels.readTarget = texD->target;
2597 cmd.args.readPixels.slice3D = u.rb.layer();
2598 } else {
2599 const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
2600 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
2601 cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer());
2602 }
2603 cmd.args.readPixels.level = u.rb.level();
2604 }
2608 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2610 cmd.args.genMip.target = texD->target;
2611 cmd.args.genMip.texture = texD->texture;
2612 }
2613 }
2614
2615 ud->free();
2616}
2617
2619{
2620 switch (t) {
2622 return GL_TRIANGLES;
2624 return GL_TRIANGLE_STRIP;
2626 return GL_TRIANGLE_FAN;
2628 return GL_LINES;
2630 return GL_LINE_STRIP;
2632 return GL_POINTS;
2634 return GL_PATCHES;
2635 default:
2636 Q_UNREACHABLE_RETURN(GL_TRIANGLES);
2637 }
2638}
2639
2641{
2642 switch (c) {
2644 return GL_FRONT;
2646 return GL_BACK;
2647 default:
2648 Q_UNREACHABLE_RETURN(GL_BACK);
2649 }
2650}
2651
2653{
2654 switch (f) {
2656 return GL_CCW;
2658 return GL_CW;
2659 default:
2660 Q_UNREACHABLE_RETURN(GL_CCW);
2661 }
2662}
2663
2665{
2666 switch (f) {
2668 return GL_ZERO;
2670 return GL_ONE;
2672 return GL_SRC_COLOR;
2674 return GL_ONE_MINUS_SRC_COLOR;
2676 return GL_DST_COLOR;
2678 return GL_ONE_MINUS_DST_COLOR;
2680 return GL_SRC_ALPHA;
2682 return GL_ONE_MINUS_SRC_ALPHA;
2684 return GL_DST_ALPHA;
2686 return GL_ONE_MINUS_DST_ALPHA;
2688 return GL_CONSTANT_COLOR;
2692 return GL_CONSTANT_ALPHA;
2696 return GL_SRC_ALPHA_SATURATE;
2701 qWarning("Unsupported blend factor %d", f);
2702 return GL_ZERO;
2703 default:
2704 Q_UNREACHABLE_RETURN(GL_ZERO);
2705 }
2706}
2707
2709{
2710 switch (op) {
2712 return GL_FUNC_ADD;
2714 return GL_FUNC_SUBTRACT;
2718 return GL_MIN;
2720 return GL_MAX;
2721 default:
2722 Q_UNREACHABLE_RETURN(GL_FUNC_ADD);
2723 }
2724}
2725
2727{
2728 switch (op) {
2730 return GL_NEVER;
2732 return GL_LESS;
2734 return GL_EQUAL;
2736 return GL_LEQUAL;
2738 return GL_GREATER;
2740 return GL_NOTEQUAL;
2742 return GL_GEQUAL;
2744 return GL_ALWAYS;
2745 default:
2746 Q_UNREACHABLE_RETURN(GL_ALWAYS);
2747 }
2748}
2749
2751{
2752 switch (op) {
2754 return GL_ZERO;
2756 return GL_KEEP;
2758 return GL_REPLACE;
2760 return GL_INCR;
2762 return GL_DECR;
2764 return GL_INVERT;
2766 return GL_INCR_WRAP;
2768 return GL_DECR_WRAP;
2769 default:
2770 Q_UNREACHABLE_RETURN(GL_KEEP);
2771 }
2772}
2773
2775{
2776 switch (mode) {
2778 return GL_FILL;
2780 return GL_LINE;
2781 default:
2782 Q_UNREACHABLE_RETURN(GL_FILL);
2783 }
2784}
2785
2787{
2788 switch (f) {
2790 if (m == QRhiSampler::None)
2791 return GL_NEAREST;
2792 else
2793 return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
2795 if (m == QRhiSampler::None)
2796 return GL_LINEAR;
2797 else
2798 return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
2799 default:
2800 Q_UNREACHABLE_RETURN(GL_LINEAR);
2801 }
2802}
2803
2805{
2806 switch (f) {
2808 return GL_NEAREST;
2810 return GL_LINEAR;
2811 default:
2812 Q_UNREACHABLE_RETURN(GL_LINEAR);
2813 }
2814}
2815
2817{
2818 switch (m) {
2820 return GL_REPEAT;
2822 return GL_CLAMP_TO_EDGE;
2824 return GL_MIRRORED_REPEAT;
2825 default:
2826 Q_UNREACHABLE_RETURN(GL_CLAMP_TO_EDGE);
2827 }
2828}
2829
2831{
2832 switch (op) {
2833 case QRhiSampler::Never:
2834 return GL_NEVER;
2835 case QRhiSampler::Less:
2836 return GL_LESS;
2837 case QRhiSampler::Equal:
2838 return GL_EQUAL;
2840 return GL_LEQUAL;
2842 return GL_GREATER;
2844 return GL_NOTEQUAL;
2846 return GL_GEQUAL;
2848 return GL_ALWAYS;
2849 default:
2850 Q_UNREACHABLE_RETURN(GL_NEVER);
2851 }
2852}
2853
2875
2877{
2879 u.layout = 0; // N/A
2880 u.access = bufUsage.access;
2881 u.stage = 0; // N/A
2882 return u;
2883}
2884
2906
2908{
2910 u.layout = 0; // N/A
2911 u.access = texUsage.access;
2912 u.stage = 0; // N/A
2913 return u;
2914}
2915
2917 QGles2Buffer *bufD,
2920{
2921 QGles2Buffer::UsageState &u(bufD->usageState);
2922 passResTracker->registerBuffer(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
2924}
2925
2927 QGles2Texture *texD,
2930{
2931 QGles2Texture::UsageState &u(texD->usageState);
2932 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
2934}
2935
2955
2956// Helper that must be used in executeCommandBuffer() whenever changing the
2957// ARRAY or ELEMENT_ARRAY buffer binding outside of Command::BindVertexBuffer
2958// and Command::BindIndexBuffer.
2961 GLenum target,
2962 GLuint buffer)
2963{
2964 state->currentArrayBuffer = 0;
2965 state->currentElementArrayBuffer = 0;
2966 state->lastBindVertexBuffer.buffer = 0;
2967 f->glBindBuffer(target, buffer);
2968}
2969
2971{
2974
2975 for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
2976 const QGles2CommandBuffer::Command &cmd(*it);
2977 switch (cmd.cmd) {
2979 if (cmd.args.beginFrame.timestampQuery)
2980 glQueryCounter(cmd.args.beginFrame.timestampQuery, GL_TIMESTAMP);
2981 if (caps.coreProfile) {
2982 if (!vao)
2983 f->glGenVertexArrays(1, &vao);
2984 f->glBindVertexArray(vao);
2985 }
2986 break;
2988 if (state.instancedAttributesUsed) {
2990 if (state.nonzeroAttribDivisor[i])
2991 f->glVertexAttribDivisor(GLuint(i), 0);
2992 }
2993 for (int i = CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; i <= state.maxUntrackedInstancedAttribute; ++i)
2994 f->glVertexAttribDivisor(GLuint(i), 0);
2995 state.instancedAttributesUsed = false;
2996 }
2997#ifdef Q_OS_WASM
2999 if (state.enabledAttribArrays[i]) {
3000 f->glDisableVertexAttribArray(GLuint(i));
3001 state.enabledAttribArrays[i] = false;
3002 }
3003 }
3004#endif
3005 if (vao)
3006 f->glBindVertexArray(0);
3007 if (cmd.args.endFrame.timestampQuery)
3008 glQueryCounter(cmd.args.endFrame.timestampQuery, GL_TIMESTAMP);
3009 break;
3011 if (vao)
3012 f->glBindVertexArray(vao);
3013 break;
3015 f->glViewport(GLint(cmd.args.viewport.x), GLint(cmd.args.viewport.y), GLsizei(cmd.args.viewport.w), GLsizei(cmd.args.viewport.h));
3016 f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
3017 break;
3019 f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
3020 break;
3022 f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
3023 break;
3025 {
3027 if (psD) {
3028 const GLint ref = GLint(cmd.args.stencilRef.ref);
3029 f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), ref, psD->m_stencilReadMask);
3030 f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), ref, psD->m_stencilReadMask);
3031 cbD->graphicsPassState.dynamic.stencilRef = ref;
3032 } else {
3033 qWarning("No graphics pipeline active for setStencilRef; ignored");
3034 }
3035 }
3036 break;
3038 {
3039 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
3040 if (psD) {
3041 if (state.lastBindVertexBuffer.ps == psD
3042 && state.lastBindVertexBuffer.buffer == cmd.args.bindVertexBuffer.buffer
3043 && state.lastBindVertexBuffer.offset == cmd.args.bindVertexBuffer.offset
3044 && state.lastBindVertexBuffer.binding == cmd.args.bindVertexBuffer.binding)
3045 {
3046 // The pipeline and so the vertex input layout is
3047 // immutable, no point in issuing the exact same set of
3048 // glVertexAttribPointer again and again for the same buffer.
3049 break;
3050 }
3051 state.lastBindVertexBuffer.ps = psD;
3052 state.lastBindVertexBuffer.buffer = cmd.args.bindVertexBuffer.buffer;
3053 state.lastBindVertexBuffer.offset = cmd.args.bindVertexBuffer.offset;
3054 state.lastBindVertexBuffer.binding = cmd.args.bindVertexBuffer.binding;
3055
3056 if (cmd.args.bindVertexBuffer.buffer != state.currentArrayBuffer) {
3057 state.currentArrayBuffer = cmd.args.bindVertexBuffer.buffer;
3058 // we do not support more than one vertex buffer
3059 f->glBindBuffer(GL_ARRAY_BUFFER, state.currentArrayBuffer);
3060 }
3061 for (auto it = psD->m_vertexInputLayout.cbeginAttributes(), itEnd = psD->m_vertexInputLayout.cendAttributes();
3062 it != itEnd; ++it)
3063 {
3064 const int bindingIdx = it->binding();
3065 if (bindingIdx != cmd.args.bindVertexBuffer.binding)
3066 continue;
3067
3068 const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(bindingIdx);
3069 const int stride = int(inputBinding->stride());
3070 int size = 1;
3072 bool normalize = false;
3073 switch (it->format()) {
3075 type = GL_FLOAT;
3076 size = 4;
3077 break;
3079 type = GL_FLOAT;
3080 size = 3;
3081 break;
3083 type = GL_FLOAT;
3084 size = 2;
3085 break;
3087 type = GL_FLOAT;
3088 size = 1;
3089 break;
3092 normalize = true;
3093 size = 4;
3094 break;
3097 normalize = true;
3098 size = 2;
3099 break;
3102 normalize = true;
3103 size = 1;
3104 break;
3106 type = GL_UNSIGNED_INT;
3107 size = 4;
3108 break;
3110 type = GL_UNSIGNED_INT;
3111 size = 3;
3112 break;
3114 type = GL_UNSIGNED_INT;
3115 size = 2;
3116 break;
3118 type = GL_UNSIGNED_INT;
3119 size = 1;
3120 break;
3122 type = GL_INT;
3123 size = 4;
3124 break;
3126 type = GL_INT;
3127 size = 3;
3128 break;
3130 type = GL_INT;
3131 size = 2;
3132 break;
3134 type = GL_INT;
3135 size = 1;
3136 break;
3139 size = 4;
3140 break;
3143 size = 3;
3144 break;
3147 size = 2;
3148 break;
3151 size = 1;
3152 break;
3154 type = GL_UNSIGNED_SHORT;
3155 size = 4;
3156 break;
3158 type = GL_UNSIGNED_SHORT;
3159 size = 3;
3160 break;
3162 type = GL_UNSIGNED_SHORT;
3163 size = 2;
3164 break;
3166 type = GL_UNSIGNED_SHORT;
3167 size = 1;
3168 break;
3170 type = GL_SHORT;
3171 size = 4;
3172 break;
3174 type = GL_SHORT;
3175 size = 3;
3176 break;
3178 type = GL_SHORT;
3179 size = 2;
3180 break;
3182 type = GL_SHORT;
3183 size = 1;
3184 break;
3185 default:
3186 break;
3187 }
3188
3189 const int locationIdx = it->location();
3190 quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset;
3191 if (type == GL_UNSIGNED_INT || type == GL_INT) {
3192 if (caps.intAttributes) {
3193 f->glVertexAttribIPointer(GLuint(locationIdx), size, type, stride,
3194 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3195 } else {
3196 qWarning("Current RHI backend does not support IntAttributes. Check supported features.");
3197 // This is a trick to disable this attribute
3199 state.enabledAttribArrays[locationIdx] = true;
3200 }
3201 } else {
3202 f->glVertexAttribPointer(GLuint(locationIdx), size, type, normalize, stride,
3203 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3204 }
3205 if (locationIdx >= CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT || !state.enabledAttribArrays[locationIdx]) {
3207 state.enabledAttribArrays[locationIdx] = true;
3208 f->glEnableVertexAttribArray(GLuint(locationIdx));
3209 }
3210 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing) {
3211 f->glVertexAttribDivisor(GLuint(locationIdx), inputBinding->instanceStepRate());
3213 state.nonzeroAttribDivisor[locationIdx] = true;
3214 else
3215 state.maxUntrackedInstancedAttribute = qMax(state.maxUntrackedInstancedAttribute, locationIdx);
3216 state.instancedAttributesUsed = true;
3218 && state.nonzeroAttribDivisor[locationIdx])
3220 && locationIdx <= state.maxUntrackedInstancedAttribute))
3221 {
3222 f->glVertexAttribDivisor(GLuint(locationIdx), 0);
3224 state.nonzeroAttribDivisor[locationIdx] = false;
3225 }
3226 }
3227 } else {
3228 qWarning("No graphics pipeline active for setVertexInput; ignored");
3229 }
3230 }
3231 break;
3233 state.indexType = cmd.args.bindIndexBuffer.type;
3234 state.indexStride = state.indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
3235 state.indexOffset = cmd.args.bindIndexBuffer.offset;
3236 if (state.currentElementArrayBuffer != cmd.args.bindIndexBuffer.buffer) {
3237 state.currentElementArrayBuffer = cmd.args.bindIndexBuffer.buffer;
3238 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.currentElementArrayBuffer);
3239 }
3240 break;
3242 {
3244 if (psD) {
3245 if (cmd.args.draw.instanceCount == 1 || !caps.instancing) {
3246 f->glDrawArrays(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount));
3247 } else {
3248 f->glDrawArraysInstanced(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount),
3249 GLsizei(cmd.args.draw.instanceCount));
3250 }
3251 } else {
3252 qWarning("No graphics pipeline active for draw; ignored");
3253 }
3254 }
3255 break;
3257 {
3258 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
3259 if (psD) {
3260 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
3261 quintptr(cmd.args.drawIndexed.firstIndex * state.indexStride + state.indexOffset));
3262 if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) {
3263 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
3264 f->glDrawElementsBaseVertex(psD->drawMode,
3265 GLsizei(cmd.args.drawIndexed.indexCount),
3266 state.indexType,
3267 ofs,
3268 cmd.args.drawIndexed.baseVertex);
3269 } else {
3270 f->glDrawElements(psD->drawMode,
3271 GLsizei(cmd.args.drawIndexed.indexCount),
3272 state.indexType,
3273 ofs);
3274 }
3275 } else {
3276 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
3277 f->glDrawElementsInstancedBaseVertex(psD->drawMode,
3278 GLsizei(cmd.args.drawIndexed.indexCount),
3279 state.indexType,
3280 ofs,
3281 GLsizei(cmd.args.drawIndexed.instanceCount),
3282 cmd.args.drawIndexed.baseVertex);
3283 } else {
3284 f->glDrawElementsInstanced(psD->drawMode,
3285 GLsizei(cmd.args.drawIndexed.indexCount),
3286 state.indexType,
3287 ofs,
3288 GLsizei(cmd.args.drawIndexed.instanceCount));
3289 }
3290 }
3291 } else {
3292 qWarning("No graphics pipeline active for drawIndexed; ignored");
3293 }
3294 }
3295 break;
3297 executeBindGraphicsPipeline(cbD, QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindGraphicsPipeline.ps));
3298 break;
3301 cmd.args.bindShaderResources.maybeGraphicsPs,
3302 cmd.args.bindShaderResources.maybeComputePs,
3303 cmd.args.bindShaderResources.srb,
3304 cmd.args.bindShaderResources.dynamicOffsetPairs,
3305 cmd.args.bindShaderResources.dynamicOffsetCount);
3306 break;
3308 {
3309 QVarLengthArray<GLenum, 8> bufs;
3310 if (cmd.args.bindFramebuffer.fbo) {
3311 f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo);
3312 const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
3313 bufs.append(colorAttCount > 0 ? GL_COLOR_ATTACHMENT0 : GL_NONE);
3314 if (caps.maxDrawBuffers > 1) {
3315 for (int i = 1; i < colorAttCount; ++i)
3316 bufs.append(GL_COLOR_ATTACHMENT0 + uint(i));
3317 }
3318 } else {
3319 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3320 if (cmd.args.bindFramebuffer.stereo && cmd.args.bindFramebuffer.stereoTarget == QRhiSwapChain::RightBuffer)
3321 bufs.append(GL_BACK_RIGHT);
3322 else
3323 bufs.append(caps.gles ? GL_BACK : GL_BACK_LEFT);
3324 }
3325 if (caps.hasDrawBuffersFunc)
3326 f->glDrawBuffers(bufs.count(), bufs.constData());
3327 if (caps.srgbWriteControl) {
3328 if (cmd.args.bindFramebuffer.srgb)
3329 f->glEnable(GL_FRAMEBUFFER_SRGB);
3330 else
3331 f->glDisable(GL_FRAMEBUFFER_SRGB);
3332 }
3333 }
3334 break;
3336 f->glDisable(GL_SCISSOR_TEST);
3337 if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
3338 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3339 f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
3340 }
3341 if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
3342 f->glDepthMask(GL_TRUE);
3343 f->glClearDepthf(cmd.args.clear.d);
3344 }
3345 if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) {
3346 f->glStencilMask(0xFF);
3347 f->glClearStencil(GLint(cmd.args.clear.s));
3348 }
3349 f->glClear(cmd.args.clear.mask);
3350 cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking
3351 break;
3353 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
3354 f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
3355 cmd.args.bufferSubData.data);
3356 break;
3358 {
3359 QRhiReadbackResult *result = cmd.args.getBufferSubData.result;
3360 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
3361 if (caps.gles) {
3362 if (caps.properMapBuffer) {
3363 void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target,
3364 cmd.args.getBufferSubData.offset,
3365 cmd.args.getBufferSubData.size,
3367 if (p) {
3368 result->data.resize(cmd.args.getBufferSubData.size);
3369 memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size));
3370 f->glUnmapBuffer(cmd.args.getBufferSubData.target);
3371 }
3372 }
3373 } else {
3374 result->data.resize(cmd.args.getBufferSubData.size);
3375 f->glGetBufferSubData(cmd.args.getBufferSubData.target,
3376 cmd.args.getBufferSubData.offset,
3377 cmd.args.getBufferSubData.size,
3378 result->data.data());
3379 }
3380 if (result->completed)
3381 result->completed();
3382 }
3383 break;
3385 {
3386 GLuint fbo;
3387 f->glGenFramebuffers(1, &fbo);
3388 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3389 if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D
3390 || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY
3391 || cmd.args.copyTex.srcTarget == GL_TEXTURE_1D_ARRAY) {
3392 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
3393 cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
3394 } else if (cmd.args.copyTex.srcTarget == GL_TEXTURE_1D) {
3395 glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3396 cmd.args.copyTex.srcTarget, cmd.args.copyTex.srcTexture,
3397 cmd.args.copyTex.srcLevel);
3398 } else {
3399 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3400 cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
3401 }
3402 f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
3403 if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D || cmd.args.copyTex.dstTarget == GL_TEXTURE_2D_ARRAY) {
3404 f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
3405 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
3406 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
3407 cmd.args.copyTex.w, cmd.args.copyTex.h);
3408 } else if (cmd.args.copyTex.dstTarget == GL_TEXTURE_1D) {
3409 glCopyTexSubImage1D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
3410 cmd.args.copyTex.dstX, cmd.args.copyTex.srcX,
3411 cmd.args.copyTex.srcY, cmd.args.copyTex.w);
3412 } else {
3413 f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
3414 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
3415 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
3416 cmd.args.copyTex.w, cmd.args.copyTex.h);
3417 }
3418 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3419 f->glDeleteFramebuffers(1, &fbo);
3420 }
3421 break;
3423 {
3424 QRhiReadbackResult *result = cmd.args.readPixels.result;
3425 GLuint tex = cmd.args.readPixels.texture;
3426 GLuint fbo = 0;
3427 int mipLevel = 0;
3428 if (tex) {
3429 result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
3430 result->format = cmd.args.readPixels.format;
3431 mipLevel = cmd.args.readPixels.level;
3432 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
3433 f->glGenFramebuffers(1, &fbo);
3434 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3435 if (cmd.args.readPixels.slice3D >= 0) {
3436 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3437 tex, mipLevel, cmd.args.readPixels.slice3D);
3438 } else if (cmd.args.readPixels.readTarget == GL_TEXTURE_1D) {
3439 glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3440 cmd.args.readPixels.readTarget, tex, mipLevel);
3441 } else {
3442 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3443 cmd.args.readPixels.readTarget, tex, mipLevel);
3444 }
3445 }
3446 } else {
3447 result->pixelSize = currentSwapChain->pixelSize;
3448 result->format = QRhiTexture::RGBA8;
3449 // readPixels handles multisample resolving implicitly
3450 }
3451 const int w = result->pixelSize.width();
3452 const int h = result->pixelSize.height();
3453 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
3454 // With GLES, GL_RGBA is the only mandated readback format, so stick with it.
3455 // (and that's why we return false for the ReadBackAnyTextureFormat feature)
3456 if (result->format == QRhiTexture::R8 || result->format == QRhiTexture::RED_OR_ALPHA8) {
3457 result->data.resize(w * h);
3458 QByteArray tmpBuf;
3459 tmpBuf.resize(w * h * 4);
3460 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tmpBuf.data());
3461 const quint8 *srcBase = reinterpret_cast<const quint8 *>(tmpBuf.constData());
3462 quint8 *dstBase = reinterpret_cast<quint8 *>(result->data.data());
3463 const int componentIndex = isFeatureSupported(QRhi::RedOrAlpha8IsRed) ? 0 : 3;
3464 for (int y = 0; y < h; ++y) {
3465 const quint8 *src = srcBase + y * w * 4;
3466 quint8 *dst = dstBase + y * w;
3467 int count = w;
3468 while (count-- > 0) {
3469 *dst++ = src[componentIndex];
3470 src += 4;
3471 }
3472 }
3473 } else {
3474 switch (result->format) {
3475 // For floating point formats try it because this can be
3476 // relevant for some use cases; if it works, then fine, if
3477 // not, there's nothing we can do.
3479 result->data.resize(w * h * 8);
3480 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_HALF_FLOAT, result->data.data());
3481 break;
3482 case QRhiTexture::R16F:
3483 result->data.resize(w * h * 2);
3484 f->glReadPixels(0, 0, w, h, GL_RED, GL_HALF_FLOAT, result->data.data());
3485 break;
3486 case QRhiTexture::R32F:
3487 result->data.resize(w * h * 4);
3488 f->glReadPixels(0, 0, w, h, GL_RED, GL_FLOAT, result->data.data());
3489 break;
3491 result->data.resize(w * h * 16);
3492 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, result->data.data());
3493 break;
3495 result->data.resize(w * h * 4);
3496 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, result->data.data());
3497 break;
3498 default:
3499 result->data.resize(w * h * 4);
3500 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, result->data.data());
3501 break;
3502 }
3503 }
3504 } else {
3505 result->data.resize(w * h * 4);
3506 result->data.fill('\0');
3507 }
3508 if (fbo) {
3509 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3510 f->glDeleteFramebuffers(1, &fbo);
3511 }
3512 if (result->completed)
3513 result->completed();
3514 }
3515 break;
3517 f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
3518 if (cmd.args.subImage.rowStartAlign != 4)
3519 f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
3520 if (cmd.args.subImage.rowLength != 0)
3521 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
3522 if (cmd.args.subImage.target == GL_TEXTURE_3D || cmd.args.subImage.target == GL_TEXTURE_2D_ARRAY) {
3523 f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level,
3524 cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
3525 cmd.args.subImage.w, cmd.args.subImage.h, 1,
3526 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
3527 cmd.args.subImage.data);
3528 } else if (cmd.args.subImage.target == GL_TEXTURE_1D) {
3529 glTexSubImage1D(cmd.args.subImage.target, cmd.args.subImage.level,
3530 cmd.args.subImage.dx, cmd.args.subImage.w,
3531 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
3532 cmd.args.subImage.data);
3533 } else {
3534 f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
3535 cmd.args.subImage.dx, cmd.args.subImage.dy,
3536 cmd.args.subImage.w, cmd.args.subImage.h,
3537 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
3538 cmd.args.subImage.data);
3539 }
3540 if (cmd.args.subImage.rowStartAlign != 4)
3541 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
3542 if (cmd.args.subImage.rowLength != 0)
3543 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3544 break;
3546 f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
3547 if (cmd.args.compressedImage.target == GL_TEXTURE_3D || cmd.args.compressedImage.target == GL_TEXTURE_2D_ARRAY) {
3548 f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
3549 cmd.args.compressedImage.glintformat,
3550 cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
3551 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
3552 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
3553 glCompressedTexImage1D(
3554 cmd.args.compressedImage.target, cmd.args.compressedImage.level,
3555 cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, 0,
3556 cmd.args.compressedImage.size, cmd.args.compressedImage.data);
3557 } else {
3558 f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
3559 cmd.args.compressedImage.glintformat,
3560 cmd.args.compressedImage.w, cmd.args.compressedImage.h,
3561 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
3562 }
3563 break;
3565 f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
3566 if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D || cmd.args.compressedSubImage.target == GL_TEXTURE_2D_ARRAY) {
3567 f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
3568 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
3569 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
3570 cmd.args.compressedSubImage.glintformat,
3571 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
3572 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
3573 glCompressedTexSubImage1D(
3574 cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
3575 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.w,
3576 cmd.args.compressedSubImage.glintformat, cmd.args.compressedSubImage.size,
3577 cmd.args.compressedSubImage.data);
3578 } else {
3579 f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
3580 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
3581 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
3582 cmd.args.compressedSubImage.glintformat,
3583 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
3584 }
3585 break;
3587 {
3588 // Altering the scissor state, so reset the stored state, although
3589 // not strictly required as long as blit is done in endPass() only.
3590 cbD->graphicsPassState.reset();
3591 f->glDisable(GL_SCISSOR_TEST);
3592 GLuint fbo[2];
3593 f->glGenFramebuffers(2, fbo);
3594 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
3595 const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil;
3596 if (ds) {
3597 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3598 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
3599 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3600 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
3601 } else {
3602 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3603 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
3604 }
3605 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
3606 if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) {
3607 if (ds) {
3608 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3609 cmd.args.blitFromRenderbuffer.dstTexture,
3610 cmd.args.blitFromRenderbuffer.dstLevel,
3611 cmd.args.blitFromRenderbuffer.dstLayer);
3612 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3613 cmd.args.blitFromRenderbuffer.dstTexture,
3614 cmd.args.blitFromRenderbuffer.dstLevel,
3615 cmd.args.blitFromRenderbuffer.dstLayer);
3616 } else {
3617 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3618 cmd.args.blitFromRenderbuffer.dstTexture,
3619 cmd.args.blitFromRenderbuffer.dstLevel,
3620 cmd.args.blitFromRenderbuffer.dstLayer);
3621 }
3622 } else {
3623 if (ds) {
3624 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
3625 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
3626 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
3627 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
3628 } else {
3629 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
3630 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
3631 }
3632 }
3633 f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
3634 0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
3635 ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
3636 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
3637 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3638 f->glDeleteFramebuffers(2, fbo);
3639 }
3640 break;
3642 {
3643 // Altering the scissor state, so reset the stored state, although
3644 // not strictly required as long as blit is done in endPass() only.
3645 cbD->graphicsPassState.reset();
3646 f->glDisable(GL_SCISSOR_TEST);
3647 GLuint fbo[2];
3648 f->glGenFramebuffers(2, fbo);
3649 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
3650 const bool ds = cmd.args.blitFromTexture.isDepthStencil;
3651 if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
3652 if (ds) {
3653 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3654 cmd.args.blitFromTexture.srcTexture,
3655 cmd.args.blitFromTexture.srcLevel,
3656 cmd.args.blitFromTexture.srcLayer);
3657 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3658 cmd.args.blitFromTexture.srcTexture,
3659 cmd.args.blitFromTexture.srcLevel,
3660 cmd.args.blitFromTexture.srcLayer);
3661 } else {
3662 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3663 cmd.args.blitFromTexture.srcTexture,
3664 cmd.args.blitFromTexture.srcLevel,
3665 cmd.args.blitFromTexture.srcLayer);
3666 }
3667 } else {
3668 if (ds) {
3669 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
3670 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
3671 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
3672 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
3673 } else {
3674 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
3675 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
3676 }
3677 }
3678 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
3679 if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) {
3680 if (ds) {
3681 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3682 cmd.args.blitFromTexture.dstTexture,
3683 cmd.args.blitFromTexture.dstLevel,
3684 cmd.args.blitFromTexture.dstLayer);
3685 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3686 cmd.args.blitFromTexture.dstTexture,
3687 cmd.args.blitFromTexture.dstLevel,
3688 cmd.args.blitFromTexture.dstLayer);
3689 } else {
3690 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3691 cmd.args.blitFromTexture.dstTexture,
3692 cmd.args.blitFromTexture.dstLevel,
3693 cmd.args.blitFromTexture.dstLayer);
3694 }
3695 } else {
3696 if (ds) {
3697 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
3698 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
3699 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
3700 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
3701 } else {
3702 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
3703 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
3704 }
3705 }
3706 f->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
3707 0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
3708 ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
3709 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
3710 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3711 f->glDeleteFramebuffers(2, fbo);
3712 }
3713 break;
3715 f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
3716 f->glGenerateMipmap(cmd.args.genMip.target);
3717 break;
3719 {
3720 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cmd.args.bindComputePipeline.ps);
3721 f->glUseProgram(psD->program);
3722 }
3723 break;
3725 f->glDispatchCompute(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
3726 break;
3728 {
3729 if (!caps.compute)
3730 break;
3731 GLbitfield barriers = 0;
3732 QRhiPassResourceTracker &tracker(cbD->passResTrackers[cmd.args.barriersForPass.trackerIndex]);
3733 // we only care about after-write, not any other accesses, and
3734 // cannot tell if something was written in a shader several passes
3735 // ago: now the previously written resource may be used with an
3736 // access that was not in the previous passes, result in a missing
3737 // barrier in theory. Hence setting all barrier bits whenever
3738 // something previously written is used for the first time in a
3739 // subsequent pass.
3740 for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
3741 QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(it->stateAtPassBegin.access);
3742 if (bufferAccessIsWrite(accessBeforePass))
3743 barriers |= barriersForBuffer();
3744 }
3745 for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
3746 QGles2Texture::Access accessBeforePass = QGles2Texture::Access(it->stateAtPassBegin.access);
3747 if (textureAccessIsWrite(accessBeforePass))
3748 barriers |= barriersForTexture();
3749 }
3750 if (barriers)
3751 f->glMemoryBarrier(barriers);
3752 }
3753 break;
3755 if (caps.compute)
3756 f->glMemoryBarrier(cmd.args.barrier.barriers);
3757 break;
3759 if (caps.gles && caps.ctxMajor >= 3) {
3760 f->glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
3761 cmd.args.invalidateFramebuffer.attCount,
3762 cmd.args.invalidateFramebuffer.att);
3763 }
3764 break;
3765 default:
3766 break;
3767 }
3768 }
3769 if (state.instancedAttributesUsed) {
3771 if (state.nonzeroAttribDivisor[i])
3772 f->glVertexAttribDivisor(GLuint(i), 0);
3773 }
3774 for (int i = CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; i <= state.maxUntrackedInstancedAttribute; ++i)
3775 f->glVertexAttribDivisor(GLuint(i), 0);
3776 }
3777}
3778
3780{
3782 const bool forceUpdate = !state.valid;
3783 state.valid = true;
3784
3785 const bool scissor = psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor);
3786 if (forceUpdate || scissor != state.scissor) {
3787 state.scissor = scissor;
3788 if (scissor)
3789 f->glEnable(GL_SCISSOR_TEST);
3790 else
3791 f->glDisable(GL_SCISSOR_TEST);
3792 }
3793
3794 const bool cullFace = psD->m_cullMode != QRhiGraphicsPipeline::None;
3795 const GLenum cullMode = cullFace ? toGlCullMode(psD->m_cullMode) : GL_NONE;
3796 if (forceUpdate || cullFace != state.cullFace || cullMode != state.cullMode) {
3797 state.cullFace = cullFace;
3798 state.cullMode = cullMode;
3799 if (cullFace) {
3800 f->glEnable(GL_CULL_FACE);
3801 f->glCullFace(cullMode);
3802 } else {
3803 f->glDisable(GL_CULL_FACE);
3804 }
3805 }
3806
3807 const GLenum frontFace = toGlFrontFace(psD->m_frontFace);
3808 if (forceUpdate || frontFace != state.frontFace) {
3809 state.frontFace = frontFace;
3810 f->glFrontFace(frontFace);
3811 }
3812
3813 const GLenum polygonMode = toGlPolygonMode(psD->m_polygonMode);
3814 if (glPolygonMode && (forceUpdate || polygonMode != state.polygonMode)) {
3815 state.polygonMode = polygonMode;
3816 glPolygonMode(GL_FRONT_AND_BACK, polygonMode);
3817 }
3818
3819 if (!psD->m_targetBlends.isEmpty()) {
3820 // We do not have MRT support here, meaning all targets use the blend
3821 // params from the first one. This is technically incorrect, even if
3822 // nothing in Qt relies on it. However, considering that
3823 // glBlendFuncSeparatei is only available in GL 4.0+ and GLES 3.2+, we
3824 // may just live with this for now because no point in bothering if it
3825 // won't be usable on many GLES (3.1 or 3.0) systems.
3826 const QRhiGraphicsPipeline::TargetBlend &targetBlend(psD->m_targetBlends.first());
3827
3829 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::R),
3830 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::G),
3831 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::B),
3832 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::A)
3833 };
3834 if (forceUpdate || colorMask != state.colorMask) {
3835 state.colorMask = colorMask;
3836 f->glColorMask(colorMask.r, colorMask.g, colorMask.b, colorMask.a);
3837 }
3838
3839 const bool blendEnabled = targetBlend.enable;
3841 toGlBlendFactor(targetBlend.srcColor),
3842 toGlBlendFactor(targetBlend.dstColor),
3843 toGlBlendFactor(targetBlend.srcAlpha),
3844 toGlBlendFactor(targetBlend.dstAlpha),
3845 toGlBlendOp(targetBlend.opColor),
3846 toGlBlendOp(targetBlend.opAlpha)
3847 };
3848 if (forceUpdate || blendEnabled != state.blendEnabled || (blendEnabled && blend != state.blend)) {
3849 state.blendEnabled = blendEnabled;
3850 if (blendEnabled) {
3851 state.blend = blend;
3852 f->glEnable(GL_BLEND);
3853 f->glBlendFuncSeparate(blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
3854 f->glBlendEquationSeparate(blend.opColor, blend.opAlpha);
3855 } else {
3856 f->glDisable(GL_BLEND);
3857 }
3858 }
3859 } else {
3860 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { true, true, true, true };
3861 if (forceUpdate || colorMask != state.colorMask) {
3862 state.colorMask = colorMask;
3863 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3864 }
3865 const bool blendEnabled = false;
3866 if (forceUpdate || blendEnabled != state.blendEnabled) {
3867 state.blendEnabled = blendEnabled;
3868 f->glDisable(GL_BLEND);
3869 }
3870 }
3871
3872 const bool depthTest = psD->m_depthTest;
3873 if (forceUpdate || depthTest != state.depthTest) {
3874 state.depthTest = depthTest;
3875 if (depthTest)
3876 f->glEnable(GL_DEPTH_TEST);
3877 else
3878 f->glDisable(GL_DEPTH_TEST);
3879 }
3880
3881 const bool depthWrite = psD->m_depthWrite;
3882 if (forceUpdate || depthWrite != state.depthWrite) {
3883 state.depthWrite = depthWrite;
3884 f->glDepthMask(depthWrite);
3885 }
3886
3887 const GLenum depthFunc = toGlCompareOp(psD->m_depthOp);
3888 if (forceUpdate || depthFunc != state.depthFunc) {
3889 state.depthFunc = depthFunc;
3890 f->glDepthFunc(depthFunc);
3891 }
3892
3893 const bool stencilTest = psD->m_stencilTest;
3894 const GLuint stencilReadMask = psD->m_stencilReadMask;
3895 const GLuint stencilWriteMask = psD->m_stencilWriteMask;
3897 toGlCompareOp(psD->m_stencilFront.compareOp),
3898 toGlStencilOp(psD->m_stencilFront.failOp),
3899 toGlStencilOp(psD->m_stencilFront.depthFailOp),
3900 toGlStencilOp(psD->m_stencilFront.passOp)
3901 };
3903 toGlCompareOp(psD->m_stencilBack.compareOp),
3904 toGlStencilOp(psD->m_stencilBack.failOp),
3905 toGlStencilOp(psD->m_stencilBack.depthFailOp),
3906 toGlStencilOp(psD->m_stencilBack.passOp)
3907 };
3908 if (forceUpdate || stencilTest != state.stencilTest
3909 || (stencilTest
3910 && (stencilReadMask != state.stencilReadMask || stencilWriteMask != state.stencilWriteMask
3911 || stencilFront != state.stencil[0] || stencilBack != state.stencil[1])))
3912 {
3913 state.stencilTest = stencilTest;
3914 if (stencilTest) {
3915 state.stencilReadMask = stencilReadMask;
3916 state.stencilWriteMask = stencilWriteMask;
3917 state.stencil[0] = stencilFront;
3918 state.stencil[1] = stencilBack;
3919
3920 f->glEnable(GL_STENCIL_TEST);
3921
3922 f->glStencilFuncSeparate(GL_FRONT, stencilFront.func, state.dynamic.stencilRef, stencilReadMask);
3923 f->glStencilOpSeparate(GL_FRONT, stencilFront.failOp, stencilFront.zfailOp, stencilFront.zpassOp);
3924 f->glStencilMaskSeparate(GL_FRONT, stencilWriteMask);
3925
3926 f->glStencilFuncSeparate(GL_BACK, stencilBack.func, state.dynamic.stencilRef, stencilReadMask);
3927 f->glStencilOpSeparate(GL_BACK, stencilBack.failOp, stencilBack.zfailOp, stencilBack.zpassOp);
3928 f->glStencilMaskSeparate(GL_BACK, stencilWriteMask);
3929 } else {
3930 f->glDisable(GL_STENCIL_TEST);
3931 }
3932 }
3933
3934 const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(psD->m_slopeScaledDepthBias);
3935 const float polyOffsetFactor = psD->m_slopeScaledDepthBias;
3936 const float polyOffsetUnits = psD->m_depthBias;
3937 if (forceUpdate || state.polyOffsetFill != polyOffsetFill
3938 || polyOffsetFactor != state.polyOffsetFactor || polyOffsetUnits != state.polyOffsetUnits)
3939 {
3940 state.polyOffsetFill = polyOffsetFill;
3941 state.polyOffsetFactor = polyOffsetFactor;
3942 state.polyOffsetUnits = polyOffsetUnits;
3943 if (polyOffsetFill) {
3944 f->glPolygonOffset(polyOffsetFactor, polyOffsetUnits);
3945 f->glEnable(GL_POLYGON_OFFSET_FILL);
3946 } else {
3947 f->glDisable(GL_POLYGON_OFFSET_FILL);
3948 }
3949 }
3950
3951 if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) {
3952 const float lineWidth = psD->m_lineWidth;
3953 if (forceUpdate || lineWidth != state.lineWidth) {
3954 state.lineWidth = lineWidth;
3955 f->glLineWidth(lineWidth);
3956 }
3957 }
3958
3959 if (psD->m_topology == QRhiGraphicsPipeline::Patches) {
3960 const int cpCount = psD->m_patchControlPointCount;
3961 if (forceUpdate || cpCount != state.cpCount) {
3962 state.cpCount = cpCount;
3963 f->glPatchParameteri(GL_PATCH_VERTICES, qMax(1, cpCount));
3964 }
3965 }
3966
3967 f->glUseProgram(psD->program);
3968}
3969
3970template <typename T>
3971static inline void qrhi_std140_to_packed(T *dst, int vecSize, int elemCount, const void *src)
3972{
3973 const T *p = reinterpret_cast<const T *>(src);
3974 for (int i = 0; i < elemCount; ++i) {
3975 for (int j = 0; j < vecSize; ++j)
3976 dst[vecSize * i + j] = *p++;
3977 p += 4 - vecSize;
3978 }
3979}
3980
3982 void *ps, uint psGeneration, int glslLocation,
3983 int *texUnit, bool *activeTexUnitAltered)
3984{
3985 const bool samplerStateValid = texD->samplerState == samplerD->d;
3986 const bool cachedStateInRange = *texUnit < 16;
3987 bool updateTextureBinding = true;
3988 if (samplerStateValid && cachedStateInRange) {
3989 // If we already encountered the same texture with
3990 // the same pipeline for this texture unit in the
3991 // current pass, then the shader program already
3992 // has the uniform set. As in a 3D scene one model
3993 // often has more than one associated texture map,
3994 // the savings here can become significant,
3995 // depending on the scene.
3996 if (cbD->textureUnitState[*texUnit].ps == ps
3997 && cbD->textureUnitState[*texUnit].psGeneration == psGeneration
3998 && cbD->textureUnitState[*texUnit].texture == texD->texture)
3999 {
4000 updateTextureBinding = false;
4001 }
4002 }
4003 if (updateTextureBinding) {
4004 f->glActiveTexture(GL_TEXTURE0 + uint(*texUnit));
4005 *activeTexUnitAltered = true;
4006 f->glBindTexture(texD->target, texD->texture);
4007 f->glUniform1i(glslLocation, *texUnit);
4008 if (cachedStateInRange) {
4009 cbD->textureUnitState[*texUnit].ps = ps;
4010 cbD->textureUnitState[*texUnit].psGeneration = psGeneration;
4011 cbD->textureUnitState[*texUnit].texture = texD->texture;
4012 }
4013 }
4014 ++(*texUnit);
4015 if (!samplerStateValid) {
4016 f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
4017 f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
4018 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
4019 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
4020 if (caps.texture3D)
4021 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, GLint(samplerD->d.glwrapr));
4022 if (caps.textureCompareMode) {
4023 if (samplerD->d.gltexcomparefunc != GL_NEVER) {
4024 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
4025 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
4026 } else {
4027 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
4028 }
4029 }
4030 texD->samplerState = samplerD->d;
4031 }
4032}
4033
4035 QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
4037 const uint *dynOfsPairs, int dynOfsCount)
4038{
4040 int texUnit = 1; // start from unit 1, keep 0 for resource mgmt stuff to avoid clashes
4041 bool activeTexUnitAltered = false;
4042 union data32_t {
4043 float f;
4044 qint32 i;
4045 };
4046 QVarLengthArray<data32_t, 256> packedArray;
4047 QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
4048 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
4049 QGles2UniformState *uniformState = maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState
4050 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniformState;
4051 struct SeparateTexture {
4053 int binding;
4054 int elem;
4055 };
4056 QVarLengthArray<SeparateTexture, 8> separateTextureBindings;
4057 struct SeparateSampler {
4059 int binding;
4060 };
4061 QVarLengthArray<SeparateSampler, 4> separateSamplerBindings;
4062
4063 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
4064 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
4065
4066 switch (b->type) {
4068 {
4069 int viewOffset = b->u.ubuf.offset;
4070 for (int j = 0; j < dynOfsCount; ++j) {
4071 if (dynOfsPairs[2 * j] == uint(b->binding)) {
4072 viewOffset = int(dynOfsPairs[2 * j + 1]);
4073 break;
4074 }
4075 }
4076 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
4077 const char *bufView = bufD->data.constData() + viewOffset;
4078 for (const QGles2UniformDescription &uniform : std::as_const(uniforms)) {
4079 if (uniform.binding == b->binding) {
4080 // in a uniform buffer everything is at least 4 byte aligned
4081 // so this should not cause unaligned reads
4082 const void *src = bufView + uniform.offset;
4083
4084#ifndef QT_NO_DEBUG
4085 if (uniform.arrayDim > 0
4086 && uniform.type != QShaderDescription::Float
4087 && uniform.type != QShaderDescription::Vec2
4088 && uniform.type != QShaderDescription::Vec3
4089 && uniform.type != QShaderDescription::Vec4
4090 && uniform.type != QShaderDescription::Int
4091 && uniform.type != QShaderDescription::Int2
4092 && uniform.type != QShaderDescription::Int3
4093 && uniform.type != QShaderDescription::Int4
4094 && uniform.type != QShaderDescription::Mat3
4095 && uniform.type != QShaderDescription::Mat4)
4096 {
4097 qWarning("Uniform with buffer binding %d, buffer offset %d, type %d is an array, "
4098 "but arrays are only supported for float, vec2, vec3, vec4, int, "
4099 "ivec2, ivec3, ivec4, mat3 and mat4. "
4100 "Only the first element will be set.",
4101 uniform.binding, uniform.offset, uniform.type);
4102 }
4103#endif
4104
4105 // Our input is an std140 layout uniform block. See
4106 // "Standard Uniform Block Layout" in section 7.6.2.2 of
4107 // the OpenGL spec. This has some peculiar alignment
4108 // requirements, which is not what glUniform* wants. Hence
4109 // the unpacking/repacking for arrays and certain types.
4110
4111 switch (uniform.type) {
4113 {
4114 const int elemCount = uniform.arrayDim;
4115 if (elemCount < 1) {
4116 const float v = *reinterpret_cast<const float *>(src);
4117 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4118 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4119 if (thisUniformState.componentCount != 1 || thisUniformState.v[0] != v) {
4120 thisUniformState.componentCount = 1;
4121 thisUniformState.v[0] = v;
4122 f->glUniform1f(uniform.glslLocation, v);
4123 }
4124 } else {
4125 f->glUniform1f(uniform.glslLocation, v);
4126 }
4127 } else {
4128 // input is 16 bytes per element as per std140, have to convert to packed
4129 packedArray.resize(elemCount);
4130 qrhi_std140_to_packed(&packedArray.data()->f, 1, elemCount, src);
4131 f->glUniform1fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
4132 }
4133 }
4134 break;
4136 {
4137 const int elemCount = uniform.arrayDim;
4138 if (elemCount < 1) {
4139 const float *v = reinterpret_cast<const float *>(src);
4140 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4141 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4142 if (thisUniformState.componentCount != 2
4143 || thisUniformState.v[0] != v[0]
4144 || thisUniformState.v[1] != v[1])
4145 {
4146 thisUniformState.componentCount = 2;
4147 thisUniformState.v[0] = v[0];
4148 thisUniformState.v[1] = v[1];
4149 f->glUniform2fv(uniform.glslLocation, 1, v);
4150 }
4151 } else {
4152 f->glUniform2fv(uniform.glslLocation, 1, v);
4153 }
4154 } else {
4155 packedArray.resize(elemCount * 2);
4156 qrhi_std140_to_packed(&packedArray.data()->f, 2, elemCount, src);
4157 f->glUniform2fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
4158 }
4159 }
4160 break;
4162 {
4163 const int elemCount = uniform.arrayDim;
4164 if (elemCount < 1) {
4165 const float *v = reinterpret_cast<const float *>(src);
4166 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4167 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4168 if (thisUniformState.componentCount != 3
4169 || thisUniformState.v[0] != v[0]
4170 || thisUniformState.v[1] != v[1]
4171 || thisUniformState.v[2] != v[2])
4172 {
4173 thisUniformState.componentCount = 3;
4174 thisUniformState.v[0] = v[0];
4175 thisUniformState.v[1] = v[1];
4176 thisUniformState.v[2] = v[2];
4177 f->glUniform3fv(uniform.glslLocation, 1, v);
4178 }
4179 } else {
4180 f->glUniform3fv(uniform.glslLocation, 1, v);
4181 }
4182 } else {
4183 packedArray.resize(elemCount * 3);
4184 qrhi_std140_to_packed(&packedArray.data()->f, 3, elemCount, src);
4185 f->glUniform3fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
4186 }
4187 }
4188 break;
4190 {
4191 const int elemCount = uniform.arrayDim;
4192 if (elemCount < 1) {
4193 const float *v = reinterpret_cast<const float *>(src);
4194 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4195 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4196 if (thisUniformState.componentCount != 4
4197 || thisUniformState.v[0] != v[0]
4198 || thisUniformState.v[1] != v[1]
4199 || thisUniformState.v[2] != v[2]
4200 || thisUniformState.v[3] != v[3])
4201 {
4202 thisUniformState.componentCount = 4;
4203 thisUniformState.v[0] = v[0];
4204 thisUniformState.v[1] = v[1];
4205 thisUniformState.v[2] = v[2];
4206 thisUniformState.v[3] = v[3];
4207 f->glUniform4fv(uniform.glslLocation, 1, v);
4208 }
4209 } else {
4210 f->glUniform4fv(uniform.glslLocation, 1, v);
4211 }
4212 } else {
4213 f->glUniform4fv(uniform.glslLocation, elemCount, reinterpret_cast<const float *>(src));
4214 }
4215 }
4216 break;
4218 f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
4219 break;
4221 {
4222 const int elemCount = uniform.arrayDim;
4223 if (elemCount < 1) {
4224 // 4 floats per column (or row, if row-major)
4225 float mat[9];
4226 const float *srcMat = reinterpret_cast<const float *>(src);
4227 memcpy(mat, srcMat, 3 * sizeof(float));
4228 memcpy(mat + 3, srcMat + 4, 3 * sizeof(float));
4229 memcpy(mat + 6, srcMat + 8, 3 * sizeof(float));
4230 f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, mat);
4231 } else {
4232 packedArray.resize(elemCount * 9);
4233 qrhi_std140_to_packed(&packedArray.data()->f, 3, elemCount * 3, src);
4234 f->glUniformMatrix3fv(uniform.glslLocation, elemCount, GL_FALSE, &packedArray.constData()->f);
4235 }
4236 }
4237 break;
4239 f->glUniformMatrix4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), GL_FALSE, reinterpret_cast<const float *>(src));
4240 break;
4242 {
4243 const int elemCount = uniform.arrayDim;
4244 if (elemCount < 1) {
4245 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
4246 } else {
4247 packedArray.resize(elemCount);
4248 qrhi_std140_to_packed(&packedArray.data()->i, 1, elemCount, src);
4249 f->glUniform1iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
4250 }
4251 }
4252 break;
4254 {
4255 const int elemCount = uniform.arrayDim;
4256 if (elemCount < 1) {
4257 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4258 } else {
4259 packedArray.resize(elemCount * 2);
4260 qrhi_std140_to_packed(&packedArray.data()->i, 2, elemCount, src);
4261 f->glUniform2iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
4262 }
4263 }
4264 break;
4266 {
4267 const int elemCount = uniform.arrayDim;
4268 if (elemCount < 1) {
4269 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4270 } else {
4271 packedArray.resize(elemCount * 3);
4272 qrhi_std140_to_packed(&packedArray.data()->i, 3, elemCount, src);
4273 f->glUniform3iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
4274 }
4275 }
4276 break;
4278 f->glUniform4iv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const qint32 *>(src));
4279 break;
4281 f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
4282 break;
4284 f->glUniform2uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
4285 break;
4287 f->glUniform3uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
4288 break;
4290 f->glUniform4uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
4291 break;
4292 case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
4293 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
4294 break;
4296 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4297 break;
4299 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4300 break;
4302 f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4303 break;
4304 default:
4305 qWarning("Uniform with buffer binding %d, buffer offset %d has unsupported type %d",
4306 uniform.binding, uniform.offset, uniform.type);
4307 break;
4308 }
4309 }
4310 }
4311 }
4312 break;
4314 {
4315 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
4316 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
4317 void *ps;
4318 uint psGeneration;
4319 if (maybeGraphicsPs) {
4320 ps = maybeGraphicsPs;
4321 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
4322 } else {
4323 ps = maybeComputePs;
4324 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
4325 }
4326 for (int elem = 0; elem < b->u.stex.count; ++elem) {
4327 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex);
4328 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[elem].sampler);
4329 for (const QGles2SamplerDescription &shaderSampler : samplers) {
4330 if (shaderSampler.combinedBinding == b->binding) {
4331 const int loc = shaderSampler.glslLocation + elem;
4332 bindCombinedSampler(cbD, texD, samplerD, ps, psGeneration, loc, &texUnit, &activeTexUnitAltered);
4333 break;
4334 }
4335 }
4336 }
4337 }
4338 break;
4340 for (int elem = 0; elem < b->u.stex.count; ++elem) {
4341 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex);
4342 separateTextureBindings.append({ texD, b->binding, elem });
4343 }
4344 break;
4346 {
4347 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[0].sampler);
4348 separateSamplerBindings.append({ samplerD, b->binding });
4349 }
4350 break;
4354 {
4355 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
4356 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
4357 const bool layered = texD->m_flags.testFlag(QRhiTexture::CubeMap);
4361 else if (b->type == QRhiShaderResourceBinding::ImageStore)
4363 f->glBindImageTexture(GLuint(b->binding), texD->texture,
4364 b->u.simage.level, layered, 0,
4365 access, texD->glsizedintformat);
4366 }
4367 break;
4371 {
4372 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
4373 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
4374 if (b->u.sbuf.offset == 0 && b->u.sbuf.maybeSize == 0)
4375 f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer);
4376 else
4377 f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer,
4378 b->u.sbuf.offset, b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->m_size);
4379 }
4380 break;
4381 default:
4382 Q_UNREACHABLE();
4383 break;
4384 }
4385 }
4386
4387 if (!separateTextureBindings.isEmpty() || !separateSamplerBindings.isEmpty()) {
4388 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
4389 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
4390 void *ps;
4391 uint psGeneration;
4392 if (maybeGraphicsPs) {
4393 ps = maybeGraphicsPs;
4394 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
4395 } else {
4396 ps = maybeComputePs;
4397 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
4398 }
4399 for (const QGles2SamplerDescription &shaderSampler : samplers) {
4400 if (shaderSampler.combinedBinding >= 0)
4401 continue;
4402 for (const SeparateSampler &sepSampler : separateSamplerBindings) {
4403 if (sepSampler.binding != shaderSampler.sbinding)
4404 continue;
4405 for (const SeparateTexture &sepTex : separateTextureBindings) {
4406 if (sepTex.binding != shaderSampler.tbinding)
4407 continue;
4408 const int loc = shaderSampler.glslLocation + sepTex.elem;
4409 bindCombinedSampler(cbD, sepTex.texture, sepSampler.sampler, ps, psGeneration,
4410 loc, &texUnit, &activeTexUnitAltered);
4411 }
4412 }
4413 }
4414 }
4415
4416 if (activeTexUnitAltered)
4417 f->glActiveTexture(GL_TEXTURE0);
4418}
4419
4426
4428 bool *wantsColorClear, bool *wantsDsClear)
4429{
4430 QGles2RenderTargetData *rtD = nullptr;
4432
4433 QGles2CommandBuffer::Command &fbCmd(cbD->commands.get());
4435
4436 static const bool doClearBuffers = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_BUFFERS") == 0;
4437 static const bool doClearColorBuffer = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_COLOR_BUFFER") == 0;
4438
4439 switch (rt->resourceType()) {
4442 if (wantsColorClear)
4443 *wantsColorClear = doClearBuffers && doClearColorBuffer;
4444 if (wantsDsClear)
4445 *wantsDsClear = doClearBuffers;
4446 fbCmd.args.bindFramebuffer.fbo = 0;
4447 fbCmd.args.bindFramebuffer.colorAttCount = 1;
4448 fbCmd.args.bindFramebuffer.stereo = rtD->stereoTarget.has_value();
4449 if (fbCmd.args.bindFramebuffer.stereo)
4450 fbCmd.args.bindFramebuffer.stereoTarget = rtD->stereoTarget.value();
4451 break;
4453 {
4455 rtD = &rtTex->d;
4456 if (wantsColorClear)
4457 *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4458 if (wantsDsClear)
4459 *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4460 fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
4461 fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
4462 fbCmd.args.bindFramebuffer.stereo = false;
4463
4464 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4465 it != itEnd; ++it)
4466 {
4467 const QRhiColorAttachment &colorAtt(*it);
4468 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
4469 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
4470 if (texD && cbD->passNeedsResourceTracking) {
4471 trackedRegisterTexture(&passResTracker, texD,
4474 }
4475 if (resolveTexD && cbD->passNeedsResourceTracking) {
4476 trackedRegisterTexture(&passResTracker, resolveTexD,
4479 }
4480 // renderbuffers cannot be written in shaders (no image store) so
4481 // they do not matter here
4482 }
4483 if (rtTex->m_desc.depthTexture() && cbD->passNeedsResourceTracking) {
4484 trackedRegisterTexture(&passResTracker, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
4487 }
4488 }
4489 break;
4490 default:
4491 Q_UNREACHABLE();
4492 break;
4493 }
4494
4495 fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
4496
4497 return rtD;
4498}
4499
4508
4510 QRhiRenderTarget *rt,
4511 const QColor &colorClearValue,
4512 const QRhiDepthStencilClearValue &depthStencilClearValue,
4513 QRhiResourceUpdateBatch *resourceUpdates,
4514 QRhiCommandBuffer::BeginPassFlags flags)
4515{
4518
4519 if (resourceUpdates)
4520 enqueueResourceUpdates(cb, resourceUpdates);
4521
4522 // Get a new resource tracker. Then add a command that will generate
4523 // glMemoryBarrier() calls based on that tracker when submitted.
4525
4528 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(rtTex->description(), rtTex->d.currentResIdList))
4529 rtTex->create();
4530 }
4531
4532 bool wantsColorClear, wantsDsClear;
4533 QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
4534
4535 QGles2CommandBuffer::Command &clearCmd(cbD->commands.get());
4537 clearCmd.args.clear.mask = 0;
4538 if (rtD->colorAttCount && wantsColorClear)
4539 clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
4540 if (rtD->dsAttCount && wantsDsClear)
4541 clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
4542 clearCmd.args.clear.c[0] = float(colorClearValue.redF());
4543 clearCmd.args.clear.c[1] = float(colorClearValue.greenF());
4544 clearCmd.args.clear.c[2] = float(colorClearValue.blueF());
4545 clearCmd.args.clear.c[3] = float(colorClearValue.alphaF());
4546 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
4547 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
4548
4551 cbD->currentTarget = rt;
4552
4553 cbD->resetCachedState();
4554}
4555
4557{
4560
4563 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4564 it != itEnd; ++it)
4565 {
4566 const QRhiColorAttachment &colorAtt(*it);
4567 if (!colorAtt.resolveTexture())
4568 continue;
4569
4570 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
4571 const QSize size = resolveTexD->pixelSize();
4572 if (colorAtt.renderBuffer()) {
4574 if (rbD->pixelSize() != size) {
4575 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
4576 rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
4577 }
4578 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4580 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
4581 cmd.args.blitFromRenderbuffer.w = size.width();
4582 cmd.args.blitFromRenderbuffer.h = size.height();
4583 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
4584 cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
4585 else
4586 cmd.args.blitFromRenderbuffer.target = resolveTexD->target;
4587 cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture;
4588 cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel();
4589 const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
4590 || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray);
4591 cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
4592 cmd.args.blitFromRenderbuffer.isDepthStencil = false;
4593 } else if (caps.glesMultisampleRenderToTexture) {
4594 // Nothing to do, resolving into colorAtt.resolveTexture() is automatic,
4595 // colorAtt.texture() is in fact not used for anything.
4596 } else {
4597 Q_ASSERT(colorAtt.texture());
4598 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
4599 if (texD->pixelSize() != size) {
4600 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
4601 texD->pixelSize().width(), texD->pixelSize().height(), size.width(), size.height());
4602 }
4603 const int resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
4604 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
4605 const int srcLayer = colorAtt.layer() + resolveIdx;
4606 const int dstLayer = colorAtt.resolveLayer() + resolveIdx;
4607 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4609 if (texD->m_flags.testFlag(QRhiTexture::CubeMap))
4610 cmd.args.blitFromTexture.srcTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(srcLayer);
4611 else
4612 cmd.args.blitFromTexture.srcTarget = texD->target;
4613 cmd.args.blitFromTexture.srcTexture = texD->texture;
4614 cmd.args.blitFromTexture.srcLevel = colorAtt.level();
4615 cmd.args.blitFromTexture.srcLayer = 0;
4616 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || texD->m_flags.testFlag(QRhiTexture::TextureArray))
4617 cmd.args.blitFromTexture.srcLayer = srcLayer;
4618 cmd.args.blitFromTexture.w = size.width();
4619 cmd.args.blitFromTexture.h = size.height();
4620 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
4621 cmd.args.blitFromTexture.dstTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(dstLayer);
4622 else
4623 cmd.args.blitFromTexture.dstTarget = resolveTexD->target;
4624 cmd.args.blitFromTexture.dstTexture = resolveTexD->texture;
4625 cmd.args.blitFromTexture.dstLevel = colorAtt.resolveLevel();
4626 cmd.args.blitFromTexture.dstLayer = 0;
4627 if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray))
4628 cmd.args.blitFromTexture.dstLayer = dstLayer;
4629 cmd.args.blitFromTexture.isDepthStencil = false;
4630 }
4631 }
4632 }
4633
4634 if (rtTex->m_desc.depthResolveTexture()) {
4635 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture());
4636 const QSize size = depthResolveTexD->pixelSize();
4637 if (rtTex->m_desc.depthStencilBuffer()) {
4638 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer());
4639 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4641 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
4642 cmd.args.blitFromRenderbuffer.w = size.width();
4643 cmd.args.blitFromRenderbuffer.h = size.height();
4644 cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target;
4645 cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture;
4646 cmd.args.blitFromRenderbuffer.dstLevel = 0;
4647 cmd.args.blitFromRenderbuffer.dstLayer = 0;
4648 cmd.args.blitFromRenderbuffer.isDepthStencil = true;
4649 } else if (caps.glesMultisampleRenderToTexture) {
4650 // Nothing to do, resolving into depthResolveTexture() is automatic.
4651 } else {
4652 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture());
4653 const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1;
4654 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
4655 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4657 cmd.args.blitFromTexture.srcTarget = depthTexD->target;
4658 cmd.args.blitFromTexture.srcTexture = depthTexD->texture;
4659 cmd.args.blitFromTexture.srcLevel = 0;
4660 cmd.args.blitFromTexture.srcLayer = resolveIdx;
4661 cmd.args.blitFromTexture.w = size.width();
4662 cmd.args.blitFromTexture.h = size.height();
4663 cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target;
4664 cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture;
4665 cmd.args.blitFromTexture.dstLevel = 0;
4666 cmd.args.blitFromTexture.dstLayer = resolveIdx;
4667 cmd.args.blitFromTexture.isDepthStencil = true;
4668 }
4669 }
4670 }
4671
4672 const bool mayDiscardDepthStencil =
4673 (rtTex->m_desc.depthStencilBuffer()
4674 || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)))
4675 && !rtTex->m_desc.depthResolveTexture();
4676 if (mayDiscardDepthStencil) {
4677 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4679 if (caps.needsDepthStencilCombinedAttach) {
4680 cmd.args.invalidateFramebuffer.attCount = 1;
4681 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_STENCIL_ATTACHMENT;
4682 } else {
4683 cmd.args.invalidateFramebuffer.attCount = 2;
4684 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_ATTACHMENT;
4685 cmd.args.invalidateFramebuffer.att[1] = GL_STENCIL_ATTACHMENT;
4686 }
4687 }
4688 }
4689
4691 cbD->currentTarget = nullptr;
4692
4693 if (resourceUpdates)
4694 enqueueResourceUpdates(cb, resourceUpdates);
4695}
4696
4698 QRhiResourceUpdateBatch *resourceUpdates,
4699 QRhiCommandBuffer::BeginPassFlags)
4700{
4703
4704 if (resourceUpdates)
4705 enqueueResourceUpdates(cb, resourceUpdates);
4706
4708
4710
4711 cbD->resetCachedState();
4712}
4713
4724
4726{
4730 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
4731
4732 if (pipelineChanged) {
4733 cbD->currentGraphicsPipeline = nullptr;
4734 cbD->currentComputePipeline = ps;
4735 cbD->currentPipelineGeneration = psD->generation;
4736
4737 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4739 cmd.args.bindComputePipeline.ps = ps;
4740 }
4741}
4742
4743template<typename T>
4744inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
4746 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
4747{
4748 int access = 0;
4749 if (bindingType == loadTypeVal) {
4751 } else {
4753 if (bindingType == loadStoreTypeVal)
4755 }
4756 auto it = writtenResources->find(resource);
4757 if (it != writtenResources->end())
4758 it->first |= access;
4759 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
4760 writtenResources->insert(resource, { access, true });
4761}
4762
4764{
4767
4768 if (cbD->currentComputeSrb) {
4769 GLbitfield barriers = 0;
4770
4771 // The key in the writtenResources map indicates that the resource was
4772 // written in a previous dispatch, whereas the value accumulates the
4773 // access mask in the current one.
4774 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
4775 accessAndIsNewFlag = { 0, false };
4776
4778 const int bindingCount = srbD->m_bindings.size();
4779 for (int i = 0; i < bindingCount; ++i) {
4780 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
4781 switch (b->type) {
4786 b->u.simage.tex,
4787 b->type,
4791 break;
4796 b->u.sbuf.buf,
4797 b->type,
4801 break;
4802 default:
4803 break;
4804 }
4805 }
4806
4807 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
4808 const int accessInThisDispatch = it->first;
4809 const bool isNewInThisDispatch = it->second;
4810 if (accessInThisDispatch && !isNewInThisDispatch) {
4811 if (it.key()->resourceType() == QRhiResource::Texture)
4813 else
4815 }
4816 // Anything that was previously written, but is only read now, can be
4817 // removed from the written list (because that previous write got a
4818 // corresponding barrier now).
4819 if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
4820 it = cbD->computePassState.writtenResources.erase(it);
4821 else
4822 ++it;
4823 }
4824
4825 if (barriers) {
4826 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4828 cmd.args.barrier.barriers = barriers;
4829 }
4830 }
4831
4832 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4834 cmd.args.dispatch.x = GLuint(x);
4835 cmd.args.dispatch.y = GLuint(y);
4836 cmd.args.dispatch.z = GLuint(z);
4837}
4838
4840{
4841 switch (type) {
4843 return GL_VERTEX_SHADER;
4849 return GL_GEOMETRY_SHADER;
4851 return GL_FRAGMENT_SHADER;
4853 return GL_COMPUTE_SHADER;
4854 default:
4855 Q_UNREACHABLE_RETURN(GL_VERTEX_SHADER);
4856 }
4857}
4858
4860{
4861 const QShader bakedShader = shaderStage.shader();
4862 QList<int> versionsToTry;
4864 if (caps.gles) {
4865 if (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)) {
4866 versionsToTry << 320 << 310 << 300 << 100;
4867 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
4868 versionsToTry << 310 << 300 << 100;
4869 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
4870 versionsToTry << 300 << 100;
4871 } else {
4872 versionsToTry << 100;
4873 }
4874 for (int v : versionsToTry) {
4876 source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
4877 if (!source.isEmpty()) {
4878 if (shaderVersion)
4879 *shaderVersion = ver;
4880 break;
4881 }
4882 }
4883 } else {
4884 if (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6)) {
4885 versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4886 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 5) {
4887 versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4888 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 4) {
4889 versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4890 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 3) {
4891 versionsToTry << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4892 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 2) {
4893 versionsToTry << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4894 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 1) {
4895 versionsToTry << 410 << 400 << 330 << 150 << 140 << 130;
4896 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 0) {
4897 versionsToTry << 400 << 330 << 150 << 140 << 130;
4898 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 3) {
4899 versionsToTry << 330 << 150 << 140 << 130;
4900 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 2) {
4901 versionsToTry << 150 << 140 << 130;
4902 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
4903 versionsToTry << 140 << 130;
4904 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
4905 versionsToTry << 130;
4906 }
4907 if (!caps.coreProfile)
4908 versionsToTry << 120;
4909 for (int v : versionsToTry) {
4910 source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
4911 if (!source.isEmpty()) {
4912 if (shaderVersion)
4913 *shaderVersion = v;
4914 break;
4915 }
4916 }
4917 }
4918 if (source.isEmpty()) {
4919 qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
4920 << ") in baked shader" << bakedShader;
4921 }
4922 return source;
4923}
4924
4926{
4927 const QByteArray source = shaderSource(shaderStage, shaderVersion);
4928 if (source.isEmpty())
4929 return false;
4930
4931 GLuint shader;
4932 auto cacheIt = m_shaderCache.constFind(shaderStage);
4933 if (cacheIt != m_shaderCache.constEnd()) {
4934 shader = *cacheIt;
4935 } else {
4936 shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
4937 const char *srcStr = source.constData();
4938 const GLint srcLength = source.size();
4939 f->glShaderSource(shader, 1, &srcStr, &srcLength);
4940 f->glCompileShader(shader);
4941 GLint compiled = 0;
4942 f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
4943 if (!compiled) {
4944 GLint infoLogLength = 0;
4945 f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
4946 QByteArray log;
4947 if (infoLogLength > 1) {
4948 GLsizei length = 0;
4949 log.resize(infoLogLength);
4950 f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
4951 }
4952 qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
4953 return false;
4954 }
4956 // Use the simplest strategy: too many cached shaders -> drop them all.
4957 for (uint shader : m_shaderCache)
4958 f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
4960 }
4961 m_shaderCache.insert(shaderStage, shader);
4962 }
4963
4964 f->glAttachShader(program, shader);
4965
4966 return true;
4967}
4968
4970{
4971 f->glLinkProgram(program);
4972 GLint linked = 0;
4973 f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
4974 if (!linked) {
4975 GLint infoLogLength = 0;
4976 f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
4977 QByteArray log;
4978 if (infoLogLength > 1) {
4979 GLsizei length = 0;
4980 log.resize(infoLogLength);
4981 f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
4982 }
4983 qWarning("Failed to link shader program: %s", log.constData());
4984 return false;
4985 }
4986 return true;
4987}
4988
4990 const QByteArray &namePrefix,
4991 int binding,
4992 int baseOffset,
4994 QDuplicateTracker<int, 256> *activeUniformLocations,
4996{
4997 if (var.type == QShaderDescription::Struct) {
4998 qWarning("Nested structs are not supported at the moment. '%s' ignored.",
4999 var.name.constData());
5000 return;
5001 }
5003 uniform.type = var.type;
5004 const QByteArray name = namePrefix + var.name;
5005 // Here we expect that the OpenGL implementation has proper active uniform
5006 // handling, meaning that a uniform that is declared but not accessed
5007 // elsewhere in the code is reported as -1 when querying the location. If
5008 // that is not the case, it won't break anything, but we'll generate
5009 // unnecessary glUniform* calls then.
5010 uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
5011 if (uniform.glslLocation >= 0 && !activeUniformLocations->hasSeen(uniform.glslLocation)) {
5012 if (var.arrayDims.size() > 1) {
5013 qWarning("Array '%s' has more than one dimension. This is not supported.",
5014 var.name.constData());
5015 return;
5016 }
5017 uniform.binding = binding;
5018 uniform.offset = uint(baseOffset + var.offset);
5019 uniform.size = var.size;
5020 uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
5021 dst->append(uniform);
5022 }
5023}
5024
5027 QDuplicateTracker<int, 256> *activeUniformLocations,
5029{
5030 QByteArray prefix = ub.structName + '.';
5031 for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
5032 if (blockMember.type == QShaderDescription::Struct) {
5033 QByteArray structPrefix = prefix + blockMember.name;
5034
5035 const int baseOffset = blockMember.offset;
5036 if (blockMember.arrayDims.isEmpty()) {
5037 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
5038 registerUniformIfActive(structMember, structPrefix + ".", ub.binding,
5039 baseOffset, program, activeUniformLocations, dst);
5040 } else {
5041 if (blockMember.arrayDims.size() > 1) {
5042 qWarning("Array of struct '%s' has more than one dimension. Only the first "
5043 "dimension is used.",
5044 blockMember.name.constData());
5045 }
5046 const int dim = blockMember.arrayDims.first();
5047 const int elemSize = blockMember.size / dim;
5048 int elemOffset = baseOffset;
5049 for (int di = 0; di < dim; ++di) {
5050 const QByteArray arrayPrefix = structPrefix + '[' + QByteArray::number(di) + ']' + '.';
5051 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
5052 registerUniformIfActive(structMember, arrayPrefix, ub.binding, elemOffset, program, activeUniformLocations, dst);
5053 elemOffset += elemSize;
5054 }
5055 }
5056 } else {
5057 registerUniformIfActive(blockMember, prefix, ub.binding, 0, program, activeUniformLocations, dst);
5058 }
5059 }
5060}
5061
5065{
5067 sampler.glslLocation = f->glGetUniformLocation(program, v.name.constData());
5068 if (sampler.glslLocation >= 0) {
5069 sampler.combinedBinding = v.binding;
5070 sampler.tbinding = -1;
5071 sampler.sbinding = -1;
5072 dst->append(sampler);
5073 }
5074}
5075
5079{
5081 sampler.glslLocation = f->glGetUniformLocation(program, mapping.combinedSamplerName.constData());
5082 if (sampler.glslLocation >= 0) {
5083 sampler.combinedBinding = -1;
5084 sampler.tbinding = mapping.textureBinding;
5085 sampler.sbinding = mapping.samplerBinding;
5086 dst->append(sampler);
5087 }
5088}
5089
5091{
5092 if (!vsDesc.isValid() || !fsDesc.isValid())
5093 return;
5094
5095 // Print a warning if the fragment shader input for a given location uses a
5096 // name that does not match the vertex shader output at the same location.
5097 // This is not an error with any other API and not with GLSL >= 330 either,
5098 // but matters for older GLSL code that has no location qualifiers.
5099 for (const QShaderDescription::InOutVariable &outVar : vsDesc.outputVariables()) {
5100 for (const QShaderDescription::InOutVariable &inVar : fsDesc.inputVariables()) {
5101 if (inVar.location == outVar.location) {
5102 if (inVar.name != outVar.name) {
5103 qWarning("Vertex output name '%s' does not match fragment input '%s'. "
5104 "This should be avoided because it causes problems with older GLSL versions.",
5105 outVar.name.constData(), inVar.name.constData());
5106 }
5107 break;
5108 }
5109 }
5110 }
5111}
5112
5114{
5116 return checker.get(ctx)->isSupported();
5117}
5118
5120
5140
5142 int stageCount,
5144 const QVector<QShaderDescription::InOutVariable> &inputVars,
5146{
5148
5149 // the traditional QOpenGL disk cache since Qt 5.9
5150 const bool legacyDiskCacheEnabled = isProgramBinaryDiskCacheEnabled();
5151
5152 // QRhi's own (set)PipelineCacheData()
5153 const bool pipelineCacheEnabled = caps.programBinary && !m_pipelineCache.isEmpty();
5154
5155 // calculating the cache key based on the source code is common for both types of caches
5156 if (legacyDiskCacheEnabled || pipelineCacheEnabled) {
5158 for (int i = 0; i < stageCount; ++i) {
5159 const QRhiShaderStage &stage(stages[i]);
5160 QByteArray source = shaderSource(stage, nullptr);
5161 if (source.isEmpty())
5163
5164 if (stage.type() == QRhiShaderStage::Vertex) {
5165 // Now add something to the key that indicates the vertex input locations.
5166 // A GLSL shader lower than 330 (150, 140, ...) will not have location
5167 // qualifiers. This means that the shader source code is the same
5168 // regardless of what locations inputVars contains. This becomes a problem
5169 // because we'll glBindAttribLocation the shader based on inputVars, but
5170 // that's only when compiling/linking when there was no cache hit. Picking
5171 // from the cache afterwards should take the input locations into account
5172 // since if inputVars has now different locations for the attributes, then
5173 // it is not ok to reuse a program binary that used different attribute
5174 // locations. For a lot of clients this would not be an issue since they
5175 // typically hardcode and use the same vertex locations on every run. Some
5176 // systems that dynamically generate shaders may end up with a non-stable
5177 // order (and so location numbers), however. This is sub-optimal because
5178 // it makes caching inefficient, and said clients should be fixed, but in
5179 // any case this should not break rendering. Hence including the locations
5180 // in the cache key.
5181 QMap<QByteArray, int> inputLocations; // sorted by key when iterating
5182 for (const QShaderDescription::InOutVariable &var : inputVars)
5183 inputLocations.insert(var.name, var.location);
5184 source += QByteArrayLiteral("\n // "); // just to be nice; treated as an arbitrary string regardless
5185 for (auto it = inputLocations.cbegin(), end = inputLocations.cend(); it != end; ++it) {
5186 source += it.key();
5187 source += QByteArray::number(it.value());
5188 }
5189 source += QByteArrayLiteral("\n");
5190 }
5191
5193 }
5194
5195 *cacheKey = binaryProgram.cacheKey();
5196
5197 // Try our pipeline cache simulation first, if it got seeded with
5198 // setPipelineCacheData and there's a hit, then no need to go to the
5199 // filesystem at all.
5200 if (pipelineCacheEnabled) {
5201 auto it = m_pipelineCache.constFind(*cacheKey);
5202 if (it != m_pipelineCache.constEnd()) {
5203 GLenum err;
5204 for ( ; ; ) {
5205 err = f->glGetError();
5206 if (err == GL_NO_ERROR || err == GL_CONTEXT_LOST)
5207 break;
5208 }
5209 f->glProgramBinary(program, it->format, it->data.constData(), it->data.size());
5210 err = f->glGetError();
5211 if (err == GL_NO_ERROR) {
5212 GLint linkStatus = 0;
5213 f->glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
5214 if (linkStatus == GL_TRUE)
5216 }
5217 }
5218 }
5219
5220 if (legacyDiskCacheEnabled && qrhi_programBinaryCache()->load(*cacheKey, program)) {
5221 // use the logging category QOpenGLShaderProgram would
5222 qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
5223 program, cacheKey->constData());
5225 }
5226 }
5227
5229}
5230
5232{
5233 // This is only for the traditional QOpenGL disk cache since Qt 5.9.
5234
5236 // use the logging category QOpenGLShaderProgram would
5237 qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
5238 program, cacheKey.constData());
5239 qrhi_programBinaryCache()->save(cacheKey, program);
5240 }
5241}
5242
5244{
5245 // This handles our own simulated "pipeline cache". (specific to QRhi, not
5246 // shared with legacy QOpenGL* stuff)
5247
5248 if (caps.programBinary && (force || !m_pipelineCache.contains(cacheKey))) {
5249 GLint blobSize = 0;
5250 f->glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &blobSize);
5251 QByteArray blob(blobSize, Qt::Uninitialized);
5252 GLint outSize = 0;
5253 GLenum binaryFormat = 0;
5254 f->glGetProgramBinary(program, blobSize, &outSize, &binaryFormat, blob.data());
5255 if (blobSize == outSize)
5256 m_pipelineCache.insert(cacheKey, { binaryFormat, blob });
5257 }
5258}
5259
5264
5269
5271{
5272 data.clear();
5273 if (!buffer)
5274 return;
5275
5278
5279 e.buffer.buffer = buffer;
5280 buffer = 0;
5281
5283 if (rhiD) {
5284 rhiD->releaseQueue.append(e);
5285 rhiD->unregisterResource(this);
5286 }
5287}
5288
5290{
5291 if (buffer)
5292 destroy();
5293
5295
5296 nonZeroSize = m_size <= 0 ? 256 : m_size;
5297
5298 if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
5299 if (int(m_usage) != QRhiBuffer::UniformBuffer) {
5300 qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
5301 return false;
5302 }
5303 data.resize(nonZeroSize);
5304 return true;
5305 }
5306
5307 if (!rhiD->ensureContext())
5308 return false;
5309
5311 if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
5313 else if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
5315
5316 rhiD->f->glGenBuffers(1, &buffer);
5317 rhiD->f->glBindBuffer(targetForDataOps, buffer);
5318 rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
5319
5320 if (rhiD->glObjectLabel)
5321 rhiD->glObjectLabel(GL_BUFFER, buffer, -1, m_objectName.constData());
5322
5324
5325 rhiD->registerResource(this);
5326 return true;
5327}
5328
5330{
5331 if (m_usage.testFlag(QRhiBuffer::UniformBuffer))
5332 return { {}, 0 };
5333
5334 return { { &buffer }, 1 };
5335}
5336
5338{
5340 if (!m_usage.testFlag(UniformBuffer)) {
5342 rhiD->f->glBindBuffer(targetForDataOps, buffer);
5343 if (rhiD->caps.properMapBuffer) {
5344 return static_cast<char *>(rhiD->f->glMapBufferRange(targetForDataOps, 0, nonZeroSize,
5346 } else {
5347 // Need some storage for the data, use the otherwise unused 'data' member.
5348 if (data.isEmpty())
5349 data.resize(nonZeroSize);
5350 }
5351 }
5352 return data.data();
5353}
5354
5356{
5357 if (!m_usage.testFlag(UniformBuffer)) {
5359 if (rhiD->caps.properMapBuffer)
5360 rhiD->f->glUnmapBuffer(targetForDataOps);
5361 else
5362 rhiD->f->glBufferSubData(targetForDataOps, 0, nonZeroSize, data.data());
5363 }
5364}
5365
5367 int sampleCount, QRhiRenderBuffer::Flags flags,
5368 QRhiTexture::Format backingFormatHint)
5369 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
5370{
5371}
5372
5377
5379{
5380 if (!renderbuffer)
5381 return;
5382
5385
5386 e.renderbuffer.renderbuffer = renderbuffer;
5387 e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
5388
5389 renderbuffer = 0;
5391
5393 if (rhiD) {
5394 if (owns)
5395 rhiD->releaseQueue.append(e);
5396 rhiD->unregisterResource(this);
5397 }
5398}
5399
5401{
5402 if (renderbuffer)
5403 destroy();
5404
5406 samples = rhiD->effectiveSampleCount(m_sampleCount);
5407
5408 if (m_flags.testFlag(UsedWithSwapChainOnly)) {
5409 if (m_type == DepthStencil)
5410 return true;
5411
5412 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
5413 }
5414
5415 if (!rhiD->ensureContext())
5416 return false;
5417
5418 rhiD->f->glGenRenderbuffers(1, &renderbuffer);
5419 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
5420
5421 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
5422
5423 switch (m_type) {
5425 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
5426 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
5427 size.width(), size.height());
5429 } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
5430 const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
5431 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage,
5432 size.width(), size.height());
5434 } else {
5435 GLenum depthStorage = GL_DEPTH_COMPONENT;
5436 if (rhiD->caps.gles) {
5437 if (rhiD->caps.depth24)
5438 depthStorage = GL_DEPTH_COMPONENT24;
5439 else
5440 depthStorage = GL_DEPTH_COMPONENT16; // plain ES 2.0 only has this
5441 }
5442 const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
5443 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage,
5444 size.width(), size.height());
5445 rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
5446 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
5447 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
5448 size.width(), size.height());
5449 }
5450 break;
5452 {
5453 GLenum internalFormat = GL_RGBA4; // ES 2.0
5454 if (rhiD->caps.rgba8Format) {
5457 GLenum glintformat, glformat, gltype;
5458 // only care about the sized internal format, the rest is not used here
5460 &glintformat, &internalFormat, &glformat, &gltype);
5461 }
5462 }
5463 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
5464 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalFormat,
5465 size.width(), size.height());
5466 } else {
5467 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat,
5468 size.width(), size.height());
5469 }
5470 }
5471 break;
5472 default:
5473 Q_UNREACHABLE();
5474 break;
5475 }
5476
5477 if (rhiD->glObjectLabel)
5478 rhiD->glObjectLabel(GL_RENDERBUFFER, renderbuffer, -1, m_objectName.constData());
5479
5480 owns = true;
5481 generation += 1;
5482 rhiD->registerResource(this);
5483 return true;
5484}
5485
5487{
5488 if (!src.object)
5489 return false;
5490
5491 if (renderbuffer)
5492 destroy();
5493
5495 samples = rhiD->effectiveSampleCount(m_sampleCount);
5496
5497 if (m_flags.testFlag(UsedWithSwapChainOnly))
5498 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless when importing an existing native object");
5499
5500 if (!rhiD->ensureContext())
5501 return false;
5502
5503 renderbuffer = src.object;
5504
5505 owns = false;
5506 generation += 1;
5507 rhiD->registerResource(this);
5508 return true;
5509}
5510
5518
5520 int arraySize, int sampleCount, Flags flags)
5521 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
5522{
5523}
5524
5529
5531{
5532 if (!texture)
5533 return;
5534
5537
5538 e.texture.texture = texture;
5539
5540 texture = 0;
5541 specified = false;
5542 zeroInitialized = false;
5543
5545 if (rhiD) {
5546 if (owns)
5547 rhiD->releaseQueue.append(e);
5548 rhiD->unregisterResource(this);
5549 }
5550}
5551
5553{
5554 if (texture)
5555 destroy();
5556
5558 if (!rhiD->ensureContext())
5559 return false;
5560
5561 const bool isCube = m_flags.testFlag(CubeMap);
5562 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
5563 const bool is3D = m_flags.testFlag(ThreeDimensional);
5564 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5565 const bool isCompressed = rhiD->isCompressedFormat(m_format);
5566 const bool is1D = m_flags.testFlag(OneDimensional);
5567
5568 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
5569 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
5570
5571 if (is3D && !rhiD->caps.texture3D) {
5572 qWarning("3D textures are not supported");
5573 return false;
5574 }
5575 if (isCube && is3D) {
5576 qWarning("Texture cannot be both cube and 3D");
5577 return false;
5578 }
5579 if (isArray && is3D) {
5580 qWarning("Texture cannot be both array and 3D");
5581 return false;
5582 }
5583 if (is1D && !rhiD->caps.texture1D) {
5584 qWarning("1D textures are not supported");
5585 return false;
5586 }
5587 if (is1D && is3D) {
5588 qWarning("Texture cannot be both 1D and 3D");
5589 return false;
5590 }
5591 if (is1D && isCube) {
5592 qWarning("Texture cannot be both 1D and cube");
5593 return false;
5594 }
5595
5596 if (m_depth > 1 && !is3D) {
5597 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
5598 return false;
5599 }
5600 if (m_arraySize > 0 && !isArray) {
5601 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
5602 return false;
5603 }
5604 if (m_arraySize < 1 && isArray) {
5605 qWarning("Texture is an array but array size is %d", m_arraySize);
5606 return false;
5607 }
5608
5609 target = isCube ? GL_TEXTURE_CUBE_MAP
5611 : (is3D ? GL_TEXTURE_3D
5612 : (is1D ? (isArray ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D)
5613 : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)));
5614
5615 if (m_flags.testFlag(ExternalOES))
5617 else if (m_flags.testFlag(TextureRectangleGL))
5619
5620 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
5622
5623 if (isCompressed) {
5624 if (m_flags.testFlag(UsedWithLoadStore)) {
5625 qWarning("Compressed texture cannot be used with image load/store");
5626 return false;
5627 }
5629 if (!glintformat) {
5630 qWarning("Compressed format %d not mappable to GL compressed format", m_format);
5631 return false;
5632 }
5634 glformat = GL_RGBA;
5635 } else {
5636 toGlTextureFormat(m_format, rhiD->caps,
5638 }
5639
5641
5643
5644 if (adjustedSize)
5645 *adjustedSize = size;
5646
5647 return true;
5648}
5649
5651{
5652 QSize size;
5653 if (!prepareCreate(&size))
5654 return false;
5655
5657 rhiD->f->glGenTextures(1, &texture);
5658
5659 const bool isCube = m_flags.testFlag(CubeMap);
5660 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
5661 const bool is3D = m_flags.testFlag(ThreeDimensional);
5662 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5663 const bool isCompressed = rhiD->isCompressedFormat(m_format);
5664 const bool is1D = m_flags.testFlag(OneDimensional);
5665
5666 if (!isCompressed) {
5667 rhiD->f->glBindTexture(target, texture);
5668 if (!m_flags.testFlag(UsedWithLoadStore)) {
5669 if (is1D) {
5670 for (int level = 0; level < mipLevelCount; ++level) {
5671 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
5672 if (isArray)
5673 rhiD->f->glTexImage2D(target, level, GLint(glintformat), mipSize.width(),
5674 qMax(0, m_arraySize), 0, glformat, gltype, nullptr);
5675 else
5676 rhiD->glTexImage1D(target, level, GLint(glintformat), mipSize.width(), 0,
5677 glformat, gltype, nullptr);
5678 }
5679 } else if (is3D || isArray) {
5680 const int layerCount = is3D ? qMax(1, m_depth) : qMax(0, m_arraySize);
5681 if (hasMipMaps) {
5682 for (int level = 0; level != mipLevelCount; ++level) {
5683 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
5684 rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), layerCount,
5685 0, glformat, gltype, nullptr);
5686 }
5687 } else {
5688 rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), layerCount,
5689 0, glformat, gltype, nullptr);
5690 }
5691 } else if (hasMipMaps || isCube) {
5692 const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
5693 for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
5694 for (int level = 0; level != mipLevelCount; ++level) {
5695 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
5696 rhiD->f->glTexImage2D(faceTargetBase + uint(layer), level, GLint(glintformat),
5697 mipSize.width(), mipSize.height(), 0,
5698 glformat, gltype, nullptr);
5699 }
5700 }
5701 } else {
5702 // 2D texture. For multisample textures the GLES 3.1
5703 // glStorage2DMultisample must be used for portability.
5704 if (m_sampleCount > 1 && rhiD->caps.multisampledTexture) {
5705 // internal format must be sized
5706 rhiD->f->glTexStorage2DMultisample(target, m_sampleCount, glsizedintformat,
5707 size.width(), size.height(), GL_TRUE);
5708 } else {
5709 rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
5710 0, glformat, gltype, nullptr);
5711 }
5712 }
5713 } else {
5714 // Must be specified with immutable storage functions otherwise
5715 // bindImageTexture may fail. Also, the internal format must be a
5716 // sized format here.
5717 if (is1D && !isArray)
5718 rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
5719 else if (!is1D && (is3D || isArray))
5720 rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(),
5721 is3D ? qMax(1, m_depth) : qMax(0, m_arraySize));
5722 else if (m_sampleCount > 1)
5723 rhiD->f->glTexStorage2DMultisample(target, m_sampleCount, glsizedintformat,
5724 size.width(), size.height(), GL_TRUE);
5725 else
5726 rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
5727 is1D ? qMax(0, m_arraySize) : size.height());
5728 }
5729 specified = true;
5730 } else {
5731 // Cannot use glCompressedTexImage2D without valid data, so defer.
5732 // Compressed textures will not be used as render targets so this is
5733 // not an issue.
5734 specified = false;
5735 }
5736
5737 if (rhiD->glObjectLabel)
5738 rhiD->glObjectLabel(GL_TEXTURE, texture, -1, m_objectName.constData());
5739
5740 owns = true;
5741
5742 generation += 1;
5743 rhiD->registerResource(this);
5744 return true;
5745}
5746
5748{
5749 const uint textureId = uint(src.object);
5750 if (textureId == 0)
5751 return false;
5752
5753 if (!prepareCreate())
5754 return false;
5755
5756 texture = textureId;
5757 specified = true;
5758 zeroInitialized = true;
5759
5760 owns = false;
5761
5762 generation += 1;
5764 rhiD->registerResource(this);
5765 return true;
5766}
5767
5772
5775 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
5776{
5777}
5778
5783
5785{
5787 if (rhiD)
5788 rhiD->unregisterResource(this);
5789}
5790
5805
5806// dummy, no Vulkan-style RenderPass+Framebuffer concept here
5811
5816
5818{
5820 if (rhiD)
5821 rhiD->unregisterResource(this);
5822}
5823
5825{
5826 Q_UNUSED(other);
5827 return true;
5828}
5829
5837
5839{
5840 return {};
5841}
5842
5848
5853
5855{
5856 // nothing to do here
5857}
5858
5863
5865{
5866 return d.dpr;
5867}
5868
5870{
5871 return d.sampleCount;
5872}
5873
5881
5886
5888{
5889 if (!framebuffer)
5890 return;
5891
5894
5895 e.textureRenderTarget.framebuffer = framebuffer;
5896 e.textureRenderTarget.nonMsaaThrowawayDepthTexture = nonMsaaThrowawayDepthTexture;
5897
5898 framebuffer = 0;
5900
5902 if (rhiD) {
5903 rhiD->releaseQueue.append(e);
5904 rhiD->unregisterResource(this);
5905 }
5906}
5907
5915
5917{
5919
5920 if (framebuffer)
5921 destroy();
5922
5923 const bool hasColorAttachments = m_desc.colorAttachmentCount() > 0;
5924 Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
5926 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5927
5928 if (hasColorAttachments) {
5929 const int count = int(m_desc.colorAttachmentCount());
5930 if (count > rhiD->caps.maxDrawBuffers) {
5931 qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
5932 count, rhiD->caps.maxDrawBuffers);
5933 }
5934 }
5935 if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
5936 qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
5937
5938 if (!rhiD->ensureContext())
5939 return false;
5940
5941 rhiD->f->glGenFramebuffers(1, &framebuffer);
5942 rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
5943
5944 d.colorAttCount = 0;
5945 int attIndex = 0;
5946 int multiViewCount = 0;
5947 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
5948 d.colorAttCount += 1;
5949 const QRhiColorAttachment &colorAtt(*it);
5950 QRhiTexture *texture = colorAtt.texture();
5951 QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
5952 Q_ASSERT(texture || renderBuffer);
5953 if (texture) {
5955 Q_ASSERT(texD->texture && texD->specified);
5956 if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
5957 if (colorAtt.multiViewCount() < 2) {
5958 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
5959 colorAtt.level(), colorAtt.layer());
5960 } else {
5961 multiViewCount = colorAtt.multiViewCount();
5962 if (texD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture && colorAtt.resolveTexture()) {
5963 // Special path for GLES and GL_OVR_multiview_multisampled_render_to_texture:
5964 // ignore the color attachment's (multisample) texture
5965 // array and give the resolve texture array to GL. (no
5966 // explicit resolving is needed by us later on)
5967 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5968 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
5969 GL_COLOR_ATTACHMENT0 + uint(attIndex),
5970 resolveTexD->texture,
5971 colorAtt.resolveLevel(),
5972 texD->sampleCount(),
5973 colorAtt.resolveLayer(),
5974 multiViewCount);
5975 } else {
5976 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER,
5977 GL_COLOR_ATTACHMENT0 + uint(attIndex),
5978 texD->texture,
5979 colorAtt.level(),
5980 colorAtt.layer(),
5981 multiViewCount);
5982 }
5983 }
5984 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
5985 rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
5986 texD->target + uint(colorAtt.layer()), texD->texture,
5987 colorAtt.level());
5988 } else {
5989 if (texD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
5990 // Special path for GLES and GL_EXT_multisampled_render_to_texture:
5991 // ignore the color attachment's (multisample) texture and
5992 // give the resolve texture to GL. (no explicit resolving is
5993 // needed by us later on)
5994 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5995 const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
5996 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
5997 resolveTexD->texture, colorAtt.level(), texD->sampleCount());
5998 } else {
5999 const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
6000 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
6001 texD->texture, colorAtt.level());
6002 }
6003 }
6004 if (attIndex == 0) {
6005 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
6006 d.sampleCount = texD->sampleCount();
6007 }
6008 } else if (renderBuffer) {
6009 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
6010 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer);
6011 if (attIndex == 0) {
6012 d.pixelSize = rbD->pixelSize();
6013 d.sampleCount = rbD->samples;
6014 }
6015 }
6016 }
6017
6018 if (hasDepthStencil) {
6019 if (m_desc.depthStencilBuffer()) {
6021 if (rhiD->caps.needsDepthStencilCombinedAttach) {
6022 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
6023 depthRbD->renderbuffer);
6024 } else {
6025 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
6026 depthRbD->renderbuffer);
6027 if (depthRbD->stencilRenderbuffer) {
6028 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
6029 depthRbD->stencilRenderbuffer);
6030 } else {
6031 // packed depth-stencil
6032 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
6033 depthRbD->renderbuffer);
6034 }
6035 }
6036 if (d.colorAttCount == 0) {
6037 d.pixelSize = depthRbD->pixelSize();
6038 d.sampleCount = depthRbD->samples;
6039 }
6040 } else {
6042 if (multiViewCount < 2) {
6043 if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) {
6044 // Special path for GLES and
6045 // GL_EXT_multisampled_render_to_texture, for depth-stencil.
6046 // Relevant only when depthResolveTexture is set.
6048 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target,
6049 depthResolveTexD->texture, 0, depthTexD->sampleCount());
6050 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
6051 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target,
6052 depthResolveTexD->texture, 0, depthTexD->sampleCount());
6053 }
6054 } else {
6055 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
6056 depthTexD->texture, 0);
6057 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
6058 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
6059 depthTexD->texture, 0);
6060 }
6061 }
6062 } else {
6063 if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) {
6064 // And so it turns out
6065 // https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt
6066 // does not work with multisample 2D texture arrays. (at least
6067 // that's what Issue 30 in the extension spec seems to imply)
6068 //
6069 // There is https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multiview_texture_multisample.txt
6070 // that seems to resolve that, but that does not seem to
6071 // work (or not available) on GLES devices such as the Quest 3.
6072 //
6073 // So instead, on GLES we can use the
6074 // multisample-multiview-auto-resolving version (which in
6075 // turn is not supported on desktop GL e.g. by NVIDIA), too
6076 // bad we have a multisample depth texture array here as
6077 // every other API out there requires that. So, in absence
6078 // of a depthResolveTexture, create a temporary one ignoring
6079 // what the user has already created.
6080 //
6082 qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set."
6083 " This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create"
6084 " a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture.");
6085 }
6088 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6090 depthResolveTexD->texture,
6091 0,
6092 depthTexD->sampleCount(),
6093 0,
6094 multiViewCount);
6095 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
6096 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6098 depthResolveTexD->texture,
6099 0,
6100 depthTexD->sampleCount(),
6101 0,
6102 multiViewCount);
6103 }
6104 } else {
6106 rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
6107 rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
6108 rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
6109 depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
6110 }
6111 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6114 0,
6115 depthTexD->sampleCount(),
6116 0,
6117 multiViewCount);
6118 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6121 0,
6122 depthTexD->sampleCount(),
6123 0,
6124 multiViewCount);
6125 }
6126 } else {
6127 // The depth texture here must be an array with at least
6128 // multiViewCount elements, and the format should be D24 or D32F
6129 // for depth only, or D24S8 for depth and stencil.
6130 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
6131 0, 0, multiViewCount);
6132 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
6133 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
6134 0, 0, multiViewCount);
6135 }
6136 }
6137 }
6138 if (d.colorAttCount == 0) {
6139 d.pixelSize = depthTexD->pixelSize();
6140 d.sampleCount = depthTexD->sampleCount();
6141 }
6142 }
6143 d.dsAttCount = 1;
6144 } else {
6145 d.dsAttCount = 0;
6146 }
6147
6148 d.dpr = 1;
6150
6151 GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
6152 if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
6153 qWarning("Framebuffer incomplete: 0x%x", status);
6154 return false;
6155 }
6156
6157 if (rhiD->glObjectLabel)
6158 rhiD->glObjectLabel(GL_FRAMEBUFFER, framebuffer, -1, m_objectName.constData());
6159
6160 QRhiRenderTargetAttachmentTracker::updateResIdList<QGles2Texture, QGles2RenderBuffer>(m_desc, &d.currentResIdList);
6161
6162 rhiD->registerResource(this);
6163 return true;
6164}
6165
6167{
6168 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(m_desc, d.currentResIdList))
6169 const_cast<QGles2TextureRenderTarget *>(this)->create();
6170
6171 return d.pixelSize;
6172}
6173
6175{
6176 return d.dpr;
6177}
6178
6180{
6181 return d.sampleCount;
6182}
6183
6188
6193
6195{
6197 if (rhiD)
6198 rhiD->unregisterResource(this);
6199}
6200
6202{
6204 if (!rhiD->sanityCheckShaderResourceBindings(this))
6205 return false;
6206
6207 hasDynamicOffset = false;
6208 for (int i = 0, ie = m_bindings.size(); i != ie; ++i) {
6211 if (b->u.ubuf.hasDynamicOffset) {
6212 hasDynamicOffset = true;
6213 break;
6214 }
6215 }
6216 }
6217
6218 rhiD->updateLayoutDesc(this);
6219
6220 generation += 1;
6221 rhiD->registerResource(this, false);
6222 return true;
6223}
6224
6226{
6227 Q_UNUSED(flags);
6228 generation += 1;
6229}
6230
6235
6240
6242{
6243 if (!program)
6244 return;
6245
6248
6249 e.pipeline.program = program;
6250
6251 program = 0;
6252 uniforms.clear();
6253 samplers.clear();
6254
6256 if (rhiD) {
6257 rhiD->releaseQueue.append(e);
6258 rhiD->unregisterResource(this);
6259 }
6260}
6261
6262static inline bool isGraphicsStage(const QRhiShaderStage &shaderStage)
6263{
6264 const QRhiShaderStage::Type t = shaderStage.type();
6265 return t == QRhiShaderStage::Vertex
6270}
6271
6273{
6275
6276 if (program)
6277 destroy();
6278
6279 if (!rhiD->ensureContext())
6280 return false;
6281
6282 rhiD->pipelineCreationStart();
6283 if (!rhiD->sanityCheckGraphicsPipeline(this))
6284 return false;
6285
6287
6288 program = rhiD->f->glCreateProgram();
6289
6290 enum {
6291 VtxIdx = 0,
6292 TCIdx,
6293 TEIdx,
6294 GeomIdx,
6295 FragIdx,
6296 LastIdx
6297 };
6298 const auto descIdxForStage = [](const QRhiShaderStage &shaderStage) {
6299 switch (shaderStage.type()) {
6301 return VtxIdx;
6303 return TCIdx;
6305 return TEIdx;
6307 return GeomIdx;
6309 return FragIdx;
6310 default:
6311 break;
6312 }
6313 Q_UNREACHABLE_RETURN(VtxIdx);
6314 };
6315 QShaderDescription desc[LastIdx];
6316 QShader::SeparateToCombinedImageSamplerMappingList samplerMappingList[LastIdx];
6317 bool vertexFragmentOnly = true;
6318 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6319 if (isGraphicsStage(shaderStage)) {
6320 const int idx = descIdxForStage(shaderStage);
6321 if (idx != VtxIdx && idx != FragIdx)
6322 vertexFragmentOnly = false;
6323 QShader shader = shaderStage.shader();
6324 QShaderVersion shaderVersion;
6325 desc[idx] = shader.description();
6326 if (!rhiD->shaderSource(shaderStage, &shaderVersion).isEmpty()) {
6327 samplerMappingList[idx] = shader.separateToCombinedImageSamplerMappingList(
6328 { QShader::GlslShader, shaderVersion, shaderStage.shaderVariant() });
6329 }
6330 }
6331 }
6332
6334 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(m_shaderStages.constData(),
6336 program,
6337 desc[VtxIdx].inputVariables(),
6338 &cacheKey);
6339 if (cacheResult == QRhiGles2::ProgramCacheError)
6340 return false;
6341
6342 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
6343 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6344 if (isGraphicsStage(shaderStage)) {
6345 if (!rhiD->compileShader(program, shaderStage, nullptr))
6346 return false;
6347 }
6348 }
6349
6350 // important when GLSL <= 150 is used that does not have location qualifiers
6351 for (const QShaderDescription::InOutVariable &inVar : desc[VtxIdx].inputVariables())
6352 rhiD->f->glBindAttribLocation(program, GLuint(inVar.location), inVar.name);
6353
6354 if (vertexFragmentOnly)
6355 rhiD->sanityCheckVertexFragmentInterface(desc[VtxIdx], desc[FragIdx]);
6356
6357 if (!rhiD->linkProgram(program))
6358 return false;
6359
6360 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6361 // force replacing existing cache entry (if there is one, then
6362 // something is wrong with it, as there was no hit)
6363 rhiD->trySaveToPipelineCache(program, cacheKey, true);
6364 } else {
6365 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
6366 // was not enabled, so instead store to the Qt 5 disk cache
6367 rhiD->trySaveToDiskCache(program, cacheKey);
6368 }
6369 } else {
6370 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
6371 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6372 // just so that it ends up in the pipeline cache also when the hit was
6373 // from the disk cache
6374 rhiD->trySaveToPipelineCache(program, cacheKey);
6375 }
6376 }
6377
6378 // Use the same work area for the vertex & fragment stages, thus ensuring
6379 // that we will not do superfluous glUniform calls for uniforms that are
6380 // present in both shaders.
6381 QDuplicateTracker<int, 256> activeUniformLocations;
6382
6383 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6384 if (isGraphicsStage(shaderStage)) {
6385 const int idx = descIdxForStage(shaderStage);
6386 for (const QShaderDescription::UniformBlock &ub : desc[idx].uniformBlocks())
6387 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
6388 for (const QShaderDescription::InOutVariable &v : desc[idx].combinedImageSamplers())
6389 rhiD->gatherSamplers(program, v, &samplers);
6390 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : samplerMappingList[idx])
6391 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
6392 }
6393 }
6394
6395 std::sort(uniforms.begin(), uniforms.end(),
6397 {
6398 return a.offset < b.offset;
6399 });
6400
6401 memset(uniformState, 0, sizeof(uniformState));
6402
6403 currentSrb = nullptr;
6405
6406 if (rhiD->glObjectLabel)
6407 rhiD->glObjectLabel(GL_PROGRAM, program, -1, m_objectName.constData());
6408
6409 rhiD->pipelineCreationEnd();
6410 generation += 1;
6411 rhiD->registerResource(this);
6412 return true;
6413}
6414
6419
6424
6426{
6427 if (!program)
6428 return;
6429
6432
6433 e.pipeline.program = program;
6434
6435 program = 0;
6436 uniforms.clear();
6437 samplers.clear();
6438
6440 if (rhiD) {
6441 rhiD->releaseQueue.append(e);
6442 rhiD->unregisterResource(this);
6443 }
6444}
6445
6447{
6449
6450 if (program)
6451 destroy();
6452
6453 if (!rhiD->ensureContext())
6454 return false;
6455
6456 rhiD->pipelineCreationStart();
6457
6460 QShaderVersion shaderVersion;
6461 if (!rhiD->shaderSource(m_shaderStage, &shaderVersion).isEmpty()) {
6463 { QShader::GlslShader, shaderVersion, m_shaderStage.shaderVariant() });
6464 }
6465
6466 program = rhiD->f->glCreateProgram();
6467
6469 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(&m_shaderStage, 1, program, {}, &cacheKey);
6470 if (cacheResult == QRhiGles2::ProgramCacheError)
6471 return false;
6472
6473 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
6474 if (!rhiD->compileShader(program, m_shaderStage, nullptr))
6475 return false;
6476
6477 if (!rhiD->linkProgram(program))
6478 return false;
6479
6480 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6481 // force replacing existing cache entry (if there is one, then
6482 // something is wrong with it, as there was no hit)
6483 rhiD->trySaveToPipelineCache(program, cacheKey, true);
6484 } else {
6485 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
6486 // was not enabled, so instead store to the Qt 5 disk cache
6487 rhiD->trySaveToDiskCache(program, cacheKey);
6488 }
6489 } else {
6490 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
6491 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6492 // just so that it ends up in the pipeline cache also when the hit was
6493 // from the disk cache
6494 rhiD->trySaveToPipelineCache(program, cacheKey);
6495 }
6496 }
6497
6498 QDuplicateTracker<int, 256> activeUniformLocations;
6499 for (const QShaderDescription::UniformBlock &ub : csDesc.uniformBlocks())
6500 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
6501 for (const QShaderDescription::InOutVariable &v : csDesc.combinedImageSamplers())
6502 rhiD->gatherSamplers(program, v, &samplers);
6503 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : csSamplerMappingList)
6504 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
6505
6506 // storage images and buffers need no special steps here
6507
6508 memset(uniformState, 0, sizeof(uniformState));
6509
6510 currentSrb = nullptr;
6512
6513 rhiD->pipelineCreationEnd();
6514 generation += 1;
6515 rhiD->registerResource(this);
6516 return true;
6517}
6518
6524
6529
6531{
6532 // nothing to do here
6533}
6534
6536 : QRhiSwapChain(rhi),
6537 rt(rhi, this),
6538 rtLeft(rhi, this),
6539 rtRight(rhi, this),
6540 cb(rhi)
6541{
6542}
6543
6548
6550{
6552 if (rhiD)
6553 rhiD->unregisterResource(this);
6554}
6555
6560
6565
6567{
6568 if (targetBuffer == LeftBuffer)
6569 return rtLeft.d.isValid() ? &rtLeft : &rt;
6570 else if (targetBuffer == RightBuffer)
6571 return rtRight.d.isValid() ? &rtRight : &rt;
6572 else
6573 Q_UNREACHABLE_RETURN(nullptr);
6574}
6575
6577{
6579 if (QPlatformWindow *platformWindow = m_window->handle())
6580 // Prefer using QPlatformWindow geometry and DPR in order to avoid
6581 // errors due to rounded QWindow geometry.
6582 return platformWindow->geometry().size() * platformWindow->devicePixelRatio();
6583 else
6584 return m_window->size() * m_window->devicePixelRatio();
6585}
6586
6588{
6589 return f == SDR;
6590}
6591
6593{
6596 rhiD->registerResource(rpD, false);
6597 return rpD;
6598}
6599
6601{
6602 rt->setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
6605 rt->d.dpr = float(m_window->devicePixelRatio());
6606 rt->d.sampleCount = qBound(1, m_sampleCount, 64);
6607 rt->d.colorAttCount = 1;
6608 rt->d.dsAttCount = m_depthStencil ? 1 : 0;
6610}
6611
6613{
6614 // can be called multiple times due to window resizes
6615 const bool needsRegistration = !surface || surface != m_window;
6616 if (surface && surface != m_window)
6617 destroy();
6618
6619 surface = m_window;
6622
6625 {
6628 }
6629
6631
6632 if (m_window->format().stereo()) {
6637 }
6638
6639 frameCount = 0;
6640
6642 if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps) && rhiD->caps.timestamps)
6643 timestamps.prepare(rhiD);
6644
6645 // The only reason to register this fairly fake gl swapchain
6646 // object with no native resources underneath is to be able to
6647 // implement a safe destroy().
6648 if (needsRegistration)
6649 rhiD->registerResource(this, false);
6650
6651 return true;
6652}
6653
6655{
6656 if (!query[0])
6657 rhiD->f->glGenQueries(TIMESTAMP_PAIRS * 2, query);
6658}
6659
6661{
6662 rhiD->f->glDeleteQueries(TIMESTAMP_PAIRS * 2, query);
6663 memset(active, 0, sizeof(active));
6664 memset(query, 0, sizeof(query));
6665}
6666
6667bool QGles2SwapChainTimestamps::tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
6668{
6669 if (!active[pairIndex])
6670 return false;
6671
6672 GLuint tsStart = query[pairIndex * 2];
6673 GLuint tsEnd = query[pairIndex * 2 + 1];
6674
6675 GLuint ready = GL_FALSE;
6676 rhiD->f->glGetQueryObjectuiv(tsEnd, GL_QUERY_RESULT_AVAILABLE, &ready);
6677
6678 if (!ready)
6679 return false;
6680
6681 bool result = false;
6682 quint64 timestamps[2];
6683 rhiD->glGetQueryObjectui64v(tsStart, GL_QUERY_RESULT, &timestamps[0]);
6684 rhiD->glGetQueryObjectui64v(tsEnd, GL_QUERY_RESULT, &timestamps[1]);
6685
6686 if (timestamps[1] >= timestamps[0]) {
6687 const quint64 nanoseconds = timestamps[1] - timestamps[0];
6688 *elapsedSec = nanoseconds / 1000000000.0;
6689 result = true;
6690 }
6691
6692 active[pairIndex] = false;
6693 return result;
6694}
6695
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1219
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtGui
Definition qimage.h:37
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1222
Definition qlist.h:75
bool isEmpty() const noexcept
Definition qlist.h:401
void append(parameter_type t)
Definition qlist.h:458
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtGui
void setFormat(const QSurfaceFormat &format)
Sets the offscreen surface format.
\inmodule QtGui
QOpenGLContext * shareContext() const
Returns the share context this context was created with.
void setFormat(const QSurfaceFormat &format)
Sets the format the OpenGL context should be compatible with.
static QOpenGLContext * currentContext()
Returns the last context which called makeCurrent in the current thread, or \nullptr,...
QScreen * screen() const
Returns the screen the context was created for.
QOpenGLProgramBinarySupportCheck * get(QOpenGLContext *context)
The QPlatformWindow class provides an abstraction for top-level windows.
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0, otherwise returns false.
Definition qpoint.h:125
quint32 size() const
Definition qrhi_p.h:357
const char * constData() const
Definition qrhi_p.h:353
\inmodule QtGui
Definition qrhi.h:846
UsageFlags m_usage
Definition qrhi.h:888
Type m_type
Definition qrhi.h:887
Type
Specifies storage type of buffer resource.
Definition qrhi.h:848
@ Dynamic
Definition qrhi.h:851
@ IndexBuffer
Definition qrhi.h:856
@ VertexBuffer
Definition qrhi.h:855
@ UniformBuffer
Definition qrhi.h:857
@ StorageBuffer
Definition qrhi.h:858
quint32 m_size
Definition qrhi.h:889
\inmodule QtGui
Definition qrhi.h:576
QRhiRenderBuffer * renderBuffer() const
Definition qrhi.h:585
int multiViewCount() const
Definition qrhi.h:603
int resolveLevel() const
Definition qrhi.h:600
QRhiTexture * texture() const
Definition qrhi.h:582
int resolveLayer() const
Definition qrhi.h:597
QRhiTexture * resolveTexture() const
Definition qrhi.h:594
int level() const
Definition qrhi.h:591
int layer() const
Definition qrhi.h:588
\inmodule QtGui
Definition qrhi.h:1651
@ DoNotTrackResourcesForCompute
Definition qrhi.h:1660
QPair< int, quint32 > DynamicOffset
Synonym for QPair<int, quint32>.
Definition qrhi.h:1676
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1680
IndexFormat
Specifies the index data type.
Definition qrhi.h:1653
\inmodule QtGui
Definition qrhi.h:1622
QRhiShaderStage m_shaderStage
Definition qrhi.h:1644
\inmodule QtGui
Definition qrhi.h:44
\inmodule QtGui
\variable QRhiGles2InitParams::format
QHash< QRhiShaderStage, uint > m_shaderCache
QRhiStats statistics() override
bool contextLost
void gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub, QDuplicateTracker< int, 256 > *activeUniformLocations, QGles2UniformDescriptionVector *dst)
void registerUniformIfActive(const QShaderDescription::BlockVariable &var, const QByteArray &namePrefix, int binding, int baseOffset, GLuint program, QDuplicateTracker< int, 256 > *activeUniformLocations, QGles2UniformDescriptionVector *dst)
const GLvoid const GLvoid GLuint
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) override
QOpenGLContext * maybeShareContext
void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override
void enqueueBarriersForPass(QGles2CommandBuffer *cbD)
QList< DeferredReleaseEntry > releaseQueue
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
QSurfaceFormat requestedFormat
QHash< QByteArray, PipelineCacheData > m_pipelineCache
bool isProgramBinaryDiskCacheEnabled() const
bool needsMakeCurrentDueToSwap
void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access)
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, QGles2Buffer *bufD, QRhiPassResourceTracker::BufferAccess access, QRhiPassResourceTracker::BufferStage stage)
QRhiComputePipeline * createComputePipeline() override
QRhiDriverInfo driverInfoStruct
QSurface * evaluateFallbackSurface() const
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
void sanityCheckVertexFragmentInterface(const QShaderDescription &vsDesc, const QShaderDescription &fsDesc)
QRhiGles2NativeHandles nativeHandlesStruct
QMatrix4x4 clipSpaceCorrMatrix() const override
QRhi::FrameOpResult finish() override
void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override
QList< int > supportedSampleCounts() const override
void gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v, QGles2SamplerDescriptionVector *dst)
QSet< GLint > supportedCompressedFormats
QRhiSwapChain * createSwapChain() override
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
void destroy() override
QRhiRenderBuffer * createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint) override
void executeCommandBuffer(QRhiCommandBuffer *cb)
void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) override
QSurface * fallbackSurface
QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice=nullptr)
QRhi::Flags rhiFlags
void trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
struct QRhiGles2::Caps caps
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion)
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
QOpenGLContext * ctx
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
void endExternal(QRhiCommandBuffer *cb) override
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
QByteArray shaderSource(const QRhiShaderStage &shaderStage, QShaderVersion *shaderVersion)
QRhiBuffer * createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size) override
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
ProgramCacheResult tryLoadFromDiskOrPipelineCache(const QRhiShaderStage *stages, int stageCount, GLuint program, const QVector< QShaderDescription::InOutVariable > &inputVars, QByteArray *cacheKey)
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
@ ProgramCacheError
QGles2RenderTargetData * enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD, bool *wantsColorClear=nullptr, bool *wantsDsClear=nullptr)
void debugMarkEnd(QRhiCommandBuffer *cb) override
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
void releaseCachedResources() override
bool isClipDepthZeroToOne() const override
void trySaveToPipelineCache(GLuint program, const QByteArray &cacheKey, bool force=false)
void executeDeferredReleases()
QByteArray pipelineCacheData() override
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
QList< int > supportedSampleCountList
QPointer< QWindow > maybeWindow
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
struct QRhiGles2::OffscreenFrame ofr
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
void setPipelineCacheData(const QByteArray &data) override
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
void gatherGeneratedSamplers(GLuint program, const QShader::SeparateToCombinedImageSamplerMapping &mapping, QGles2SamplerDescriptionVector *dst)
const QRhiNativeHandles * nativeHandles() override
\inmodule QtGui
Definition qrhi.h:1270
BlendOp
Specifies the blend operation.
Definition qrhi.h:1331
PolygonMode
Specifies the polygon rasterization mode.
Definition qrhi.h:1379
FrontFace
Specifies the front face winding order.
Definition qrhi.h:1296
BlendFactor
Specifies the blend factor.
Definition qrhi.h:1309
CompareOp
Specifies the depth or stencil comparison function.
Definition qrhi.h:1350
Topology m_topology
Definition qrhi.h:1481
CullMode
Specifies the culling mode.
Definition qrhi.h:1290
QVarLengthArray< QRhiShaderStage, 4 > m_shaderStages
Definition qrhi.h:1500
Topology
Specifies the primitive topology.
Definition qrhi.h:1280
StencilOp
Specifies the stencil operation.
Definition qrhi.h:1361
bool isCompressedFormat(QRhiTexture::Format format) const
Definition qrhi.cpp:8058
static const QRhiShaderResourceBinding::Data * shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
Definition qrhi_p.h:220
quint32 pipelineCacheRhiId() const
Definition qrhi_p.h:196
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, QSize *blockDim) const
Definition qrhi.cpp:8065
static const int MAX_SHADER_CACHE_ENTRIES
Definition qrhi_p.h:239
qint64 totalPipelineCreationTime() const
Definition qrhi_p.h:212
void textureFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const
Definition qrhi.cpp:8185
static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:11088
static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:11069
int layer() const
Definition qrhi.h:785
QRhiTexture * texture() const
Definition qrhi.h:782
int level() const
Definition qrhi.h:788
\inmodule QtGui
Definition qrhi.h:1094
Flags flags() const
Definition qrhi.h:1121
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1116
QSize pixelSize() const
Definition qrhi.h:1115
int m_sampleCount
Definition qrhi.h:1134
QRhiTexture::Format m_backingFormatHint
Definition qrhi.h:1136
QSize m_pixelSize
Definition qrhi.h:1133
Type
Specifies the type of the renderbuffer.
Definition qrhi.h:1096
virtual bool create()=0
Creates the corresponding native graphics resources.
@ UsedWithSwapChainOnly
Definition qrhi.h:1102
Flags m_flags
Definition qrhi.h:1135
\inmodule QtGui
Definition qrhi.h:1142
\inmodule QtGui
Definition qrhi.h:1158
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1165
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1169
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:536
\inmodule QtGui
Definition qrhi.h:1731
\inmodule QtGui
Definition qrhi.h:804
QByteArray m_objectName
Definition qrhi.h:842
@ SwapChainRenderTarget
Definition qrhi.h:812
@ TextureRenderTarget
Definition qrhi.h:813
virtual Type resourceType() const =0
QRhiImplementation * m_rhi
Definition qrhi.h:840
\inmodule QtGui
Definition qrhi.h:1030
Filter m_minFilter
Definition qrhi.h:1085
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1032
AddressMode m_addressV
Definition qrhi.h:1088
Filter m_mipmapMode
Definition qrhi.h:1086
AddressMode m_addressU
Definition qrhi.h:1087
AddressMode
Specifies the addressing mode.
Definition qrhi.h:1038
@ ClampToEdge
Definition qrhi.h:1040
CompareOp
Specifies the texture comparison function.
Definition qrhi.h:1044
@ LessOrEqual
Definition qrhi.h:1048
@ GreaterOrEqual
Definition qrhi.h:1051
CompareOp m_compareOp
Definition qrhi.h:1090
AddressMode m_addressW
Definition qrhi.h:1089
Filter m_magFilter
Definition qrhi.h:1084
\inmodule QtGui
Definition qrhi.h:138
std::array< int, 4 > scissor() const
Definition qrhi.h:143
Type
Specifies type of the shader resource bound to a binding point.
Definition qrhi.h:441
\inmodule QtGui
Definition qrhi.h:1214
QVarLengthArray< QRhiShaderResourceBinding, BINDING_PREALLOC > m_bindings
Definition qrhi.h:1246
\inmodule QtGui
Definition qrhi.h:379
QShader::Variant shaderVariant() const
Definition qrhi.h:400
QShader shader() const
Definition qrhi.h:397
Type
Specifies the type of the shader stage.
Definition qrhi.h:381
@ TessellationControl
Definition qrhi.h:383
@ TessellationEvaluation
Definition qrhi.h:384
Type type() const
Definition qrhi.h:394
\inmodule QtGui
Definition qrhi.h:1173
\inmodule QtGui
Definition qrhi.h:1549
QWindow * m_window
Definition qrhi.h:1609
int m_sampleCount
Definition qrhi.h:1613
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1614
QSize m_currentPixelSize
Definition qrhi.h:1615
Flags m_flags
Definition qrhi.h:1610
Format
Describes the swapchain format.
Definition qrhi.h:1561
StereoTargetBuffer
Selects the backbuffer to use with a stereoscopic swapchain.
Definition qrhi.h:1568
QRhiRenderBuffer * m_depthStencil
Definition qrhi.h:1612
QPoint destinationTopLeft() const
Definition qrhi.h:761
QPoint sourceTopLeft() const
Definition qrhi.h:752
int destinationLevel() const
Definition qrhi.h:758
int sourceLevel() const
Definition qrhi.h:749
QSize pixelSize() const
Definition qrhi.h:743
int sourceLayer() const
Definition qrhi.h:746
int destinationLayer() const
Definition qrhi.h:755
const QRhiColorAttachment * cbeginColorAttachments() const
Definition qrhi.h:634
QRhiTexture * depthTexture() const
Definition qrhi.h:642
const QRhiColorAttachment * cendColorAttachments() const
Definition qrhi.h:635
QRhiRenderBuffer * depthStencilBuffer() const
Definition qrhi.h:639
qsizetype colorAttachmentCount() const
Definition qrhi.h:637
QRhiTexture * depthResolveTexture() const
Definition qrhi.h:645
\inmodule QtGui
Definition qrhi.h:1184
QRhiTextureRenderTargetDescription m_desc
Definition qrhi.h:1207
\inmodule QtGui
Definition qrhi.h:895
QSize m_pixelSize
Definition qrhi.h:1016
int m_arraySize
Definition qrhi.h:1018
int m_depth
Definition qrhi.h:1017
@ ThreeDimensional
Definition qrhi.h:907
@ UsedAsCompressedAtlas
Definition qrhi.h:905
@ UsedWithLoadStore
Definition qrhi.h:904
@ MipMapped
Definition qrhi.h:900
@ OneDimensional
Definition qrhi.h:910
@ TextureArray
Definition qrhi.h:909
@ TextureRectangleGL
Definition qrhi.h:908
@ CubeMap
Definition qrhi.h:899
@ ExternalOES
Definition qrhi.h:906
Format
Specifies the texture format.
Definition qrhi.h:914
@ ASTC_10x8
Definition qrhi.h:959
@ ASTC_12x12
Definition qrhi.h:962
@ ASTC_8x5
Definition qrhi.h:954
@ ASTC_10x5
Definition qrhi.h:957
@ RGBA32F
Definition qrhi.h:926
@ ETC2_RGBA8
Definition qrhi.h:947
@ ASTC_5x5
Definition qrhi.h:951
@ ASTC_4x4
Definition qrhi.h:949
@ ASTC_6x6
Definition qrhi.h:953
@ ASTC_12x10
Definition qrhi.h:961
@ ETC2_RGB8
Definition qrhi.h:945
@ ASTC_5x4
Definition qrhi.h:950
@ RED_OR_ALPHA8
Definition qrhi.h:923
@ ASTC_6x5
Definition qrhi.h:952
@ ASTC_8x8
Definition qrhi.h:956
@ RGBA16F
Definition qrhi.h:925
@ RGB10A2
Definition qrhi.h:930
@ ASTC_10x6
Definition qrhi.h:958
@ ASTC_10x10
Definition qrhi.h:960
@ UnknownFormat
Definition qrhi.h:915
@ ETC2_RGB8A1
Definition qrhi.h:946
@ ASTC_8x6
Definition qrhi.h:955
Format m_format
Definition qrhi.h:1015
Flags m_flags
Definition qrhi.h:1020
int m_sampleCount
Definition qrhi.h:1019
\inmodule QtGui
Definition qrhi.h:179
\inmodule QtGui
Definition qrhi.h:85
static constexpr int MAX_MIP_LEVELS
Definition qrhi.h:1997
ResourceLimit
Describes the resource limit to query.
Definition qrhi.h:1886
@ MaxThreadsPerThreadGroup
Definition qrhi.h:1893
@ MaxThreadGroupZ
Definition qrhi.h:1896
@ FramesInFlight
Definition qrhi.h:1890
@ TextureSizeMin
Definition qrhi.h:1887
@ MaxThreadGroupsPerDimension
Definition qrhi.h:1892
@ MaxAsyncReadbackFrames
Definition qrhi.h:1891
@ TextureArraySizeMax
Definition qrhi.h:1897
@ MaxColorAttachments
Definition qrhi.h:1889
@ MaxThreadGroupY
Definition qrhi.h:1895
@ MaxVertexInputs
Definition qrhi.h:1899
@ MaxThreadGroupX
Definition qrhi.h:1894
@ TextureSizeMax
Definition qrhi.h:1888
@ MaxVertexOutputs
Definition qrhi.h:1900
@ MaxUniformBufferRange
Definition qrhi.h:1898
@ SkipPresent
Definition qrhi.h:1882
Feature
Flag values to indicate what features are supported by the backend currently in use.
Definition qrhi.h:1831
@ HalfAttributes
Definition qrhi.h:1869
@ CustomInstanceStepRate
Definition qrhi.h:1837
@ NonDynamicUniformBuffers
Definition qrhi.h:1839
@ ElementIndexUint
Definition qrhi.h:1843
@ RenderToNonBaseMipLevel
Definition qrhi.h:1853
@ MultisampleRenderBuffer
Definition qrhi.h:1833
@ RenderTo3DTextureSlice
Definition qrhi.h:1861
@ Tessellation
Definition qrhi.h:1863
@ IntAttributes
Definition qrhi.h:1854
@ TextureArrays
Definition qrhi.h:1862
@ PipelineCacheDataLoadSave
Definition qrhi.h:1857
@ ReadBackNonUniformBuffer
Definition qrhi.h:1850
@ MultiView
Definition qrhi.h:1872
@ TexelFetch
Definition qrhi.h:1852
@ TextureArrayRange
Definition qrhi.h:1865
@ RenderToOneDimensionalTexture
Definition qrhi.h:1870
@ BaseVertex
Definition qrhi.h:1847
@ GeometryShader
Definition qrhi.h:1864
@ Compute
Definition qrhi.h:1844
@ OneDimensionalTextureMipmaps
Definition qrhi.h:1868
@ WideLines
Definition qrhi.h:1845
@ TriangleFanTopology
Definition qrhi.h:1849
@ OneDimensionalTextures
Definition qrhi.h:1867
@ ImageDataStride
Definition qrhi.h:1858
@ TextureViewFormat
Definition qrhi.h:1873
@ BaseInstance
Definition qrhi.h:1848
@ DebugMarkers
Definition qrhi.h:1834
@ ReadBackNonBaseMipLevel
Definition qrhi.h:1851
@ MultisampleTexture
Definition qrhi.h:1832
@ ThreeDimensionalTextureMipmaps
Definition qrhi.h:1871
@ NonFourAlignedEffectiveIndexBufferOffset
Definition qrhi.h:1840
@ RedOrAlpha8IsRed
Definition qrhi.h:1842
@ NonFillPolygonMode
Definition qrhi.h:1866
@ Timestamps
Definition qrhi.h:1835
@ ThreeDimensionalTextures
Definition qrhi.h:1860
@ PrimitiveRestart
Definition qrhi.h:1838
@ ReadBackAnyTextureFormat
Definition qrhi.h:1856
@ RenderBufferImport
Definition qrhi.h:1859
@ ScreenSpaceDerivatives
Definition qrhi.h:1855
@ VertexShaderPointSize
Definition qrhi.h:1846
@ NPOTTextureRepeat
Definition qrhi.h:1841
@ Instancing
Definition qrhi.h:1836
@ ResolveDepthStencil
Definition qrhi.h:1874
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1824
@ FrameOpSuccess
Definition qrhi.h:1825
@ FrameOpDeviceLost
Definition qrhi.h:1828
@ FrameOpError
Definition qrhi.h:1826
@ EnablePipelineCacheDataSave
Definition qrhi.h:1818
@ EnableTimestamps
Definition qrhi.h:1819
qsizetype size() const
Definition qset.h:50
bool contains(const T &value) const
Definition qset.h:71
const_iterator cbegin() const noexcept
Definition qset.h:138
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtGui
Definition qshader.h:32
\inmodule QtGui
Definition qshader.h:81
SeparateToCombinedImageSamplerMappingList separateToCombinedImageSamplerMappingList(const QShaderKey &key) const
\variable QShader::SeparateToCombinedImageSamplerMapping::combinedSamplerName
Definition qshader.cpp:1104
QShaderCode shader(const QShaderKey &key) const
Definition qshader.cpp:395
@ GlslShader
Definition qshader.h:94
QShaderDescription description() const
Definition qshader.cpp:370
Stage
Describes the stage of the graphics pipeline the shader is suitable for.
Definition qshader.h:83
@ GeometryStage
Definition qshader.h:87
@ ComputeStage
Definition qshader.h:89
@ TessellationEvaluationStage
Definition qshader.h:86
@ VertexStage
Definition qshader.h:84
@ FragmentStage
Definition qshader.h:88
@ TessellationControlStage
Definition qshader.h:85
\inmodule QtCore
Definition qsize.h:25
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
static QSurfaceFormat defaultFormat()
Returns the global default surface format.
bool stereo() const
Returns true if stereo buffering is enabled; otherwise returns false.
\inmodule QtGui
Definition qsurface.h:21
SurfaceClass surfaceClass() const
Returns the surface class of this surface.
Definition qsurface.cpp:121
virtual QPlatformSurface * surfaceHandle() const =0
Returns a handle to the platform-specific implementation of the surface.
constexpr size_type size() const noexcept
const T & at(qsizetype idx) const
iterator end() noexcept
void append(const T &t)
const T * constData() const
iterator begin() noexcept
const void * constData() const
Definition qvariant.h:451
QSurfaceFormat format() const override
Returns the actual format of this window.
Definition qwindow.cpp:946
QSize size() const override
Returns the size of the window excluding any window frame.
Definition qwindow.h:210
EGLContext ctx
#define this
Definition dialogs.cpp:9
p1 load("image.bmp")
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:108
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define Q_UNLIKELY(x)
#define Q_LIKELY(x)
static int instanceCount
static QString header(const QString &name)
static bool isCubeMap(const DDSHeader &dds)
static const qint64 headerSize
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT layer
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
Flags
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
static QByteArray cacheKey(Args &&...args)
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
#define GL_CONTEXT_LOST
Definition qopengl.cpp:30
#define QOPENGLF_APIENTRYP
Definition qopengl.h:275
char GLchar
Definition qopengl.h:158
QOpenGLContext * qt_gl_global_share_context()
#define GL_MAP_READ_BIT
GLboolean GLboolean GLboolean b
typedef GLint(GL_APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program
GLbitfield stages
GLsizei const GLfloat * v
[13]
GLint GLboolean layered
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLsizei samples
GLenum mode
const GLfloat * m
GLenum GLuint GLint level
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLint GLenum GLint components
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLuint sampler
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
const GLenum * bufs
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
#define GL_TEXTURE_3D
GLenum src
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLenum type
GLenum GLenum dst
#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY
#define GL_MAP_WRITE_BIT
#define GL_MIN
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLenum access
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLbitfield flags
#define GL_TEXTURE_2D_MULTISAMPLE
GLenum GLuint texture
GLuint program
GLenum GLuint GLintptr offset
GLsizei GLsizei GLenum * binaryFormat
#define GL_TEXTURE_2D_ARRAY
GLint ref
#define GL_MAX
GLuint name
#define GL_TEXTURE_EXTERNAL_OES
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
GLsizei GLenum internalFormat
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
void ** params
#define GL_NUM_PROGRAM_BINARY_FORMATS
#define GL_MIRRORED_REPEAT
Definition qopenglext.h:331
#define GL_TEXTURE_COMPARE_FUNC
Definition qopenglext.h:338
GLdouble s
[6]
Definition qopenglext.h:235
#define GL_MAX_VARYING_VECTORS
GLenum query
#define GL_TEXTURE0
Definition qopenglext.h:129
#define GL_MAX_COMPUTE_WORK_GROUP_COUNT
#define GL_FRAGMENT_SHADER
Definition qopenglext.h:609
#define GL_TEXTURE_WRAP_R
Definition qopenglext.h:87
const GLubyte * c
#define GL_DEPTH24_STENCIL8
#define GL_DEPTH_COMPONENT16
Definition qopenglext.h:328
#define GL_TEXTURE_CUBE_MAP
Definition qopenglext.h:170
GLuint renderbuffer
#define GL_PRIMITIVE_RESTART_FIXED_INDEX
#define GL_ONE_MINUS_CONSTANT_ALPHA
Definition qopenglext.h:367
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
GLint void * img
Definition qopenglext.h:233
#define GL_COMPRESSED_TEXTURE_FORMATS
Definition qopenglext.h:186
#define GL_CONSTANT_COLOR
Definition qopenglext.h:364
#define GL_FRAMEBUFFER_SRGB
#define GL_MAX_VERTEX_OUTPUT_COMPONENTS
#define GL_COLOR_ATTACHMENT0
#define GL_DEPTH_STENCIL_ATTACHMENT
#define GL_TEXTURE_CUBE_MAP_SEAMLESS
GLuint shader
Definition qopenglext.h:665
#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_MAX_VERTEX_ATTRIBS
Definition qopenglext.h:606
#define GL_STENCIL_INDEX8
#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS
Definition qopenglext.h:611
typedef GLbitfield(APIENTRYP PFNGLQUERYMATRIXXOESPROC)(GLfixed *mantissa
GLint limit
#define GL_ARRAY_BUFFER
Definition qopenglext.h:487
#define GL_FRAMEBUFFER_COMPLETE
#define GL_MAX_COMPUTE_WORK_GROUP_SIZE
#define GL_VERTEX_PROGRAM_POINT_SIZE
Definition qopenglext.h:582
GLdouble GLdouble t
Definition qopenglext.h:243
#define GL_DRAW_FRAMEBUFFER
#define GL_PROGRAM_BINARY_LENGTH
#define GL_MAX_SAMPLES
#define GL_FUNC_REVERSE_SUBTRACT
Definition qopenglext.h:369
GLuint * samplers
#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_RENDERBUFFER
#define GL_COMPILE_STATUS
Definition qopenglext.h:637
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
#define GL_MAX_DRAW_BUFFERS
Definition qopenglext.h:588
#define GL_FRAMEBUFFER
GLuint framebuffer
GLuint64EXT * result
[6]
#define GL_QUERY_RESULT_AVAILABLE
Definition qopenglext.h:486
#define GL_HALF_FLOAT
GLenum GLenum GLenum GLenum mapping
GLfloat GLfloat p
[1]
#define GL_MAX_ARRAY_TEXTURE_LAYERS
Definition qopenglext.h:916
#define GL_READ_WRITE
Definition qopenglext.h:494
#define GL_CONSTANT_ALPHA
Definition qopenglext.h:366
#define GL_POINT_SPRITE
Definition qopenglext.h:657
GLenum GLsizei len
#define GL_ONE_MINUS_CONSTANT_COLOR
Definition qopenglext.h:365
#define GL_MAX_VERTEX_UNIFORM_VECTORS
#define GL_DEPTH_STENCIL
#define GL_WRITE_ONLY
Definition qopenglext.h:493
#define GL_READ_FRAMEBUFFER
#define GL_STATIC_DRAW
Definition qopenglext.h:501
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X
Definition qopenglext.h:172
#define GL_BUFFER
#define GL_MAX_FRAGMENT_UNIFORM_VECTORS
#define GL_DYNAMIC_DRAW
Definition qopenglext.h:504
#define GL_DEPTH_ATTACHMENT
#define GL_SHADER_STORAGE_BARRIER_BIT
#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS
#define GL_VERTEX_SHADER
Definition qopenglext.h:610
#define GL_PROGRAM
#define GL_FUNC_SUBTRACT
Definition qopenglext.h:370
#define GL_ELEMENT_ARRAY_BUFFER
Definition qopenglext.h:488
#define GL_DEPTH_COMPONENT24
Definition qopenglext.h:329
#define GL_PATCH_VERTICES
#define GL_MAX_VERTEX_UNIFORM_COMPONENTS
Definition qopenglext.h:612
#define GL_DECR_WRAP
Definition qopenglext.h:335
#define GL_LINK_STATUS
Definition qopenglext.h:638
#define GL_INFO_LOG_LENGTH
Definition qopenglext.h:640
#define GL_CLAMP_TO_EDGE
Definition qopenglext.h:100
#define GL_INCR_WRAP
Definition qopenglext.h:334
#define GL_STENCIL_ATTACHMENT
#define GL_MAX_VARYING_COMPONENTS
Definition qopenglext.h:921
#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
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
#define GL_READ_ONLY
Definition qopenglext.h:492
static void normalize(double &x, double &y)
#define GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_UNPACK_ROW_LENGTH
void forceUpdate(QQuickItem *item)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:29
#define QRHI_RES(t, x)
Definition qrhi_p.h:28
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)
#define GL_DEPTH_COMPONENT32F
static GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
#define GL_GEOMETRY_SHADER
static GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
#define GL_DEPTH24_STENCIL8
#define GL_DEPTH_COMPONENT16
#define GL_R16
static GLenum toGlWrapMode(QRhiSampler::AddressMode m)
static GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
#define GL_RG16
#define GL_R8
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
#define GL_RGBA8
#define GL_RGBA16F
static GLbitfield barriersForTexture()
#define GL_RG8
#define GL_TESS_CONTROL_SHADER
static GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
#define GL_BGRA
\variable QRhiGles2NativeHandles::context
#define GL_UNIFORM_BARRIER_BIT
#define GL_BACK_LEFT
#define GL_R32F
#define GL_ELEMENT_ARRAY_BARRIER_BIT
#define GL_TEXTURE_FETCH_BARRIER_BIT
static void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type, GLuint tsQuery=0)
#define GL_UNSIGNED_INT_24_8
#define GL_COMPUTE_SHADER
static QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
static bool bufferAccessIsWrite(QGles2Buffer::Access access)
#define GL_RED
#define GL_RG
#define GL_R16F
static bool isGraphicsStage(const QRhiShaderStage &shaderStage)
#define GL_FILL
static QShader::Stage toShaderStage(QRhiShaderStage::Type type)
#define GL_RGB10_A2
static GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
#define GL_HALF_FLOAT
#define GL_TESS_EVALUATION_SHADER
#define GL_PIXEL_BUFFER_BARRIER_BIT
static void toGlTextureFormat(QRhiTexture::Format format, const QRhiGles2::Caps &caps, GLenum *glintformat, GLenum *glsizedintformat, GLenum *glformat, GLenum *gltype)
#define GL_DEPTH_STENCIL
#define GL_TEXTURE_UPDATE_BARRIER_BIT
static GLenum toGlShaderType(QRhiShaderStage::Type type)
#define GL_BUFFER_UPDATE_BARRIER_BIT
static GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
#define GL_LINE
static GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
static GLbitfield barriersForBuffer()
static GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
#define GL_SHADER_STORAGE_BARRIER_BIT
#define GL_RGBA32F
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)
#define GL_DEPTH_COMPONENT24
static bool textureAccessIsWrite(QGles2Texture::Access access)
#define GL_TEXTURE_1D
static GLenum toGlMagFilter(QRhiSampler::Filter f)
#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
#define GL_FRAMEBUFFER_BARRIER_BIT
static void qrhi_std140_to_packed(T *dst, int vecSize, int elemCount, const void *src)
#define GL_UNSIGNED_INT_2_10_10_10_REV
#define GL_PATCHES
#define GL_STENCIL_INDEX
static GLenum toGlPolygonMode(QRhiGraphicsPipeline::PolygonMode mode)
static QSurface * currentSurfaceForCurrentContext(QOpenGLContext *ctx)
#define GL_BACK_RIGHT
#define GLuint
#define GL_FLOAT
#define GL_UNSIGNED_BYTE
#define GL_RGBA
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define sp
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
unsigned short quint16
Definition qtypes.h:48
size_t quintptr
Definition qtypes.h:167
int qint32
Definition qtypes.h:49
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned char quint8
Definition qtypes.h:46
QVideoFrameFormat::PixelFormat fmt
QStorageInfo storage
[1]
QSharedPointer< T > other(t)
[5]
view viewport() -> scroll(dx, dy, deviceRect)
QSvgRenderer * renderer
[0]
struct CommandBufferExecTrackedState::@364 lastBindVertexBuffer
bool nonzeroAttribDivisor[TRACKED_ATTRIB_COUNT]
static const int TRACKED_ATTRIB_COUNT
QRhiGraphicsPipeline * ps
bool enabledAttribArrays[TRACKED_ATTRIB_COUNT]
QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
@ AccessStorageReadWrite
Definition qrhigles2_p.h:53
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
char * beginFullDynamicBufferUpdateForCurrentFrame() override
void endFullDynamicBufferUpdateForCurrentFrame() override
To be called when the entire contents of the buffer data has been updated in the memory block returne...
QRhiBuffer::NativeBuffer nativeBuffer() override
bool create() override
Creates the corresponding native graphics resources.
GLenum targetForDataOps
Definition qrhigles2_p.h:44
quint32 nonZeroSize
Definition qrhigles2_p.h:42
UsageState usageState
Definition qrhigles2_p.h:59
GLuint buffer
Definition qrhigles2_p.h:43
union QGles2CommandBuffer::Command::Args args
struct QGles2CommandBuffer::ComputePassState computePassState
QRhiShaderResourceBindings * currentGraphicsSrb
static const int MAX_DYNAMIC_OFFSET_COUNT
struct QGles2CommandBuffer::GraphicsPassState graphicsPassState
QRhiBackendCommandList< Command > commands
QRhiComputePipeline * currentComputePipeline
QRhiRenderTarget * currentTarget
QRhiShaderResourceBindings * currentComputeSrb
QVarLengthArray< QRhiPassResourceTracker, 8 > passResTrackers
QGles2CommandBuffer(QRhiImplementation *rhi)
const void * retainImage(const QImage &image)
const void * retainData(const QByteArray &data)
const uchar * retainBufferData(const QRhiBufferData &data)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
struct QGles2CommandBuffer::TextureUnitState textureUnitState[16]
QRhiGraphicsPipeline * currentGraphicsPipeline
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QGles2UniformState uniformState[QGles2UniformState::MAX_TRACKED_LOCATION+1]
QGles2UniformDescriptionVector uniforms
QRhiShaderResourceBindings * currentSrb
QGles2ComputePipeline(QRhiImplementation *rhi)
bool create() override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool create() override
Creates the corresponding native graphics resources.
QGles2GraphicsPipeline(QRhiImplementation *rhi)
QRhiShaderResourceBindings * currentSrb
QGles2UniformDescriptionVector uniforms
QGles2UniformState uniformState[QGles2UniformState::MAX_TRACKED_LOCATION+1]
bool create() override
Creates the corresponding native graphics resources.
QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint)
bool createFrom(NativeRenderBuffer src) override
Similar to create() except that no new native renderbuffer objects are created.
QRhiTexture::Format backingFormat() const override
GLuint stencilRenderbuffer
Definition qrhigles2_p.h:75
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVector< quint32 > serializedFormat() const override
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
bool isCompatible(const QRhiRenderPassDescriptor *other) const override
QGles2RenderPassDescriptor(QRhiImplementation *rhi)
std::optional< QRhiSwapChain::StereoTargetBuffer > stereoTarget
QGles2RenderPassDescriptor * rp
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList
GLenum gltexcomparefunc
Definition qrhigles2_p.h:89
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool create() override
QGles2SamplerData d
QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v, AddressMode w)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
void updateResources(UpdateFlags flags) override
QGles2ShaderResourceBindings(QRhiImplementation *rhi)
float devicePixelRatio() const override
QGles2SwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
QSize pixelSize() const override
int sampleCount() const override
QGles2RenderTargetData d
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
void prepare(QRhiGles2 *rhiD)
bool active[TIMESTAMP_PAIRS]
bool tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
static const int TIMESTAMP_PAIRS
void destroy(QRhiGles2 *rhiD)
void initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt)
QGles2SwapChainRenderTarget rtRight
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QGles2CommandBuffer cb
QGles2SwapChainRenderTarget rt
QSurface * surface
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
bool isFormatSupported(Format f) override
bool createOrResize() override
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
QGles2SwapChainTimestamps timestamps
QGles2SwapChain(QRhiImplementation *rhi)
QRhiCommandBuffer * currentFrameCommandBuffer() override
QGles2SwapChainRenderTarget rtLeft
QSize surfacePixelSize() override
QRhiRenderTarget * currentFrameRenderTarget() override
int sampleCount() const override
QGles2TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
float devicePixelRatio() const override
QSize pixelSize() const override
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
bool create() override
Creates the corresponding native graphics resources.
QGles2RenderTargetData d
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
QGles2SamplerData samplerState
GLenum glsizedintformat
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
UsageState usageState
bool prepareCreate(QSize *adjustedSize=nullptr)
GLenum glintformat
bool create() override
Creates the corresponding native graphics resources.
NativeTexture nativeTexture() override
QShaderDescription::VariableType type
static constexpr int MAX_TRACKED_LOCATION
\inmodule QtGui
Definition qrhi.h:862
\inmodule QtGui
Definition qrhi.h:1759
QByteArray deviceName
Definition qrhi.h:1769
struct QRhiGles2::DeferredReleaseEntry::@302::@308 textureRenderTarget
struct QRhiGles2::DeferredReleaseEntry::@302::@305 pipeline
\variable QRhiReadbackResult::completed
Definition qrhi.h:800
\inmodule QtGui
Definition qrhi.h:1723
QByteArray data
Definition qrhi.h:1727
std::function< void()> completed
Definition qrhi.h:1724
QRhiTextureCopyDescription desc
Definition qrhi_p.h:471
QVarLengthArray< MipLevelUploadList, 6 > subresDesc
Definition qrhi_p.h:469
\inmodule QtGui
Definition qrhi.h:1782
qint64 totalPipelineCreationTime
Definition qrhi.h:1783
\inmodule QtGui
Definition qrhi.h:965
\variable QShaderDescription::InOutVariable::name
\variable QShaderDescription::BlockVariable::name
Definition moc.h:23