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_DEPTH32F_STENCIL8
220#define GL_DEPTH32F_STENCIL8 0x8CAD
221#endif
222
223#ifndef GL_FLOAT_32_UNSIGNED_INT_24_8_REV
224#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
225#endif
226
227#ifndef GL_UNSIGNED_INT_24_8
228#define GL_UNSIGNED_INT_24_8 0x84FA
229#endif
230
231#ifndef GL_STENCIL_INDEX
232#define GL_STENCIL_INDEX 0x1901
233#endif
234
235#ifndef GL_STENCIL_INDEX8
236#define GL_STENCIL_INDEX8 0x8D48
237#endif
238
239#ifndef GL_DEPTH24_STENCIL8
240#define GL_DEPTH24_STENCIL8 0x88F0
241#endif
242
243#ifndef GL_DEPTH_STENCIL_ATTACHMENT
244#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
245#endif
246
247#ifndef GL_DEPTH_STENCIL
248#define GL_DEPTH_STENCIL 0x84F9
249#endif
250
251#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
252#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
253#endif
254
255#ifndef GL_FRAMEBUFFER_SRGB
256#define GL_FRAMEBUFFER_SRGB 0x8DB9
257#endif
258
259#ifndef GL_READ_FRAMEBUFFER
260#define GL_READ_FRAMEBUFFER 0x8CA8
261#endif
262
263#ifndef GL_DRAW_FRAMEBUFFER
264#define GL_DRAW_FRAMEBUFFER 0x8CA9
265#endif
266
267#ifndef GL_MAX_DRAW_BUFFERS
268#define GL_MAX_DRAW_BUFFERS 0x8824
269#endif
270
271#ifndef GL_TEXTURE_COMPARE_MODE
272#define GL_TEXTURE_COMPARE_MODE 0x884C
273#endif
274
275#ifndef GL_COMPARE_REF_TO_TEXTURE
276#define GL_COMPARE_REF_TO_TEXTURE 0x884E
277#endif
278
279#ifndef GL_TEXTURE_COMPARE_FUNC
280#define GL_TEXTURE_COMPARE_FUNC 0x884D
281#endif
282
283#ifndef GL_MAX_SAMPLES
284#define GL_MAX_SAMPLES 0x8D57
285#endif
286
287#ifndef GL_SHADER_STORAGE_BUFFER
288#define GL_SHADER_STORAGE_BUFFER 0x90D2
289#endif
290
291#ifndef GL_READ_ONLY
292#define GL_READ_ONLY 0x88B8
293#endif
294
295#ifndef GL_WRITE_ONLY
296#define GL_WRITE_ONLY 0x88B9
297#endif
298
299#ifndef GL_READ_WRITE
300#define GL_READ_WRITE 0x88BA
301#endif
302
303#ifndef GL_COMPUTE_SHADER
304#define GL_COMPUTE_SHADER 0x91B9
305#endif
306
307#ifndef GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT
308#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
309#endif
310
311#ifndef GL_ELEMENT_ARRAY_BARRIER_BIT
312#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
313#endif
314
315#ifndef GL_UNIFORM_BARRIER_BIT
316#define GL_UNIFORM_BARRIER_BIT 0x00000004
317#endif
318
319#ifndef GL_BUFFER_UPDATE_BARRIER_BIT
320#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200
321#endif
322
323#ifndef GL_SHADER_STORAGE_BARRIER_BIT
324#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
325#endif
326
327#ifndef GL_TEXTURE_FETCH_BARRIER_BIT
328#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
329#endif
330
331#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
332#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
333#endif
334
335#ifndef GL_PIXEL_BUFFER_BARRIER_BIT
336#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080
337#endif
338
339#ifndef GL_TEXTURE_UPDATE_BARRIER_BIT
340#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100
341#endif
342
343#ifndef GL_FRAMEBUFFER_BARRIER_BIT
344#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
345#endif
346
347#ifndef GL_ALL_BARRIER_BITS
348#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
349#endif
350
351#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
352#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
353#endif
354
355#ifndef GL_POINT_SPRITE
356#define GL_POINT_SPRITE 0x8861
357#endif
358
359#ifndef GL_MAP_READ_BIT
360#define GL_MAP_READ_BIT 0x0001
361#endif
362
363#ifndef GL_MAP_WRITE_BIT
364#define GL_MAP_WRITE_BIT 0x0002
365#endif
366
367#ifndef GL_TEXTURE_2D_MULTISAMPLE
368#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
369#endif
370
371#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY
372#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
373#endif
374
375#ifndef GL_TEXTURE_EXTERNAL_OES
376#define GL_TEXTURE_EXTERNAL_OES 0x8D65
377#endif
378
379#ifndef GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS
380#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB
381#endif
382
383#ifndef GL_MAX_COMPUTE_WORK_GROUP_COUNT
384#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE
385#endif
386
387#ifndef GL_MAX_COMPUTE_WORK_GROUP_SIZE
388#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF
389#endif
390
391#ifndef GL_TEXTURE_CUBE_MAP_SEAMLESS
392#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
393#endif
394
395#ifndef GL_CONTEXT_LOST
396#define GL_CONTEXT_LOST 0x0507
397#endif
398
399#ifndef GL_PROGRAM_BINARY_LENGTH
400#define GL_PROGRAM_BINARY_LENGTH 0x8741
401#endif
402
403#ifndef GL_NUM_PROGRAM_BINARY_FORMATS
404#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
405#endif
406
407#ifndef GL_UNPACK_ROW_LENGTH
408#define GL_UNPACK_ROW_LENGTH 0x0CF2
409#endif
410
411#ifndef GL_TEXTURE_3D
412#define GL_TEXTURE_3D 0x806F
413#endif
414
415#ifndef GL_TEXTURE_WRAP_R
416#define GL_TEXTURE_WRAP_R 0x8072
417#endif
418
419#ifndef GL_TEXTURE_RECTANGLE
420#define GL_TEXTURE_RECTANGLE 0x84F5
421#endif
422
423#ifndef GL_TEXTURE_2D_ARRAY
424#define GL_TEXTURE_2D_ARRAY 0x8C1A
425#endif
426
427#ifndef GL_MAX_ARRAY_TEXTURE_LAYERS
428#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
429#endif
430
431#ifndef GL_MAX_VERTEX_UNIFORM_COMPONENTS
432#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
433#endif
434
435#ifndef GL_MAX_FRAGMENT_UNIFORM_COMPONENTS
436#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
437#endif
438
439#ifndef GL_MAX_VERTEX_UNIFORM_VECTORS
440#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
441#endif
442
443#ifndef GL_MAX_FRAGMENT_UNIFORM_VECTORS
444#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
445#endif
446
447#ifndef GL_RGB10_A2
448#define GL_RGB10_A2 0x8059
449#endif
450
451#ifndef GL_UNSIGNED_INT_2_10_10_10_REV
452#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
453#endif
454
455#ifndef GL_MAX_VARYING_COMPONENTS
456#define GL_MAX_VARYING_COMPONENTS 0x8B4B
457#endif
458
459#ifndef GL_MAX_VARYING_FLOATS
460#define GL_MAX_VARYING_FLOATS 0x8B4B
461#endif
462
463#ifndef GL_MAX_VARYING_VECTORS
464#define GL_MAX_VARYING_VECTORS 0x8DFC
465#endif
466
467#ifndef GL_TESS_CONTROL_SHADER
468#define GL_TESS_CONTROL_SHADER 0x8E88
469#endif
470
471#ifndef GL_TESS_EVALUATION_SHADER
472#define GL_TESS_EVALUATION_SHADER 0x8E87
473#endif
474
475#ifndef GL_PATCH_VERTICES
476#define GL_PATCH_VERTICES 0x8E72
477#endif
478
479#ifndef GL_LINE
480#define GL_LINE 0x1B01
481#endif
482
483#ifndef GL_FILL
484#define GL_FILL 0x1B02
485#endif
486
487#ifndef GL_PATCHES
488#define GL_PATCHES 0x000E
489#endif
490
491#ifndef GL_GEOMETRY_SHADER
492#define GL_GEOMETRY_SHADER 0x8DD9
493#endif
494
495#ifndef GL_BACK_LEFT
496#define GL_BACK_LEFT 0x0402
497#endif
498
499#ifndef GL_BACK_RIGHT
500#define GL_BACK_RIGHT 0x0403
501#endif
502
503#ifndef GL_TEXTURE_1D
504# define GL_TEXTURE_1D 0x0DE0
505#endif
506
507#ifndef GL_TEXTURE_1D_ARRAY
508# define GL_TEXTURE_1D_ARRAY 0x8C18
509#endif
510
511#ifndef GL_HALF_FLOAT
512#define GL_HALF_FLOAT 0x140B
513#endif
514
515#ifndef GL_MAX_VERTEX_OUTPUT_COMPONENTS
516#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
517#endif
518
519#ifndef GL_TIMESTAMP
520#define GL_TIMESTAMP 0x8E28
521#endif
522
523#ifndef GL_QUERY_RESULT
524#define GL_QUERY_RESULT 0x8866
525#endif
526
527#ifndef GL_QUERY_RESULT_AVAILABLE
528#define GL_QUERY_RESULT_AVAILABLE 0x8867
529#endif
530
531#ifndef GL_BUFFER
532#define GL_BUFFER 0x82E0
533#endif
534
535#ifndef GL_PROGRAM
536#define GL_PROGRAM 0x82E2
537#endif
538
544QRhiGles2InitParams::QRhiGles2InitParams()
545{
547}
548
565QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
566{
568
569 // To resolve all fields in the format as much as possible, create a context.
570 // This may be heavy, but allows avoiding BAD_MATCH on some systems.
571 QOpenGLContext tempContext;
572 tempContext.setFormat(fmt);
573 if (tempContext.create())
574 fmt = tempContext.format();
575 else
576 qWarning("QRhiGles2: Failed to create temporary context");
577
579 s->setFormat(fmt);
580 s->create();
581
582 return s;
583}
584
586 : ofr(this)
587{
588 requestedFormat = params->format;
589 fallbackSurface = params->fallbackSurface;
590 maybeWindow = params->window; // may be null
591 maybeShareContext = params->shareContext; // may be null
592
593 importedContext = importDevice != nullptr;
594 if (importedContext) {
595 ctx = importDevice->context;
596 if (!ctx) {
597 qWarning("No OpenGL context given, cannot import");
598 importedContext = false;
599 }
600 }
601}
602
604{
606 return nullptr;
607
608 QSurface *currentSurface = ctx->surface();
609 if (!currentSurface)
610 return nullptr;
611
612 if (currentSurface->surfaceClass() == QSurface::Window && !currentSurface->surfaceHandle())
613 return nullptr;
614
615 return currentSurface;
616}
617
619{
620 // With Apple's deprecated OpenGL support we need to minimize the usage of
621 // QOffscreenSurface since delicate problems can pop up with
622 // NSOpenGLContext and drawables.
623#if defined(Q_OS_MACOS)
624 return maybeWindow && maybeWindow->handle() ? static_cast<QSurface *>(maybeWindow) : fallbackSurface;
625#else
626 return fallbackSurface;
627#endif
628}
629
631{
632 if (!surface) {
633 // null means any surface is good because not going to render
635 return true;
636 // if the context is not already current with a valid surface, use our
637 // fallback surface, but platform specific quirks may apply
638 surface = evaluateFallbackSurface();
639 } else if (surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
640 // the window is not usable anymore (no native window underneath), behave as if offscreen
641 surface = evaluateFallbackSurface();
643 // bail out if the makeCurrent is not necessary
644 return true;
645 }
647
648 if (!ctx->makeCurrent(surface)) {
649 if (ctx->isValid()) {
650 qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen.");
651 } else {
652 qWarning("QRhiGles2: Context is lost.");
653 contextLost = true;
654 }
655 return false;
656 }
657
658 return true;
659}
660
662{
663 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
664 switch (format) {
665 case QRhiTexture::BC1:
666 return srgb ? 0x8C4C : 0x83F0;
667 case QRhiTexture::BC2:
668 return srgb ? 0x8C4E : 0x83F2;
669 case QRhiTexture::BC3:
670 return srgb ? 0x8C4F : 0x83F3;
671
673 return srgb ? 0x9275 : 0x9274;
675 return srgb ? 0x9277 : 0x9276;
677 return srgb ? 0x9279 : 0x9278;
678
680 return srgb ? 0x93D0 : 0x93B0;
682 return srgb ? 0x93D1 : 0x93B1;
684 return srgb ? 0x93D2 : 0x93B2;
686 return srgb ? 0x93D3 : 0x93B3;
688 return srgb ? 0x93D4 : 0x93B4;
690 return srgb ? 0x93D5 : 0x93B5;
692 return srgb ? 0x93D6 : 0x93B6;
694 return srgb ? 0x93D7 : 0x93B7;
696 return srgb ? 0x93D8 : 0x93B8;
698 return srgb ? 0x93D9 : 0x93B9;
700 return srgb ? 0x93DA : 0x93BA;
702 return srgb ? 0x93DB : 0x93BB;
704 return srgb ? 0x93DC : 0x93BC;
706 return srgb ? 0x93DD : 0x93BD;
707
708 default:
709 return 0; // this is reachable, just return an invalid format
710 }
711}
712
713bool QRhiGles2::create(QRhi::Flags flags)
714{
716 rhiFlags = flags;
717
718 if (!importedContext) {
719 ctx = new QOpenGLContext;
721 if (maybeShareContext) {
722 ctx->setShareContext(maybeShareContext);
723 ctx->setScreen(maybeShareContext->screen());
724 } else if (QOpenGLContext *shareContext = qt_gl_global_share_context()) {
725 ctx->setShareContext(shareContext);
726 ctx->setScreen(shareContext->screen());
727 } else if (maybeWindow) {
728 ctx->setScreen(maybeWindow->screen());
729 }
730 if (!ctx->create()) {
731 qWarning("QRhiGles2: Failed to create context");
732 delete ctx;
733 ctx = nullptr;
734 return false;
735 }
736 qCDebug(QRHI_LOG_INFO) << "Created OpenGL context" << ctx->format();
737 }
738
739 if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
740 return false;
741
742 f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
743 const QSurfaceFormat actualFormat = ctx->format();
744 caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
745
746 if (!caps.gles) {
747 glPolygonMode = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum)>(
748 ctx->getProcAddress(QByteArrayLiteral("glPolygonMode")));
749
750 glTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
751 GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void *)>(
752 ctx->getProcAddress(QByteArrayLiteral("glTexImage1D")));
753
754 glTexStorage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei)>(
755 ctx->getProcAddress(QByteArrayLiteral("glTexStorage1D")));
756
757 glTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
758 GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const GLvoid *)>(
759 ctx->getProcAddress(QByteArrayLiteral("glTexSubImage1D")));
760
761 glCopyTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint,
762 GLint, GLsizei)>(
763 ctx->getProcAddress(QByteArrayLiteral("glCopyTexSubImage1D")));
764
765 glCompressedTexImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
766 GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid *)>(
767 ctx->getProcAddress(QByteArrayLiteral("glCompressedTexImage1D")));
768
769 glCompressedTexSubImage1D = reinterpret_cast<void(QOPENGLF_APIENTRYP)(
770 GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid *)>(
771 ctx->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage1D")));
772
773 glFramebufferTexture1D =
774 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint)>(
775 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture1D")));
776 }
777
778 const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
779 const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
780 const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
781 if (vendor && renderer && version)
782 qCDebug(QRHI_LOG_INFO, "OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
783
784 if (vendor) {
787 }
788 if (renderer) {
791 }
792 if (version)
794
795 caps.ctxMajor = actualFormat.majorVersion();
796 caps.ctxMinor = actualFormat.minorVersion();
797
798 GLint n = 0;
799 f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
800 if (n > 0) {
801 QVarLengthArray<GLint, 16> compressedTextureFormats(n);
802 f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compressedTextureFormats.data());
803 for (GLint format : compressedTextureFormats)
805
806 }
807 // The above looks nice, if only it worked always. With GLES the list we
808 // query is likely the full list of compressed formats (mostly anything
809 // that can be decoded). With OpenGL however the list is not required to
810 // include all formats due to the way the spec is worded. For instance, we
811 // cannot rely on ASTC formats being present in the list on non-ES. Some
812 // drivers do include them (Intel, NVIDIA), some don't (Mesa). On the other
813 // hand, relying on extension strings only is not ok: for example, Intel
814 // reports GL_KHR_texture_compression_astc_ldr whereas NVIDIA doesn't. So
815 // the only reasonable thing to do is to query the list always and then see
816 // if there is something we can add - if not already in there.
817 std::array<QRhiTexture::Flags, 2> textureVariantFlags;
818 textureVariantFlags[0] = {};
819 textureVariantFlags[1] = QRhiTexture::sRGB;
820 if (f->hasOpenGLExtension(QOpenGLExtensions::DDSTextureCompression)) {
821 for (QRhiTexture::Flags f : textureVariantFlags) {
825 }
826 }
827 if (f->hasOpenGLExtension(QOpenGLExtensions::ETC2TextureCompression)) {
828 for (QRhiTexture::Flags f : textureVariantFlags) {
832 }
833 }
834 if (f->hasOpenGLExtension(QOpenGLExtensions::ASTCTextureCompression)) {
835 for (QRhiTexture::Flags f : textureVariantFlags) {
849 }
850 }
851
852 f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
853
854 if (!caps.gles || caps.ctxMajor >= 3) {
855 // non-ES or ES 3.0+
856 f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
857 caps.hasDrawBuffersFunc = true;
858 f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
859 caps.maxSamples = qMax(1, caps.maxSamples);
860 } else {
861 // ES 2.0 / WebGL 1
862 caps.maxDrawBuffers = 1;
863 caps.hasDrawBuffersFunc = false;
864 // This does not mean MSAA is not supported, just that we cannot query
865 // the supported sample counts. Assume that 4x is always supported.
866 caps.maxSamples = 4;
867 }
868
869 caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
870 && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
871
872 caps.npotTextureFull = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)
873 && f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
874
875 if (caps.gles)
876 caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
877 else
878 caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
879
880 if (caps.fixedIndexPrimitiveRestart) {
881#ifdef Q_OS_WASM
882 // WebGL 2 behaves as if GL_PRIMITIVE_RESTART_FIXED_INDEX was always
883 // enabled (i.e. matching D3D/Metal), and the value cannot be passed to
884 // glEnable, so skip the call.
885#else
887#endif
888 }
889
890 caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
891 caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
892 caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
893 caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
894 caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
895 caps.rgb10Formats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
896 caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
897 caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
898#ifdef Q_OS_WASM
899 caps.needsDepthStencilCombinedAttach = true;
900#else
901 caps.needsDepthStencilCombinedAttach = false;
902#endif
903
904 // QOpenGLExtensions::SRGBFrameBuffer is not useful here. We need to know if
905 // controlling the sRGB-on-shader-write state is supported, not that if the
906 // default framebuffer is sRGB-capable. And there are two different
907 // extensions for desktop and ES.
908 caps.srgbWriteControl = ctx->hasExtension("GL_EXT_framebuffer_sRGB") || ctx->hasExtension("GL_EXT_sRGB_write_control");
909
910 caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
911
912 if (caps.gles)
913 caps.uniformBuffers = caps.ctxMajor >= 3; // ES 3.0
914 else
915 caps.uniformBuffers = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // 3.1
916
917 caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
918 caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
919 caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats);
920
921 if (caps.gles)
922 caps.instancing = caps.ctxMajor >= 3; // ES 3.0
923 else
924 caps.instancing = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3); // 3.3
925
926 caps.baseVertex = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2 or ES 3.2
927
928 if (caps.gles)
929 caps.compute = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
930 else
931 caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
932
933 if (caps.compute) {
934 f->glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &caps.maxThreadsPerThreadGroup);
935 GLint tgPerDim[3];
936 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &tgPerDim[0]);
937 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &tgPerDim[1]);
938 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &tgPerDim[2]);
939 caps.maxThreadGroupsPerDimension = qMin(tgPerDim[0], qMin(tgPerDim[1], tgPerDim[2]));
940 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &caps.maxThreadGroupsX);
941 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &caps.maxThreadGroupsY);
942 f->glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &caps.maxThreadGroupsZ);
943 }
944
945 if (caps.gles)
946 caps.textureCompareMode = caps.ctxMajor >= 3; // ES 3.0
947 else
948 caps.textureCompareMode = true;
949
950 // proper as in ES 3.0 (glMapBufferRange), not the old glMapBuffer
951 // extension(s) (which is not in ES 3.0...messy)
952 caps.properMapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange);
953
954 if (caps.gles)
955 caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0
956 else
957 caps.nonBaseLevelFramebufferTexture = true;
958
959 caps.texelFetch = caps.ctxMajor >= 3; // 3.0 or ES 3.0
960 caps.intAttributes = caps.ctxMajor >= 3; // 3.0 or ES 3.0
961 caps.screenSpaceDerivatives = f->hasOpenGLExtension(QOpenGLExtensions::StandardDerivatives);
962
963 if (caps.gles)
964 caps.multisampledTexture = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
965 else
966 caps.multisampledTexture = caps.ctxMajor >= 3; // 3.0
967
968 // Program binary support: only the core stuff, do not bother with the old
969 // extensions like GL_OES_get_program_binary
970 if (caps.gles)
971 caps.programBinary = caps.ctxMajor >= 3; // ES 3.0
972 else
973 caps.programBinary = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 1); // 4.1
974
975 if (caps.programBinary) {
976 GLint fmtCount = 0;
977 f->glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &fmtCount);
978 if (fmtCount < 1)
979 caps.programBinary = false;
980 }
981
982 caps.texture3D = caps.ctxMajor >= 3; // 3.0
983
984 if (caps.gles)
985 caps.texture1D = false; // ES
986 else
987 caps.texture1D = glTexImage1D && (caps.ctxMajor >= 2); // 2.0
988
989 if (caps.gles)
990 caps.tessellation = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
991 else
992 caps.tessellation = caps.ctxMajor >= 4; // 4.0
993
994 if (caps.gles)
995 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // ES 3.2
996 else
997 caps.geometryShader = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2
998
999 if (caps.ctxMajor >= 3) { // 3.0 or ES 3.0
1000 GLint maxArraySize = 0;
1001 f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArraySize);
1002 caps.maxTextureArraySize = maxArraySize;
1003 } else {
1004 caps.maxTextureArraySize = 0;
1005 }
1006
1007 // The ES 2.0 spec only has MAX_xxxx_VECTORS. ES 3.0 and up has both
1008 // *VECTORS and *COMPONENTS. OpenGL 2.0-4.0 only has MAX_xxxx_COMPONENTS.
1009 // 4.1 and above has both. What a mess.
1010 if (caps.gles) {
1011 GLint maxVertexUniformVectors = 0;
1012 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniformVectors);
1013 GLint maxFragmentUniformVectors = 0;
1014 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniformVectors);
1015 caps.maxUniformVectors = qMin(maxVertexUniformVectors, maxFragmentUniformVectors);
1016 } else {
1017 GLint maxVertexUniformComponents = 0;
1018 f->glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxVertexUniformComponents);
1019 GLint maxFragmentUniformComponents = 0;
1020 f->glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &maxFragmentUniformComponents);
1021 caps.maxUniformVectors = qMin(maxVertexUniformComponents, maxFragmentUniformComponents) / 4;
1022 }
1023
1024 f->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.maxVertexInputs);
1025
1026 if (caps.gles) {
1027 f->glGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.maxVertexOutputs);
1028 } else if (caps.ctxMajor >= 3) {
1029 GLint components = 0;
1031 caps.maxVertexOutputs = components / 4;
1032 } else {
1033 // OpenGL before 3.0 only has this, and not the same as
1034 // MAX_VARYING_COMPONENTS strictly speaking, but will do.
1035 GLint components = 0;
1036 f->glGetIntegerv(GL_MAX_VARYING_FLOATS, &components);
1037 if (components > 0)
1038 caps.maxVertexOutputs = components / 4;
1039 }
1040
1041 if (!caps.gles) {
1043 if (!caps.coreProfile)
1044 f->glEnable(GL_POINT_SPRITE);
1045 } // else (with gles) these are always on
1046
1047 // Match D3D and others when it comes to seamless cubemap filtering.
1048 // ES 3.0+ has this always enabled. (hopefully)
1049 // ES 2.0 and GL < 3.2 will not have it.
1050 if (!caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)))
1052
1053 caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
1054
1055 // We always require GL_OVR_multiview2 for symmetry with other backends.
1056 caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView)
1057 && f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended);
1058 if (caps.multiView) {
1059 glFramebufferTextureMultiviewOVR =
1060 reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>(
1061 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
1062 }
1063
1064 // Only do timestamp queries on OpenGL 3.3+.
1065 caps.timestamps = !caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3));
1066 if (caps.timestamps) {
1067 glQueryCounter = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum)>(
1068 ctx->getProcAddress(QByteArrayLiteral("glQueryCounter")));
1069 glGetQueryObjectui64v = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum, quint64 *)>(
1070 ctx->getProcAddress(QByteArrayLiteral("glGetQueryObjectui64v")));
1071 if (!glQueryCounter || !glGetQueryObjectui64v)
1072 caps.timestamps = false;
1073 }
1074
1075 // glObjectLabel is available on OpenGL ES 3.2+ and OpenGL 4.3+
1076 if (caps.gles)
1077 caps.objectLabel = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2);
1078 else
1079 caps.objectLabel = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
1080 if (caps.objectLabel) {
1081 glObjectLabel = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLuint, GLsizei, const GLchar *)>(
1082 ctx->getProcAddress(QByteArrayLiteral("glObjectLabel")));
1083 }
1084
1085 if (caps.gles) {
1086 // This is the third way to get multisample rendering with GLES. (1. is
1087 // multisample render buffer -> resolve to texture; 2. is multisample
1088 // texture with GLES 3.1; 3. is this, avoiding the explicit multisample
1089 // buffer and should be more efficient with tiled architectures.
1090 // Interesting also because 2. does not seem to work in practice on
1091 // devices such as the Quest 3)
1092 caps.glesMultisampleRenderToTexture = ctx->hasExtension("GL_EXT_multisampled_render_to_texture");
1093 if (caps.glesMultisampleRenderToTexture) {
1094 glFramebufferTexture2DMultisampleEXT = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei)>(
1095 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTexture2DMultisampleEXT")));
1096 }
1097 caps.glesMultiviewMultisampleRenderToTexture = ctx->hasExtension("GL_OVR_multiview_multisampled_render_to_texture");
1098 if (caps.glesMultiviewMultisampleRenderToTexture) {
1099 glFramebufferTextureMultisampleMultiviewOVR = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei)>(
1100 ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultisampleMultiviewOVR")));
1101 }
1102 } else {
1103 caps.glesMultisampleRenderToTexture = false;
1104 caps.glesMultiviewMultisampleRenderToTexture = false;
1105 }
1106
1107 caps.unpackRowLength = !caps.gles || caps.ctxMajor >= 3;
1108
1109 nativeHandlesStruct.context = ctx;
1110
1111 contextLost = false;
1112
1113 return true;
1114}
1115
1117{
1118 if (!f)
1119 return;
1120
1121 ensureContext();
1123
1124 if (ofr.tsQueries[0]) {
1125 f->glDeleteQueries(2, ofr.tsQueries);
1126 ofr.tsQueries[0] = ofr.tsQueries[1] = 0;
1127 }
1128
1129 if (vao) {
1130 f->glDeleteVertexArrays(1, &vao);
1131 vao = 0;
1132 }
1133
1134 for (uint shader : m_shaderCache)
1135 f->glDeleteShader(shader);
1137
1138 if (!importedContext) {
1139 delete ctx;
1140 ctx = nullptr;
1141 }
1142
1143 f = nullptr;
1144}
1145
1147{
1148 for (int i = releaseQueue.size() - 1; i >= 0; --i) {
1150 switch (e.type) {
1152 f->glDeleteBuffers(1, &e.buffer.buffer);
1153 break;
1155 f->glDeleteProgram(e.pipeline.program);
1156 break;
1158 f->glDeleteTextures(1, &e.texture.texture);
1159 break;
1161 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
1162 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
1163 break;
1165 f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
1166 f->glDeleteTextures(1, &e.textureRenderTarget.nonMsaaThrowawayDepthTexture);
1167 break;
1168 default:
1169 Q_UNREACHABLE();
1170 break;
1171 }
1172 releaseQueue.removeAt(i);
1173 }
1174}
1175
1177{
1179 // 1, 2, 4, 8, ...
1180 for (int i = 1; i <= caps.maxSamples; i *= 2)
1182 }
1184}
1185
1187{
1188 return new QGles2SwapChain(this);
1189}
1190
1192{
1193 return new QGles2Buffer(this, type, usage, size);
1194}
1195
1197{
1198 // No real uniform buffers are used so no need to pretend there is any
1199 // alignment requirement.
1200 return 1;
1201}
1202
1204{
1205 return true;
1206}
1207
1209{
1210 return true;
1211}
1212
1214{
1215 return false;
1216}
1217
1219{
1220 return QMatrix4x4(); // identity
1221}
1222
1224 GLenum *glintformat, GLenum *glsizedintformat,
1225 GLenum *glformat, GLenum *gltype)
1226{
1227 switch (format) {
1228 case QRhiTexture::RGBA8:
1229 *glintformat = GL_RGBA;
1230 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1231 *glformat = GL_RGBA;
1232 *gltype = GL_UNSIGNED_BYTE;
1233 break;
1234 case QRhiTexture::BGRA8:
1235 *glintformat = caps.bgraInternalFormat ? GL_BGRA : GL_RGBA;
1236 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1237 *glformat = GL_BGRA;
1238 *gltype = GL_UNSIGNED_BYTE;
1239 break;
1240 case QRhiTexture::R16:
1241 *glintformat = GL_R16;
1242 *glsizedintformat = *glintformat;
1243 *glformat = GL_RED;
1244 *gltype = GL_UNSIGNED_SHORT;
1245 break;
1246 case QRhiTexture::RG16:
1247 *glintformat = GL_RG16;
1248 *glsizedintformat = *glintformat;
1249 *glformat = GL_RG;
1250 *gltype = GL_UNSIGNED_SHORT;
1251 break;
1252 case QRhiTexture::R8:
1253 *glintformat = GL_R8;
1254 *glsizedintformat = *glintformat;
1255 *glformat = GL_RED;
1256 *gltype = GL_UNSIGNED_BYTE;
1257 break;
1258 case QRhiTexture::RG8:
1259 *glintformat = GL_RG8;
1260 *glsizedintformat = *glintformat;
1261 *glformat = GL_RG;
1262 *gltype = GL_UNSIGNED_BYTE;
1263 break;
1265 *glintformat = caps.coreProfile ? GL_R8 : GL_ALPHA;
1266 *glsizedintformat = *glintformat;
1267 *glformat = caps.coreProfile ? GL_RED : GL_ALPHA;
1268 *gltype = GL_UNSIGNED_BYTE;
1269 break;
1271 *glintformat = GL_RGBA16F;
1272 *glsizedintformat = *glintformat;
1273 *glformat = GL_RGBA;
1274 *gltype = GL_HALF_FLOAT;
1275 break;
1277 *glintformat = GL_RGBA32F;
1278 *glsizedintformat = *glintformat;
1279 *glformat = GL_RGBA;
1280 *gltype = GL_FLOAT;
1281 break;
1282 case QRhiTexture::R16F:
1283 *glintformat = GL_R16F;
1284 *glsizedintformat = *glintformat;
1285 *glformat = GL_RED;
1286 *gltype = GL_HALF_FLOAT;
1287 break;
1288 case QRhiTexture::R32F:
1289 *glintformat = GL_R32F;
1290 *glsizedintformat = *glintformat;
1291 *glformat = GL_RED;
1292 *gltype = GL_FLOAT;
1293 break;
1295 *glintformat = GL_RGB10_A2;
1296 *glsizedintformat = *glintformat;
1297 *glformat = GL_RGBA;
1299 break;
1300 case QRhiTexture::D16:
1301 *glintformat = GL_DEPTH_COMPONENT16;
1302 *glsizedintformat = *glintformat;
1303 *glformat = GL_DEPTH_COMPONENT;
1304 *gltype = GL_UNSIGNED_SHORT;
1305 break;
1306 case QRhiTexture::D24:
1307 *glintformat = GL_DEPTH_COMPONENT24;
1308 *glsizedintformat = *glintformat;
1309 *glformat = GL_DEPTH_COMPONENT;
1310 *gltype = GL_UNSIGNED_INT;
1311 break;
1312 case QRhiTexture::D24S8:
1313 *glintformat = GL_DEPTH24_STENCIL8;
1314 *glsizedintformat = *glintformat;
1315 *glformat = GL_DEPTH_STENCIL;
1316 *gltype = GL_UNSIGNED_INT_24_8;
1317 break;
1318 case QRhiTexture::D32F:
1319 *glintformat = GL_DEPTH_COMPONENT32F;
1320 *glsizedintformat = *glintformat;
1321 *glformat = GL_DEPTH_COMPONENT;
1322 *gltype = GL_FLOAT;
1323 break;
1325 *glintformat = GL_DEPTH32F_STENCIL8;
1326 *glsizedintformat = *glintformat;
1327 *glformat = GL_DEPTH_STENCIL;
1329 break;
1330 default:
1331 Q_UNREACHABLE();
1332 *glintformat = GL_RGBA;
1333 *glsizedintformat = caps.rgba8Format ? GL_RGBA8 : GL_RGBA;
1334 *glformat = GL_RGBA;
1335 *gltype = GL_UNSIGNED_BYTE;
1336 break;
1337 }
1338}
1339
1341{
1344
1345 switch (format) {
1346 case QRhiTexture::D16:
1347 case QRhiTexture::D32F:
1349 return caps.depthTexture;
1350
1351 case QRhiTexture::D24:
1352 return caps.depth24;
1353
1354 case QRhiTexture::D24S8:
1355 return caps.depth24 && caps.packedDepthStencil;
1356
1357 case QRhiTexture::BGRA8:
1358 return caps.bgraExternalFormat;
1359
1360 case QRhiTexture::R8:
1361 return caps.r8Format;
1362
1363 case QRhiTexture::RG8:
1364 return caps.r8Format;
1365
1366 case QRhiTexture::R16:
1367 return caps.r16Format;
1368
1369 case QRhiTexture::RG16:
1370 return caps.r16Format;
1371
1374 return caps.floatFormats;
1375
1376 case QRhiTexture::R16F:
1377 case QRhiTexture::R32F:
1378 return caps.floatFormats;
1379
1381 return caps.rgb10Formats;
1382
1383 default:
1384 break;
1385 }
1386
1387 return true;
1388}
1389
1391{
1392 switch (feature) {
1394 return caps.multisampledTexture;
1396 return caps.msaaRenderBuffer;
1397 case QRhi::DebugMarkers:
1398 return false;
1399 case QRhi::Timestamps:
1400 return caps.timestamps;
1401 case QRhi::Instancing:
1402 return caps.instancing;
1404 return false;
1406 return caps.fixedIndexPrimitiveRestart;
1408 return true;
1410 return true;
1412 return caps.npotTextureFull;
1414 return caps.coreProfile;
1416 return caps.elementIndexUint;
1417 case QRhi::Compute:
1418 return caps.compute;
1419 case QRhi::WideLines:
1420 return !caps.coreProfile;
1422 return true;
1423 case QRhi::BaseVertex:
1424 return caps.baseVertex;
1425 case QRhi::BaseInstance:
1426 return false; // not in ES 3.2, so won't bother
1428 return true;
1430 return !caps.gles || caps.properMapBuffer;
1432 return caps.nonBaseLevelFramebufferTexture;
1433 case QRhi::TexelFetch:
1434 return caps.texelFetch;
1436 return caps.nonBaseLevelFramebufferTexture;
1438 return caps.intAttributes;
1440 return caps.screenSpaceDerivatives;
1442 return false;
1444 return caps.programBinary;
1446 return caps.unpackRowLength;
1448 return true;
1450 return caps.texture3D;
1452 return caps.texture3D;
1454 return caps.maxTextureArraySize > 0;
1455 case QRhi::Tessellation:
1456 return caps.tessellation;
1458 return caps.geometryShader;
1460 return false;
1462 return !caps.gles;
1464 return caps.texture1D;
1466 return caps.texture1D;
1468 return caps.halfAttributes;
1470 return caps.texture1D;
1472 return caps.texture3D;
1473 case QRhi::MultiView:
1474 return caps.multiView && caps.maxTextureArraySize > 0;
1476 return false;
1478 return true;
1479 default:
1480 Q_UNREACHABLE_RETURN(false);
1481 }
1482}
1483
1485{
1486 switch (limit) {
1488 return 1;
1490 return caps.maxTextureSize;
1492 return caps.maxDrawBuffers;
1494 // From our perspective. What the GL impl does internally is another
1495 // question, but that's out of our hands and does not concern us here.
1496 return 1;
1498 return 1;
1500 return caps.maxThreadGroupsPerDimension;
1502 return caps.maxThreadsPerThreadGroup;
1504 return caps.maxThreadGroupsX;
1506 return caps.maxThreadGroupsY;
1508 return caps.maxThreadGroupsZ;
1510 return 2048;
1512 return int(qMin<qint64>(INT_MAX, caps.maxUniformVectors * qint64(16)));
1514 return caps.maxVertexInputs;
1516 return caps.maxVertexOutputs;
1517 default:
1518 Q_UNREACHABLE_RETURN(0);
1519 }
1520}
1521
1526
1531
1538
1540{
1541 if (inFrame && !ofr.active)
1543 else
1544 return ensureContext();
1545}
1546
1548{
1549 if (!ensureContext())
1550 return;
1551
1552 for (uint shader : m_shaderCache)
1553 f->glDeleteShader(shader);
1554
1556
1557 m_pipelineCache.clear();
1558}
1559
1561{
1562 return contextLost;
1563}
1564
1573
1575{
1577
1578 if (m_pipelineCache.isEmpty())
1579 return QByteArray();
1580
1582 memset(&header, 0, sizeof(header));
1583 header.rhiId = pipelineCacheRhiId();
1584 header.arch = quint32(sizeof(void*));
1585 header.programBinaryCount = m_pipelineCache.size();
1586 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
1587 if (driverStrLen)
1588 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1589 header.driver[driverStrLen] = '\0';
1590
1591 const size_t dataOffset = sizeof(header);
1592 size_t dataSize = 0;
1593 for (auto it = m_pipelineCache.cbegin(), end = m_pipelineCache.cend(); it != end; ++it) {
1594 dataSize += sizeof(quint32) + it.key().size()
1595 + sizeof(quint32) + it->data.size()
1596 + sizeof(quint32);
1597 }
1598
1600 char *p = buf.data() + dataOffset;
1601 for (auto it = m_pipelineCache.cbegin(), end = m_pipelineCache.cend(); it != end; ++it) {
1602 const QByteArray key = it.key();
1603 const QByteArray data = it->data;
1604 const quint32 format = it->format;
1605
1606 quint32 i = key.size();
1607 memcpy(p, &i, 4);
1608 p += 4;
1609 memcpy(p, key.constData(), key.size());
1610 p += key.size();
1611
1612 i = data.size();
1613 memcpy(p, &i, 4);
1614 p += 4;
1615 memcpy(p, data.constData(), data.size());
1616 p += data.size();
1617
1618 memcpy(p, &format, 4);
1619 p += 4;
1620 }
1621 Q_ASSERT(p == buf.data() + dataOffset + dataSize);
1622
1623 header.dataSize = quint32(dataSize);
1624 memcpy(buf.data(), &header, sizeof(header));
1625
1626 return buf;
1627}
1628
1630{
1631 if (data.isEmpty())
1632 return;
1633
1634 const size_t headerSize = sizeof(QGles2PipelineCacheDataHeader);
1635 if (data.size() < qsizetype(headerSize)) {
1636 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
1637 return;
1638 }
1639 const size_t dataOffset = headerSize;
1641 memcpy(&header, data.constData(), headerSize);
1642
1643 const quint32 rhiId = pipelineCacheRhiId();
1644 if (header.rhiId != rhiId) {
1645 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1646 rhiId, header.rhiId);
1647 return;
1648 }
1649 const quint32 arch = quint32(sizeof(void*));
1650 if (header.arch != arch) {
1651 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
1652 arch, header.arch);
1653 return;
1654 }
1655 if (header.programBinaryCount == 0)
1656 return;
1657
1658 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.size()));
1659 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1660 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OpenGL vendor/renderer/version does not match");
1661 return;
1662 }
1663
1664 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1665 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
1666 return;
1667 }
1668
1669 m_pipelineCache.clear();
1670
1671 const char *p = data.constData() + dataOffset;
1672 for (quint32 i = 0; i < header.programBinaryCount; ++i) {
1673 quint32 len = 0;
1674 memcpy(&len, p, 4);
1675 p += 4;
1677 memcpy(key.data(), p, len);
1678 p += len;
1679
1680 memcpy(&len, p, 4);
1681 p += 4;
1683 memcpy(data.data(), p, len);
1684 p += len;
1685
1687 memcpy(&format, p, 4);
1688 p += 4;
1689
1690 m_pipelineCache.insert(key, { format, data });
1691 }
1692
1693 qCDebug(QRHI_LOG_INFO, "Seeded pipeline cache with %d program binaries", int(m_pipelineCache.size()));
1694}
1695
1697 int sampleCount, QRhiRenderBuffer::Flags flags,
1698 QRhiTexture::Format backingFormatHint)
1699{
1700 return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
1701}
1702
1704 const QSize &pixelSize, int depth, int arraySize,
1705 int sampleCount, QRhiTexture::Flags flags)
1706{
1707 return new QGles2Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
1708}
1709
1711 QRhiSampler::Filter mipmapMode,
1713{
1714 return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
1715}
1716
1718 QRhiTextureRenderTarget::Flags flags)
1719{
1720 return new QGles2TextureRenderTarget(this, desc, flags);
1721}
1722
1727
1732
1737
1739{
1743 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
1744
1745 if (pipelineChanged) {
1746 cbD->currentGraphicsPipeline = ps;
1747 cbD->currentComputePipeline = nullptr;
1748 cbD->currentPipelineGeneration = psD->generation;
1749
1750 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1752 cmd.args.bindGraphicsPipeline.ps = ps;
1753 }
1754}
1755
1757 int dynamicOffsetCount,
1758 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1759{
1764
1765 if (!srb) {
1766 if (gfxPsD)
1767 srb = gfxPsD->m_shaderResourceBindings;
1768 else
1769 srb = compPsD->m_shaderResourceBindings;
1770 }
1771
1773 if (cbD->passNeedsResourceTracking) {
1775 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
1776 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
1777 switch (b->type) {
1779 // no BufUniformRead / AccessUniform because no real uniform buffers are used
1780 break;
1783 for (int elem = 0; elem < b->u.stex.count; ++elem) {
1784 trackedRegisterTexture(&passResTracker,
1785 QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex),
1788 }
1789 break;
1793 {
1794 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
1798 else if (b->type == QRhiShaderResourceBinding::ImageStore)
1800 else
1802 trackedRegisterTexture(&passResTracker, texD, access,
1804 }
1805 break;
1809 {
1810 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
1814 else if (b->type == QRhiShaderResourceBinding::BufferStore)
1816 else
1818 trackedRegisterBuffer(&passResTracker, bufD, access,
1820 }
1821 break;
1822 default:
1823 break;
1824 }
1825 }
1826 }
1827
1828 bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
1829
1830 // The Command::BindShaderResources command generated below is what will
1831 // cause uniforms to be set (glUniformNxx). This needs some special
1832 // handling here in this backend without real uniform buffers, because,
1833 // like in other backends, we optimize out the setShaderResources when the
1834 // srb that was set before is attempted to be set again on the command
1835 // buffer, but that is incorrect if the same srb is now used with another
1836 // pipeline. (because that could mean a glUseProgram not followed by
1837 // up-to-date glUniform calls, i.e. with GL we have a strong dependency
1838 // between the pipeline (== program) and the srb, unlike other APIs) This
1839 // is the reason there is a second level of srb(+generation) tracking in
1840 // the pipeline objects.
1841 if (gfxPsD && (gfxPsD->currentSrb != srb || gfxPsD->currentSrbGeneration != srbD->generation)) {
1842 srbChanged = true;
1843 gfxPsD->currentSrb = srb;
1844 gfxPsD->currentSrbGeneration = srbD->generation;
1845 } else if (compPsD && (compPsD->currentSrb != srb || compPsD->currentSrbGeneration != srbD->generation)) {
1846 srbChanged = true;
1847 compPsD->currentSrb = srb;
1848 compPsD->currentSrbGeneration = srbD->generation;
1849 }
1850
1851 if (srbChanged || cbD->currentSrbGeneration != srbD->generation || srbD->hasDynamicOffset) {
1852 if (gfxPsD) {
1853 cbD->currentGraphicsSrb = srb;
1854 cbD->currentComputeSrb = nullptr;
1855 } else {
1856 cbD->currentGraphicsSrb = nullptr;
1857 cbD->currentComputeSrb = srb;
1858 }
1859 cbD->currentSrbGeneration = srbD->generation;
1860
1861 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1863 cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
1864 cmd.args.bindShaderResources.maybeComputePs = compPsD;
1865 cmd.args.bindShaderResources.srb = srb;
1866 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
1867 if (srbD->hasDynamicOffset) {
1868 if (dynamicOffsetCount < QGles2CommandBuffer::MAX_DYNAMIC_OFFSET_COUNT) {
1869 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
1870 uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
1871 for (int i = 0; i < dynamicOffsetCount; ++i) {
1872 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1873 *p++ = uint(dynOfs.first);
1874 *p++ = dynOfs.second;
1875 }
1876 } else {
1877 qWarning("Too many dynamic offsets (%d, max is %d)",
1879 }
1880 }
1881 }
1882}
1883
1885 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1886 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1887{
1891
1892 for (int i = 0; i < bindingCount; ++i) {
1893 QRhiBuffer *buf = bindings[i].first;
1894 quint32 ofs = bindings[i].second;
1896 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
1897
1898 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1900 cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
1901 cmd.args.bindVertexBuffer.buffer = bufD->buffer;
1902 cmd.args.bindVertexBuffer.offset = ofs;
1903 cmd.args.bindVertexBuffer.binding = startBinding + i;
1904
1905 if (cbD->passNeedsResourceTracking) {
1908 }
1909 }
1910
1911 if (indexBuf) {
1912 QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
1913 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
1914
1915 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1917 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
1918 cmd.args.bindIndexBuffer.offset = indexOffset;
1919 cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
1920
1921 if (cbD->passNeedsResourceTracking) {
1924 }
1925 }
1926}
1927
1929{
1932
1933 const std::array<float, 4> r = viewport.viewport();
1934 // A negative width or height is an error. A negative x or y is not.
1935 if (r[2] < 0.0f || r[3] < 0.0f)
1936 return;
1937
1938 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1940 cmd.args.viewport.x = r[0];
1941 cmd.args.viewport.y = r[1];
1942 cmd.args.viewport.w = r[2];
1943 cmd.args.viewport.h = r[3];
1944 cmd.args.viewport.d0 = viewport.minDepth();
1945 cmd.args.viewport.d1 = viewport.maxDepth();
1946}
1947
1949{
1952
1953 const std::array<int, 4> r = scissor.scissor();
1954 // A negative width or height is an error. A negative x or y is not.
1955 if (r[2] < 0 || r[3] < 0)
1956 return;
1957
1958 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1960 cmd.args.scissor.x = r[0];
1961 cmd.args.scissor.y = r[1];
1962 cmd.args.scissor.w = r[2];
1963 cmd.args.scissor.h = r[3];
1964}
1965
1967{
1970
1971 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1973 cmd.args.blendConstants.r = float(c.redF());
1974 cmd.args.blendConstants.g = float(c.greenF());
1975 cmd.args.blendConstants.b = float(c.blueF());
1976 cmd.args.blendConstants.a = float(c.alphaF());
1977}
1978
1980{
1983
1984 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1986 cmd.args.stencilRef.ref = refValue;
1987 cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
1988}
1989
1991 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
1992{
1995
1996 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
1998 cmd.args.draw.ps = cbD->currentGraphicsPipeline;
1999 cmd.args.draw.vertexCount = vertexCount;
2000 cmd.args.draw.firstVertex = firstVertex;
2001 cmd.args.draw.instanceCount = instanceCount;
2002 cmd.args.draw.baseInstance = firstInstance;
2003}
2004
2006 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2007{
2010
2011 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2013 cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
2014 cmd.args.drawIndexed.indexCount = indexCount;
2015 cmd.args.drawIndexed.firstIndex = firstIndex;
2016 cmd.args.drawIndexed.instanceCount = instanceCount;
2017 cmd.args.drawIndexed.baseInstance = firstInstance;
2018 cmd.args.drawIndexed.baseVertex = vertexOffset;
2019}
2020
2022{
2023 if (!debugMarkers)
2024 return;
2025
2026 Q_UNUSED(cb);
2027 Q_UNUSED(name);
2028}
2029
2031{
2032 if (!debugMarkers)
2033 return;
2034
2035 Q_UNUSED(cb);
2036}
2037
2039{
2040 if (!debugMarkers)
2041 return;
2042
2043 Q_UNUSED(cb);
2044 Q_UNUSED(msg);
2045}
2046
2048{
2049 Q_UNUSED(cb);
2050 return nullptr;
2051}
2052
2054{
2055 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2056 cmd.cmd = type;
2058 cmd.args.beginFrame.timestampQuery = tsQuery;
2060 cmd.args.endFrame.timestampQuery = tsQuery;
2061}
2062
2064{
2065 if (ofr.active) {
2067 if (!ensureContext())
2068 return;
2069 } else {
2072 return;
2073 }
2074
2076
2078 && !cbD->computePassState.writtenResources.isEmpty())
2079 {
2080 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2082 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
2083 }
2084
2086
2087 cbD->resetCommands();
2088
2089 if (vao) {
2090 f->glBindVertexArray(0);
2091 } else {
2092 f->glBindBuffer(GL_ARRAY_BUFFER, 0);
2093 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2094 if (caps.compute)
2095 f->glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
2096 }
2097}
2098
2100{
2102 Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
2103
2104 cbD->resetCachedState();
2105
2107 // Commands that come after this point need a resource tracker and also
2108 // a BarriersForPass command enqueued. (the ones we had from
2109 // beginPass() are now gone since beginExternal() processed all that
2110 // due to calling executeCommandBuffer()).
2112 }
2113
2115
2116 if (cbD->currentTarget)
2118}
2119
2125
2127{
2128 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2129 if (!ensureContext(swapChainD->surface))
2131
2132 ctx->handle()->beginFrame();
2133
2134 currentSwapChain = swapChainD;
2135
2137 swapChainD->cb.resetState();
2138
2139 if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
2140 double elapsedSec = 0;
2141 if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, this, &elapsedSec))
2142 swapChainD->cb.lastGpuTime = elapsedSec;
2143 }
2144
2145 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2146 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2147 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2148
2149 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame, recordTimestamps ? tsStart : 0);
2150
2151 return QRhi::FrameOpSuccess;
2152}
2153
2155{
2156 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
2157 Q_ASSERT(currentSwapChain == swapChainD);
2158
2159 GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
2160 GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
2161 const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
2162 if (recordTimestamps) {
2163 swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
2164 swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QGles2SwapChainTimestamps::TIMESTAMP_PAIRS;
2165 }
2166
2167 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame, recordTimestamps ? tsEnd : 0);
2168
2169 if (!ensureContext(swapChainD->surface))
2171
2172 executeCommandBuffer(&swapChainD->cb);
2173
2174 if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
2175 ctx->swapBuffers(swapChainD->surface);
2177 } else {
2178 f->glFlush();
2179 }
2180
2181 swapChainD->frameCount += 1;
2182 currentSwapChain = nullptr;
2183
2184 ctx->handle()->endFrame();
2185
2186 return QRhi::FrameOpSuccess;
2187}
2188
2190{
2191 if (!ensureContext())
2193
2194 ofr.active = true;
2195
2197 ofr.cbWrapper.resetState();
2198
2199 if (rhiFlags.testFlag(QRhi::EnableTimestamps) && caps.timestamps) {
2200 if (!ofr.tsQueries[0])
2201 f->glGenQueries(2, ofr.tsQueries);
2202 }
2203
2205 *cb = &ofr.cbWrapper;
2206
2207 return QRhi::FrameOpSuccess;
2208}
2209
2211{
2212 Q_UNUSED(flags);
2213 Q_ASSERT(ofr.active);
2214 ofr.active = false;
2215
2217
2218 if (!ensureContext())
2220
2221 executeCommandBuffer(&ofr.cbWrapper);
2222
2223 // Just as endFrame() does a flush when skipping the swapBuffers(), do it
2224 // here as well. This has the added benefit of playing nice when rendering
2225 // to a texture from a context and then consuming that texture from
2226 // another, sharing context.
2227 f->glFlush();
2228
2229 if (ofr.tsQueries[0]) {
2230 quint64 timestamps[2];
2231 glGetQueryObjectui64v(ofr.tsQueries[1], GL_QUERY_RESULT, &timestamps[1]);
2232 glGetQueryObjectui64v(ofr.tsQueries[0], GL_QUERY_RESULT, &timestamps[0]);
2233 if (timestamps[1] >= timestamps[0]) {
2234 const quint64 nanoseconds = timestamps[1] - timestamps[0];
2235 ofr.cbWrapper.lastGpuTime = nanoseconds / 1000000000.0; // seconds
2236 }
2237 }
2238
2239 return QRhi::FrameOpSuccess;
2240}
2241
2243{
2244 if (inFrame) {
2245 if (ofr.active) {
2247 Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
2248 if (!ensureContext())
2250 executeCommandBuffer(&ofr.cbWrapper);
2251 ofr.cbWrapper.resetCommands();
2252 } else {
2259 }
2260 // Do an actual glFinish(). May seem superfluous, but this is what
2261 // matches most other backends e.g. Vulkan/Metal that do a heavyweight
2262 // wait-for-idle blocking in their finish(). More importantly, this
2263 // allows clients simply call finish() in threaded or shared context
2264 // situations where one explicitly needs to do a glFlush or Finish.
2265 f->glFinish();
2266 }
2267 return QRhi::FrameOpSuccess;
2268}
2269
2276
2284
2293
2302
2304{
2305 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2306 if (!bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer))
2307 return;
2308
2309 const QGles2Buffer::Access prevAccess = bufD->usageState.access;
2310 if (access == prevAccess)
2311 return;
2312
2313 if (bufferAccessIsWrite(prevAccess)) {
2314 // Generating the minimal barrier set is way too complicated to do
2315 // correctly (prevAccess is overwritten so we won't have proper
2316 // tracking across multiple passes) so setting all barrier bits will do
2317 // for now.
2318 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2320 cmd.args.barrier.barriers = barriersForBuffer();
2321 }
2322
2323 bufD->usageState.access = access;
2324}
2325
2327{
2328 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
2329 if (!texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
2330 return;
2331
2332 const QGles2Texture::Access prevAccess = texD->usageState.access;
2333 if (access == prevAccess)
2334 return;
2335
2336 if (textureAccessIsWrite(prevAccess)) {
2337 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2339 cmd.args.barrier.barriers = barriersForTexture();
2340 }
2341
2342 texD->usageState.access = access;
2343}
2344
2346 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
2347{
2349 const bool isCompressed = isCompressedFormat(texD->m_format);
2350 const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
2351 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2352 const bool is1D = texD->m_flags.testFlag(QRhiTexture::OneDimensional);
2353 const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
2354 const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
2355 const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
2356 const QPoint dp = subresDesc.destinationTopLeft();
2357 const QByteArray rawData = subresDesc.data();
2358
2359 auto setCmdByNotCompressedData = [&](const void* data, QSize size, quint32 dataStride)
2360 {
2361 quint32 bytesPerLine = 0;
2362 quint32 bytesPerPixel = 0;
2363 textureFormatInfo(texD->m_format, size, &bytesPerLine, nullptr, &bytesPerPixel);
2364
2365 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2367 cmd.args.subImage.target = texD->target;
2368 cmd.args.subImage.texture = texD->texture;
2369 cmd.args.subImage.faceTarget = effectiveTarget;
2370 cmd.args.subImage.level = level;
2371 cmd.args.subImage.dx = dp.x();
2372 cmd.args.subImage.dy = is1D && isArray ? layer : dp.y();
2373 cmd.args.subImage.dz = is3D || isArray ? layer : 0;
2374 cmd.args.subImage.w = size.width();
2375 cmd.args.subImage.h = size.height();
2376 cmd.args.subImage.glformat = texD->glformat;
2377 cmd.args.subImage.gltype = texD->gltype;
2378
2379 if (dataStride == 0)
2380 dataStride = bytesPerLine;
2381
2382 cmd.args.subImage.rowStartAlign = (dataStride & 3) ? 1 : 4;
2383 cmd.args.subImage.rowLength = caps.unpackRowLength ? (bytesPerPixel ? dataStride / bytesPerPixel : 0) : 0;
2384
2385 cmd.args.subImage.data = data;
2386 };
2387
2388 if (!subresDesc.image().isNull()) {
2389 QImage img = subresDesc.image();
2390 QSize size = img.size();
2391 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2392 const QPoint sp = subresDesc.sourceTopLeft();
2393 if (!subresDesc.sourceSize().isEmpty())
2394 size = subresDesc.sourceSize();
2395
2396 if (caps.unpackRowLength) {
2397 cbD->retainImage(img);
2398 // create a non-owning wrapper for the subimage
2399 const uchar *data = img.constBits() + sp.y() * img.bytesPerLine() + sp.x() * (qMax(1, img.depth() / 8));
2400 img = QImage(data, size.width(), size.height(), img.bytesPerLine(), img.format());
2401 } else {
2402 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
2403 }
2404 }
2405
2406 setCmdByNotCompressedData(cbD->retainImage(img), size, img.bytesPerLine());
2407 } else if (!rawData.isEmpty() && isCompressed) {
2408 const int depth = qMax(1, texD->m_depth);
2409 const int arraySize = qMax(0, texD->m_arraySize);
2410 if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray)
2411 && !texD->zeroInitialized)
2412 {
2413 // Create on first upload since glCompressedTexImage2D cannot take
2414 // nullptr data. We have a rule in the QRhi docs that the first
2415 // upload for a compressed texture must cover the entire image, but
2416 // that is clearly not ideal when building a texture atlas, or when
2417 // having a 3D texture with per-slice data.
2418 quint32 byteSize = 0;
2419 compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
2420 if (is3D)
2421 byteSize *= depth;
2422 if (isArray)
2423 byteSize *= arraySize;
2424 QByteArray zeroBuf(byteSize, 0);
2425 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2427 cmd.args.compressedImage.target = texD->target;
2428 cmd.args.compressedImage.texture = texD->texture;
2429 cmd.args.compressedImage.faceTarget = effectiveTarget;
2430 cmd.args.compressedImage.level = level;
2431 cmd.args.compressedImage.glintformat = texD->glintformat;
2432 cmd.args.compressedImage.w = texD->m_pixelSize.width();
2433 cmd.args.compressedImage.h = is1D && isArray ? arraySize : texD->m_pixelSize.height();
2434 cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
2435 cmd.args.compressedImage.size = byteSize;
2436 cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
2437 texD->zeroInitialized = true;
2438 }
2439
2440 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
2441 : subresDesc.sourceSize();
2442 if (texD->specified || texD->zeroInitialized) {
2443 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2445 cmd.args.compressedSubImage.target = texD->target;
2446 cmd.args.compressedSubImage.texture = texD->texture;
2447 cmd.args.compressedSubImage.faceTarget = effectiveTarget;
2448 cmd.args.compressedSubImage.level = level;
2449 cmd.args.compressedSubImage.dx = dp.x();
2450 cmd.args.compressedSubImage.dy = is1D && isArray ? layer : dp.y();
2451 cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
2452 cmd.args.compressedSubImage.w = size.width();
2453 cmd.args.compressedSubImage.h = size.height();
2454 cmd.args.compressedSubImage.glintformat = texD->glintformat;
2455 cmd.args.compressedSubImage.size = rawData.size();
2456 cmd.args.compressedSubImage.data = cbD->retainData(rawData);
2457 } else {
2458 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2460 cmd.args.compressedImage.target = texD->target;
2461 cmd.args.compressedImage.texture = texD->texture;
2462 cmd.args.compressedImage.faceTarget = effectiveTarget;
2463 cmd.args.compressedImage.level = level;
2464 cmd.args.compressedImage.glintformat = texD->glintformat;
2465 cmd.args.compressedImage.w = size.width();
2466 cmd.args.compressedImage.h = is1D && isArray ? arraySize : size.height();
2467 cmd.args.compressedImage.depth = is3D ? depth : (isArray ? arraySize : 0);
2468 cmd.args.compressedImage.size = rawData.size();
2469 cmd.args.compressedImage.data = cbD->retainData(rawData);
2470 }
2471 } else if (!rawData.isEmpty()) {
2472 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
2473 : subresDesc.sourceSize();
2474
2475 setCmdByNotCompressedData(cbD->retainData(rawData), size, subresDesc.dataStride());
2476 } else {
2477 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2478 }
2479}
2480
2482{
2485
2486 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
2487 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
2490 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2491 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
2492 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
2493 } else {
2495 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2497 cmd.args.bufferSubData.target = bufD->targetForDataOps;
2498 cmd.args.bufferSubData.buffer = bufD->buffer;
2499 cmd.args.bufferSubData.offset = u.offset;
2500 cmd.args.bufferSubData.size = u.data.size();
2501 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
2502 }
2505 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2506 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
2507 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
2508 memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size()));
2509 } else {
2511 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2513 cmd.args.bufferSubData.target = bufD->targetForDataOps;
2514 cmd.args.bufferSubData.buffer = bufD->buffer;
2515 cmd.args.bufferSubData.offset = u.offset;
2516 cmd.args.bufferSubData.size = u.data.size();
2517 cmd.args.bufferSubData.data = cbD->retainBufferData(u.data);
2518 }
2521 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
2522 u.result->data.resize(u.readSize);
2523 memcpy(u.result->data.data(), bufD->data.constData() + u.offset, size_t(u.readSize));
2524 if (u.result->completed)
2525 u.result->completed();
2526 } else {
2527 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2529 cmd.args.getBufferSubData.result = u.result;
2530 cmd.args.getBufferSubData.target = bufD->targetForDataOps;
2531 cmd.args.getBufferSubData.buffer = bufD->buffer;
2532 cmd.args.getBufferSubData.offset = u.offset;
2533 cmd.args.getBufferSubData.size = u.readSize;
2534 }
2535 }
2536 }
2537
2538 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
2539 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
2542 for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
2543 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2544 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2545 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
2546 }
2547 }
2548 texD->specified = true;
2550 Q_ASSERT(u.src && u.dst);
2553
2556
2557 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2558 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2559 // do not translate coordinates, even if sp is bottom-left from gl's pov
2560 const QPoint sp = u.desc.sourceTopLeft();
2561 const QPoint dp = u.desc.destinationTopLeft();
2562
2563 const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
2564 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
2565 const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
2566 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
2567
2568 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2570
2571 const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
2572 const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
2573 const bool dstIs1dArray = dstD->m_flags.testFlag(QRhiTexture::OneDimensional)
2574 && dstD->m_flags.testFlag(QRhiTexture::TextureArray);
2575
2576 cmd.args.copyTex.srcTarget = srcD->target;
2577 cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
2578 cmd.args.copyTex.srcTexture = srcD->texture;
2579 cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
2580 cmd.args.copyTex.srcX = sp.x();
2581 cmd.args.copyTex.srcY = sp.y();
2582 cmd.args.copyTex.srcZ = srcHasZ ? u.desc.sourceLayer() : 0;
2583
2584 cmd.args.copyTex.dstTarget = dstD->target;
2585 cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + (dstHasZ ? 0u : uint(u.desc.destinationLayer()));
2586 cmd.args.copyTex.dstTexture = dstD->texture;
2587 cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
2588 cmd.args.copyTex.dstX = dp.x();
2589 cmd.args.copyTex.dstY = dstIs1dArray ? u.desc.destinationLayer() : dp.y();
2590 cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
2591
2592 cmd.args.copyTex.w = copySize.width();
2593 cmd.args.copyTex.h = copySize.height();
2595 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2597 cmd.args.readPixels.result = u.result;
2599 if (texD)
2601 cmd.args.readPixels.texture = texD ? texD->texture : 0;
2602 cmd.args.readPixels.slice3D = -1;
2603 if (texD) {
2604 const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
2605 cmd.args.readPixels.w = readImageSize.width();
2606 cmd.args.readPixels.h = readImageSize.height();
2607 cmd.args.readPixels.format = texD->m_format;
2608 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
2609 || texD->m_flags.testFlag(QRhiTexture::TextureArray))
2610 {
2611 cmd.args.readPixels.readTarget = texD->target;
2612 cmd.args.readPixels.slice3D = u.rb.layer();
2613 } else {
2614 const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
2615 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
2616 cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer());
2617 }
2618 cmd.args.readPixels.level = u.rb.level();
2619 }
2623 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
2625 cmd.args.genMip.target = texD->target;
2626 cmd.args.genMip.texture = texD->texture;
2627 }
2628 }
2629
2630 ud->free();
2631}
2632
2634{
2635 switch (t) {
2637 return GL_TRIANGLES;
2639 return GL_TRIANGLE_STRIP;
2641 return GL_TRIANGLE_FAN;
2643 return GL_LINES;
2645 return GL_LINE_STRIP;
2647 return GL_POINTS;
2649 return GL_PATCHES;
2650 default:
2651 Q_UNREACHABLE_RETURN(GL_TRIANGLES);
2652 }
2653}
2654
2656{
2657 switch (c) {
2659 return GL_FRONT;
2661 return GL_BACK;
2662 default:
2663 Q_UNREACHABLE_RETURN(GL_BACK);
2664 }
2665}
2666
2668{
2669 switch (f) {
2671 return GL_CCW;
2673 return GL_CW;
2674 default:
2675 Q_UNREACHABLE_RETURN(GL_CCW);
2676 }
2677}
2678
2680{
2681 switch (f) {
2683 return GL_ZERO;
2685 return GL_ONE;
2687 return GL_SRC_COLOR;
2689 return GL_ONE_MINUS_SRC_COLOR;
2691 return GL_DST_COLOR;
2693 return GL_ONE_MINUS_DST_COLOR;
2695 return GL_SRC_ALPHA;
2697 return GL_ONE_MINUS_SRC_ALPHA;
2699 return GL_DST_ALPHA;
2701 return GL_ONE_MINUS_DST_ALPHA;
2703 return GL_CONSTANT_COLOR;
2707 return GL_CONSTANT_ALPHA;
2711 return GL_SRC_ALPHA_SATURATE;
2716 qWarning("Unsupported blend factor %d", f);
2717 return GL_ZERO;
2718 default:
2719 Q_UNREACHABLE_RETURN(GL_ZERO);
2720 }
2721}
2722
2724{
2725 switch (op) {
2727 return GL_FUNC_ADD;
2729 return GL_FUNC_SUBTRACT;
2733 return GL_MIN;
2735 return GL_MAX;
2736 default:
2737 Q_UNREACHABLE_RETURN(GL_FUNC_ADD);
2738 }
2739}
2740
2742{
2743 switch (op) {
2745 return GL_NEVER;
2747 return GL_LESS;
2749 return GL_EQUAL;
2751 return GL_LEQUAL;
2753 return GL_GREATER;
2755 return GL_NOTEQUAL;
2757 return GL_GEQUAL;
2759 return GL_ALWAYS;
2760 default:
2761 Q_UNREACHABLE_RETURN(GL_ALWAYS);
2762 }
2763}
2764
2766{
2767 switch (op) {
2769 return GL_ZERO;
2771 return GL_KEEP;
2773 return GL_REPLACE;
2775 return GL_INCR;
2777 return GL_DECR;
2779 return GL_INVERT;
2781 return GL_INCR_WRAP;
2783 return GL_DECR_WRAP;
2784 default:
2785 Q_UNREACHABLE_RETURN(GL_KEEP);
2786 }
2787}
2788
2790{
2791 switch (mode) {
2793 return GL_FILL;
2795 return GL_LINE;
2796 default:
2797 Q_UNREACHABLE_RETURN(GL_FILL);
2798 }
2799}
2800
2802{
2803 switch (f) {
2805 if (m == QRhiSampler::None)
2806 return GL_NEAREST;
2807 else
2808 return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
2810 if (m == QRhiSampler::None)
2811 return GL_LINEAR;
2812 else
2813 return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
2814 default:
2815 Q_UNREACHABLE_RETURN(GL_LINEAR);
2816 }
2817}
2818
2820{
2821 switch (f) {
2823 return GL_NEAREST;
2825 return GL_LINEAR;
2826 default:
2827 Q_UNREACHABLE_RETURN(GL_LINEAR);
2828 }
2829}
2830
2832{
2833 switch (m) {
2835 return GL_REPEAT;
2837 return GL_CLAMP_TO_EDGE;
2839 return GL_MIRRORED_REPEAT;
2840 default:
2841 Q_UNREACHABLE_RETURN(GL_CLAMP_TO_EDGE);
2842 }
2843}
2844
2846{
2847 switch (op) {
2848 case QRhiSampler::Never:
2849 return GL_NEVER;
2850 case QRhiSampler::Less:
2851 return GL_LESS;
2852 case QRhiSampler::Equal:
2853 return GL_EQUAL;
2855 return GL_LEQUAL;
2857 return GL_GREATER;
2859 return GL_NOTEQUAL;
2861 return GL_GEQUAL;
2863 return GL_ALWAYS;
2864 default:
2865 Q_UNREACHABLE_RETURN(GL_NEVER);
2866 }
2867}
2868
2890
2892{
2894 u.layout = 0; // N/A
2895 u.access = bufUsage.access;
2896 u.stage = 0; // N/A
2897 return u;
2898}
2899
2921
2923{
2925 u.layout = 0; // N/A
2926 u.access = texUsage.access;
2927 u.stage = 0; // N/A
2928 return u;
2929}
2930
2932 QGles2Buffer *bufD,
2935{
2936 QGles2Buffer::UsageState &u(bufD->usageState);
2937 passResTracker->registerBuffer(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
2939}
2940
2942 QGles2Texture *texD,
2945{
2946 QGles2Texture::UsageState &u(texD->usageState);
2947 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
2949}
2950
2970
2971// Helper that must be used in executeCommandBuffer() whenever changing the
2972// ARRAY or ELEMENT_ARRAY buffer binding outside of Command::BindVertexBuffer
2973// and Command::BindIndexBuffer.
2976 GLenum target,
2977 GLuint buffer)
2978{
2979 state->currentArrayBuffer = 0;
2980 state->currentElementArrayBuffer = 0;
2981 state->lastBindVertexBuffer.buffer = 0;
2982 f->glBindBuffer(target, buffer);
2983}
2984
2986{
2989
2990 for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) {
2991 const QGles2CommandBuffer::Command &cmd(*it);
2992 switch (cmd.cmd) {
2994 if (cmd.args.beginFrame.timestampQuery)
2995 glQueryCounter(cmd.args.beginFrame.timestampQuery, GL_TIMESTAMP);
2996 if (caps.coreProfile) {
2997 if (!vao)
2998 f->glGenVertexArrays(1, &vao);
2999 f->glBindVertexArray(vao);
3000 }
3001 break;
3003 if (state.instancedAttributesUsed) {
3005 if (state.nonzeroAttribDivisor[i])
3006 f->glVertexAttribDivisor(GLuint(i), 0);
3007 }
3008 for (int i = CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; i <= state.maxUntrackedInstancedAttribute; ++i)
3009 f->glVertexAttribDivisor(GLuint(i), 0);
3010 state.instancedAttributesUsed = false;
3011 }
3012#ifdef Q_OS_WASM
3014 if (state.enabledAttribArrays[i]) {
3015 f->glDisableVertexAttribArray(GLuint(i));
3016 state.enabledAttribArrays[i] = false;
3017 }
3018 }
3019#endif
3020 if (vao)
3021 f->glBindVertexArray(0);
3022 if (cmd.args.endFrame.timestampQuery)
3023 glQueryCounter(cmd.args.endFrame.timestampQuery, GL_TIMESTAMP);
3024 break;
3026 if (vao)
3027 f->glBindVertexArray(vao);
3028 break;
3030 f->glViewport(GLint(cmd.args.viewport.x), GLint(cmd.args.viewport.y), GLsizei(cmd.args.viewport.w), GLsizei(cmd.args.viewport.h));
3031 f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
3032 break;
3034 f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
3035 break;
3037 f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
3038 break;
3040 {
3042 if (psD) {
3043 const GLint ref = GLint(cmd.args.stencilRef.ref);
3044 f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), ref, psD->m_stencilReadMask);
3045 f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), ref, psD->m_stencilReadMask);
3046 cbD->graphicsPassState.dynamic.stencilRef = ref;
3047 } else {
3048 qWarning("No graphics pipeline active for setStencilRef; ignored");
3049 }
3050 }
3051 break;
3053 {
3054 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
3055 if (psD) {
3056 if (state.lastBindVertexBuffer.ps == psD
3057 && state.lastBindVertexBuffer.buffer == cmd.args.bindVertexBuffer.buffer
3058 && state.lastBindVertexBuffer.offset == cmd.args.bindVertexBuffer.offset
3059 && state.lastBindVertexBuffer.binding == cmd.args.bindVertexBuffer.binding)
3060 {
3061 // The pipeline and so the vertex input layout is
3062 // immutable, no point in issuing the exact same set of
3063 // glVertexAttribPointer again and again for the same buffer.
3064 break;
3065 }
3066 state.lastBindVertexBuffer.ps = psD;
3067 state.lastBindVertexBuffer.buffer = cmd.args.bindVertexBuffer.buffer;
3068 state.lastBindVertexBuffer.offset = cmd.args.bindVertexBuffer.offset;
3069 state.lastBindVertexBuffer.binding = cmd.args.bindVertexBuffer.binding;
3070
3071 if (cmd.args.bindVertexBuffer.buffer != state.currentArrayBuffer) {
3072 state.currentArrayBuffer = cmd.args.bindVertexBuffer.buffer;
3073 // we do not support more than one vertex buffer
3074 f->glBindBuffer(GL_ARRAY_BUFFER, state.currentArrayBuffer);
3075 }
3076 for (auto it = psD->m_vertexInputLayout.cbeginAttributes(), itEnd = psD->m_vertexInputLayout.cendAttributes();
3077 it != itEnd; ++it)
3078 {
3079 const int bindingIdx = it->binding();
3080 if (bindingIdx != cmd.args.bindVertexBuffer.binding)
3081 continue;
3082
3083 const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(bindingIdx);
3084 const int stride = int(inputBinding->stride());
3085 int size = 1;
3087 bool normalize = false;
3088 switch (it->format()) {
3090 type = GL_FLOAT;
3091 size = 4;
3092 break;
3094 type = GL_FLOAT;
3095 size = 3;
3096 break;
3098 type = GL_FLOAT;
3099 size = 2;
3100 break;
3102 type = GL_FLOAT;
3103 size = 1;
3104 break;
3107 normalize = true;
3108 size = 4;
3109 break;
3112 normalize = true;
3113 size = 2;
3114 break;
3117 normalize = true;
3118 size = 1;
3119 break;
3121 type = GL_UNSIGNED_INT;
3122 size = 4;
3123 break;
3125 type = GL_UNSIGNED_INT;
3126 size = 3;
3127 break;
3129 type = GL_UNSIGNED_INT;
3130 size = 2;
3131 break;
3133 type = GL_UNSIGNED_INT;
3134 size = 1;
3135 break;
3137 type = GL_INT;
3138 size = 4;
3139 break;
3141 type = GL_INT;
3142 size = 3;
3143 break;
3145 type = GL_INT;
3146 size = 2;
3147 break;
3149 type = GL_INT;
3150 size = 1;
3151 break;
3154 size = 4;
3155 break;
3158 size = 3;
3159 break;
3162 size = 2;
3163 break;
3166 size = 1;
3167 break;
3169 type = GL_UNSIGNED_SHORT;
3170 size = 4;
3171 break;
3173 type = GL_UNSIGNED_SHORT;
3174 size = 3;
3175 break;
3177 type = GL_UNSIGNED_SHORT;
3178 size = 2;
3179 break;
3181 type = GL_UNSIGNED_SHORT;
3182 size = 1;
3183 break;
3185 type = GL_SHORT;
3186 size = 4;
3187 break;
3189 type = GL_SHORT;
3190 size = 3;
3191 break;
3193 type = GL_SHORT;
3194 size = 2;
3195 break;
3197 type = GL_SHORT;
3198 size = 1;
3199 break;
3200 default:
3201 break;
3202 }
3203
3204 const int locationIdx = it->location();
3205 quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset;
3206 if (type == GL_UNSIGNED_INT || type == GL_INT) {
3207 if (caps.intAttributes) {
3208 f->glVertexAttribIPointer(GLuint(locationIdx), size, type, stride,
3209 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3210 } else {
3211 qWarning("Current RHI backend does not support IntAttributes. Check supported features.");
3212 // This is a trick to disable this attribute
3214 state.enabledAttribArrays[locationIdx] = true;
3215 }
3216 } else {
3217 f->glVertexAttribPointer(GLuint(locationIdx), size, type, normalize, stride,
3218 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
3219 }
3220 if (locationIdx >= CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT || !state.enabledAttribArrays[locationIdx]) {
3222 state.enabledAttribArrays[locationIdx] = true;
3223 f->glEnableVertexAttribArray(GLuint(locationIdx));
3224 }
3225 if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing) {
3226 f->glVertexAttribDivisor(GLuint(locationIdx), inputBinding->instanceStepRate());
3228 state.nonzeroAttribDivisor[locationIdx] = true;
3229 else
3230 state.maxUntrackedInstancedAttribute = qMax(state.maxUntrackedInstancedAttribute, locationIdx);
3231 state.instancedAttributesUsed = true;
3233 && state.nonzeroAttribDivisor[locationIdx])
3235 && locationIdx <= state.maxUntrackedInstancedAttribute))
3236 {
3237 f->glVertexAttribDivisor(GLuint(locationIdx), 0);
3239 state.nonzeroAttribDivisor[locationIdx] = false;
3240 }
3241 }
3242 } else {
3243 qWarning("No graphics pipeline active for setVertexInput; ignored");
3244 }
3245 }
3246 break;
3248 state.indexType = cmd.args.bindIndexBuffer.type;
3249 state.indexStride = state.indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
3250 state.indexOffset = cmd.args.bindIndexBuffer.offset;
3251 if (state.currentElementArrayBuffer != cmd.args.bindIndexBuffer.buffer) {
3252 state.currentElementArrayBuffer = cmd.args.bindIndexBuffer.buffer;
3253 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.currentElementArrayBuffer);
3254 }
3255 break;
3257 {
3259 if (psD) {
3260 if (cmd.args.draw.instanceCount == 1 || !caps.instancing) {
3261 f->glDrawArrays(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount));
3262 } else {
3263 f->glDrawArraysInstanced(psD->drawMode, GLint(cmd.args.draw.firstVertex), GLsizei(cmd.args.draw.vertexCount),
3264 GLsizei(cmd.args.draw.instanceCount));
3265 }
3266 } else {
3267 qWarning("No graphics pipeline active for draw; ignored");
3268 }
3269 }
3270 break;
3272 {
3273 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
3274 if (psD) {
3275 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
3276 quintptr(cmd.args.drawIndexed.firstIndex * state.indexStride + state.indexOffset));
3277 if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) {
3278 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
3279 f->glDrawElementsBaseVertex(psD->drawMode,
3280 GLsizei(cmd.args.drawIndexed.indexCount),
3281 state.indexType,
3282 ofs,
3283 cmd.args.drawIndexed.baseVertex);
3284 } else {
3285 f->glDrawElements(psD->drawMode,
3286 GLsizei(cmd.args.drawIndexed.indexCount),
3287 state.indexType,
3288 ofs);
3289 }
3290 } else {
3291 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
3292 f->glDrawElementsInstancedBaseVertex(psD->drawMode,
3293 GLsizei(cmd.args.drawIndexed.indexCount),
3294 state.indexType,
3295 ofs,
3296 GLsizei(cmd.args.drawIndexed.instanceCount),
3297 cmd.args.drawIndexed.baseVertex);
3298 } else {
3299 f->glDrawElementsInstanced(psD->drawMode,
3300 GLsizei(cmd.args.drawIndexed.indexCount),
3301 state.indexType,
3302 ofs,
3303 GLsizei(cmd.args.drawIndexed.instanceCount));
3304 }
3305 }
3306 } else {
3307 qWarning("No graphics pipeline active for drawIndexed; ignored");
3308 }
3309 }
3310 break;
3312 executeBindGraphicsPipeline(cbD, QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindGraphicsPipeline.ps));
3313 break;
3316 cmd.args.bindShaderResources.maybeGraphicsPs,
3317 cmd.args.bindShaderResources.maybeComputePs,
3318 cmd.args.bindShaderResources.srb,
3319 cmd.args.bindShaderResources.dynamicOffsetPairs,
3320 cmd.args.bindShaderResources.dynamicOffsetCount);
3321 break;
3323 {
3324 QVarLengthArray<GLenum, 8> bufs;
3325 if (cmd.args.bindFramebuffer.fbo) {
3326 f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo);
3327 const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
3328 bufs.append(colorAttCount > 0 ? GL_COLOR_ATTACHMENT0 : GL_NONE);
3329 if (caps.maxDrawBuffers > 1) {
3330 for (int i = 1; i < colorAttCount; ++i)
3331 bufs.append(GL_COLOR_ATTACHMENT0 + uint(i));
3332 }
3333 } else {
3334 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3335 if (cmd.args.bindFramebuffer.stereo && cmd.args.bindFramebuffer.stereoTarget == QRhiSwapChain::RightBuffer)
3336 bufs.append(GL_BACK_RIGHT);
3337 else
3338 bufs.append(caps.gles ? GL_BACK : GL_BACK_LEFT);
3339 }
3340 if (caps.hasDrawBuffersFunc)
3341 f->glDrawBuffers(bufs.count(), bufs.constData());
3342 if (caps.srgbWriteControl) {
3343 if (cmd.args.bindFramebuffer.srgb)
3344 f->glEnable(GL_FRAMEBUFFER_SRGB);
3345 else
3346 f->glDisable(GL_FRAMEBUFFER_SRGB);
3347 }
3348 }
3349 break;
3351 f->glDisable(GL_SCISSOR_TEST);
3352 if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
3353 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3354 f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
3355 }
3356 if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
3357 f->glDepthMask(GL_TRUE);
3358 f->glClearDepthf(cmd.args.clear.d);
3359 }
3360 if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) {
3361 f->glStencilMask(0xFF);
3362 f->glClearStencil(GLint(cmd.args.clear.s));
3363 }
3364 f->glClear(cmd.args.clear.mask);
3365 cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking
3366 break;
3368 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
3369 f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
3370 cmd.args.bufferSubData.data);
3371 break;
3373 {
3374 QRhiReadbackResult *result = cmd.args.getBufferSubData.result;
3375 bindVertexIndexBufferWithStateReset(&state, f, cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
3376 if (caps.gles) {
3377 if (caps.properMapBuffer) {
3378 void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target,
3379 cmd.args.getBufferSubData.offset,
3380 cmd.args.getBufferSubData.size,
3382 if (p) {
3383 result->data.resize(cmd.args.getBufferSubData.size);
3384 memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size));
3385 f->glUnmapBuffer(cmd.args.getBufferSubData.target);
3386 }
3387 }
3388 } else {
3389 result->data.resize(cmd.args.getBufferSubData.size);
3390 f->glGetBufferSubData(cmd.args.getBufferSubData.target,
3391 cmd.args.getBufferSubData.offset,
3392 cmd.args.getBufferSubData.size,
3393 result->data.data());
3394 }
3395 if (result->completed)
3396 result->completed();
3397 }
3398 break;
3400 {
3401 GLuint fbo;
3402 f->glGenFramebuffers(1, &fbo);
3403 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3404 if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D
3405 || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY
3406 || cmd.args.copyTex.srcTarget == GL_TEXTURE_1D_ARRAY) {
3407 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
3408 cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
3409 } else if (cmd.args.copyTex.srcTarget == GL_TEXTURE_1D) {
3410 glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3411 cmd.args.copyTex.srcTarget, cmd.args.copyTex.srcTexture,
3412 cmd.args.copyTex.srcLevel);
3413 } else {
3414 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3415 cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
3416 }
3417 f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
3418 if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D || cmd.args.copyTex.dstTarget == GL_TEXTURE_2D_ARRAY) {
3419 f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
3420 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
3421 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
3422 cmd.args.copyTex.w, cmd.args.copyTex.h);
3423 } else if (cmd.args.copyTex.dstTarget == GL_TEXTURE_1D) {
3424 glCopyTexSubImage1D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
3425 cmd.args.copyTex.dstX, cmd.args.copyTex.srcX,
3426 cmd.args.copyTex.srcY, cmd.args.copyTex.w);
3427 } else {
3428 f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
3429 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
3430 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
3431 cmd.args.copyTex.w, cmd.args.copyTex.h);
3432 }
3433 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3434 f->glDeleteFramebuffers(1, &fbo);
3435 }
3436 break;
3438 {
3439 QRhiReadbackResult *result = cmd.args.readPixels.result;
3440 GLuint tex = cmd.args.readPixels.texture;
3441 GLuint fbo = 0;
3442 int mipLevel = 0;
3443 if (tex) {
3444 result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
3445 result->format = cmd.args.readPixels.format;
3446 mipLevel = cmd.args.readPixels.level;
3447 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
3448 f->glGenFramebuffers(1, &fbo);
3449 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3450 if (cmd.args.readPixels.slice3D >= 0) {
3451 f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3452 tex, mipLevel, cmd.args.readPixels.slice3D);
3453 } else if (cmd.args.readPixels.readTarget == GL_TEXTURE_1D) {
3454 glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3455 cmd.args.readPixels.readTarget, tex, mipLevel);
3456 } else {
3457 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3458 cmd.args.readPixels.readTarget, tex, mipLevel);
3459 }
3460 }
3461 } else {
3462 result->pixelSize = currentSwapChain->pixelSize;
3463 result->format = QRhiTexture::RGBA8;
3464 // readPixels handles multisample resolving implicitly
3465 }
3466 const int w = result->pixelSize.width();
3467 const int h = result->pixelSize.height();
3468 if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
3469 // With GLES, GL_RGBA is the only mandated readback format, so stick with it.
3470 // (and that's why we return false for the ReadBackAnyTextureFormat feature)
3471 if (result->format == QRhiTexture::R8 || result->format == QRhiTexture::RED_OR_ALPHA8) {
3472 result->data.resize(w * h);
3473 QByteArray tmpBuf;
3474 tmpBuf.resize(w * h * 4);
3475 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tmpBuf.data());
3476 const quint8 *srcBase = reinterpret_cast<const quint8 *>(tmpBuf.constData());
3477 quint8 *dstBase = reinterpret_cast<quint8 *>(result->data.data());
3478 const int componentIndex = isFeatureSupported(QRhi::RedOrAlpha8IsRed) ? 0 : 3;
3479 for (int y = 0; y < h; ++y) {
3480 const quint8 *src = srcBase + y * w * 4;
3481 quint8 *dst = dstBase + y * w;
3482 int count = w;
3483 while (count-- > 0) {
3484 *dst++ = src[componentIndex];
3485 src += 4;
3486 }
3487 }
3488 } else {
3489 switch (result->format) {
3490 // For floating point formats try it because this can be
3491 // relevant for some use cases; if it works, then fine, if
3492 // not, there's nothing we can do.
3494 result->data.resize(w * h * 8);
3495 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_HALF_FLOAT, result->data.data());
3496 break;
3497 case QRhiTexture::R16F:
3498 result->data.resize(w * h * 2);
3499 f->glReadPixels(0, 0, w, h, GL_RED, GL_HALF_FLOAT, result->data.data());
3500 break;
3501 case QRhiTexture::R32F:
3502 result->data.resize(w * h * 4);
3503 f->glReadPixels(0, 0, w, h, GL_RED, GL_FLOAT, result->data.data());
3504 break;
3506 result->data.resize(w * h * 16);
3507 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, result->data.data());
3508 break;
3510 result->data.resize(w * h * 4);
3511 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, result->data.data());
3512 break;
3513 default:
3514 result->data.resize(w * h * 4);
3515 f->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, result->data.data());
3516 break;
3517 }
3518 }
3519 } else {
3520 result->data.resize(w * h * 4);
3521 result->data.fill('\0');
3522 }
3523 if (fbo) {
3524 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3525 f->glDeleteFramebuffers(1, &fbo);
3526 }
3527 if (result->completed)
3528 result->completed();
3529 }
3530 break;
3532 f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
3533 if (cmd.args.subImage.rowStartAlign != 4)
3534 f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
3535 if (cmd.args.subImage.rowLength != 0)
3536 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
3537 if (cmd.args.subImage.target == GL_TEXTURE_3D || cmd.args.subImage.target == GL_TEXTURE_2D_ARRAY) {
3538 f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level,
3539 cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
3540 cmd.args.subImage.w, cmd.args.subImage.h, 1,
3541 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
3542 cmd.args.subImage.data);
3543 } else if (cmd.args.subImage.target == GL_TEXTURE_1D) {
3544 glTexSubImage1D(cmd.args.subImage.target, cmd.args.subImage.level,
3545 cmd.args.subImage.dx, cmd.args.subImage.w,
3546 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
3547 cmd.args.subImage.data);
3548 } else {
3549 f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
3550 cmd.args.subImage.dx, cmd.args.subImage.dy,
3551 cmd.args.subImage.w, cmd.args.subImage.h,
3552 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
3553 cmd.args.subImage.data);
3554 }
3555 if (cmd.args.subImage.rowStartAlign != 4)
3556 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
3557 if (cmd.args.subImage.rowLength != 0)
3558 f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3559 break;
3561 f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
3562 if (cmd.args.compressedImage.target == GL_TEXTURE_3D || cmd.args.compressedImage.target == GL_TEXTURE_2D_ARRAY) {
3563 f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
3564 cmd.args.compressedImage.glintformat,
3565 cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
3566 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
3567 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
3568 glCompressedTexImage1D(
3569 cmd.args.compressedImage.target, cmd.args.compressedImage.level,
3570 cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, 0,
3571 cmd.args.compressedImage.size, cmd.args.compressedImage.data);
3572 } else {
3573 f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
3574 cmd.args.compressedImage.glintformat,
3575 cmd.args.compressedImage.w, cmd.args.compressedImage.h,
3576 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
3577 }
3578 break;
3580 f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
3581 if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D || cmd.args.compressedSubImage.target == GL_TEXTURE_2D_ARRAY) {
3582 f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
3583 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
3584 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
3585 cmd.args.compressedSubImage.glintformat,
3586 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
3587 } else if (cmd.args.compressedImage.target == GL_TEXTURE_1D) {
3588 glCompressedTexSubImage1D(
3589 cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
3590 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.w,
3591 cmd.args.compressedSubImage.glintformat, cmd.args.compressedSubImage.size,
3592 cmd.args.compressedSubImage.data);
3593 } else {
3594 f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
3595 cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
3596 cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
3597 cmd.args.compressedSubImage.glintformat,
3598 cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
3599 }
3600 break;
3602 {
3603 // Altering the scissor state, so reset the stored state, although
3604 // not strictly required as long as blit is done in endPass() only.
3605 cbD->graphicsPassState.reset();
3606 f->glDisable(GL_SCISSOR_TEST);
3607 GLuint fbo[2];
3608 f->glGenFramebuffers(2, fbo);
3609 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
3610 const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil;
3611 if (ds) {
3612 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3613 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
3614 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3615 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
3616 } else {
3617 f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3618 GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
3619 }
3620 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
3621 if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) {
3622 if (ds) {
3623 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3624 cmd.args.blitFromRenderbuffer.dstTexture,
3625 cmd.args.blitFromRenderbuffer.dstLevel,
3626 cmd.args.blitFromRenderbuffer.dstLayer);
3627 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3628 cmd.args.blitFromRenderbuffer.dstTexture,
3629 cmd.args.blitFromRenderbuffer.dstLevel,
3630 cmd.args.blitFromRenderbuffer.dstLayer);
3631 } else {
3632 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3633 cmd.args.blitFromRenderbuffer.dstTexture,
3634 cmd.args.blitFromRenderbuffer.dstLevel,
3635 cmd.args.blitFromRenderbuffer.dstLayer);
3636 }
3637 } else {
3638 if (ds) {
3639 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
3640 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
3641 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
3642 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
3643 } else {
3644 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
3645 cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
3646 }
3647 }
3648 f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
3649 0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
3650 ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
3651 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
3652 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3653 f->glDeleteFramebuffers(2, fbo);
3654 }
3655 break;
3657 {
3658 // Altering the scissor state, so reset the stored state, although
3659 // not strictly required as long as blit is done in endPass() only.
3660 cbD->graphicsPassState.reset();
3661 f->glDisable(GL_SCISSOR_TEST);
3662 GLuint fbo[2];
3663 f->glGenFramebuffers(2, fbo);
3664 f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
3665 const bool ds = cmd.args.blitFromTexture.isDepthStencil;
3666 if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
3667 if (ds) {
3668 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3669 cmd.args.blitFromTexture.srcTexture,
3670 cmd.args.blitFromTexture.srcLevel,
3671 cmd.args.blitFromTexture.srcLayer);
3672 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3673 cmd.args.blitFromTexture.srcTexture,
3674 cmd.args.blitFromTexture.srcLevel,
3675 cmd.args.blitFromTexture.srcLayer);
3676 } else {
3677 f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3678 cmd.args.blitFromTexture.srcTexture,
3679 cmd.args.blitFromTexture.srcLevel,
3680 cmd.args.blitFromTexture.srcLayer);
3681 }
3682 } else {
3683 if (ds) {
3684 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
3685 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
3686 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
3687 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
3688 } else {
3689 f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
3690 cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
3691 }
3692 }
3693 f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
3694 if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) {
3695 if (ds) {
3696 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
3697 cmd.args.blitFromTexture.dstTexture,
3698 cmd.args.blitFromTexture.dstLevel,
3699 cmd.args.blitFromTexture.dstLayer);
3700 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
3701 cmd.args.blitFromTexture.dstTexture,
3702 cmd.args.blitFromTexture.dstLevel,
3703 cmd.args.blitFromTexture.dstLayer);
3704 } else {
3705 f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
3706 cmd.args.blitFromTexture.dstTexture,
3707 cmd.args.blitFromTexture.dstLevel,
3708 cmd.args.blitFromTexture.dstLayer);
3709 }
3710 } else {
3711 if (ds) {
3712 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
3713 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
3714 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
3715 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
3716 } else {
3717 f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
3718 cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
3719 }
3720 }
3721 f->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
3722 0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
3723 ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
3724 GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
3725 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
3726 f->glDeleteFramebuffers(2, fbo);
3727 }
3728 break;
3730 f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
3731 f->glGenerateMipmap(cmd.args.genMip.target);
3732 break;
3734 {
3735 QGles2ComputePipeline *psD = QRHI_RES(QGles2ComputePipeline, cmd.args.bindComputePipeline.ps);
3736 f->glUseProgram(psD->program);
3737 }
3738 break;
3740 f->glDispatchCompute(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
3741 break;
3743 {
3744 if (!caps.compute)
3745 break;
3746 GLbitfield barriers = 0;
3747 QRhiPassResourceTracker &tracker(cbD->passResTrackers[cmd.args.barriersForPass.trackerIndex]);
3748 // we only care about after-write, not any other accesses, and
3749 // cannot tell if something was written in a shader several passes
3750 // ago: now the previously written resource may be used with an
3751 // access that was not in the previous passes, result in a missing
3752 // barrier in theory. Hence setting all barrier bits whenever
3753 // something previously written is used for the first time in a
3754 // subsequent pass.
3755 for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
3756 QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(it->stateAtPassBegin.access);
3757 if (bufferAccessIsWrite(accessBeforePass))
3758 barriers |= barriersForBuffer();
3759 }
3760 for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
3761 QGles2Texture::Access accessBeforePass = QGles2Texture::Access(it->stateAtPassBegin.access);
3762 if (textureAccessIsWrite(accessBeforePass))
3763 barriers |= barriersForTexture();
3764 }
3765 if (barriers)
3766 f->glMemoryBarrier(barriers);
3767 }
3768 break;
3770 if (caps.compute)
3771 f->glMemoryBarrier(cmd.args.barrier.barriers);
3772 break;
3774 if (caps.gles && caps.ctxMajor >= 3) {
3775 f->glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
3776 cmd.args.invalidateFramebuffer.attCount,
3777 cmd.args.invalidateFramebuffer.att);
3778 }
3779 break;
3780 default:
3781 break;
3782 }
3783 }
3784 if (state.instancedAttributesUsed) {
3786 if (state.nonzeroAttribDivisor[i])
3787 f->glVertexAttribDivisor(GLuint(i), 0);
3788 }
3789 for (int i = CommandBufferExecTrackedState::TRACKED_ATTRIB_COUNT; i <= state.maxUntrackedInstancedAttribute; ++i)
3790 f->glVertexAttribDivisor(GLuint(i), 0);
3791 }
3792}
3793
3795{
3797 const bool forceUpdate = !state.valid;
3798 state.valid = true;
3799
3800 const bool scissor = psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor);
3801 if (forceUpdate || scissor != state.scissor) {
3802 state.scissor = scissor;
3803 if (scissor)
3804 f->glEnable(GL_SCISSOR_TEST);
3805 else
3806 f->glDisable(GL_SCISSOR_TEST);
3807 }
3808
3809 const bool cullFace = psD->m_cullMode != QRhiGraphicsPipeline::None;
3810 const GLenum cullMode = cullFace ? toGlCullMode(psD->m_cullMode) : GL_NONE;
3811 if (forceUpdate || cullFace != state.cullFace || cullMode != state.cullMode) {
3812 state.cullFace = cullFace;
3813 state.cullMode = cullMode;
3814 if (cullFace) {
3815 f->glEnable(GL_CULL_FACE);
3816 f->glCullFace(cullMode);
3817 } else {
3818 f->glDisable(GL_CULL_FACE);
3819 }
3820 }
3821
3822 const GLenum frontFace = toGlFrontFace(psD->m_frontFace);
3823 if (forceUpdate || frontFace != state.frontFace) {
3824 state.frontFace = frontFace;
3825 f->glFrontFace(frontFace);
3826 }
3827
3828 const GLenum polygonMode = toGlPolygonMode(psD->m_polygonMode);
3829 if (glPolygonMode && (forceUpdate || polygonMode != state.polygonMode)) {
3830 state.polygonMode = polygonMode;
3831 glPolygonMode(GL_FRONT_AND_BACK, polygonMode);
3832 }
3833
3834 if (!psD->m_targetBlends.isEmpty()) {
3835 // We do not have MRT support here, meaning all targets use the blend
3836 // params from the first one. This is technically incorrect, even if
3837 // nothing in Qt relies on it. However, considering that
3838 // glBlendFuncSeparatei is only available in GL 4.0+ and GLES 3.2+, we
3839 // may just live with this for now because no point in bothering if it
3840 // won't be usable on many GLES (3.1 or 3.0) systems.
3841 const QRhiGraphicsPipeline::TargetBlend &targetBlend(psD->m_targetBlends.first());
3842
3844 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::R),
3845 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::G),
3846 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::B),
3847 targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::A)
3848 };
3849 if (forceUpdate || colorMask != state.colorMask) {
3850 state.colorMask = colorMask;
3851 f->glColorMask(colorMask.r, colorMask.g, colorMask.b, colorMask.a);
3852 }
3853
3854 const bool blendEnabled = targetBlend.enable;
3856 toGlBlendFactor(targetBlend.srcColor),
3857 toGlBlendFactor(targetBlend.dstColor),
3858 toGlBlendFactor(targetBlend.srcAlpha),
3859 toGlBlendFactor(targetBlend.dstAlpha),
3860 toGlBlendOp(targetBlend.opColor),
3861 toGlBlendOp(targetBlend.opAlpha)
3862 };
3863 if (forceUpdate || blendEnabled != state.blendEnabled || (blendEnabled && blend != state.blend)) {
3864 state.blendEnabled = blendEnabled;
3865 if (blendEnabled) {
3866 state.blend = blend;
3867 f->glEnable(GL_BLEND);
3868 f->glBlendFuncSeparate(blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha);
3869 f->glBlendEquationSeparate(blend.opColor, blend.opAlpha);
3870 } else {
3871 f->glDisable(GL_BLEND);
3872 }
3873 }
3874 } else {
3875 const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { true, true, true, true };
3876 if (forceUpdate || colorMask != state.colorMask) {
3877 state.colorMask = colorMask;
3878 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
3879 }
3880 const bool blendEnabled = false;
3881 if (forceUpdate || blendEnabled != state.blendEnabled) {
3882 state.blendEnabled = blendEnabled;
3883 f->glDisable(GL_BLEND);
3884 }
3885 }
3886
3887 const bool depthTest = psD->m_depthTest;
3888 if (forceUpdate || depthTest != state.depthTest) {
3889 state.depthTest = depthTest;
3890 if (depthTest)
3891 f->glEnable(GL_DEPTH_TEST);
3892 else
3893 f->glDisable(GL_DEPTH_TEST);
3894 }
3895
3896 const bool depthWrite = psD->m_depthWrite;
3897 if (forceUpdate || depthWrite != state.depthWrite) {
3898 state.depthWrite = depthWrite;
3899 f->glDepthMask(depthWrite);
3900 }
3901
3902 const GLenum depthFunc = toGlCompareOp(psD->m_depthOp);
3903 if (forceUpdate || depthFunc != state.depthFunc) {
3904 state.depthFunc = depthFunc;
3905 f->glDepthFunc(depthFunc);
3906 }
3907
3908 const bool stencilTest = psD->m_stencilTest;
3909 const GLuint stencilReadMask = psD->m_stencilReadMask;
3910 const GLuint stencilWriteMask = psD->m_stencilWriteMask;
3912 toGlCompareOp(psD->m_stencilFront.compareOp),
3913 toGlStencilOp(psD->m_stencilFront.failOp),
3914 toGlStencilOp(psD->m_stencilFront.depthFailOp),
3915 toGlStencilOp(psD->m_stencilFront.passOp)
3916 };
3918 toGlCompareOp(psD->m_stencilBack.compareOp),
3919 toGlStencilOp(psD->m_stencilBack.failOp),
3920 toGlStencilOp(psD->m_stencilBack.depthFailOp),
3921 toGlStencilOp(psD->m_stencilBack.passOp)
3922 };
3923 if (forceUpdate || stencilTest != state.stencilTest
3924 || (stencilTest
3925 && (stencilReadMask != state.stencilReadMask || stencilWriteMask != state.stencilWriteMask
3926 || stencilFront != state.stencil[0] || stencilBack != state.stencil[1])))
3927 {
3928 state.stencilTest = stencilTest;
3929 if (stencilTest) {
3930 state.stencilReadMask = stencilReadMask;
3931 state.stencilWriteMask = stencilWriteMask;
3932 state.stencil[0] = stencilFront;
3933 state.stencil[1] = stencilBack;
3934
3935 f->glEnable(GL_STENCIL_TEST);
3936
3937 f->glStencilFuncSeparate(GL_FRONT, stencilFront.func, state.dynamic.stencilRef, stencilReadMask);
3938 f->glStencilOpSeparate(GL_FRONT, stencilFront.failOp, stencilFront.zfailOp, stencilFront.zpassOp);
3939 f->glStencilMaskSeparate(GL_FRONT, stencilWriteMask);
3940
3941 f->glStencilFuncSeparate(GL_BACK, stencilBack.func, state.dynamic.stencilRef, stencilReadMask);
3942 f->glStencilOpSeparate(GL_BACK, stencilBack.failOp, stencilBack.zfailOp, stencilBack.zpassOp);
3943 f->glStencilMaskSeparate(GL_BACK, stencilWriteMask);
3944 } else {
3945 f->glDisable(GL_STENCIL_TEST);
3946 }
3947 }
3948
3949 const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(psD->m_slopeScaledDepthBias);
3950 const float polyOffsetFactor = psD->m_slopeScaledDepthBias;
3951 const float polyOffsetUnits = psD->m_depthBias;
3952 if (forceUpdate || state.polyOffsetFill != polyOffsetFill
3953 || polyOffsetFactor != state.polyOffsetFactor || polyOffsetUnits != state.polyOffsetUnits)
3954 {
3955 state.polyOffsetFill = polyOffsetFill;
3956 state.polyOffsetFactor = polyOffsetFactor;
3957 state.polyOffsetUnits = polyOffsetUnits;
3958 if (polyOffsetFill) {
3959 f->glPolygonOffset(polyOffsetFactor, polyOffsetUnits);
3960 f->glEnable(GL_POLYGON_OFFSET_FILL);
3961 } else {
3962 f->glDisable(GL_POLYGON_OFFSET_FILL);
3963 }
3964 }
3965
3966 if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) {
3967 const float lineWidth = psD->m_lineWidth;
3968 if (forceUpdate || lineWidth != state.lineWidth) {
3969 state.lineWidth = lineWidth;
3970 f->glLineWidth(lineWidth);
3971 }
3972 }
3973
3974 if (psD->m_topology == QRhiGraphicsPipeline::Patches) {
3975 const int cpCount = psD->m_patchControlPointCount;
3976 if (forceUpdate || cpCount != state.cpCount) {
3977 state.cpCount = cpCount;
3978 f->glPatchParameteri(GL_PATCH_VERTICES, qMax(1, cpCount));
3979 }
3980 }
3981
3982 f->glUseProgram(psD->program);
3983}
3984
3985template <typename T>
3986static inline void qrhi_std140_to_packed(T *dst, int vecSize, int elemCount, const void *src)
3987{
3988 const T *p = reinterpret_cast<const T *>(src);
3989 for (int i = 0; i < elemCount; ++i) {
3990 for (int j = 0; j < vecSize; ++j)
3991 dst[vecSize * i + j] = *p++;
3992 p += 4 - vecSize;
3993 }
3994}
3995
3997 void *ps, uint psGeneration, int glslLocation,
3998 int *texUnit, bool *activeTexUnitAltered)
3999{
4000 const bool samplerStateValid = texD->samplerState == samplerD->d;
4001 const bool cachedStateInRange = *texUnit < 16;
4002 bool updateTextureBinding = true;
4003 if (samplerStateValid && cachedStateInRange) {
4004 // If we already encountered the same texture with
4005 // the same pipeline for this texture unit in the
4006 // current pass, then the shader program already
4007 // has the uniform set. As in a 3D scene one model
4008 // often has more than one associated texture map,
4009 // the savings here can become significant,
4010 // depending on the scene.
4011 if (cbD->textureUnitState[*texUnit].ps == ps
4012 && cbD->textureUnitState[*texUnit].psGeneration == psGeneration
4013 && cbD->textureUnitState[*texUnit].texture == texD->texture)
4014 {
4015 updateTextureBinding = false;
4016 }
4017 }
4018 if (updateTextureBinding) {
4019 f->glActiveTexture(GL_TEXTURE0 + uint(*texUnit));
4020 *activeTexUnitAltered = true;
4021 f->glBindTexture(texD->target, texD->texture);
4022 f->glUniform1i(glslLocation, *texUnit);
4023 if (cachedStateInRange) {
4024 cbD->textureUnitState[*texUnit].ps = ps;
4025 cbD->textureUnitState[*texUnit].psGeneration = psGeneration;
4026 cbD->textureUnitState[*texUnit].texture = texD->texture;
4027 }
4028 }
4029 ++(*texUnit);
4030 if (!samplerStateValid) {
4031 f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter));
4032 f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
4033 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
4034 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
4035 if (caps.texture3D && texD->target == GL_TEXTURE_3D)
4036 f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, GLint(samplerD->d.glwrapr));
4037 if (caps.textureCompareMode) {
4038 if (samplerD->d.gltexcomparefunc != GL_NEVER) {
4039 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
4040 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc));
4041 } else {
4042 f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
4043 }
4044 }
4045 texD->samplerState = samplerD->d;
4046 }
4047}
4048
4050 QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
4052 const uint *dynOfsPairs, int dynOfsCount)
4053{
4055 int texUnit = 1; // start from unit 1, keep 0 for resource mgmt stuff to avoid clashes
4056 bool activeTexUnitAltered = false;
4057 union data32_t {
4058 float f;
4059 qint32 i;
4060 };
4061 QVarLengthArray<data32_t, 256> packedArray;
4062 QGles2UniformDescriptionVector &uniforms(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniforms
4063 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniforms);
4064 QGles2UniformState *uniformState = maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->uniformState
4065 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->uniformState;
4066 struct SeparateTexture {
4068 int binding;
4069 int elem;
4070 };
4071 QVarLengthArray<SeparateTexture, 8> separateTextureBindings;
4072 struct SeparateSampler {
4074 int binding;
4075 };
4076 QVarLengthArray<SeparateSampler, 4> separateSamplerBindings;
4077
4078 for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) {
4079 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
4080
4081 switch (b->type) {
4083 {
4084 int viewOffset = b->u.ubuf.offset;
4085 for (int j = 0; j < dynOfsCount; ++j) {
4086 if (dynOfsPairs[2 * j] == uint(b->binding)) {
4087 viewOffset = int(dynOfsPairs[2 * j + 1]);
4088 break;
4089 }
4090 }
4091 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
4092 const char *bufView = bufD->data.constData() + viewOffset;
4093 for (const QGles2UniformDescription &uniform : std::as_const(uniforms)) {
4094 if (uniform.binding == b->binding) {
4095 // in a uniform buffer everything is at least 4 byte aligned
4096 // so this should not cause unaligned reads
4097 const void *src = bufView + uniform.offset;
4098
4099#ifndef QT_NO_DEBUG
4100 if (uniform.arrayDim > 0
4101 && uniform.type != QShaderDescription::Float
4102 && uniform.type != QShaderDescription::Vec2
4103 && uniform.type != QShaderDescription::Vec3
4104 && uniform.type != QShaderDescription::Vec4
4105 && uniform.type != QShaderDescription::Int
4106 && uniform.type != QShaderDescription::Int2
4107 && uniform.type != QShaderDescription::Int3
4108 && uniform.type != QShaderDescription::Int4
4109 && uniform.type != QShaderDescription::Mat3
4110 && uniform.type != QShaderDescription::Mat4)
4111 {
4112 qWarning("Uniform with buffer binding %d, buffer offset %d, type %d is an array, "
4113 "but arrays are only supported for float, vec2, vec3, vec4, int, "
4114 "ivec2, ivec3, ivec4, mat3 and mat4. "
4115 "Only the first element will be set.",
4116 uniform.binding, uniform.offset, uniform.type);
4117 }
4118#endif
4119
4120 // Our input is an std140 layout uniform block. See
4121 // "Standard Uniform Block Layout" in section 7.6.2.2 of
4122 // the OpenGL spec. This has some peculiar alignment
4123 // requirements, which is not what glUniform* wants. Hence
4124 // the unpacking/repacking for arrays and certain types.
4125
4126 switch (uniform.type) {
4128 {
4129 const int elemCount = uniform.arrayDim;
4130 if (elemCount < 1) {
4131 const float v = *reinterpret_cast<const float *>(src);
4132 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4133 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4134 if (thisUniformState.componentCount != 1 || thisUniformState.v[0] != v) {
4135 thisUniformState.componentCount = 1;
4136 thisUniformState.v[0] = v;
4137 f->glUniform1f(uniform.glslLocation, v);
4138 }
4139 } else {
4140 f->glUniform1f(uniform.glslLocation, v);
4141 }
4142 } else {
4143 // input is 16 bytes per element as per std140, have to convert to packed
4144 packedArray.resize(elemCount);
4145 qrhi_std140_to_packed(&packedArray.data()->f, 1, elemCount, src);
4146 f->glUniform1fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
4147 }
4148 }
4149 break;
4151 {
4152 const int elemCount = uniform.arrayDim;
4153 if (elemCount < 1) {
4154 const float *v = reinterpret_cast<const float *>(src);
4155 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4156 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4157 if (thisUniformState.componentCount != 2
4158 || thisUniformState.v[0] != v[0]
4159 || thisUniformState.v[1] != v[1])
4160 {
4161 thisUniformState.componentCount = 2;
4162 thisUniformState.v[0] = v[0];
4163 thisUniformState.v[1] = v[1];
4164 f->glUniform2fv(uniform.glslLocation, 1, v);
4165 }
4166 } else {
4167 f->glUniform2fv(uniform.glslLocation, 1, v);
4168 }
4169 } else {
4170 packedArray.resize(elemCount * 2);
4171 qrhi_std140_to_packed(&packedArray.data()->f, 2, elemCount, src);
4172 f->glUniform2fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
4173 }
4174 }
4175 break;
4177 {
4178 const int elemCount = uniform.arrayDim;
4179 if (elemCount < 1) {
4180 const float *v = reinterpret_cast<const float *>(src);
4181 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4182 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4183 if (thisUniformState.componentCount != 3
4184 || thisUniformState.v[0] != v[0]
4185 || thisUniformState.v[1] != v[1]
4186 || thisUniformState.v[2] != v[2])
4187 {
4188 thisUniformState.componentCount = 3;
4189 thisUniformState.v[0] = v[0];
4190 thisUniformState.v[1] = v[1];
4191 thisUniformState.v[2] = v[2];
4192 f->glUniform3fv(uniform.glslLocation, 1, v);
4193 }
4194 } else {
4195 f->glUniform3fv(uniform.glslLocation, 1, v);
4196 }
4197 } else {
4198 packedArray.resize(elemCount * 3);
4199 qrhi_std140_to_packed(&packedArray.data()->f, 3, elemCount, src);
4200 f->glUniform3fv(uniform.glslLocation, elemCount, &packedArray.constData()->f);
4201 }
4202 }
4203 break;
4205 {
4206 const int elemCount = uniform.arrayDim;
4207 if (elemCount < 1) {
4208 const float *v = reinterpret_cast<const float *>(src);
4209 if (uniform.glslLocation <= QGles2UniformState::MAX_TRACKED_LOCATION) {
4210 QGles2UniformState &thisUniformState(uniformState[uniform.glslLocation]);
4211 if (thisUniformState.componentCount != 4
4212 || thisUniformState.v[0] != v[0]
4213 || thisUniformState.v[1] != v[1]
4214 || thisUniformState.v[2] != v[2]
4215 || thisUniformState.v[3] != v[3])
4216 {
4217 thisUniformState.componentCount = 4;
4218 thisUniformState.v[0] = v[0];
4219 thisUniformState.v[1] = v[1];
4220 thisUniformState.v[2] = v[2];
4221 thisUniformState.v[3] = v[3];
4222 f->glUniform4fv(uniform.glslLocation, 1, v);
4223 }
4224 } else {
4225 f->glUniform4fv(uniform.glslLocation, 1, v);
4226 }
4227 } else {
4228 f->glUniform4fv(uniform.glslLocation, elemCount, reinterpret_cast<const float *>(src));
4229 }
4230 }
4231 break;
4233 f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
4234 break;
4236 {
4237 const int elemCount = uniform.arrayDim;
4238 if (elemCount < 1) {
4239 // 4 floats per column (or row, if row-major)
4240 float mat[9];
4241 const float *srcMat = reinterpret_cast<const float *>(src);
4242 memcpy(mat, srcMat, 3 * sizeof(float));
4243 memcpy(mat + 3, srcMat + 4, 3 * sizeof(float));
4244 memcpy(mat + 6, srcMat + 8, 3 * sizeof(float));
4245 f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, mat);
4246 } else {
4247 packedArray.resize(elemCount * 9);
4248 qrhi_std140_to_packed(&packedArray.data()->f, 3, elemCount * 3, src);
4249 f->glUniformMatrix3fv(uniform.glslLocation, elemCount, GL_FALSE, &packedArray.constData()->f);
4250 }
4251 }
4252 break;
4254 f->glUniformMatrix4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), GL_FALSE, reinterpret_cast<const float *>(src));
4255 break;
4257 {
4258 const int elemCount = uniform.arrayDim;
4259 if (elemCount < 1) {
4260 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
4261 } else {
4262 packedArray.resize(elemCount);
4263 qrhi_std140_to_packed(&packedArray.data()->i, 1, elemCount, src);
4264 f->glUniform1iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
4265 }
4266 }
4267 break;
4269 {
4270 const int elemCount = uniform.arrayDim;
4271 if (elemCount < 1) {
4272 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4273 } else {
4274 packedArray.resize(elemCount * 2);
4275 qrhi_std140_to_packed(&packedArray.data()->i, 2, elemCount, src);
4276 f->glUniform2iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
4277 }
4278 }
4279 break;
4281 {
4282 const int elemCount = uniform.arrayDim;
4283 if (elemCount < 1) {
4284 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4285 } else {
4286 packedArray.resize(elemCount * 3);
4287 qrhi_std140_to_packed(&packedArray.data()->i, 3, elemCount, src);
4288 f->glUniform3iv(uniform.glslLocation, elemCount, &packedArray.constData()->i);
4289 }
4290 }
4291 break;
4293 f->glUniform4iv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const qint32 *>(src));
4294 break;
4296 f->glUniform1ui(uniform.glslLocation, *reinterpret_cast<const quint32 *>(src));
4297 break;
4299 f->glUniform2uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
4300 break;
4302 f->glUniform3uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
4303 break;
4305 f->glUniform4uiv(uniform.glslLocation, 1, reinterpret_cast<const quint32 *>(src));
4306 break;
4307 case QShaderDescription::Bool: // a glsl bool is 4 bytes, like (u)int
4308 f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
4309 break;
4311 f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4312 break;
4314 f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4315 break;
4317 f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
4318 break;
4319 default:
4320 qWarning("Uniform with buffer binding %d, buffer offset %d has unsupported type %d",
4321 uniform.binding, uniform.offset, uniform.type);
4322 break;
4323 }
4324 }
4325 }
4326 }
4327 break;
4329 {
4330 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
4331 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
4332 void *ps;
4333 uint psGeneration;
4334 if (maybeGraphicsPs) {
4335 ps = maybeGraphicsPs;
4336 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
4337 } else {
4338 ps = maybeComputePs;
4339 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
4340 }
4341 for (int elem = 0; elem < b->u.stex.count; ++elem) {
4342 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex);
4343 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[elem].sampler);
4344 for (const QGles2SamplerDescription &shaderSampler : samplers) {
4345 if (shaderSampler.combinedBinding == b->binding) {
4346 const int loc = shaderSampler.glslLocation + elem;
4347 bindCombinedSampler(cbD, texD, samplerD, ps, psGeneration, loc, &texUnit, &activeTexUnitAltered);
4348 break;
4349 }
4350 }
4351 }
4352 }
4353 break;
4355 for (int elem = 0; elem < b->u.stex.count; ++elem) {
4356 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex);
4357 separateTextureBindings.append({ texD, b->binding, elem });
4358 }
4359 break;
4361 {
4362 QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[0].sampler);
4363 separateSamplerBindings.append({ samplerD, b->binding });
4364 }
4365 break;
4369 {
4370 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
4371 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
4372 const bool layered = texD->m_flags.testFlag(QRhiTexture::CubeMap);
4376 else if (b->type == QRhiShaderResourceBinding::ImageStore)
4378 f->glBindImageTexture(GLuint(b->binding), texD->texture,
4379 b->u.simage.level, layered, 0,
4380 access, texD->glsizedintformat);
4381 }
4382 break;
4386 {
4387 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
4388 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
4389 if (b->u.sbuf.offset == 0 && b->u.sbuf.maybeSize == 0)
4390 f->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer);
4391 else
4392 f->glBindBufferRange(GL_SHADER_STORAGE_BUFFER, GLuint(b->binding), bufD->buffer,
4393 b->u.sbuf.offset, b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->m_size);
4394 }
4395 break;
4396 default:
4397 Q_UNREACHABLE();
4398 break;
4399 }
4400 }
4401
4402 if (!separateTextureBindings.isEmpty() || !separateSamplerBindings.isEmpty()) {
4403 const QGles2SamplerDescriptionVector &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers
4404 : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers);
4405 void *ps;
4406 uint psGeneration;
4407 if (maybeGraphicsPs) {
4408 ps = maybeGraphicsPs;
4409 psGeneration = QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->generation;
4410 } else {
4411 ps = maybeComputePs;
4412 psGeneration = QRHI_RES(QGles2ComputePipeline, maybeComputePs)->generation;
4413 }
4414 for (const QGles2SamplerDescription &shaderSampler : samplers) {
4415 if (shaderSampler.combinedBinding >= 0)
4416 continue;
4417 for (const SeparateSampler &sepSampler : separateSamplerBindings) {
4418 if (sepSampler.binding != shaderSampler.sbinding)
4419 continue;
4420 for (const SeparateTexture &sepTex : separateTextureBindings) {
4421 if (sepTex.binding != shaderSampler.tbinding)
4422 continue;
4423 const int loc = shaderSampler.glslLocation + sepTex.elem;
4424 bindCombinedSampler(cbD, sepTex.texture, sepSampler.sampler, ps, psGeneration,
4425 loc, &texUnit, &activeTexUnitAltered);
4426 }
4427 }
4428 }
4429 }
4430
4431 if (activeTexUnitAltered)
4432 f->glActiveTexture(GL_TEXTURE0);
4433}
4434
4441
4443 bool *wantsColorClear, bool *wantsDsClear)
4444{
4445 QGles2RenderTargetData *rtD = nullptr;
4447
4448 QGles2CommandBuffer::Command &fbCmd(cbD->commands.get());
4450
4451 static const bool doClearBuffers = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_BUFFERS") == 0;
4452 static const bool doClearColorBuffer = qEnvironmentVariableIntValue("QT_GL_NO_CLEAR_COLOR_BUFFER") == 0;
4453
4454 switch (rt->resourceType()) {
4457 if (wantsColorClear)
4458 *wantsColorClear = doClearBuffers && doClearColorBuffer;
4459 if (wantsDsClear)
4460 *wantsDsClear = doClearBuffers;
4461 fbCmd.args.bindFramebuffer.fbo = 0;
4462 fbCmd.args.bindFramebuffer.colorAttCount = 1;
4463 fbCmd.args.bindFramebuffer.stereo = rtD->stereoTarget.has_value();
4464 if (fbCmd.args.bindFramebuffer.stereo)
4465 fbCmd.args.bindFramebuffer.stereoTarget = rtD->stereoTarget.value();
4466 break;
4468 {
4470 rtD = &rtTex->d;
4471 if (wantsColorClear)
4472 *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4473 if (wantsDsClear)
4474 *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4475 fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
4476 fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
4477 fbCmd.args.bindFramebuffer.stereo = false;
4478
4479 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4480 it != itEnd; ++it)
4481 {
4482 const QRhiColorAttachment &colorAtt(*it);
4483 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
4484 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
4485 if (texD && cbD->passNeedsResourceTracking) {
4486 trackedRegisterTexture(&passResTracker, texD,
4489 }
4490 if (resolveTexD && cbD->passNeedsResourceTracking) {
4491 trackedRegisterTexture(&passResTracker, resolveTexD,
4494 }
4495 // renderbuffers cannot be written in shaders (no image store) so
4496 // they do not matter here
4497 }
4498 if (rtTex->m_desc.depthTexture() && cbD->passNeedsResourceTracking) {
4499 trackedRegisterTexture(&passResTracker, QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()),
4502 }
4503 }
4504 break;
4505 default:
4506 Q_UNREACHABLE();
4507 break;
4508 }
4509
4510 fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
4511
4512 return rtD;
4513}
4514
4523
4525 QRhiRenderTarget *rt,
4526 const QColor &colorClearValue,
4527 const QRhiDepthStencilClearValue &depthStencilClearValue,
4528 QRhiResourceUpdateBatch *resourceUpdates,
4529 QRhiCommandBuffer::BeginPassFlags flags)
4530{
4533
4534 if (resourceUpdates)
4535 enqueueResourceUpdates(cb, resourceUpdates);
4536
4537 // Get a new resource tracker. Then add a command that will generate
4538 // glMemoryBarrier() calls based on that tracker when submitted.
4540
4543 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(rtTex->description(), rtTex->d.currentResIdList))
4544 rtTex->create();
4545 }
4546
4547 bool wantsColorClear, wantsDsClear;
4548 QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
4549
4550 QGles2CommandBuffer::Command &clearCmd(cbD->commands.get());
4552 clearCmd.args.clear.mask = 0;
4553 if (rtD->colorAttCount && wantsColorClear)
4554 clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
4555 if (rtD->dsAttCount && wantsDsClear)
4556 clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
4557 clearCmd.args.clear.c[0] = float(colorClearValue.redF());
4558 clearCmd.args.clear.c[1] = float(colorClearValue.greenF());
4559 clearCmd.args.clear.c[2] = float(colorClearValue.blueF());
4560 clearCmd.args.clear.c[3] = float(colorClearValue.alphaF());
4561 clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
4562 clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
4563
4566 cbD->currentTarget = rt;
4567
4568 cbD->resetCachedState();
4569}
4570
4572{
4575
4578 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4579 it != itEnd; ++it)
4580 {
4581 const QRhiColorAttachment &colorAtt(*it);
4582 if (!colorAtt.resolveTexture())
4583 continue;
4584
4585 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
4586 const QSize size = resolveTexD->pixelSize();
4587 if (colorAtt.renderBuffer()) {
4589 if (rbD->pixelSize() != size) {
4590 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
4591 rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
4592 }
4593 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4595 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
4596 cmd.args.blitFromRenderbuffer.w = size.width();
4597 cmd.args.blitFromRenderbuffer.h = size.height();
4598 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
4599 cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
4600 else
4601 cmd.args.blitFromRenderbuffer.target = resolveTexD->target;
4602 cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture;
4603 cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel();
4604 const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
4605 || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray);
4606 cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
4607 cmd.args.blitFromRenderbuffer.isDepthStencil = false;
4608 } else if (caps.glesMultisampleRenderToTexture) {
4609 // Nothing to do, resolving into colorAtt.resolveTexture() is automatic,
4610 // colorAtt.texture() is in fact not used for anything.
4611 } else {
4612 Q_ASSERT(colorAtt.texture());
4613 QGles2Texture *texD = QRHI_RES(QGles2Texture, colorAtt.texture());
4614 if (texD->pixelSize() != size) {
4615 qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
4616 texD->pixelSize().width(), texD->pixelSize().height(), size.width(), size.height());
4617 }
4618 const int resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1;
4619 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
4620 const int srcLayer = colorAtt.layer() + resolveIdx;
4621 const int dstLayer = colorAtt.resolveLayer() + resolveIdx;
4622 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4624 if (texD->m_flags.testFlag(QRhiTexture::CubeMap))
4625 cmd.args.blitFromTexture.srcTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(srcLayer);
4626 else
4627 cmd.args.blitFromTexture.srcTarget = texD->target;
4628 cmd.args.blitFromTexture.srcTexture = texD->texture;
4629 cmd.args.blitFromTexture.srcLevel = colorAtt.level();
4630 cmd.args.blitFromTexture.srcLayer = 0;
4631 if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || texD->m_flags.testFlag(QRhiTexture::TextureArray))
4632 cmd.args.blitFromTexture.srcLayer = srcLayer;
4633 cmd.args.blitFromTexture.w = size.width();
4634 cmd.args.blitFromTexture.h = size.height();
4635 if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap))
4636 cmd.args.blitFromTexture.dstTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(dstLayer);
4637 else
4638 cmd.args.blitFromTexture.dstTarget = resolveTexD->target;
4639 cmd.args.blitFromTexture.dstTexture = resolveTexD->texture;
4640 cmd.args.blitFromTexture.dstLevel = colorAtt.resolveLevel();
4641 cmd.args.blitFromTexture.dstLayer = 0;
4642 if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray))
4643 cmd.args.blitFromTexture.dstLayer = dstLayer;
4644 cmd.args.blitFromTexture.isDepthStencil = false;
4645 }
4646 }
4647 }
4648
4649 if (rtTex->m_desc.depthResolveTexture()) {
4650 QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture());
4651 const QSize size = depthResolveTexD->pixelSize();
4652 if (rtTex->m_desc.depthStencilBuffer()) {
4653 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer());
4654 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4656 cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
4657 cmd.args.blitFromRenderbuffer.w = size.width();
4658 cmd.args.blitFromRenderbuffer.h = size.height();
4659 cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target;
4660 cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture;
4661 cmd.args.blitFromRenderbuffer.dstLevel = 0;
4662 cmd.args.blitFromRenderbuffer.dstLayer = 0;
4663 cmd.args.blitFromRenderbuffer.isDepthStencil = true;
4664 } else if (caps.glesMultisampleRenderToTexture) {
4665 // Nothing to do, resolving into depthResolveTexture() is automatic.
4666 } else {
4667 QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture());
4668 const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1;
4669 for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
4670 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4672 cmd.args.blitFromTexture.srcTarget = depthTexD->target;
4673 cmd.args.blitFromTexture.srcTexture = depthTexD->texture;
4674 cmd.args.blitFromTexture.srcLevel = 0;
4675 cmd.args.blitFromTexture.srcLayer = resolveIdx;
4676 cmd.args.blitFromTexture.w = size.width();
4677 cmd.args.blitFromTexture.h = size.height();
4678 cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target;
4679 cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture;
4680 cmd.args.blitFromTexture.dstLevel = 0;
4681 cmd.args.blitFromTexture.dstLayer = resolveIdx;
4682 cmd.args.blitFromTexture.isDepthStencil = true;
4683 }
4684 }
4685 }
4686
4687 const bool mayDiscardDepthStencil =
4688 (rtTex->m_desc.depthStencilBuffer()
4689 || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)))
4690 && !rtTex->m_desc.depthResolveTexture();
4691 if (mayDiscardDepthStencil) {
4692 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4694 if (caps.needsDepthStencilCombinedAttach) {
4695 cmd.args.invalidateFramebuffer.attCount = 1;
4696 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_STENCIL_ATTACHMENT;
4697 } else {
4698 cmd.args.invalidateFramebuffer.attCount = 2;
4699 cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_ATTACHMENT;
4700 cmd.args.invalidateFramebuffer.att[1] = GL_STENCIL_ATTACHMENT;
4701 }
4702 }
4703 }
4704
4706 cbD->currentTarget = nullptr;
4707
4708 if (resourceUpdates)
4709 enqueueResourceUpdates(cb, resourceUpdates);
4710}
4711
4713 QRhiResourceUpdateBatch *resourceUpdates,
4714 QRhiCommandBuffer::BeginPassFlags)
4715{
4718
4719 if (resourceUpdates)
4720 enqueueResourceUpdates(cb, resourceUpdates);
4721
4723
4725
4726 cbD->resetCachedState();
4727}
4728
4739
4741{
4745 const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
4746
4747 if (pipelineChanged) {
4748 cbD->currentGraphicsPipeline = nullptr;
4749 cbD->currentComputePipeline = ps;
4750 cbD->currentPipelineGeneration = psD->generation;
4751
4752 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4754 cmd.args.bindComputePipeline.ps = ps;
4755 }
4756}
4757
4758template<typename T>
4759inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
4761 int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
4762{
4763 int access = 0;
4764 if (bindingType == loadTypeVal) {
4766 } else {
4768 if (bindingType == loadStoreTypeVal)
4770 }
4771 auto it = writtenResources->find(resource);
4772 if (it != writtenResources->end())
4773 it->first |= access;
4774 else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
4775 writtenResources->insert(resource, { access, true });
4776}
4777
4779{
4782
4783 if (cbD->currentComputeSrb) {
4784 GLbitfield barriers = 0;
4785
4786 // The key in the writtenResources map indicates that the resource was
4787 // written in a previous dispatch, whereas the value accumulates the
4788 // access mask in the current one.
4789 for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
4790 accessAndIsNewFlag = { 0, false };
4791
4793 const int bindingCount = srbD->m_bindings.size();
4794 for (int i = 0; i < bindingCount; ++i) {
4795 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings.at(i));
4796 switch (b->type) {
4801 b->u.simage.tex,
4802 b->type,
4806 break;
4811 b->u.sbuf.buf,
4812 b->type,
4816 break;
4817 default:
4818 break;
4819 }
4820 }
4821
4822 for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
4823 const int accessInThisDispatch = it->first;
4824 const bool isNewInThisDispatch = it->second;
4825 if (accessInThisDispatch && !isNewInThisDispatch) {
4826 if (it.key()->resourceType() == QRhiResource::Texture)
4828 else
4830 }
4831 // Anything that was previously written, but is only read now, can be
4832 // removed from the written list (because that previous write got a
4833 // corresponding barrier now).
4834 if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
4835 it = cbD->computePassState.writtenResources.erase(it);
4836 else
4837 ++it;
4838 }
4839
4840 if (barriers) {
4841 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4843 cmd.args.barrier.barriers = barriers;
4844 }
4845 }
4846
4847 QGles2CommandBuffer::Command &cmd(cbD->commands.get());
4849 cmd.args.dispatch.x = GLuint(x);
4850 cmd.args.dispatch.y = GLuint(y);
4851 cmd.args.dispatch.z = GLuint(z);
4852}
4853
4855{
4856 switch (type) {
4858 return GL_VERTEX_SHADER;
4864 return GL_GEOMETRY_SHADER;
4866 return GL_FRAGMENT_SHADER;
4868 return GL_COMPUTE_SHADER;
4869 default:
4870 Q_UNREACHABLE_RETURN(GL_VERTEX_SHADER);
4871 }
4872}
4873
4875{
4876 const QShader bakedShader = shaderStage.shader();
4877 QList<int> versionsToTry;
4879 if (caps.gles) {
4880 if (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2)) {
4881 versionsToTry << 320 << 310 << 300 << 100;
4882 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
4883 versionsToTry << 310 << 300 << 100;
4884 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
4885 versionsToTry << 300 << 100;
4886 } else {
4887 versionsToTry << 100;
4888 }
4889 for (int v : versionsToTry) {
4891 source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
4892 if (!source.isEmpty()) {
4893 if (shaderVersion)
4894 *shaderVersion = ver;
4895 break;
4896 }
4897 }
4898 } else {
4899 if (caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 6)) {
4900 versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4901 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 5) {
4902 versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4903 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 4) {
4904 versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4905 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 3) {
4906 versionsToTry << 430 << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4907 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 2) {
4908 versionsToTry << 420 << 410 << 400 << 330 << 150 << 140 << 130;
4909 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 1) {
4910 versionsToTry << 410 << 400 << 330 << 150 << 140 << 130;
4911 } else if (caps.ctxMajor == 4 && caps.ctxMinor == 0) {
4912 versionsToTry << 400 << 330 << 150 << 140 << 130;
4913 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 3) {
4914 versionsToTry << 330 << 150 << 140 << 130;
4915 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 2) {
4916 versionsToTry << 150 << 140 << 130;
4917 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 1) {
4918 versionsToTry << 140 << 130;
4919 } else if (caps.ctxMajor == 3 && caps.ctxMinor == 0) {
4920 versionsToTry << 130;
4921 }
4922 if (!caps.coreProfile)
4923 versionsToTry << 120;
4924 for (int v : versionsToTry) {
4925 source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
4926 if (!source.isEmpty()) {
4927 if (shaderVersion)
4928 *shaderVersion = v;
4929 break;
4930 }
4931 }
4932 }
4933 if (source.isEmpty()) {
4934 qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
4935 << ") in baked shader" << bakedShader;
4936 }
4937 return source;
4938}
4939
4941{
4942 const QByteArray source = shaderSource(shaderStage, shaderVersion);
4943 if (source.isEmpty())
4944 return false;
4945
4946 GLuint shader;
4947 auto cacheIt = m_shaderCache.constFind(shaderStage);
4948 if (cacheIt != m_shaderCache.constEnd()) {
4949 shader = *cacheIt;
4950 } else {
4951 shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
4952 const char *srcStr = source.constData();
4953 const GLint srcLength = source.size();
4954 f->glShaderSource(shader, 1, &srcStr, &srcLength);
4955 f->glCompileShader(shader);
4956 GLint compiled = 0;
4957 f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
4958 if (!compiled) {
4959 GLint infoLogLength = 0;
4960 f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
4961 QByteArray log;
4962 if (infoLogLength > 1) {
4963 GLsizei length = 0;
4964 log.resize(infoLogLength);
4965 f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
4966 }
4967 qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
4968 return false;
4969 }
4971 // Use the simplest strategy: too many cached shaders -> drop them all.
4972 for (uint shader : m_shaderCache)
4973 f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
4975 }
4976 m_shaderCache.insert(shaderStage, shader);
4977 }
4978
4979 f->glAttachShader(program, shader);
4980
4981 return true;
4982}
4983
4985{
4986 f->glLinkProgram(program);
4987 GLint linked = 0;
4988 f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
4989 if (!linked) {
4990 GLint infoLogLength = 0;
4991 f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
4992 QByteArray log;
4993 if (infoLogLength > 1) {
4994 GLsizei length = 0;
4995 log.resize(infoLogLength);
4996 f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
4997 }
4998 qWarning("Failed to link shader program: %s", log.constData());
4999 return false;
5000 }
5001 return true;
5002}
5003
5005 const QByteArray &namePrefix,
5006 int binding,
5007 int baseOffset,
5009 QDuplicateTracker<int, 256> *activeUniformLocations,
5011{
5012 if (var.type == QShaderDescription::Struct) {
5013 qWarning("Nested structs are not supported at the moment. '%s' ignored.",
5014 var.name.constData());
5015 return;
5016 }
5018 uniform.type = var.type;
5019 const QByteArray name = namePrefix + var.name;
5020 // Here we expect that the OpenGL implementation has proper active uniform
5021 // handling, meaning that a uniform that is declared but not accessed
5022 // elsewhere in the code is reported as -1 when querying the location. If
5023 // that is not the case, it won't break anything, but we'll generate
5024 // unnecessary glUniform* calls then.
5025 uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
5026 if (uniform.glslLocation >= 0 && !activeUniformLocations->hasSeen(uniform.glslLocation)) {
5027 if (var.arrayDims.size() > 1) {
5028 qWarning("Array '%s' has more than one dimension. This is not supported.",
5029 var.name.constData());
5030 return;
5031 }
5032 uniform.binding = binding;
5033 uniform.offset = uint(baseOffset + var.offset);
5034 uniform.size = var.size;
5035 uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
5036 dst->append(uniform);
5037 }
5038}
5039
5042 QDuplicateTracker<int, 256> *activeUniformLocations,
5044{
5045 QByteArray prefix = ub.structName + '.';
5046 for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
5047 if (blockMember.type == QShaderDescription::Struct) {
5048 QByteArray structPrefix = prefix + blockMember.name;
5049
5050 const int baseOffset = blockMember.offset;
5051 if (blockMember.arrayDims.isEmpty()) {
5052 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
5053 registerUniformIfActive(structMember, structPrefix + ".", ub.binding,
5054 baseOffset, program, activeUniformLocations, dst);
5055 } else {
5056 if (blockMember.arrayDims.size() > 1) {
5057 qWarning("Array of struct '%s' has more than one dimension. Only the first "
5058 "dimension is used.",
5059 blockMember.name.constData());
5060 }
5061 const int dim = blockMember.arrayDims.first();
5062 const int elemSize = blockMember.size / dim;
5063 int elemOffset = baseOffset;
5064 for (int di = 0; di < dim; ++di) {
5065 const QByteArray arrayPrefix = structPrefix + '[' + QByteArray::number(di) + ']' + '.';
5066 for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
5067 registerUniformIfActive(structMember, arrayPrefix, ub.binding, elemOffset, program, activeUniformLocations, dst);
5068 elemOffset += elemSize;
5069 }
5070 }
5071 } else {
5072 registerUniformIfActive(blockMember, prefix, ub.binding, 0, program, activeUniformLocations, dst);
5073 }
5074 }
5075}
5076
5080{
5082 sampler.glslLocation = f->glGetUniformLocation(program, v.name.constData());
5083 if (sampler.glslLocation >= 0) {
5084 sampler.combinedBinding = v.binding;
5085 sampler.tbinding = -1;
5086 sampler.sbinding = -1;
5087 dst->append(sampler);
5088 }
5089}
5090
5094{
5096 sampler.glslLocation = f->glGetUniformLocation(program, mapping.combinedSamplerName.constData());
5097 if (sampler.glslLocation >= 0) {
5098 sampler.combinedBinding = -1;
5099 sampler.tbinding = mapping.textureBinding;
5100 sampler.sbinding = mapping.samplerBinding;
5101 dst->append(sampler);
5102 }
5103}
5104
5106{
5107 if (!vsDesc.isValid() || !fsDesc.isValid())
5108 return;
5109
5110 // Print a warning if the fragment shader input for a given location uses a
5111 // name that does not match the vertex shader output at the same location.
5112 // This is not an error with any other API and not with GLSL >= 330 either,
5113 // but matters for older GLSL code that has no location qualifiers.
5114 for (const QShaderDescription::InOutVariable &outVar : vsDesc.outputVariables()) {
5115 for (const QShaderDescription::InOutVariable &inVar : fsDesc.inputVariables()) {
5116 if (inVar.location == outVar.location) {
5117 if (inVar.name != outVar.name) {
5118 qWarning("Vertex output name '%s' does not match fragment input '%s'. "
5119 "This should be avoided because it causes problems with older GLSL versions.",
5120 outVar.name.constData(), inVar.name.constData());
5121 }
5122 break;
5123 }
5124 }
5125 }
5126}
5127
5129{
5131 return checker.get(ctx)->isSupported();
5132}
5133
5135
5155
5157 int stageCount,
5159 const QVector<QShaderDescription::InOutVariable> &inputVars,
5161{
5163
5164 // the traditional QOpenGL disk cache since Qt 5.9
5165 const bool legacyDiskCacheEnabled = isProgramBinaryDiskCacheEnabled();
5166
5167 // QRhi's own (set)PipelineCacheData()
5168 const bool pipelineCacheEnabled = caps.programBinary && !m_pipelineCache.isEmpty();
5169
5170 // calculating the cache key based on the source code is common for both types of caches
5171 if (legacyDiskCacheEnabled || pipelineCacheEnabled) {
5173 for (int i = 0; i < stageCount; ++i) {
5174 const QRhiShaderStage &stage(stages[i]);
5175 QByteArray source = shaderSource(stage, nullptr);
5176 if (source.isEmpty())
5178
5179 if (stage.type() == QRhiShaderStage::Vertex) {
5180 // Now add something to the key that indicates the vertex input locations.
5181 // A GLSL shader lower than 330 (150, 140, ...) will not have location
5182 // qualifiers. This means that the shader source code is the same
5183 // regardless of what locations inputVars contains. This becomes a problem
5184 // because we'll glBindAttribLocation the shader based on inputVars, but
5185 // that's only when compiling/linking when there was no cache hit. Picking
5186 // from the cache afterwards should take the input locations into account
5187 // since if inputVars has now different locations for the attributes, then
5188 // it is not ok to reuse a program binary that used different attribute
5189 // locations. For a lot of clients this would not be an issue since they
5190 // typically hardcode and use the same vertex locations on every run. Some
5191 // systems that dynamically generate shaders may end up with a non-stable
5192 // order (and so location numbers), however. This is sub-optimal because
5193 // it makes caching inefficient, and said clients should be fixed, but in
5194 // any case this should not break rendering. Hence including the locations
5195 // in the cache key.
5196 QMap<QByteArray, int> inputLocations; // sorted by key when iterating
5197 for (const QShaderDescription::InOutVariable &var : inputVars)
5198 inputLocations.insert(var.name, var.location);
5199 source += QByteArrayLiteral("\n // "); // just to be nice; treated as an arbitrary string regardless
5200 for (auto it = inputLocations.cbegin(), end = inputLocations.cend(); it != end; ++it) {
5201 source += it.key();
5202 source += QByteArray::number(it.value());
5203 }
5204 source += QByteArrayLiteral("\n");
5205 }
5206
5208 }
5209
5210 *cacheKey = binaryProgram.cacheKey();
5211
5212 // Try our pipeline cache simulation first, if it got seeded with
5213 // setPipelineCacheData and there's a hit, then no need to go to the
5214 // filesystem at all.
5215 if (pipelineCacheEnabled) {
5216 auto it = m_pipelineCache.constFind(*cacheKey);
5217 if (it != m_pipelineCache.constEnd()) {
5218 GLenum err;
5219 for ( ; ; ) {
5220 err = f->glGetError();
5221 if (err == GL_NO_ERROR || err == GL_CONTEXT_LOST)
5222 break;
5223 }
5224 f->glProgramBinary(program, it->format, it->data.constData(), it->data.size());
5225 err = f->glGetError();
5226 if (err == GL_NO_ERROR) {
5227 GLint linkStatus = 0;
5228 f->glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
5229 if (linkStatus == GL_TRUE)
5231 }
5232 }
5233 }
5234
5235 if (legacyDiskCacheEnabled && qrhi_programBinaryCache()->load(*cacheKey, program)) {
5236 // use the logging category QOpenGLShaderProgram would
5237 qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
5238 program, cacheKey->constData());
5240 }
5241 }
5242
5244}
5245
5247{
5248 // This is only for the traditional QOpenGL disk cache since Qt 5.9.
5249
5251 // use the logging category QOpenGLShaderProgram would
5252 qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
5253 program, cacheKey.constData());
5254 qrhi_programBinaryCache()->save(cacheKey, program);
5255 }
5256}
5257
5259{
5260 // This handles our own simulated "pipeline cache". (specific to QRhi, not
5261 // shared with legacy QOpenGL* stuff)
5262
5263 if (caps.programBinary && (force || !m_pipelineCache.contains(cacheKey))) {
5264 GLint blobSize = 0;
5265 f->glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &blobSize);
5266 QByteArray blob(blobSize, Qt::Uninitialized);
5267 GLint outSize = 0;
5268 GLenum binaryFormat = 0;
5269 f->glGetProgramBinary(program, blobSize, &outSize, &binaryFormat, blob.data());
5270 if (blobSize == outSize)
5271 m_pipelineCache.insert(cacheKey, { binaryFormat, blob });
5272 }
5273}
5274
5279
5284
5286{
5287 data.clear();
5288 if (!buffer)
5289 return;
5290
5293
5294 e.buffer.buffer = buffer;
5295 buffer = 0;
5296
5298 if (rhiD) {
5299 rhiD->releaseQueue.append(e);
5300 rhiD->unregisterResource(this);
5301 }
5302}
5303
5305{
5306 if (buffer)
5307 destroy();
5308
5310
5311 nonZeroSize = m_size <= 0 ? 256 : m_size;
5312
5313 if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
5314 if (int(m_usage) != QRhiBuffer::UniformBuffer) {
5315 qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
5316 return false;
5317 }
5318 data.resize(nonZeroSize);
5319 return true;
5320 }
5321
5322 if (!rhiD->ensureContext())
5323 return false;
5324
5326 if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
5328 else if (m_usage.testFlag(QRhiBuffer::StorageBuffer))
5330
5331 rhiD->f->glGenBuffers(1, &buffer);
5332 rhiD->f->glBindBuffer(targetForDataOps, buffer);
5333 rhiD->f->glBufferData(targetForDataOps, nonZeroSize, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
5334
5335 if (rhiD->glObjectLabel)
5336 rhiD->glObjectLabel(GL_BUFFER, buffer, -1, m_objectName.constData());
5337
5339
5340 rhiD->registerResource(this);
5341 return true;
5342}
5343
5345{
5346 if (m_usage.testFlag(QRhiBuffer::UniformBuffer))
5347 return { {}, 0 };
5348
5349 return { { &buffer }, 1 };
5350}
5351
5353{
5355 if (!m_usage.testFlag(UniformBuffer)) {
5357 rhiD->f->glBindBuffer(targetForDataOps, buffer);
5358 if (rhiD->caps.properMapBuffer) {
5359 return static_cast<char *>(rhiD->f->glMapBufferRange(targetForDataOps, 0, nonZeroSize,
5361 } else {
5362 // Need some storage for the data, use the otherwise unused 'data' member.
5363 if (data.isEmpty())
5364 data.resize(nonZeroSize);
5365 }
5366 }
5367 return data.data();
5368}
5369
5371{
5372 if (!m_usage.testFlag(UniformBuffer)) {
5374 if (rhiD->caps.properMapBuffer)
5375 rhiD->f->glUnmapBuffer(targetForDataOps);
5376 else
5377 rhiD->f->glBufferSubData(targetForDataOps, 0, nonZeroSize, data.data());
5378 }
5379}
5380
5382 int sampleCount, QRhiRenderBuffer::Flags flags,
5383 QRhiTexture::Format backingFormatHint)
5384 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
5385{
5386}
5387
5392
5394{
5395 if (!renderbuffer)
5396 return;
5397
5400
5401 e.renderbuffer.renderbuffer = renderbuffer;
5402 e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
5403
5404 renderbuffer = 0;
5406
5408 if (rhiD) {
5409 if (owns)
5410 rhiD->releaseQueue.append(e);
5411 rhiD->unregisterResource(this);
5412 }
5413}
5414
5416{
5417 if (renderbuffer)
5418 destroy();
5419
5421 samples = rhiD->effectiveSampleCount(m_sampleCount);
5422
5423 if (m_flags.testFlag(UsedWithSwapChainOnly)) {
5424 if (m_type == DepthStencil)
5425 return true;
5426
5427 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
5428 }
5429
5430 if (!rhiD->ensureContext())
5431 return false;
5432
5433 rhiD->f->glGenRenderbuffers(1, &renderbuffer);
5434 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
5435
5436 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
5437
5438 switch (m_type) {
5440 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
5441 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
5442 size.width(), size.height());
5444 } else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
5445 const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
5446 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, storage,
5447 size.width(), size.height());
5449 } else {
5450 GLenum depthStorage = GL_DEPTH_COMPONENT;
5451 if (rhiD->caps.gles) {
5452 if (rhiD->caps.depth24)
5453 depthStorage = GL_DEPTH_COMPONENT24;
5454 else
5455 depthStorage = GL_DEPTH_COMPONENT16; // plain ES 2.0 only has this
5456 }
5457 const GLenum stencilStorage = rhiD->caps.gles ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX;
5458 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, depthStorage,
5459 size.width(), size.height());
5460 rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
5461 rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
5462 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
5463 size.width(), size.height());
5464 }
5465 break;
5467 {
5468 GLenum internalFormat = GL_RGBA4; // ES 2.0
5469 if (rhiD->caps.rgba8Format) {
5472 GLenum glintformat, glformat, gltype;
5473 // only care about the sized internal format, the rest is not used here
5475 &glintformat, &internalFormat, &glformat, &gltype);
5476 }
5477 }
5478 if (rhiD->caps.msaaRenderBuffer && samples > 1) {
5479 rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalFormat,
5480 size.width(), size.height());
5481 } else {
5482 rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat,
5483 size.width(), size.height());
5484 }
5485 }
5486 break;
5487 default:
5488 Q_UNREACHABLE();
5489 break;
5490 }
5491
5492 if (rhiD->glObjectLabel)
5493 rhiD->glObjectLabel(GL_RENDERBUFFER, renderbuffer, -1, m_objectName.constData());
5494
5495 owns = true;
5496 generation += 1;
5497 rhiD->registerResource(this);
5498 return true;
5499}
5500
5502{
5503 if (!src.object)
5504 return false;
5505
5506 if (renderbuffer)
5507 destroy();
5508
5510 samples = rhiD->effectiveSampleCount(m_sampleCount);
5511
5512 if (m_flags.testFlag(UsedWithSwapChainOnly))
5513 qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless when importing an existing native object");
5514
5515 if (!rhiD->ensureContext())
5516 return false;
5517
5518 renderbuffer = src.object;
5519
5520 owns = false;
5521 generation += 1;
5522 rhiD->registerResource(this);
5523 return true;
5524}
5525
5533
5535 int arraySize, int sampleCount, Flags flags)
5536 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
5537{
5538}
5539
5544
5546{
5547 if (!texture)
5548 return;
5549
5552
5553 e.texture.texture = texture;
5554
5555 texture = 0;
5556 specified = false;
5557 zeroInitialized = false;
5558
5560 if (rhiD) {
5561 if (owns)
5562 rhiD->releaseQueue.append(e);
5563 rhiD->unregisterResource(this);
5564 }
5565}
5566
5568{
5569 if (texture)
5570 destroy();
5571
5573 if (!rhiD->ensureContext())
5574 return false;
5575
5576 const bool isCube = m_flags.testFlag(CubeMap);
5577 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
5578 const bool is3D = m_flags.testFlag(ThreeDimensional);
5579 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5580 const bool isCompressed = rhiD->isCompressedFormat(m_format);
5581 const bool is1D = m_flags.testFlag(OneDimensional);
5582
5583 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
5584 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
5585
5586 if (is3D && !rhiD->caps.texture3D) {
5587 qWarning("3D textures are not supported");
5588 return false;
5589 }
5590 if (isCube && is3D) {
5591 qWarning("Texture cannot be both cube and 3D");
5592 return false;
5593 }
5594 if (isArray && is3D) {
5595 qWarning("Texture cannot be both array and 3D");
5596 return false;
5597 }
5598 if (is1D && !rhiD->caps.texture1D) {
5599 qWarning("1D textures are not supported");
5600 return false;
5601 }
5602 if (is1D && is3D) {
5603 qWarning("Texture cannot be both 1D and 3D");
5604 return false;
5605 }
5606 if (is1D && isCube) {
5607 qWarning("Texture cannot be both 1D and cube");
5608 return false;
5609 }
5610
5611 if (m_depth > 1 && !is3D) {
5612 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
5613 return false;
5614 }
5615 if (m_arraySize > 0 && !isArray) {
5616 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
5617 return false;
5618 }
5619 if (m_arraySize < 1 && isArray) {
5620 qWarning("Texture is an array but array size is %d", m_arraySize);
5621 return false;
5622 }
5623
5624 target = isCube ? GL_TEXTURE_CUBE_MAP
5626 : (is3D ? GL_TEXTURE_3D
5627 : (is1D ? (isArray ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D)
5628 : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)));
5629
5630 if (m_flags.testFlag(ExternalOES))
5632 else if (m_flags.testFlag(TextureRectangleGL))
5634
5635 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
5637
5638 if (isCompressed) {
5639 if (m_flags.testFlag(UsedWithLoadStore)) {
5640 qWarning("Compressed texture cannot be used with image load/store");
5641 return false;
5642 }
5644 if (!glintformat) {
5645 qWarning("Compressed format %d not mappable to GL compressed format", m_format);
5646 return false;
5647 }
5649 glformat = GL_RGBA;
5650 } else {
5651 toGlTextureFormat(m_format, rhiD->caps,
5653 }
5654
5656
5658
5659 if (adjustedSize)
5660 *adjustedSize = size;
5661
5662 return true;
5663}
5664
5666{
5667 QSize size;
5668 if (!prepareCreate(&size))
5669 return false;
5670
5672 rhiD->f->glGenTextures(1, &texture);
5673
5674 const bool isCube = m_flags.testFlag(CubeMap);
5675 const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
5676 const bool is3D = m_flags.testFlag(ThreeDimensional);
5677 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5678 const bool isCompressed = rhiD->isCompressedFormat(m_format);
5679 const bool is1D = m_flags.testFlag(OneDimensional);
5680
5681 if (!isCompressed) {
5682 rhiD->f->glBindTexture(target, texture);
5683 if (!m_flags.testFlag(UsedWithLoadStore)) {
5684 if (is1D) {
5685 for (int level = 0; level < mipLevelCount; ++level) {
5686 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
5687 if (isArray)
5688 rhiD->f->glTexImage2D(target, level, GLint(glintformat), mipSize.width(),
5689 qMax(0, m_arraySize), 0, glformat, gltype, nullptr);
5690 else
5691 rhiD->glTexImage1D(target, level, GLint(glintformat), mipSize.width(), 0,
5692 glformat, gltype, nullptr);
5693 }
5694 } else if (is3D || isArray) {
5695 const int layerCount = is3D ? qMax(1, m_depth) : qMax(0, m_arraySize);
5696 if (hasMipMaps) {
5697 for (int level = 0; level != mipLevelCount; ++level) {
5698 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
5699 rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), layerCount,
5700 0, glformat, gltype, nullptr);
5701 }
5702 } else {
5703 rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), layerCount,
5704 0, glformat, gltype, nullptr);
5705 }
5706 } else if (hasMipMaps || isCube) {
5707 const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
5708 for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
5709 for (int level = 0; level != mipLevelCount; ++level) {
5710 const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
5711 rhiD->f->glTexImage2D(faceTargetBase + uint(layer), level, GLint(glintformat),
5712 mipSize.width(), mipSize.height(), 0,
5713 glformat, gltype, nullptr);
5714 }
5715 }
5716 } else {
5717 // 2D texture. For multisample textures the GLES 3.1
5718 // glStorage2DMultisample must be used for portability.
5719 if (m_sampleCount > 1 && rhiD->caps.multisampledTexture) {
5720 // internal format must be sized
5721 rhiD->f->glTexStorage2DMultisample(target, m_sampleCount, glsizedintformat,
5722 size.width(), size.height(), GL_TRUE);
5723 } else {
5724 rhiD->f->glTexImage2D(target, 0, GLint(glintformat), size.width(), size.height(),
5725 0, glformat, gltype, nullptr);
5726 }
5727 }
5728 } else {
5729 // Must be specified with immutable storage functions otherwise
5730 // bindImageTexture may fail. Also, the internal format must be a
5731 // sized format here.
5732 if (is1D && !isArray)
5733 rhiD->glTexStorage1D(target, mipLevelCount, glsizedintformat, size.width());
5734 else if (!is1D && (is3D || isArray))
5735 rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(),
5736 is3D ? qMax(1, m_depth) : qMax(0, m_arraySize));
5737 else if (m_sampleCount > 1)
5738 rhiD->f->glTexStorage2DMultisample(target, m_sampleCount, glsizedintformat,
5739 size.width(), size.height(), GL_TRUE);
5740 else
5741 rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(),
5742 is1D ? qMax(0, m_arraySize) : size.height());
5743 }
5744 specified = true;
5745 } else {
5746 // Cannot use glCompressedTexImage2D without valid data, so defer.
5747 // Compressed textures will not be used as render targets so this is
5748 // not an issue.
5749 specified = false;
5750 }
5751
5752 if (rhiD->glObjectLabel)
5753 rhiD->glObjectLabel(GL_TEXTURE, texture, -1, m_objectName.constData());
5754
5755 owns = true;
5756
5757 generation += 1;
5758 rhiD->registerResource(this);
5759 return true;
5760}
5761
5763{
5764 const uint textureId = uint(src.object);
5765 if (textureId == 0)
5766 return false;
5767
5768 if (!prepareCreate())
5769 return false;
5770
5771 texture = textureId;
5772 specified = true;
5773 zeroInitialized = true;
5774
5775 owns = false;
5776
5777 generation += 1;
5779 rhiD->registerResource(this);
5780 return true;
5781}
5782
5787
5790 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
5791{
5792}
5793
5798
5800{
5802 if (rhiD)
5803 rhiD->unregisterResource(this);
5804}
5805
5820
5821// dummy, no Vulkan-style RenderPass+Framebuffer concept here
5826
5831
5833{
5835 if (rhiD)
5836 rhiD->unregisterResource(this);
5837}
5838
5840{
5841 Q_UNUSED(other);
5842 return true;
5843}
5844
5852
5854{
5855 return {};
5856}
5857
5863
5868
5870{
5871 // nothing to do here
5872}
5873
5878
5880{
5881 return d.dpr;
5882}
5883
5885{
5886 return d.sampleCount;
5887}
5888
5896
5901
5903{
5904 if (!framebuffer)
5905 return;
5906
5909
5910 e.textureRenderTarget.framebuffer = framebuffer;
5911 e.textureRenderTarget.nonMsaaThrowawayDepthTexture = nonMsaaThrowawayDepthTexture;
5912
5913 framebuffer = 0;
5915
5917 if (rhiD) {
5918 rhiD->releaseQueue.append(e);
5919 rhiD->unregisterResource(this);
5920 }
5921}
5922
5930
5932{
5934
5935 if (framebuffer)
5936 destroy();
5937
5938 const bool hasColorAttachments = m_desc.colorAttachmentCount() > 0;
5939 Q_ASSERT(hasColorAttachments || m_desc.depthTexture());
5941 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5942
5943 if (hasColorAttachments) {
5944 const int count = int(m_desc.colorAttachmentCount());
5945 if (count > rhiD->caps.maxDrawBuffers) {
5946 qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
5947 count, rhiD->caps.maxDrawBuffers);
5948 }
5949 }
5950 if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
5951 qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
5952
5953 if (!rhiD->ensureContext())
5954 return false;
5955
5956 rhiD->f->glGenFramebuffers(1, &framebuffer);
5957 rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
5958
5959 d.colorAttCount = 0;
5960 int attIndex = 0;
5961 int multiViewCount = 0;
5962 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
5963 d.colorAttCount += 1;
5964 const QRhiColorAttachment &colorAtt(*it);
5965 QRhiTexture *texture = colorAtt.texture();
5966 QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
5967 Q_ASSERT(texture || renderBuffer);
5968 if (texture) {
5970 Q_ASSERT(texD->texture && texD->specified);
5971 if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
5972 if (colorAtt.multiViewCount() < 2) {
5973 rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
5974 colorAtt.level(), colorAtt.layer());
5975 } else {
5976 multiViewCount = colorAtt.multiViewCount();
5977 if (texD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture && colorAtt.resolveTexture()) {
5978 // Special path for GLES and GL_OVR_multiview_multisampled_render_to_texture:
5979 // ignore the color attachment's (multisample) texture
5980 // array and give the resolve texture array to GL. (no
5981 // explicit resolving is needed by us later on)
5982 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
5983 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
5984 GL_COLOR_ATTACHMENT0 + uint(attIndex),
5985 resolveTexD->texture,
5986 colorAtt.resolveLevel(),
5987 texD->sampleCount(),
5988 colorAtt.resolveLayer(),
5989 multiViewCount);
5990 } else {
5991 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER,
5992 GL_COLOR_ATTACHMENT0 + uint(attIndex),
5993 texD->texture,
5994 colorAtt.level(),
5995 colorAtt.layer(),
5996 multiViewCount);
5997 }
5998 }
5999 } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
6000 rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
6001 texD->target + uint(colorAtt.layer()), texD->texture,
6002 colorAtt.level());
6003 } else {
6004 if (texD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) {
6005 // Special path for GLES and GL_EXT_multisampled_render_to_texture:
6006 // ignore the color attachment's (multisample) texture and
6007 // give the resolve texture to GL. (no explicit resolving is
6008 // needed by us later on)
6009 QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
6010 const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target;
6011 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()),
6012 resolveTexD->texture, colorAtt.level(), texD->sampleCount());
6013 } else {
6014 const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
6015 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
6016 texD->texture, colorAtt.level());
6017 }
6018 }
6019 if (attIndex == 0) {
6020 d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
6021 d.sampleCount = texD->sampleCount();
6022 }
6023 } else if (renderBuffer) {
6024 QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
6025 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer);
6026 if (attIndex == 0) {
6027 d.pixelSize = rbD->pixelSize();
6028 d.sampleCount = rbD->samples;
6029 }
6030 }
6031 }
6032
6033 if (hasDepthStencil) {
6034 if (m_desc.depthStencilBuffer()) {
6036 if (rhiD->caps.needsDepthStencilCombinedAttach) {
6037 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
6038 depthRbD->renderbuffer);
6039 } else {
6040 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
6041 depthRbD->renderbuffer);
6042 if (depthRbD->stencilRenderbuffer) {
6043 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
6044 depthRbD->stencilRenderbuffer);
6045 } else {
6046 // packed depth-stencil
6047 rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
6048 depthRbD->renderbuffer);
6049 }
6050 }
6051 if (d.colorAttCount == 0) {
6052 d.pixelSize = depthRbD->pixelSize();
6053 d.sampleCount = depthRbD->samples;
6054 }
6055 } else {
6057 if (multiViewCount < 2) {
6058 if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) {
6059 // Special path for GLES and
6060 // GL_EXT_multisampled_render_to_texture, for depth-stencil.
6061 // Relevant only when depthResolveTexture is set.
6063 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target,
6064 depthResolveTexD->texture, 0, depthTexD->sampleCount());
6065 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
6066 rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target,
6067 depthResolveTexD->texture, 0, depthTexD->sampleCount());
6068 }
6069 } else {
6070 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
6071 depthTexD->texture, 0);
6072 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
6073 rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
6074 depthTexD->texture, 0);
6075 }
6076 }
6077 } else {
6078 if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) {
6079 // And so it turns out
6080 // https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt
6081 // does not work with multisample 2D texture arrays. (at least
6082 // that's what Issue 30 in the extension spec seems to imply)
6083 //
6084 // There is https://registry.khronos.org/OpenGL/extensions/EXT/EXT_multiview_texture_multisample.txt
6085 // that seems to resolve that, but that does not seem to
6086 // work (or not available) on GLES devices such as the Quest 3.
6087 //
6088 // So instead, on GLES we can use the
6089 // multisample-multiview-auto-resolving version (which in
6090 // turn is not supported on desktop GL e.g. by NVIDIA), too
6091 // bad we have a multisample depth texture array here as
6092 // every other API out there requires that. So, in absence
6093 // of a depthResolveTexture, create a temporary one ignoring
6094 // what the user has already created.
6095 //
6097 qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set."
6098 " This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create"
6099 " a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture.");
6100 }
6103 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6105 depthResolveTexD->texture,
6106 0,
6107 depthTexD->sampleCount(),
6108 0,
6109 multiViewCount);
6110 if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
6111 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6113 depthResolveTexD->texture,
6114 0,
6115 depthTexD->sampleCount(),
6116 0,
6117 multiViewCount);
6118 }
6119 } else {
6121 rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
6122 rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
6123 rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
6124 depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
6125 }
6126 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6129 0,
6130 depthTexD->sampleCount(),
6131 0,
6132 multiViewCount);
6133 rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
6136 0,
6137 depthTexD->sampleCount(),
6138 0,
6139 multiViewCount);
6140 }
6141 } else {
6142 // The depth texture here must be an array with at least
6143 // multiViewCount elements, and the format should be D24 or D32F
6144 // for depth only, or D24S8 for depth and stencil.
6145 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->texture,
6146 0, 0, multiViewCount);
6147 if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
6148 rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->texture,
6149 0, 0, multiViewCount);
6150 }
6151 }
6152 }
6153 if (d.colorAttCount == 0) {
6154 d.pixelSize = depthTexD->pixelSize();
6155 d.sampleCount = depthTexD->sampleCount();
6156 }
6157 }
6158 d.dsAttCount = 1;
6159 } else {
6160 d.dsAttCount = 0;
6161 }
6162
6163 d.dpr = 1;
6165
6166 GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
6167 if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
6168 qWarning("Framebuffer incomplete: 0x%x", status);
6169 return false;
6170 }
6171
6172 if (rhiD->glObjectLabel)
6173 rhiD->glObjectLabel(GL_FRAMEBUFFER, framebuffer, -1, m_objectName.constData());
6174
6175 QRhiRenderTargetAttachmentTracker::updateResIdList<QGles2Texture, QGles2RenderBuffer>(m_desc, &d.currentResIdList);
6176
6177 rhiD->registerResource(this);
6178 return true;
6179}
6180
6182{
6183 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QGles2Texture, QGles2RenderBuffer>(m_desc, d.currentResIdList))
6184 const_cast<QGles2TextureRenderTarget *>(this)->create();
6185
6186 return d.pixelSize;
6187}
6188
6190{
6191 return d.dpr;
6192}
6193
6195{
6196 return d.sampleCount;
6197}
6198
6203
6208
6210{
6212 if (rhiD)
6213 rhiD->unregisterResource(this);
6214}
6215
6217{
6219 if (!rhiD->sanityCheckShaderResourceBindings(this))
6220 return false;
6221
6222 hasDynamicOffset = false;
6223 for (int i = 0, ie = m_bindings.size(); i != ie; ++i) {
6226 if (b->u.ubuf.hasDynamicOffset) {
6227 hasDynamicOffset = true;
6228 break;
6229 }
6230 }
6231 }
6232
6233 rhiD->updateLayoutDesc(this);
6234
6235 generation += 1;
6236 rhiD->registerResource(this, false);
6237 return true;
6238}
6239
6241{
6242 Q_UNUSED(flags);
6243 generation += 1;
6244}
6245
6250
6255
6257{
6258 if (!program)
6259 return;
6260
6263
6264 e.pipeline.program = program;
6265
6266 program = 0;
6267 uniforms.clear();
6268 samplers.clear();
6269
6271 if (rhiD) {
6272 rhiD->releaseQueue.append(e);
6273 rhiD->unregisterResource(this);
6274 }
6275}
6276
6277static inline bool isGraphicsStage(const QRhiShaderStage &shaderStage)
6278{
6279 const QRhiShaderStage::Type t = shaderStage.type();
6280 return t == QRhiShaderStage::Vertex
6285}
6286
6288{
6290
6291 if (program)
6292 destroy();
6293
6294 if (!rhiD->ensureContext())
6295 return false;
6296
6297 rhiD->pipelineCreationStart();
6298 if (!rhiD->sanityCheckGraphicsPipeline(this))
6299 return false;
6300
6302
6303 program = rhiD->f->glCreateProgram();
6304
6305 enum {
6306 VtxIdx = 0,
6307 TCIdx,
6308 TEIdx,
6309 GeomIdx,
6310 FragIdx,
6311 LastIdx
6312 };
6313 const auto descIdxForStage = [](const QRhiShaderStage &shaderStage) {
6314 switch (shaderStage.type()) {
6316 return VtxIdx;
6318 return TCIdx;
6320 return TEIdx;
6322 return GeomIdx;
6324 return FragIdx;
6325 default:
6326 break;
6327 }
6328 Q_UNREACHABLE_RETURN(VtxIdx);
6329 };
6330 QShaderDescription desc[LastIdx];
6331 QShader::SeparateToCombinedImageSamplerMappingList samplerMappingList[LastIdx];
6332 bool vertexFragmentOnly = true;
6333 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6334 if (isGraphicsStage(shaderStage)) {
6335 const int idx = descIdxForStage(shaderStage);
6336 if (idx != VtxIdx && idx != FragIdx)
6337 vertexFragmentOnly = false;
6338 QShader shader = shaderStage.shader();
6339 QShaderVersion shaderVersion;
6340 desc[idx] = shader.description();
6341 if (!rhiD->shaderSource(shaderStage, &shaderVersion).isEmpty()) {
6342 samplerMappingList[idx] = shader.separateToCombinedImageSamplerMappingList(
6343 { QShader::GlslShader, shaderVersion, shaderStage.shaderVariant() });
6344 }
6345 }
6346 }
6347
6349 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(m_shaderStages.constData(),
6351 program,
6352 desc[VtxIdx].inputVariables(),
6353 &cacheKey);
6354 if (cacheResult == QRhiGles2::ProgramCacheError)
6355 return false;
6356
6357 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
6358 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6359 if (isGraphicsStage(shaderStage)) {
6360 if (!rhiD->compileShader(program, shaderStage, nullptr))
6361 return false;
6362 }
6363 }
6364
6365 // important when GLSL <= 150 is used that does not have location qualifiers
6366 for (const QShaderDescription::InOutVariable &inVar : desc[VtxIdx].inputVariables())
6367 rhiD->f->glBindAttribLocation(program, GLuint(inVar.location), inVar.name);
6368
6369 if (vertexFragmentOnly)
6370 rhiD->sanityCheckVertexFragmentInterface(desc[VtxIdx], desc[FragIdx]);
6371
6372 if (!rhiD->linkProgram(program))
6373 return false;
6374
6375 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6376 // force replacing existing cache entry (if there is one, then
6377 // something is wrong with it, as there was no hit)
6378 rhiD->trySaveToPipelineCache(program, cacheKey, true);
6379 } else {
6380 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
6381 // was not enabled, so instead store to the Qt 5 disk cache
6382 rhiD->trySaveToDiskCache(program, cacheKey);
6383 }
6384 } else {
6385 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
6386 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6387 // just so that it ends up in the pipeline cache also when the hit was
6388 // from the disk cache
6389 rhiD->trySaveToPipelineCache(program, cacheKey);
6390 }
6391 }
6392
6393 // Use the same work area for the vertex & fragment stages, thus ensuring
6394 // that we will not do superfluous glUniform calls for uniforms that are
6395 // present in both shaders.
6396 QDuplicateTracker<int, 256> activeUniformLocations;
6397
6398 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6399 if (isGraphicsStage(shaderStage)) {
6400 const int idx = descIdxForStage(shaderStage);
6401 for (const QShaderDescription::UniformBlock &ub : desc[idx].uniformBlocks())
6402 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
6403 for (const QShaderDescription::InOutVariable &v : desc[idx].combinedImageSamplers())
6404 rhiD->gatherSamplers(program, v, &samplers);
6405 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : samplerMappingList[idx])
6406 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
6407 }
6408 }
6409
6410 std::sort(uniforms.begin(), uniforms.end(),
6412 {
6413 return a.offset < b.offset;
6414 });
6415
6416 memset(uniformState, 0, sizeof(uniformState));
6417
6418 currentSrb = nullptr;
6420
6421 if (rhiD->glObjectLabel)
6422 rhiD->glObjectLabel(GL_PROGRAM, program, -1, m_objectName.constData());
6423
6424 rhiD->pipelineCreationEnd();
6425 generation += 1;
6426 rhiD->registerResource(this);
6427 return true;
6428}
6429
6434
6439
6441{
6442 if (!program)
6443 return;
6444
6447
6448 e.pipeline.program = program;
6449
6450 program = 0;
6451 uniforms.clear();
6452 samplers.clear();
6453
6455 if (rhiD) {
6456 rhiD->releaseQueue.append(e);
6457 rhiD->unregisterResource(this);
6458 }
6459}
6460
6462{
6464
6465 if (program)
6466 destroy();
6467
6468 if (!rhiD->ensureContext())
6469 return false;
6470
6471 rhiD->pipelineCreationStart();
6472
6475 QShaderVersion shaderVersion;
6476 if (!rhiD->shaderSource(m_shaderStage, &shaderVersion).isEmpty()) {
6478 { QShader::GlslShader, shaderVersion, m_shaderStage.shaderVariant() });
6479 }
6480
6481 program = rhiD->f->glCreateProgram();
6482
6484 QRhiGles2::ProgramCacheResult cacheResult = rhiD->tryLoadFromDiskOrPipelineCache(&m_shaderStage, 1, program, {}, &cacheKey);
6485 if (cacheResult == QRhiGles2::ProgramCacheError)
6486 return false;
6487
6488 if (cacheResult == QRhiGles2::ProgramCacheMiss) {
6489 if (!rhiD->compileShader(program, m_shaderStage, nullptr))
6490 return false;
6491
6492 if (!rhiD->linkProgram(program))
6493 return false;
6494
6495 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6496 // force replacing existing cache entry (if there is one, then
6497 // something is wrong with it, as there was no hit)
6498 rhiD->trySaveToPipelineCache(program, cacheKey, true);
6499 } else {
6500 // legacy QOpenGLShaderProgram style behavior: the "pipeline cache"
6501 // was not enabled, so instead store to the Qt 5 disk cache
6502 rhiD->trySaveToDiskCache(program, cacheKey);
6503 }
6504 } else {
6505 Q_ASSERT(cacheResult == QRhiGles2::ProgramCacheHit);
6506 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave)) {
6507 // just so that it ends up in the pipeline cache also when the hit was
6508 // from the disk cache
6509 rhiD->trySaveToPipelineCache(program, cacheKey);
6510 }
6511 }
6512
6513 QDuplicateTracker<int, 256> activeUniformLocations;
6514 for (const QShaderDescription::UniformBlock &ub : csDesc.uniformBlocks())
6515 rhiD->gatherUniforms(program, ub, &activeUniformLocations, &uniforms);
6516 for (const QShaderDescription::InOutVariable &v : csDesc.combinedImageSamplers())
6517 rhiD->gatherSamplers(program, v, &samplers);
6518 for (const QShader::SeparateToCombinedImageSamplerMapping &mapping : csSamplerMappingList)
6519 rhiD->gatherGeneratedSamplers(program, mapping, &samplers);
6520
6521 // storage images and buffers need no special steps here
6522
6523 memset(uniformState, 0, sizeof(uniformState));
6524
6525 currentSrb = nullptr;
6527
6528 rhiD->pipelineCreationEnd();
6529 generation += 1;
6530 rhiD->registerResource(this);
6531 return true;
6532}
6533
6539
6544
6546{
6547 // nothing to do here
6548}
6549
6551 : QRhiSwapChain(rhi),
6552 rt(rhi, this),
6553 rtLeft(rhi, this),
6554 rtRight(rhi, this),
6555 cb(rhi)
6556{
6557}
6558
6563
6565{
6567 if (rhiD)
6568 rhiD->unregisterResource(this);
6569}
6570
6575
6580
6582{
6583 if (targetBuffer == LeftBuffer)
6584 return rtLeft.d.isValid() ? &rtLeft : &rt;
6585 else if (targetBuffer == RightBuffer)
6586 return rtRight.d.isValid() ? &rtRight : &rt;
6587 else
6588 Q_UNREACHABLE_RETURN(nullptr);
6589}
6590
6592{
6594 if (QPlatformWindow *platformWindow = m_window->handle())
6595 // Prefer using QPlatformWindow geometry and DPR in order to avoid
6596 // errors due to rounded QWindow geometry.
6597 return platformWindow->geometry().size() * platformWindow->devicePixelRatio();
6598 else
6599 return m_window->size() * m_window->devicePixelRatio();
6600}
6601
6603{
6604 return f == SDR;
6605}
6606
6608{
6611 rhiD->registerResource(rpD, false);
6612 return rpD;
6613}
6614
6616{
6617 rt->setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
6620 rt->d.dpr = float(m_window->devicePixelRatio());
6621 rt->d.sampleCount = qBound(1, m_sampleCount, 64);
6622 rt->d.colorAttCount = 1;
6623 rt->d.dsAttCount = m_depthStencil ? 1 : 0;
6625}
6626
6628{
6629 // can be called multiple times due to window resizes
6630 const bool needsRegistration = !surface || surface != m_window;
6631 if (surface && surface != m_window)
6632 destroy();
6633
6634 surface = m_window;
6637
6640 {
6643 }
6644
6646
6647 if (m_window->format().stereo()) {
6652 }
6653
6654 frameCount = 0;
6655
6657 if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps) && rhiD->caps.timestamps)
6658 timestamps.prepare(rhiD);
6659
6660 // The only reason to register this fairly fake gl swapchain
6661 // object with no native resources underneath is to be able to
6662 // implement a safe destroy().
6663 if (needsRegistration)
6664 rhiD->registerResource(this, false);
6665
6666 return true;
6667}
6668
6670{
6671 if (!query[0])
6672 rhiD->f->glGenQueries(TIMESTAMP_PAIRS * 2, query);
6673}
6674
6676{
6677 rhiD->f->glDeleteQueries(TIMESTAMP_PAIRS * 2, query);
6678 memset(active, 0, sizeof(active));
6679 memset(query, 0, sizeof(query));
6680}
6681
6682bool QGles2SwapChainTimestamps::tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
6683{
6684 if (!active[pairIndex])
6685 return false;
6686
6687 GLuint tsStart = query[pairIndex * 2];
6688 GLuint tsEnd = query[pairIndex * 2 + 1];
6689
6690 GLuint ready = GL_FALSE;
6691 rhiD->f->glGetQueryObjectuiv(tsEnd, GL_QUERY_RESULT_AVAILABLE, &ready);
6692
6693 if (!ready)
6694 return false;
6695
6696 bool result = false;
6697 quint64 timestamps[2];
6698 rhiD->glGetQueryObjectui64v(tsStart, GL_QUERY_RESULT, &timestamps[0]);
6699 rhiD->glGetQueryObjectui64v(tsEnd, GL_QUERY_RESULT, &timestamps[1]);
6700
6701 if (timestamps[1] >= timestamps[0]) {
6702 const quint64 nanoseconds = timestamps[1] - timestamps[0];
6703 *elapsedSec = nanoseconds / 1000000000.0;
6704 result = true;
6705 }
6706
6707 active[pairIndex] = false;
6708 return result;
6709}
6710
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:612
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:495
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:928
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1300
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:1220
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:952
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1304
\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:76
bool isEmpty() const noexcept
Definition qlist.h:402
void append(parameter_type t)
Definition qlist.h:459
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:1652
@ DoNotTrackResourcesForCompute
Definition qrhi.h:1661
QPair< int, quint32 > DynamicOffset
Synonym for QPair<int, quint32>.
Definition qrhi.h:1677
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1681
IndexFormat
Specifies the index data type.
Definition qrhi.h:1654
\inmodule QtGui
Definition qrhi.h:1623
QRhiShaderStage m_shaderStage
Definition qrhi.h:1645
\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:1271
BlendOp
Specifies the blend operation.
Definition qrhi.h:1332
PolygonMode
Specifies the polygon rasterization mode.
Definition qrhi.h:1380
FrontFace
Specifies the front face winding order.
Definition qrhi.h:1297
BlendFactor
Specifies the blend factor.
Definition qrhi.h:1310
CompareOp
Specifies the depth or stencil comparison function.
Definition qrhi.h:1351
Topology m_topology
Definition qrhi.h:1482
CullMode
Specifies the culling mode.
Definition qrhi.h:1291
QVarLengthArray< QRhiShaderStage, 4 > m_shaderStages
Definition qrhi.h:1501
Topology
Specifies the primitive topology.
Definition qrhi.h:1281
StencilOp
Specifies the stencil operation.
Definition qrhi.h:1362
bool isCompressedFormat(QRhiTexture::Format format) const
Definition qrhi.cpp:8061
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:8068
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:8188
static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:11096
static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
Definition qrhi.cpp:11077
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:1095
Flags flags() const
Definition qrhi.h:1122
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1117
QSize pixelSize() const
Definition qrhi.h:1116
int m_sampleCount
Definition qrhi.h:1135
QRhiTexture::Format m_backingFormatHint
Definition qrhi.h:1137
QSize m_pixelSize
Definition qrhi.h:1134
Type
Specifies the type of the renderbuffer.
Definition qrhi.h:1097
virtual bool create()=0
Creates the corresponding native graphics resources.
@ UsedWithSwapChainOnly
Definition qrhi.h:1103
Flags m_flags
Definition qrhi.h:1136
\inmodule QtGui
Definition qrhi.h:1143
\inmodule QtGui
Definition qrhi.h:1159
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1166
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1170
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:536
\inmodule QtGui
Definition qrhi.h:1732
\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:1031
Filter m_minFilter
Definition qrhi.h:1086
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1033
AddressMode m_addressV
Definition qrhi.h:1089
Filter m_mipmapMode
Definition qrhi.h:1087
AddressMode m_addressU
Definition qrhi.h:1088
AddressMode
Specifies the addressing mode.
Definition qrhi.h:1039
@ ClampToEdge
Definition qrhi.h:1041
CompareOp
Specifies the texture comparison function.
Definition qrhi.h:1045
@ LessOrEqual
Definition qrhi.h:1049
@ GreaterOrEqual
Definition qrhi.h:1052
CompareOp m_compareOp
Definition qrhi.h:1091
AddressMode m_addressW
Definition qrhi.h:1090
Filter m_magFilter
Definition qrhi.h:1085
\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:1215
QVarLengthArray< QRhiShaderResourceBinding, BINDING_PREALLOC > m_bindings
Definition qrhi.h:1247
\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:1174
\inmodule QtGui
Definition qrhi.h:1550
QWindow * m_window
Definition qrhi.h:1610
int m_sampleCount
Definition qrhi.h:1614
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1615
QSize m_currentPixelSize
Definition qrhi.h:1616
Flags m_flags
Definition qrhi.h:1611
Format
Describes the swapchain format.
Definition qrhi.h:1562
StereoTargetBuffer
Selects the backbuffer to use with a stereoscopic swapchain.
Definition qrhi.h:1569
QRhiRenderBuffer * m_depthStencil
Definition qrhi.h:1613
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:1185
QRhiTextureRenderTargetDescription m_desc
Definition qrhi.h:1208
\inmodule QtGui
Definition qrhi.h:895
QSize m_pixelSize
Definition qrhi.h:1017
int m_arraySize
Definition qrhi.h:1019
int m_depth
Definition qrhi.h:1018
@ 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:960
@ ASTC_12x12
Definition qrhi.h:963
@ ASTC_8x5
Definition qrhi.h:955
@ ASTC_10x5
Definition qrhi.h:958
@ RGBA32F
Definition qrhi.h:926
@ ETC2_RGBA8
Definition qrhi.h:948
@ ASTC_5x5
Definition qrhi.h:952
@ ASTC_4x4
Definition qrhi.h:950
@ ASTC_6x6
Definition qrhi.h:954
@ ASTC_12x10
Definition qrhi.h:962
@ ETC2_RGB8
Definition qrhi.h:946
@ ASTC_5x4
Definition qrhi.h:951
@ RED_OR_ALPHA8
Definition qrhi.h:923
@ ASTC_6x5
Definition qrhi.h:953
@ ASTC_8x8
Definition qrhi.h:957
@ RGBA16F
Definition qrhi.h:925
@ RGB10A2
Definition qrhi.h:930
@ ASTC_10x6
Definition qrhi.h:959
@ ASTC_10x10
Definition qrhi.h:961
@ UnknownFormat
Definition qrhi.h:915
@ ETC2_RGB8A1
Definition qrhi.h:947
@ ASTC_8x6
Definition qrhi.h:956
Format m_format
Definition qrhi.h:1016
Flags m_flags
Definition qrhi.h:1021
int m_sampleCount
Definition qrhi.h:1020
\inmodule QtGui
Definition qrhi.h:179
\inmodule QtGui
Definition qrhi.h:85
static constexpr int MAX_MIP_LEVELS
Definition qrhi.h:1998
ResourceLimit
Describes the resource limit to query.
Definition qrhi.h:1887
@ MaxThreadsPerThreadGroup
Definition qrhi.h:1894
@ MaxThreadGroupZ
Definition qrhi.h:1897
@ FramesInFlight
Definition qrhi.h:1891
@ TextureSizeMin
Definition qrhi.h:1888
@ MaxThreadGroupsPerDimension
Definition qrhi.h:1893
@ MaxAsyncReadbackFrames
Definition qrhi.h:1892
@ TextureArraySizeMax
Definition qrhi.h:1898
@ MaxColorAttachments
Definition qrhi.h:1890
@ MaxThreadGroupY
Definition qrhi.h:1896
@ MaxVertexInputs
Definition qrhi.h:1900
@ MaxThreadGroupX
Definition qrhi.h:1895
@ TextureSizeMax
Definition qrhi.h:1889
@ MaxVertexOutputs
Definition qrhi.h:1901
@ MaxUniformBufferRange
Definition qrhi.h:1899
@ SkipPresent
Definition qrhi.h:1883
Feature
Flag values to indicate what features are supported by the backend currently in use.
Definition qrhi.h:1832
@ HalfAttributes
Definition qrhi.h:1870
@ CustomInstanceStepRate
Definition qrhi.h:1838
@ NonDynamicUniformBuffers
Definition qrhi.h:1840
@ ElementIndexUint
Definition qrhi.h:1844
@ RenderToNonBaseMipLevel
Definition qrhi.h:1854
@ MultisampleRenderBuffer
Definition qrhi.h:1834
@ RenderTo3DTextureSlice
Definition qrhi.h:1862
@ Tessellation
Definition qrhi.h:1864
@ IntAttributes
Definition qrhi.h:1855
@ TextureArrays
Definition qrhi.h:1863
@ PipelineCacheDataLoadSave
Definition qrhi.h:1858
@ ReadBackNonUniformBuffer
Definition qrhi.h:1851
@ MultiView
Definition qrhi.h:1873
@ TexelFetch
Definition qrhi.h:1853
@ TextureArrayRange
Definition qrhi.h:1866
@ RenderToOneDimensionalTexture
Definition qrhi.h:1871
@ BaseVertex
Definition qrhi.h:1848
@ GeometryShader
Definition qrhi.h:1865
@ Compute
Definition qrhi.h:1845
@ OneDimensionalTextureMipmaps
Definition qrhi.h:1869
@ WideLines
Definition qrhi.h:1846
@ TriangleFanTopology
Definition qrhi.h:1850
@ OneDimensionalTextures
Definition qrhi.h:1868
@ ImageDataStride
Definition qrhi.h:1859
@ TextureViewFormat
Definition qrhi.h:1874
@ BaseInstance
Definition qrhi.h:1849
@ DebugMarkers
Definition qrhi.h:1835
@ ReadBackNonBaseMipLevel
Definition qrhi.h:1852
@ MultisampleTexture
Definition qrhi.h:1833
@ ThreeDimensionalTextureMipmaps
Definition qrhi.h:1872
@ NonFourAlignedEffectiveIndexBufferOffset
Definition qrhi.h:1841
@ RedOrAlpha8IsRed
Definition qrhi.h:1843
@ NonFillPolygonMode
Definition qrhi.h:1867
@ Timestamps
Definition qrhi.h:1836
@ ThreeDimensionalTextures
Definition qrhi.h:1861
@ PrimitiveRestart
Definition qrhi.h:1839
@ ReadBackAnyTextureFormat
Definition qrhi.h:1857
@ RenderBufferImport
Definition qrhi.h:1860
@ ScreenSpaceDerivatives
Definition qrhi.h:1856
@ VertexShaderPointSize
Definition qrhi.h:1847
@ NPOTTextureRepeat
Definition qrhi.h:1842
@ Instancing
Definition qrhi.h:1837
@ ResolveDepthStencil
Definition qrhi.h:1875
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1825
@ FrameOpSuccess
Definition qrhi.h:1826
@ FrameOpDeviceLost
Definition qrhi.h:1829
@ FrameOpError
Definition qrhi.h:1827
@ EnablePipelineCacheDataSave
Definition qrhi.h:1819
@ EnableTimestamps
Definition qrhi.h:1820
qsizetype size() const
Definition qset.h:51
bool contains(const T &value) const
Definition qset.h:72
const_iterator cbegin() const noexcept
Definition qset.h:139
iterator insert(const T &value)
Definition qset.h:156
\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:956
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:167
#define qCDebug(category,...)
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:23
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
#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
#define GL_DEPTH32F_STENCIL8
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_FLOAT_32_UNSIGNED_INT_24_8_REV
#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:1760
QByteArray deviceName
Definition qrhi.h:1770
struct QRhiGles2::DeferredReleaseEntry::@302::@308 textureRenderTarget
struct QRhiGles2::DeferredReleaseEntry::@302::@305 pipeline
\variable QRhiReadbackResult::completed
Definition qrhi.h:800
\inmodule QtGui
Definition qrhi.h:1724
QByteArray data
Definition qrhi.h:1728
std::function< void()> completed
Definition qrhi.h:1725
QRhiTextureCopyDescription desc
Definition qrhi_p.h:471
QVarLengthArray< MipLevelUploadList, 6 > subresDesc
Definition qrhi_p.h:469
\inmodule QtGui
Definition qrhi.h:1783
qint64 totalPipelineCreationTime
Definition qrhi.h:1784
\inmodule QtGui
Definition qrhi.h:966
\variable QShaderDescription::InOutVariable::name
\variable QShaderDescription::BlockVariable::name
Definition moc.h:23