1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qrhigles2_p_p.h"
38#include <QWindow>
39#include <QOffscreenSurface>
40#include <QOpenGLContext>
41#include <QtGui/private/qopenglextensions_p.h>
42#include <qmath.h>
43
44QT_BEGIN_NAMESPACE
45
46/*
47 OpenGL backend. Binding vertex attribute locations and decomposing uniform
48 buffers into uniforms are handled transparently to the application via the
49 reflection data (QShaderDescription). Real uniform buffers are never used,
50 regardless of the GLSL version. Textures and buffers feature no special
51 logic, it's all just glTexSubImage2D and glBufferSubData (with "dynamic"
52 buffers set to GL_DYNAMIC_DRAW). The swapchain and the associated
53 renderbuffer for depth-stencil will be dummies since we have no control over
54 the underlying buffers here. While the baseline here is plain GLES 2.0, some
55 modern GL(ES) features like multisample renderbuffers, blits, and compute are
56 used when available. Also functional with core profile contexts.
57*/
58
59/*!
60 \class QRhiGles2InitParams
61 \inmodule QtRhi
62 \brief OpenGL specific initialization parameters.
63
64 An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
65 Additionally, while optional, it is recommended that the QWindow the first
66 QRhiSwapChain will target is passed in as well.
67
68 \badcode
69 QOffscreenSurface *fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
70 QRhiGles2InitParams params;
71 params.fallbackSurface = fallbackSurface;
72 params.window = window;
73 rhi = QRhi::create(QRhi::OpenGLES2, &params);
74 \endcode
75
76 By default QRhi creates a QOpenGLContext on its own. This approach works
77 well in most cases, included threaded scenarios, where there is a dedicated
78 QRhi for each rendering thread. As there will be a QOpenGLContext for each
79 QRhi, the OpenGL context requirements (a context can only be current on one
80 thread) are satisfied. The implicitly created context is destroyed
81 automatically together with the QRhi.
82
83 The QSurfaceFormat for the context is specified in \l format. The
84 constructor sets this to QSurfaceFormat::defaultFormat() so applications
85 that use QSurfaceFormat::setDefaultFormat() do not need to set the format
86 again.
87
88 \note The depth and stencil buffer sizes are set automatically to 24 and 8
89 when no size was explicitly set for these buffers in \l format. As there
90 are possible adjustments to \l format, applications can use
91 adjustedFormat() to query the effective format that is passed to
92 QOpenGLContext::setFormat() internally.
93
94 A QOffscreenSurface has to be specified in \l fallbackSurface. In order to
95 prevent mistakes in threaded situations, this is never created
96 automatically by the QRhi since, like QWindow, QOffscreenSurface can only
97 be created on the gui/main thread.
98
99 As a convenience, applications can use newFallbackSurface() which creates
100 and returns a QOffscreenSurface that is compatible with the QOpenGLContext
101 that is going to be created by the QRhi afterwards. Note that the ownership
102 of the returned QOffscreenSurface is transferred to the caller and the QRhi
103 will not destroy it.
104
105 \note QRhiSwapChain can only target QWindow instances that have their
106 surface type set to QSurface::OpenGLSurface.
107
108 \note \l window is optional. It is recommended to specify it whenever
109 possible, in order to avoid problems on multi-adapter and multi-screen
110 systems. When \l window is not set, the very first
111 QOpenGLContext::makeCurrent() happens with \l fallbackSurface which may be
112 an invisible window on some platforms (for example, Windows) and that may
113 trigger unexpected problems in some cases.
114
115 \section2 Working with existing OpenGL contexts
116
117 When interoperating with another graphics engine, it may be necessary to
118 get a QRhi instance that uses the same OpenGL context. This can be achieved
119 by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The
120 \l{QRhiGles2NativeHandles::context}{context} must be set to a non-null
121 value.
122
123 An alternative approach is to create a QOpenGLContext that
124 \l{QOpenGLContext::setShareContext()}{shares resources} with the other
125 engine's context and passing in that context via QRhiGles2NativeHandles.
126
127 The QRhi does not take ownership of the QOpenGLContext passed in via
128 QRhiGles2NativeHandles.
129 */
130
131/*!
132 \class QRhiGles2NativeHandles
133 \inmodule QtRhi
134 \brief Holds the OpenGL context used by the QRhi.
135 */
136
137/*!
138 \class QRhiGles2TextureNativeHandles
139 \inmodule QtRhi
140 \brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
141 */
142
143#ifndef GL_BGRA
144#define GL_BGRA 0x80E1
145#endif
146
147#ifndef GL_R8
148#define GL_R8 0x8229
149#endif
150
151#ifndef GL_R16
152#define GL_R16 0x822A
153#endif
154
155#ifndef GL_RED
156#define GL_RED 0x1903
157#endif
158
159#ifndef GL_RGBA8
160#define GL_RGBA8 0x8058
161#endif
162
163#ifndef GL_RGBA32F
164#define GL_RGBA32F 0x8814
165#endif
166
167#ifndef GL_RGBA16F
168#define GL_RGBA16F 0x881A
169#endif
170
171#ifndef GL_HALF_FLOAT
172#define GL_HALF_FLOAT 0x140B
173#endif
174
175#ifndef GL_DEPTH_COMPONENT16
176#define GL_DEPTH_COMPONENT16 0x81A5
177#endif
178
179#ifndef GL_DEPTH_COMPONENT24
180#define GL_DEPTH_COMPONENT24 0x81A6
181#endif
182
183#ifndef GL_DEPTH_COMPONENT32F
184#define GL_DEPTH_COMPONENT32F 0x8CAC
185#endif
186
187#ifndef GL_STENCIL_INDEX
188#define GL_STENCIL_INDEX 0x1901
189#endif
190
191#ifndef GL_STENCIL_INDEX8
192#define GL_STENCIL_INDEX8 0x8D48
193#endif
194
195#ifndef GL_DEPTH24_STENCIL8
196#define GL_DEPTH24_STENCIL8 0x88F0
197#endif
198
199#ifndef GL_DEPTH_STENCIL_ATTACHMENT
200#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
201#endif
202
203#ifndef GL_DEPTH_STENCIL
204#define GL_DEPTH_STENCIL 0x84F9
205#endif
206
207#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
208#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
209#endif
210
211#ifndef GL_FRAMEBUFFER_SRGB
212#define GL_FRAMEBUFFER_SRGB 0x8DB9
213#endif
214
215#ifndef GL_READ_FRAMEBUFFER
216#define GL_READ_FRAMEBUFFER 0x8CA8
217#endif
218
219#ifndef GL_DRAW_FRAMEBUFFER
220#define GL_DRAW_FRAMEBUFFER 0x8CA9
221#endif
222
223#ifndef GL_MAX_DRAW_BUFFERS
224#define GL_MAX_DRAW_BUFFERS 0x8824
225#endif
226
227#ifndef GL_TEXTURE_COMPARE_MODE
228#define GL_TEXTURE_COMPARE_MODE 0x884C
229#endif
230
231#ifndef GL_COMPARE_REF_TO_TEXTURE
232#define GL_COMPARE_REF_TO_TEXTURE 0x884E
233#endif
234
235#ifndef GL_TEXTURE_COMPARE_FUNC
236#define GL_TEXTURE_COMPARE_FUNC 0x884D
237#endif
238
239#ifndef GL_MAX_SAMPLES
240#define GL_MAX_SAMPLES 0x8D57
241#endif
242
243#ifndef GL_SHADER_STORAGE_BUFFER
244#define GL_SHADER_STORAGE_BUFFER 0x90D2
245#endif
246
247#ifndef GL_READ_ONLY
248#define GL_READ_ONLY 0x88B8
249#endif
250
251#ifndef GL_WRITE_ONLY
252#define GL_WRITE_ONLY 0x88B9
253#endif
254
255#ifndef GL_READ_WRITE
256#define GL_READ_WRITE 0x88BA
257#endif
258
259#ifndef GL_COMPUTE_SHADER
260#define GL_COMPUTE_SHADER 0x91B9
261#endif
262
263#ifndef GL_ALL_BARRIER_BITS
264#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
265#endif
266
267#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
268#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
269#endif
270
271/*!
272 Constructs a new QRhiGles2InitParams.
273
274 \l format is set to QSurfaceFormat::defaultFormat().
275 */
276QRhiGles2InitParams::QRhiGles2InitParams()
277{
278 format = QSurfaceFormat::defaultFormat();
279}
280
281/*!
282 \return the QSurfaceFormat that will be set on the QOpenGLContext before
283 calling QOpenGLContext::create(). This format is based on \a format, but
284 may be adjusted. Applicable only when QRhi creates the context.
285 Applications are advised to set this format on their QWindow in order to
286 avoid potential BAD_MATCH failures.
287 */
288QSurfaceFormat QRhiGles2InitParams::adjustedFormat(const QSurfaceFormat &format)
289{
290 QSurfaceFormat fmt = format;
291
292 if (fmt.depthBufferSize() == -1)
293 fmt.setDepthBufferSize(24);
294 if (fmt.stencilBufferSize() == -1)
295 fmt.setStencilBufferSize(8);
296
297 return fmt;
298}
299
300/*!
301 \return a new QOffscreenSurface that can be used with a QRhi by passing it
302 via a QRhiGles2InitParams.
303
304 \a format is adjusted as appropriate in order to avoid having problems
305 afterwards due to an incompatible context and surface.
306
307 \note This function must only be called on the gui/main thread.
308
309 \note It is the application's responsibility to destroy the returned
310 QOffscreenSurface on the gui/main thread once the associated QRhi has been
311 destroyed. The QRhi will not destroy the QOffscreenSurface.
312 */
313QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
314{
315 QSurfaceFormat fmt = adjustedFormat(format);
316
317 // To resolve all fields in the format as much as possible, create a context.
318 // This may be heavy, but allows avoiding BAD_MATCH on some systems.
319 QOpenGLContext tempContext;
320 tempContext.setFormat(fmt);
321 if (tempContext.create())
322 fmt = tempContext.format();
323 else
324 qWarning("QRhiGles2: Failed to create temporary context");
325
326 QOffscreenSurface *s = new QOffscreenSurface;
327 s->setFormat(fmt);
328 s->create();
329
330 return s;
331}
332
333QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
334 : ofr(this)
335{
336 requestedFormat = QRhiGles2InitParams::adjustedFormat(params->format);
337 fallbackSurface = params->fallbackSurface;
338 maybeWindow = params->window; // may be null
339
340 importedContext = importDevice != nullptr;
341 if (importedContext) {
342 ctx = importDevice->context;
343 if (!ctx) {
344 qWarning("No OpenGL context given, cannot import");
345 importedContext = false;
346 }
347 }
348}
349
350bool QRhiGles2::ensureContext(QSurface *surface) const
351{
352 bool nativeWindowGone = false;
353 if (surface && surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
354 surface = fallbackSurface;
355 nativeWindowGone = true;
356 }
357
358 if (!surface)
359 surface = fallbackSurface;
360
361 if (needsMakeCurrent)
362 needsMakeCurrent = false;
363 else if (!nativeWindowGone && QOpenGLContext::currentContext() == ctx && (surface == fallbackSurface || ctx->surface() == surface))
364 return true;
365
366 if (!ctx->makeCurrent(surface)) {
367 qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen.");
368 return false;
369 }
370
371 return true;
372}
373
374bool QRhiGles2::create(QRhi::Flags flags)
375{
376 Q_UNUSED(flags);
377 Q_ASSERT(fallbackSurface);
378
379 if (!importedContext) {
380 ctx = new QOpenGLContext;
381 ctx->setFormat(requestedFormat);
382 if (!ctx->create()) {
383 qWarning("QRhiGles2: Failed to create context");
384 delete ctx;
385 ctx = nullptr;
386 return false;
387 }
388 qDebug() << "Created OpenGL context" << ctx->format();
389 }
390
391 if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
392 return false;
393
394 f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
395
396 const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
397 const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
398 const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
399 if (vendor && renderer && version)
400 qDebug("OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
401
402 const QSurfaceFormat actualFormat = ctx->format();
403
404 caps.ctxMajor = actualFormat.majorVersion();
405 caps.ctxMinor = actualFormat.minorVersion();
406
407 GLint n = 0;
408 f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
409 supportedCompressedFormats.resize(n);
410 if (n > 0)
411 f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, supportedCompressedFormats.data());
412
413 f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
414
415 if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) {
416 f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
417 f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
418 caps.maxSamples = qMax(1, caps.maxSamples);
419 } else {
420 caps.maxDrawBuffers = 1;
421 caps.maxSamples = 1;
422 }
423
424 caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
425 && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
426
427 caps.npotTexture = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
428 caps.npotTextureRepeat = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
429
430 caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
431 if (caps.gles)
432 caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3; // ES 3.0
433 else
434 caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
435
436 if (caps.fixedIndexPrimitiveRestart)
437 f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
438
439 caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
440 caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
441 caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
442 caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
443 caps.floatFormats = caps.ctxMajor >= 3; // 3.0 or ES 3.0
444 caps.depthTexture = caps.ctxMajor >= 3; // 3.0 or ES 3.0
445 caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
446#ifdef Q_OS_WASM
447 caps.needsDepthStencilCombinedAttach = true;
448#else
449 caps.needsDepthStencilCombinedAttach = false;
450#endif
451 caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer);
452 caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
453
454 if (caps.gles)
455 caps.uniformBuffers = caps.ctxMajor >= 3; // ES 3.0
456 else
457 caps.uniformBuffers = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // 3.1
458
459 caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
460 caps.depth24 = f->hasOpenGLExtension(QOpenGLExtensions::Depth24);
461 caps.rgba8Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized8Formats);
462
463 if (caps.gles)
464 caps.instancing = caps.ctxMajor >= 3; // ES 3.0
465 else
466 caps.instancing = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3); // 3.3
467
468 caps.baseVertex = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 2); // 3.2 or ES 3.2
469
470 if (caps.gles)
471 caps.compute = caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 1); // ES 3.1
472 else
473 caps.compute = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3); // 4.3
474
475 if (!caps.gles)
476 f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
477 // else (with gles) this is always on
478
479 nativeHandlesStruct.context = ctx;
480
481 return true;
482}
483
484void QRhiGles2::destroy()
485{
486 if (!f)
487 return;
488
489 ensureContext();
490 executeDeferredReleases();
491
492 if (!importedContext) {
493 delete ctx;
494 ctx = nullptr;
495 }
496
497 f = nullptr;
498}
499
500void QRhiGles2::executeDeferredReleases()
501{
502 for (int i = releaseQueue.count() - 1; i >= 0; --i) {
503 const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
504 switch (e.type) {
505 case QRhiGles2::DeferredReleaseEntry::Buffer:
506 f->glDeleteBuffers(1, &e.buffer.buffer);
507 break;
508 case QRhiGles2::DeferredReleaseEntry::Pipeline:
509 f->glDeleteProgram(e.pipeline.program);
510 break;
511 case QRhiGles2::DeferredReleaseEntry::Texture:
512 f->glDeleteTextures(1, &e.texture.texture);
513 break;
514 case QRhiGles2::DeferredReleaseEntry::RenderBuffer:
515 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
516 f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
517 break;
518 case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget:
519 f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
520 break;
521 default:
522 Q_UNREACHABLE();
523 break;
524 }
525 releaseQueue.removeAt(i);
526 }
527}
528
529QVector<int> QRhiGles2::supportedSampleCounts() const
530{
531 if (supportedSampleCountList.isEmpty()) {
532 // 1, 2, 4, 8, ...
533 for (int i = 1; i <= caps.maxSamples; i *= 2)
534 supportedSampleCountList.append(i);
535 }
536 return supportedSampleCountList;
537}
538
539int QRhiGles2::effectiveSampleCount(int sampleCount) const
540{
541 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
542 const int s = qBound(1, sampleCount, 64);
543 if (!supportedSampleCounts().contains(s)) {
544 qWarning("Attempted to set unsupported sample count %d", sampleCount);
545 return 1;
546 }
547 return s;
548}
549
550static inline bool isPowerOfTwo(int x)
551{
552 // Assumption: x >= 1
553 return x == (x & -x);
554}
555
556QSize QRhiGles2::safeTextureSize(const QSize &pixelSize) const
557{
558 QSize size = pixelSize.isEmpty() ? QSize(1, 1) : pixelSize;
559
560 if (!caps.npotTexture) {
561 if (!isPowerOfTwo(size.width())) {
562 qWarning("Texture width %d is not a power of two, adjusting",
563 size.width());
564 size.setWidth(qNextPowerOfTwo(size.width()));
565 }
566 if (!isPowerOfTwo(size.height())) {
567 qWarning("Texture height %d is not a power of two, adjusting",
568 size.height());
569 size.setHeight(qNextPowerOfTwo(size.height()));
570 }
571 }
572
573 if (size.width() > caps.maxTextureSize) {
574 qWarning("Texture width %d exceeds maximum width %d, adjusting",
575 size.width(), caps.maxTextureSize);
576 size.setWidth(caps.maxTextureSize);
577 }
578 if (size.height() > caps.maxTextureSize) {
579 qWarning("Texture height %d exceeds maximum height %d, adjusting",
580 size.height(), caps.maxTextureSize);
581 size.setHeight(caps.maxTextureSize);
582 }
583
584 return size;
585}
586
587QRhiSwapChain *QRhiGles2::createSwapChain()
588{
589 return new QGles2SwapChain(this);
590}
591
592QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
593{
594 return new QGles2Buffer(this, type, usage, size);
595}
596
597int QRhiGles2::ubufAlignment() const
598{
599 return 256;
600}
601
602bool QRhiGles2::isYUpInFramebuffer() const
603{
604 return true;
605}
606
607bool QRhiGles2::isYUpInNDC() const
608{
609 return true;
610}
611
612bool QRhiGles2::isClipDepthZeroToOne() const
613{
614 return false;
615}
616
617QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const
618{
619 return QMatrix4x4(); // identity
620}
621
622static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
623{
624 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
625 switch (format) {
626 case QRhiTexture::BC1:
627 return srgb ? 0x8C4C : 0x83F0;
628 case QRhiTexture::BC3:
629 return srgb ? 0x8C4E : 0x83F2;
630 case QRhiTexture::BC5:
631 return srgb ? 0x8C4F : 0x83F3;
632
633 case QRhiTexture::ETC2_RGB8:
634 return srgb ? 0x9275 : 0x9274;
635 case QRhiTexture::ETC2_RGB8A1:
636 return srgb ? 0x9277 : 0x9276;
637 case QRhiTexture::ETC2_RGBA8:
638 return srgb ? 0x9279 : 0x9278;
639
640 case QRhiTexture::ASTC_4x4:
641 return srgb ? 0x93D0 : 0x93B0;
642 case QRhiTexture::ASTC_5x4:
643 return srgb ? 0x93D1 : 0x93B1;
644 case QRhiTexture::ASTC_5x5:
645 return srgb ? 0x93D2 : 0x93B2;
646 case QRhiTexture::ASTC_6x5:
647 return srgb ? 0x93D3 : 0x93B3;
648 case QRhiTexture::ASTC_6x6:
649 return srgb ? 0x93D4 : 0x93B4;
650 case QRhiTexture::ASTC_8x5:
651 return srgb ? 0x93D5 : 0x93B5;
652 case QRhiTexture::ASTC_8x6:
653 return srgb ? 0x93D6 : 0x93B6;
654 case QRhiTexture::ASTC_8x8:
655 return srgb ? 0x93D7 : 0x93B7;
656 case QRhiTexture::ASTC_10x5:
657 return srgb ? 0x93D8 : 0x93B8;
658 case QRhiTexture::ASTC_10x6:
659 return srgb ? 0x93D9 : 0x93B9;
660 case QRhiTexture::ASTC_10x8:
661 return srgb ? 0x93DA : 0x93BA;
662 case QRhiTexture::ASTC_10x10:
663 return srgb ? 0x93DB : 0x93BB;
664 case QRhiTexture::ASTC_12x10:
665 return srgb ? 0x93DC : 0x93BC;
666 case QRhiTexture::ASTC_12x12:
667 return srgb ? 0x93DD : 0x93BD;
668
669 default:
670 return 0; // this is reachable, just return an invalid format
671 }
672}
673
674bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
675{
676 if (isCompressedFormat(format))
677 return supportedCompressedFormats.contains(toGlCompressedTextureFormat(format, flags));
678
679 switch (format) {
680 case QRhiTexture::D16:
681 Q_FALLTHROUGH();
682 case QRhiTexture::D32F:
683 return caps.depthTexture;
684
685 case QRhiTexture::BGRA8:
686 return caps.bgraExternalFormat;
687
688 case QRhiTexture::R8:
689 return caps.r8Format;
690
691 case QRhiTexture::R16:
692 return caps.r16Format;
693
694 case QRhiTexture::RGBA16F:
695 Q_FALLTHROUGH();
696 case QRhiTexture::RGBA32F:
697 return caps.floatFormats;
698
699 default:
700 break;
701 }
702
703 return true;
704}
705
706bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
707{
708 switch (feature) {
709 case QRhi::MultisampleTexture:
710 return false;
711 case QRhi::MultisampleRenderBuffer:
712 return caps.msaaRenderBuffer;
713 case QRhi::DebugMarkers:
714 return false;
715 case QRhi::Timestamps:
716 return false;
717 case QRhi::Instancing:
718 return caps.instancing;
719 case QRhi::CustomInstanceStepRate:
720 return false;
721 case QRhi::PrimitiveRestart:
722 return caps.fixedIndexPrimitiveRestart;
723 case QRhi::NonDynamicUniformBuffers:
724 return true;
725 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
726 return true;
727 case QRhi::NPOTTextureRepeat:
728 return caps.npotTextureRepeat;
729 case QRhi::RedOrAlpha8IsRed:
730 return caps.coreProfile;
731 case QRhi::ElementIndexUint:
732 return caps.elementIndexUint;
733 case QRhi::Compute:
734 return caps.compute;
735 case QRhi::WideLines:
736 return true;
737 case QRhi::VertexShaderPointSize:
738 return true;
739 case QRhi::BaseVertex:
740 return caps.baseVertex;
741 case QRhi::BaseInstance:
742 return false; // not in ES 3.2, so won't bother
743 default:
744 Q_UNREACHABLE();
745 return false;
746 }
747}
748
749int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
750{
751 switch (limit) {
752 case QRhi::TextureSizeMin:
753 return 1;
754 case QRhi::TextureSizeMax:
755 return caps.maxTextureSize;
756 case QRhi::MaxColorAttachments:
757 return caps.maxDrawBuffers;
758 case QRhi::FramesInFlight:
759 return 2; // dummy
760 default:
761 Q_UNREACHABLE();
762 return 0;
763 }
764}
765
766const QRhiNativeHandles *QRhiGles2::nativeHandles()
767{
768 return &nativeHandlesStruct;
769}
770
771void QRhiGles2::sendVMemStatsToProfiler()
772{
773 // nothing to do here
774}
775
776void QRhiGles2::makeThreadLocalNativeContextCurrent()
777{
778 if (inFrame && !ofr.active)
779 ensureContext(currentSwapChain->surface);
780 else
781 ensureContext();
782}
783
784QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
785 int sampleCount, QRhiRenderBuffer::Flags flags)
786{
787 return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags);
788}
789
790QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
791 int sampleCount, QRhiTexture::Flags flags)
792{
793 return new QGles2Texture(this, format, pixelSize, sampleCount, flags);
794}
795
796QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
797 QRhiSampler::Filter mipmapMode,
798 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
799{
800 return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v);
801}
802
803QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
804 QRhiTextureRenderTarget::Flags flags)
805{
806 return new QGles2TextureRenderTarget(this, desc, flags);
807}
808
809QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline()
810{
811 return new QGles2GraphicsPipeline(this);
812}
813
814QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings()
815{
816 return new QGles2ShaderResourceBindings(this);
817}
818
819QRhiComputePipeline *QRhiGles2::createComputePipeline()
820{
821 return new QGles2ComputePipeline(this);
822}
823
824void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
825{
826 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
827 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
828 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
829 const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
830
831 if (pipelineChanged) {
832 cbD->currentGraphicsPipeline = ps;
833 cbD->currentComputePipeline = nullptr;
834 cbD->currentPipelineGeneration = psD->generation;
835
836 QGles2CommandBuffer::Command cmd;
837 cmd.cmd = QGles2CommandBuffer::Command::BindGraphicsPipeline;
838 cmd.args.bindGraphicsPipeline.ps = ps;
839 cbD->commands.append(cmd);
840 }
841}
842
843void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
844 int dynamicOffsetCount,
845 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
846{
847 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
848 Q_ASSERT(cbD->recordingPass != QGles2CommandBuffer::NoPass);
849 QGles2GraphicsPipeline *gfxPsD = QRHI_RES(QGles2GraphicsPipeline, cbD->currentGraphicsPipeline);
850 QGles2ComputePipeline *compPsD = QRHI_RES(QGles2ComputePipeline, cbD->currentComputePipeline);
851
852 if (!srb) {
853 if (gfxPsD)
854 srb = gfxPsD->m_shaderResourceBindings;
855 else
856 srb = compPsD->m_shaderResourceBindings;
857 }
858
859 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
860 QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
861 bool hasDynamicOffsetInSrb = false;
862 for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
863 const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->m_bindings[i]);
864 switch (b->type) {
865 case QRhiShaderResourceBinding::UniformBuffer:
866 // no BufUniformRead / AccessUniform because no real uniform buffers are used
867 if (b->u.ubuf.hasDynamicOffset)
868 hasDynamicOffsetInSrb = true;
869 break;
870 case QRhiShaderResourceBinding::SampledTexture:
871 trackedRegisterTexture(&passResTracker,
872 QRHI_RES(QGles2Texture, b->u.stex.tex),
873 QRhiPassResourceTracker::TexSample,
874 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
875 break;
876 case QRhiShaderResourceBinding::ImageLoad:
877 Q_FALLTHROUGH();
878 case QRhiShaderResourceBinding::ImageStore:
879 Q_FALLTHROUGH();
880 case QRhiShaderResourceBinding::ImageLoadStore:
881 {
882 QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
883 QRhiPassResourceTracker::TextureAccess access;
884 if (b->type == QRhiShaderResourceBinding::ImageLoad)
885 access = QRhiPassResourceTracker::TexStorageLoad;
886 else if (b->type == QRhiShaderResourceBinding::ImageStore)
887 access = QRhiPassResourceTracker::TexStorageStore;
888 else
889 access = QRhiPassResourceTracker::TexStorageLoadStore;
890 trackedRegisterTexture(&passResTracker, texD, access,
891 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
892 }
893 break;
894 case QRhiShaderResourceBinding::BufferLoad:
895 Q_FALLTHROUGH();
896 case QRhiShaderResourceBinding::BufferStore:
897 Q_FALLTHROUGH();
898 case QRhiShaderResourceBinding::BufferLoadStore:
899 {
900 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
901 QRhiPassResourceTracker::BufferAccess access;
902 if (b->type == QRhiShaderResourceBinding::BufferLoad)
903 access = QRhiPassResourceTracker::BufStorageLoad;
904 else if (b->type == QRhiShaderResourceBinding::BufferStore)
905 access = QRhiPassResourceTracker::BufStorageStore;
906 else
907 access = QRhiPassResourceTracker::BufStorageLoadStore;
908 trackedRegisterBuffer(&passResTracker, bufD, access,
909 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
910 }
911 break;
912 default:
913 break;
914 }
915 }
916
917 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
918 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
919
920 if (srbChanged || srbRebuilt || hasDynamicOffsetInSrb) {
921 if (gfxPsD) {
922 cbD->currentGraphicsSrb = srb;
923 cbD->currentComputeSrb = nullptr;
924 } else {
925 cbD->currentGraphicsSrb = nullptr;
926 cbD->currentComputeSrb = srb;
927 }
928 cbD->currentSrbGeneration = srbD->generation;
929
930 QGles2CommandBuffer::Command cmd;
931 cmd.cmd = QGles2CommandBuffer::Command::BindShaderResources;
932 cmd.args.bindShaderResources.maybeGraphicsPs = gfxPsD;
933 cmd.args.bindShaderResources.maybeComputePs = compPsD;
934 cmd.args.bindShaderResources.srb = srb;
935 cmd.args.bindShaderResources.dynamicOffsetCount = 0;
936 if (hasDynamicOffsetInSrb) {
937 if (dynamicOffsetCount < QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS) {
938 cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
939 uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
940 for (int i = 0; i < dynamicOffsetCount; ++i) {
941 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
942 *p++ = dynOfs.first;
943 *p++ = dynOfs.second;
944 }
945 } else {
946 qWarning("Too many dynamic offsets (%d, max is %d)",
947 dynamicOffsetCount, QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS);
948 }
949 }
950 cbD->commands.append(cmd);
951 }
952}
953
954void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
955 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
956 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
957{
958 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
959 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
960 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
961
962 for (int i = 0; i < bindingCount; ++i) {
963 QRhiBuffer *buf = bindings[i].first;
964 quint32 ofs = bindings[i].second;
965 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
966 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
967
968 QGles2CommandBuffer::Command cmd;
969 cmd.cmd = QGles2CommandBuffer::Command::BindVertexBuffer;
970 cmd.args.bindVertexBuffer.ps = cbD->currentGraphicsPipeline;
971 cmd.args.bindVertexBuffer.buffer = bufD->buffer;
972 cmd.args.bindVertexBuffer.offset = ofs;
973 cmd.args.bindVertexBuffer.binding = startBinding + i;
974 cbD->commands.append(cmd);
975
976 trackedRegisterBuffer(&passResTracker, bufD, QRhiPassResourceTracker::BufVertexInput,
977 QRhiPassResourceTracker::BufVertexInputStage);
978 }
979
980 if (indexBuf) {
981 QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
982 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
983
984 QGles2CommandBuffer::Command cmd;
985 cmd.cmd = QGles2CommandBuffer::Command::BindIndexBuffer;
986 cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
987 cmd.args.bindIndexBuffer.offset = indexOffset;
988 cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
989 cbD->commands.append(cmd);
990
991 trackedRegisterBuffer(&passResTracker, ibufD, QRhiPassResourceTracker::BufIndexRead,
992 QRhiPassResourceTracker::BufVertexInputStage);
993 }
994}
995
996void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
997{
998 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
999 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1000
1001 QGles2CommandBuffer::Command cmd;
1002 cmd.cmd = QGles2CommandBuffer::Command::Viewport;
1003 const std::array<float, 4> r = viewport.viewport();
1004 cmd.args.viewport.x = qMax(0.0f, r[0]);
1005 cmd.args.viewport.y = qMax(0.0f, r[1]);
1006 cmd.args.viewport.w = r[2];
1007 cmd.args.viewport.h = r[3];
1008 cmd.args.viewport.d0 = viewport.minDepth();
1009 cmd.args.viewport.d1 = viewport.maxDepth();
1010 cbD->commands.append(cmd);
1011}
1012
1013void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
1014{
1015 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1016 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1017
1018 QGles2CommandBuffer::Command cmd;
1019 cmd.cmd = QGles2CommandBuffer::Command::Scissor;
1020 const std::array<int, 4> r = scissor.scissor();
1021 cmd.args.scissor.x = qMax(0, r[0]);
1022 cmd.args.scissor.y = qMax(0, r[1]);
1023 cmd.args.scissor.w = r[2];
1024 cmd.args.scissor.h = r[3];
1025 cbD->commands.append(cmd);
1026}
1027
1028void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
1029{
1030 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1031 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1032
1033 QGles2CommandBuffer::Command cmd;
1034 cmd.cmd = QGles2CommandBuffer::Command::BlendConstants;
1035 cmd.args.blendConstants.r = c.redF();
1036 cmd.args.blendConstants.g = c.greenF();
1037 cmd.args.blendConstants.b = c.blueF();
1038 cmd.args.blendConstants.a = c.alphaF();
1039 cbD->commands.append(cmd);
1040}
1041
1042void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
1043{
1044 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1045 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1046
1047 QGles2CommandBuffer::Command cmd;
1048 cmd.cmd = QGles2CommandBuffer::Command::StencilRef;
1049 cmd.args.stencilRef.ref = refValue;
1050 cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
1051 cbD->commands.append(cmd);
1052}
1053
1054void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
1055 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
1056{
1057 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1058 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1059
1060 QGles2CommandBuffer::Command cmd;
1061 cmd.cmd = QGles2CommandBuffer::Command::Draw;
1062 cmd.args.draw.ps = cbD->currentGraphicsPipeline;
1063 cmd.args.draw.vertexCount = vertexCount;
1064 cmd.args.draw.firstVertex = firstVertex;
1065 cmd.args.draw.instanceCount = instanceCount;
1066 cmd.args.draw.baseInstance = firstInstance;
1067 cbD->commands.append(cmd);
1068}
1069
1070void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
1071 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
1072{
1073 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1074 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
1075
1076 QGles2CommandBuffer::Command cmd;
1077 cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed;
1078 cmd.args.drawIndexed.ps = cbD->currentGraphicsPipeline;
1079 cmd.args.drawIndexed.indexCount = indexCount;
1080 cmd.args.drawIndexed.firstIndex = firstIndex;
1081 cmd.args.drawIndexed.instanceCount = instanceCount;
1082 cmd.args.drawIndexed.baseInstance = firstInstance;
1083 cmd.args.drawIndexed.baseVertex = vertexOffset;
1084 cbD->commands.append(cmd);
1085}
1086
1087void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
1088{
1089 if (!debugMarkers)
1090 return;
1091
1092 Q_UNUSED(cb);
1093 Q_UNUSED(name);
1094}
1095
1096void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
1097{
1098 if (!debugMarkers)
1099 return;
1100
1101 Q_UNUSED(cb);
1102}
1103
1104void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
1105{
1106 if (!debugMarkers)
1107 return;
1108
1109 Q_UNUSED(cb);
1110 Q_UNUSED(msg);
1111}
1112
1113const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
1114{
1115 Q_UNUSED(cb);
1116 return nullptr;
1117}
1118
1119void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
1120{
1121 Q_UNUSED(cb);
1122 flushCommandBuffer(); // also ensures the context is current
1123}
1124
1125void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
1126{
1127 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1128 Q_ASSERT(cbD->commands.isEmpty());
1129 cbD->resetCachedState();
1130 if (cbD->currentTarget)
1131 enqueueBindFramebuffer(cbD->currentTarget, cbD);
1132}
1133
1134static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Command::Cmd type)
1135{
1136 QGles2CommandBuffer::Command cmd;
1137 cmd.cmd = type;
1138 cb->commands.append(cmd);
1139}
1140
1141QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
1142{
1143 Q_UNUSED(flags);
1144
1145 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
1146 if (!ensureContext(swapChainD->surface))
1147 return QRhi::FrameOpError;
1148
1149 currentSwapChain = swapChainD;
1150
1151 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1152 QRHI_PROF_F(beginSwapChainFrame(swapChain));
1153
1154 executeDeferredReleases();
1155 swapChainD->cb.resetState();
1156
1157 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame);
1158
1159 return QRhi::FrameOpSuccess;
1160}
1161
1162QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1163{
1164 QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
1165 Q_ASSERT(currentSwapChain == swapChainD);
1166
1167 addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame);
1168
1169 if (!ensureContext(swapChainD->surface))
1170 return QRhi::FrameOpError;
1171
1172 executeCommandBuffer(&swapChainD->cb);
1173
1174 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1175 // this must be done before the swap
1176 QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
1177
1178 if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
1179 ctx->swapBuffers(swapChainD->surface);
1180 needsMakeCurrent = true;
1181 } else {
1182 f->glFlush();
1183 }
1184
1185 swapChainD->frameCount += 1;
1186 currentSwapChain = nullptr;
1187 return QRhi::FrameOpSuccess;
1188}
1189
1190QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
1191{
1192 if (!ensureContext())
1193 return QRhi::FrameOpError;
1194
1195 ofr.active = true;
1196
1197 executeDeferredReleases();
1198 ofr.cbWrapper.resetState();
1199
1200 addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame);
1201 *cb = &ofr.cbWrapper;
1202
1203 return QRhi::FrameOpSuccess;
1204}
1205
1206QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
1207{
1208 Q_ASSERT(ofr.active);
1209 ofr.active = false;
1210
1211 addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame);
1212
1213 if (!ensureContext())
1214 return QRhi::FrameOpError;
1215
1216 executeCommandBuffer(&ofr.cbWrapper);
1217
1218 return QRhi::FrameOpSuccess;
1219}
1220
1221QRhi::FrameOpResult QRhiGles2::finish()
1222{
1223 return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess;
1224}
1225
1226QRhi::FrameOpResult QRhiGles2::flushCommandBuffer()
1227{
1228 if (ofr.active) {
1229 Q_ASSERT(!currentSwapChain);
1230 if (!ensureContext())
1231 return QRhi::FrameOpError;
1232 executeCommandBuffer(&ofr.cbWrapper);
1233 ofr.cbWrapper.resetCommands();
1234 } else {
1235 Q_ASSERT(currentSwapChain);
1236 if (!ensureContext(currentSwapChain->surface))
1237 return QRhi::FrameOpError;
1238 executeCommandBuffer(&currentSwapChain->cb);
1239 currentSwapChain->cb.resetCommands();
1240 }
1241 return QRhi::FrameOpSuccess;
1242}
1243
1244void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
1245{
1246 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
1247 const QGles2Buffer::Access prevAccess = bufD->usageState.access;
1248 if (access == prevAccess)
1249 return;
1250
1251 if (prevAccess == QGles2Buffer::AccessStorageWrite || prevAccess == QGles2Buffer::AccessStorageReadWrite) {
1252 // Generating the minimal barrier set is way too complicated to do
1253 // correctly (prevAccess is overwritten so we won't have proper
1254 // tracking across multiple passes) so setting all barrier bits will do
1255 // for now.
1256 QGles2CommandBuffer::Command cmd;
1257 cmd.cmd = QGles2CommandBuffer::Command::Barrier;
1258 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
1259 cbD->commands.append(cmd);
1260 }
1261
1262 bufD->usageState.access = access;
1263}
1264
1265void QRhiGles2::trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access)
1266{
1267 Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
1268 const QGles2Texture::Access prevAccess = texD->usageState.access;
1269 if (access == prevAccess)
1270 return;
1271
1272 if (prevAccess == QGles2Texture::AccessStorageWrite || prevAccess == QGles2Texture::AccessStorageReadWrite) {
1273 QGles2CommandBuffer::Command cmd;
1274 cmd.cmd = QGles2CommandBuffer::Command::Barrier;
1275 cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
1276 cbD->commands.append(cmd);
1277 }
1278
1279 texD->usageState.access = access;
1280}
1281
1282void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
1283 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
1284{
1285 trackedImageBarrier(cbD, texD, QGles2Texture::AccessUpdate);
1286 const bool isCompressed = isCompressedFormat(texD->m_format);
1287 const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
1288 const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
1289 const QPoint dp = subresDesc.destinationTopLeft();
1290 const QByteArray rawData = subresDesc.data();
1291 if (!subresDesc.image().isNull()) {
1292 QImage img = subresDesc.image();
1293 QSize size = img.size();
1294 QGles2CommandBuffer::Command cmd;
1295 cmd.cmd = QGles2CommandBuffer::Command::SubImage;
1296 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
1297 const QPoint sp = subresDesc.sourceTopLeft();
1298 if (!subresDesc.sourceSize().isEmpty())
1299 size = subresDesc.sourceSize();
1300 img = img.copy(sp.x(), sp.y(), size.width(), size.height());
1301 }
1302 cmd.args.subImage.target = texD->target;
1303 cmd.args.subImage.texture = texD->texture;
1304 cmd.args.subImage.faceTarget = faceTargetBase + layer;
1305 cmd.args.subImage.level = level;
1306 cmd.args.subImage.dx = dp.x();
1307 cmd.args.subImage.dy = dp.y();
1308 cmd.args.subImage.w = size.width();
1309 cmd.args.subImage.h = size.height();
1310 cmd.args.subImage.glformat = texD->glformat;
1311 cmd.args.subImage.gltype = texD->gltype;
1312 cmd.args.subImage.rowStartAlign = 4;
1313 cmd.args.subImage.data = cbD->retainImage(img);
1314 cbD->commands.append(cmd);
1315 } else if (!rawData.isEmpty() && isCompressed) {
1316 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1317 : subresDesc.sourceSize();
1318 if (texD->specified) {
1319 QGles2CommandBuffer::Command cmd;
1320 cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
1321 cmd.args.compressedSubImage.target = texD->target;
1322 cmd.args.compressedSubImage.texture = texD->texture;
1323 cmd.args.compressedSubImage.faceTarget = faceTargetBase + layer;
1324 cmd.args.compressedSubImage.level = level;
1325 cmd.args.compressedSubImage.dx = dp.x();
1326 cmd.args.compressedSubImage.dy = dp.y();
1327 cmd.args.compressedSubImage.w = size.width();
1328 cmd.args.compressedSubImage.h = size.height();
1329 cmd.args.compressedSubImage.glintformat = texD->glintformat;
1330 cmd.args.compressedSubImage.size = rawData.size();
1331 cmd.args.compressedSubImage.data = cbD->retainData(rawData);
1332 cbD->commands.append(cmd);
1333 } else {
1334 QGles2CommandBuffer::Command cmd;
1335 cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
1336 cmd.args.compressedImage.target = texD->target;
1337 cmd.args.compressedImage.texture = texD->texture;
1338 cmd.args.compressedImage.faceTarget = faceTargetBase + layer;
1339 cmd.args.compressedImage.level = level;
1340 cmd.args.compressedImage.glintformat = texD->glintformat;
1341 cmd.args.compressedImage.w = size.width();
1342 cmd.args.compressedImage.h = size.height();
1343 cmd.args.compressedImage.size = rawData.size();
1344 cmd.args.compressedImage.data = cbD->retainData(rawData);
1345 cbD->commands.append(cmd);
1346 }
1347 } else if (!rawData.isEmpty()) {
1348 const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
1349 : subresDesc.sourceSize();
1350 quint32 bpl = 0;
1351 textureFormatInfo(texD->m_format, size, &bpl, nullptr);
1352 QGles2CommandBuffer::Command cmd;
1353 cmd.cmd = QGles2CommandBuffer::Command::SubImage;
1354 cmd.args.subImage.target = texD->target;
1355 cmd.args.subImage.texture = texD->texture;
1356 cmd.args.subImage.faceTarget = faceTargetBase + layer;
1357 cmd.args.subImage.level = level;
1358 cmd.args.subImage.dx = dp.x();
1359 cmd.args.subImage.dy = dp.y();
1360 cmd.args.subImage.w = size.width();
1361 cmd.args.subImage.h = size.height();
1362 cmd.args.subImage.glformat = texD->glformat;
1363 cmd.args.subImage.gltype = texD->gltype;
1364 // Default unpack alignment (row start aligment
1365 // requirement) is 4. QImage guarantees 4 byte aligned
1366 // row starts, but our raw data here does not.
1367 cmd.args.subImage.rowStartAlign = (bpl & 3) ? 1 : 4;
1368 cmd.args.subImage.data = cbD->retainData(rawData);
1369 cbD->commands.append(cmd);
1370 } else {
1371 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
1372 }
1373}
1374
1375void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1376{
1377 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1378 QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
1379
1380 for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
1381 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
1382 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
1383 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
1384 memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
1385 } else {
1386 trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
1387 QGles2CommandBuffer::Command cmd;
1388 cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
1389 cmd.args.bufferSubData.target = bufD->targetForDataOps;
1390 cmd.args.bufferSubData.buffer = bufD->buffer;
1391 cmd.args.bufferSubData.offset = u.offset;
1392 cmd.args.bufferSubData.size = u.data.size();
1393 cmd.args.bufferSubData.data = cbD->retainData(u.data);
1394 cbD->commands.append(cmd);
1395 }
1396 }
1397
1398 for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
1399 QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
1400 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
1401 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
1402 if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
1403 memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
1404 } else {
1405 trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
1406 QGles2CommandBuffer::Command cmd;
1407 cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
1408 cmd.args.bufferSubData.target = bufD->targetForDataOps;
1409 cmd.args.bufferSubData.buffer = bufD->buffer;
1410 cmd.args.bufferSubData.offset = u.offset;
1411 cmd.args.bufferSubData.size = u.data.size();
1412 cmd.args.bufferSubData.data = cbD->retainData(u.data);
1413 cbD->commands.append(cmd);
1414 }
1415 }
1416
1417 for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
1418 if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
1419 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.upload.tex);
1420 for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
1421 for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
1422 for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
1423 enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
1424 }
1425 }
1426 texD->specified = true;
1427 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
1428 Q_ASSERT(u.copy.src && u.copy.dst);
1429 QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src);
1430 QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst);
1431
1432 trackedImageBarrier(cbD, srcD, QGles2Texture::AccessRead);
1433 trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate);
1434
1435 const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
1436 // do not translate coordinates, even if sp is bottom-left from gl's pov
1437 const QPoint sp = u.copy.desc.sourceTopLeft();
1438 const QPoint dp = u.copy.desc.destinationTopLeft();
1439
1440 const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
1441 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
1442 const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
1443 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
1444
1445 QGles2CommandBuffer::Command cmd;
1446 cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
1447
1448 cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + u.copy.desc.sourceLayer();
1449 cmd.args.copyTex.srcTexture = srcD->texture;
1450 cmd.args.copyTex.srcLevel = u.copy.desc.sourceLevel();
1451 cmd.args.copyTex.srcX = sp.x();
1452 cmd.args.copyTex.srcY = sp.y();
1453
1454 cmd.args.copyTex.dstTarget = dstD->target;
1455 cmd.args.copyTex.dstTexture = dstD->texture;
1456 cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + u.copy.desc.destinationLayer();
1457 cmd.args.copyTex.dstLevel = u.copy.desc.destinationLevel();
1458 cmd.args.copyTex.dstX = dp.x();
1459 cmd.args.copyTex.dstY = dp.y();
1460
1461 cmd.args.copyTex.w = size.width();
1462 cmd.args.copyTex.h = size.height();
1463
1464 cbD->commands.append(cmd);
1465 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
1466 QGles2CommandBuffer::Command cmd;
1467 cmd.cmd = QGles2CommandBuffer::Command::ReadPixels;
1468 cmd.args.readPixels.result = u.read.result;
1469 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture());
1470 trackedImageBarrier(cbD, texD, QGles2Texture::AccessRead);
1471 cmd.args.readPixels.texture = texD ? texD->texture : 0;
1472 if (texD) {
1473 cmd.args.readPixels.w = texD->m_pixelSize.width();
1474 cmd.args.readPixels.h = texD->m_pixelSize.height();
1475 cmd.args.readPixels.format = texD->m_format;
1476 const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
1477 ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
1478 cmd.args.readPixels.readTarget = faceTargetBase + u.read.rb.layer();
1479 cmd.args.readPixels.level = u.read.rb.level();
1480 }
1481 cbD->commands.append(cmd);
1482 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
1483 QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex);
1484 trackedImageBarrier(cbD, texD, QGles2Texture::AccessFramebuffer);
1485 QGles2CommandBuffer::Command cmd;
1486 cmd.cmd = QGles2CommandBuffer::Command::GenMip;
1487 cmd.args.genMip.target = texD->target;
1488 cmd.args.genMip.texture = texD->texture;
1489 cbD->commands.append(cmd);
1490 }
1491 }
1492
1493 ud->free();
1494}
1495
1496static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
1497{
1498 switch (t) {
1499 case QRhiGraphicsPipeline::Triangles:
1500 return GL_TRIANGLES;
1501 case QRhiGraphicsPipeline::TriangleStrip:
1502 return GL_TRIANGLE_STRIP;
1503 case QRhiGraphicsPipeline::Lines:
1504 return GL_LINES;
1505 case QRhiGraphicsPipeline::LineStrip:
1506 return GL_LINE_STRIP;
1507 case QRhiGraphicsPipeline::Points:
1508 return GL_POINTS;
1509 default:
1510 Q_UNREACHABLE();
1511 return GL_TRIANGLES;
1512 }
1513}
1514
1515static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
1516{
1517 switch (c) {
1518 case QRhiGraphicsPipeline::Front:
1519 return GL_FRONT;
1520 case QRhiGraphicsPipeline::Back:
1521 return GL_BACK;
1522 default:
1523 Q_UNREACHABLE();
1524 return GL_BACK;
1525 }
1526}
1527
1528static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
1529{
1530 switch (f) {
1531 case QRhiGraphicsPipeline::CCW:
1532 return GL_CCW;
1533 case QRhiGraphicsPipeline::CW:
1534 return GL_CW;
1535 default:
1536 Q_UNREACHABLE();
1537 return GL_CCW;
1538 }
1539}
1540
1541static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
1542{
1543 switch (f) {
1544 case QRhiGraphicsPipeline::Zero:
1545 return GL_ZERO;
1546 case QRhiGraphicsPipeline::One:
1547 return GL_ONE;
1548 case QRhiGraphicsPipeline::SrcColor:
1549 return GL_SRC_COLOR;
1550 case QRhiGraphicsPipeline::OneMinusSrcColor:
1551 return GL_ONE_MINUS_SRC_COLOR;
1552 case QRhiGraphicsPipeline::DstColor:
1553 return GL_DST_COLOR;
1554 case QRhiGraphicsPipeline::OneMinusDstColor:
1555 return GL_ONE_MINUS_DST_COLOR;
1556 case QRhiGraphicsPipeline::SrcAlpha:
1557 return GL_SRC_ALPHA;
1558 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
1559 return GL_ONE_MINUS_SRC_ALPHA;
1560 case QRhiGraphicsPipeline::DstAlpha:
1561 return GL_DST_ALPHA;
1562 case QRhiGraphicsPipeline::OneMinusDstAlpha:
1563 return GL_ONE_MINUS_DST_ALPHA;
1564 case QRhiGraphicsPipeline::ConstantColor:
1565 return GL_CONSTANT_COLOR;
1566 case QRhiGraphicsPipeline::OneMinusConstantColor:
1567 return GL_ONE_MINUS_CONSTANT_COLOR;
1568 case QRhiGraphicsPipeline::ConstantAlpha:
1569 return GL_CONSTANT_ALPHA;
1570 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
1571 return GL_ONE_MINUS_CONSTANT_ALPHA;
1572 case QRhiGraphicsPipeline::SrcAlphaSaturate:
1573 return GL_SRC_ALPHA_SATURATE;
1574 case QRhiGraphicsPipeline::Src1Color:
1575 Q_FALLTHROUGH();
1576 case QRhiGraphicsPipeline::OneMinusSrc1Color:
1577 Q_FALLTHROUGH();
1578 case QRhiGraphicsPipeline::Src1Alpha:
1579 Q_FALLTHROUGH();
1580 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
1581 qWarning("Unsupported blend factor %d", f);
1582 return GL_ZERO;
1583 default:
1584 Q_UNREACHABLE();
1585 return GL_ZERO;
1586 }
1587}
1588
1589static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
1590{
1591 switch (op) {
1592 case QRhiGraphicsPipeline::Add:
1593 return GL_FUNC_ADD;
1594 case QRhiGraphicsPipeline::Subtract:
1595 return GL_FUNC_SUBTRACT;
1596 case QRhiGraphicsPipeline::ReverseSubtract:
1597 return GL_FUNC_REVERSE_SUBTRACT;
1598 case QRhiGraphicsPipeline::Min:
1599 return GL_MIN;
1600 case QRhiGraphicsPipeline::Max:
1601 return GL_MAX;
1602 default:
1603 Q_UNREACHABLE();
1604 return GL_FUNC_ADD;
1605 }
1606}
1607
1608static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
1609{
1610 switch (op) {
1611 case QRhiGraphicsPipeline::Never:
1612 return GL_NEVER;
1613 case QRhiGraphicsPipeline::Less:
1614 return GL_LESS;
1615 case QRhiGraphicsPipeline::Equal:
1616 return GL_EQUAL;
1617 case QRhiGraphicsPipeline::LessOrEqual:
1618 return GL_LEQUAL;
1619 case QRhiGraphicsPipeline::Greater:
1620 return GL_GREATER;
1621 case QRhiGraphicsPipeline::NotEqual:
1622 return GL_NOTEQUAL;
1623 case QRhiGraphicsPipeline::GreaterOrEqual:
1624 return GL_GEQUAL;
1625 case QRhiGraphicsPipeline::Always:
1626 return GL_ALWAYS;
1627 default:
1628 Q_UNREACHABLE();
1629 return GL_ALWAYS;
1630 }
1631}
1632
1633static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
1634{
1635 switch (op) {
1636 case QRhiGraphicsPipeline::StencilZero:
1637 return GL_ZERO;
1638 case QRhiGraphicsPipeline::Keep:
1639 return GL_KEEP;
1640 case QRhiGraphicsPipeline::Replace:
1641 return GL_REPLACE;
1642 case QRhiGraphicsPipeline::IncrementAndClamp:
1643 return GL_INCR;
1644 case QRhiGraphicsPipeline::DecrementAndClamp:
1645 return GL_DECR;
1646 case QRhiGraphicsPipeline::Invert:
1647 return GL_INVERT;
1648 case QRhiGraphicsPipeline::IncrementAndWrap:
1649 return GL_INCR_WRAP;
1650 case QRhiGraphicsPipeline::DecrementAndWrap:
1651 return GL_DECR_WRAP;
1652 default:
1653 Q_UNREACHABLE();
1654 return GL_KEEP;
1655 }
1656}
1657
1658static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
1659{
1660 switch (f) {
1661 case QRhiSampler::Nearest:
1662 if (m == QRhiSampler::None)
1663 return GL_NEAREST;
1664 else
1665 return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
1666 case QRhiSampler::Linear:
1667 if (m == QRhiSampler::None)
1668 return GL_LINEAR;
1669 else
1670 return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
1671 default:
1672 Q_UNREACHABLE();
1673 return GL_LINEAR;
1674 }
1675}
1676
1677static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
1678{
1679 switch (f) {
1680 case QRhiSampler::Nearest:
1681 return GL_NEAREST;
1682 case QRhiSampler::Linear:
1683 return GL_LINEAR;
1684 default:
1685 Q_UNREACHABLE();
1686 return GL_LINEAR;
1687 }
1688}
1689
1690static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
1691{
1692 switch (m) {
1693 case QRhiSampler::Repeat:
1694 return GL_REPEAT;
1695 case QRhiSampler::ClampToEdge:
1696 return GL_CLAMP_TO_EDGE;
1697 case QRhiSampler::Mirror:
1698 return GL_MIRRORED_REPEAT;
1699 case QRhiSampler::MirrorOnce:
1700 Q_FALLTHROUGH();
1701 case QRhiSampler::Border:
1702 qWarning("Unsupported wrap mode %d", m);
1703 return GL_CLAMP_TO_EDGE;
1704 default:
1705 Q_UNREACHABLE();
1706 return GL_CLAMP_TO_EDGE;
1707 }
1708}
1709
1710static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
1711{
1712 switch (op) {
1713 case QRhiSampler::Never:
1714 return GL_NEVER;
1715 case QRhiSampler::Less:
1716 return GL_LESS;
1717 case QRhiSampler::Equal:
1718 return GL_EQUAL;
1719 case QRhiSampler::LessOrEqual:
1720 return GL_LEQUAL;
1721 case QRhiSampler::Greater:
1722 return GL_GREATER;
1723 case QRhiSampler::NotEqual:
1724 return GL_NOTEQUAL;
1725 case QRhiSampler::GreaterOrEqual:
1726 return GL_GEQUAL;
1727 case QRhiSampler::Always:
1728 return GL_ALWAYS;
1729 default:
1730 Q_UNREACHABLE();
1731 return GL_NEVER;
1732 }
1733}
1734
1735static inline QGles2Buffer::Access toGlAccess(QRhiPassResourceTracker::BufferAccess access)
1736{
1737 switch (access) {
1738 case QRhiPassResourceTracker::BufVertexInput:
1739 return QGles2Buffer::AccessVertex;
1740 case QRhiPassResourceTracker::BufIndexRead:
1741 return QGles2Buffer::AccessIndex;
1742 case QRhiPassResourceTracker::BufUniformRead:
1743 return QGles2Buffer::AccessUniform;
1744 case QRhiPassResourceTracker::BufStorageLoad:
1745 return QGles2Buffer::AccessStorageRead;
1746 case QRhiPassResourceTracker::BufStorageStore:
1747 return QGles2Buffer::AccessStorageWrite;
1748 case QRhiPassResourceTracker::BufStorageLoadStore:
1749 return QGles2Buffer::AccessStorageReadWrite;
1750 default:
1751 Q_UNREACHABLE();
1752 break;
1753 }
1754 return QGles2Buffer::AccessNone;
1755}
1756
1757static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Buffer::UsageState &bufUsage)
1758{
1759 QRhiPassResourceTracker::UsageState u;
1760 u.layout = 0; // N/A
1761 u.access = bufUsage.access;
1762 u.stage = 0; // N/A
1763 return u;
1764}
1765
1766static inline QGles2Texture::Access toGlAccess(QRhiPassResourceTracker::TextureAccess access)
1767{
1768 switch (access) {
1769 case QRhiPassResourceTracker::TexSample:
1770 return QGles2Texture::AccessSample;
1771 case QRhiPassResourceTracker::TexColorOutput:
1772 return QGles2Texture::AccessFramebuffer;
1773 case QRhiPassResourceTracker::TexDepthOutput:
1774 return QGles2Texture::AccessFramebuffer;
1775 case QRhiPassResourceTracker::TexStorageLoad:
1776 return QGles2Texture::AccessStorageRead;
1777 case QRhiPassResourceTracker::TexStorageStore:
1778 return QGles2Texture::AccessStorageWrite;
1779 case QRhiPassResourceTracker::TexStorageLoadStore:
1780 return QGles2Texture::AccessStorageReadWrite;
1781 default:
1782 Q_UNREACHABLE();
1783 break;
1784 }
1785 return QGles2Texture::AccessNone;
1786}
1787
1788static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QGles2Texture::UsageState &texUsage)
1789{
1790 QRhiPassResourceTracker::UsageState u;
1791 u.layout = 0; // N/A
1792 u.access = texUsage.access;
1793 u.stage = 0; // N/A
1794 return u;
1795}
1796
1797void QRhiGles2::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
1798 QGles2Buffer *bufD,
1799 QRhiPassResourceTracker::BufferAccess access,
1800 QRhiPassResourceTracker::BufferStage stage)
1801{
1802 QGles2Buffer::UsageState &u(bufD->usageState);
1803 passResTracker->registerBuffer(bufD, 0, &access, &stage, toPassTrackerUsageState(u));
1804 u.access = toGlAccess(access);
1805}
1806
1807void QRhiGles2::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
1808 QGles2Texture *texD,
1809 QRhiPassResourceTracker::TextureAccess access,
1810 QRhiPassResourceTracker::TextureStage stage)
1811{
1812 QGles2Texture::UsageState &u(texD->usageState);
1813 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
1814 u.access = toGlAccess(access);
1815}
1816
1817void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
1818{
1819 QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
1820 GLenum indexType = GL_UNSIGNED_SHORT;
1821 quint32 indexStride = sizeof(quint16);
1822 quint32 indexOffset = 0;
1823
1824 for (const QGles2CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
1825 switch (cmd.cmd) {
1826 case QGles2CommandBuffer::Command::BeginFrame:
1827 if (caps.coreProfile) {
1828 if (!vao)
1829 f->glGenVertexArrays(1, &vao);
1830 f->glBindVertexArray(vao);
1831 }
1832 break;
1833 case QGles2CommandBuffer::Command::EndFrame:
1834 if (vao)
1835 f->glBindVertexArray(0);
1836 break;
1837 case QGles2CommandBuffer::Command::Viewport:
1838 f->glViewport(cmd.args.viewport.x, cmd.args.viewport.y, cmd.args.viewport.w, cmd.args.viewport.h);
1839 f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
1840 break;
1841 case QGles2CommandBuffer::Command::Scissor:
1842 f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
1843 break;
1844 case QGles2CommandBuffer::Command::BlendConstants:
1845 f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
1846 break;
1847 case QGles2CommandBuffer::Command::StencilRef:
1848 {
1849 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps);
1850 if (psD) {
1851 f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask);
1852 f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask);
1853 } else {
1854 qWarning("No graphics pipeline active for setStencilRef; ignored");
1855 }
1856 }
1857 break;
1858 case QGles2CommandBuffer::Command::BindVertexBuffer:
1859 {
1860 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
1861 if (psD) {
1862 const QVector<QRhiVertexInputBinding> bindings = psD->m_vertexInputLayout.bindings();
1863 const QVector<QRhiVertexInputAttribute> attributes = psD->m_vertexInputLayout.attributes();
1864 for (const QRhiVertexInputAttribute &a : attributes) {
1865 const int bindingIdx = a.binding();
1866 if (bindingIdx != cmd.args.bindVertexBuffer.binding)
1867 continue;
1868
1869 // we do not support more than one vertex buffer
1870 f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer);
1871
1872 const int stride = bindings[bindingIdx].stride();
1873 int size = 1;
1874 GLenum type = GL_FLOAT;
1875 bool normalize = false;
1876 switch (a.format()) {
1877 case QRhiVertexInputAttribute::Float4:
1878 type = GL_FLOAT;
1879 size = 4;
1880 break;
1881 case QRhiVertexInputAttribute::Float3:
1882 type = GL_FLOAT;
1883 size = 3;
1884 break;
1885 case QRhiVertexInputAttribute::Float2:
1886 type = GL_FLOAT;
1887 size = 2;
1888 break;
1889 case QRhiVertexInputAttribute::Float:
1890 type = GL_FLOAT;
1891 size = 1;
1892 break;
1893 case QRhiVertexInputAttribute::UNormByte4:
1894 type = GL_UNSIGNED_BYTE;
1895 normalize = true;
1896 size = 4;
1897 break;
1898 case QRhiVertexInputAttribute::UNormByte2:
1899 type = GL_UNSIGNED_BYTE;
1900 normalize = true;
1901 size = 2;
1902 break;
1903 case QRhiVertexInputAttribute::UNormByte:
1904 type = GL_UNSIGNED_BYTE;
1905 normalize = true;
1906 size = 1;
1907 break;
1908 default:
1909 break;
1910 }
1911
1912 const int locationIdx = a.location();
1913 quint32 ofs = a.offset() + cmd.args.bindVertexBuffer.offset;
1914 f->glVertexAttribPointer(locationIdx, size, type, normalize, stride,
1915 reinterpret_cast<const GLvoid *>(quintptr(ofs)));
1916 f->glEnableVertexAttribArray(locationIdx);
1917 if (bindings[bindingIdx].classification() == QRhiVertexInputBinding::PerInstance
1918 && caps.instancing)
1919 {
1920 f->glVertexAttribDivisor(locationIdx, bindings[bindingIdx].instanceStepRate());
1921 }
1922 }
1923 } else {
1924 qWarning("No graphics pipeline active for setVertexInput; ignored");
1925 }
1926 }
1927 break;
1928 case QGles2CommandBuffer::Command::BindIndexBuffer:
1929 indexType = cmd.args.bindIndexBuffer.type;
1930 indexStride = indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
1931 indexOffset = cmd.args.bindIndexBuffer.offset;
1932 f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cmd.args.bindIndexBuffer.buffer);
1933 break;
1934 case QGles2CommandBuffer::Command::Draw:
1935 {
1936 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps);
1937 if (psD) {
1938 if (cmd.args.draw.instanceCount == 1 || !caps.instancing) {
1939 f->glDrawArrays(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount);
1940 } else {
1941 f->glDrawArraysInstanced(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount,
1942 cmd.args.draw.instanceCount);
1943 }
1944 } else {
1945 qWarning("No graphics pipeline active for draw; ignored");
1946 }
1947 }
1948 break;
1949 case QGles2CommandBuffer::Command::DrawIndexed:
1950 {
1951 QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
1952 if (psD) {
1953 const GLvoid *ofs = reinterpret_cast<const GLvoid *>(
1954 quintptr(cmd.args.drawIndexed.firstIndex * indexStride + indexOffset));
1955 if (cmd.args.drawIndexed.instanceCount == 1 || !caps.instancing) {
1956 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
1957 f->glDrawElementsBaseVertex(psD->drawMode,
1958 cmd.args.drawIndexed.indexCount,
1959 indexType,
1960 ofs,
1961 cmd.args.drawIndexed.baseVertex);
1962 } else {
1963 f->glDrawElements(psD->drawMode,
1964 cmd.args.drawIndexed.indexCount,
1965 indexType,
1966 ofs);
1967 }
1968 } else {
1969 if (cmd.args.drawIndexed.baseVertex != 0 && caps.baseVertex) {
1970 f->glDrawElementsInstancedBaseVertex(psD->drawMode,
1971 cmd.args.drawIndexed.indexCount,
1972 indexType,
1973 ofs,
1974 cmd.args.drawIndexed.instanceCount,
1975 cmd.args.drawIndexed.baseVertex);
1976 } else {
1977 f->glDrawElementsInstanced(psD->drawMode,
1978 cmd.args.drawIndexed.indexCount,
1979 indexType,
1980 ofs,
1981 cmd.args.drawIndexed.instanceCount);
1982 }
1983 }
1984 } else {
1985 qWarning("No graphics pipeline active for drawIndexed; ignored");
1986 }
1987 }
1988 break;
1989 case QGles2CommandBuffer::Command::BindGraphicsPipeline:
1990 executeBindGraphicsPipeline(cmd.args.bindGraphicsPipeline.ps);
1991 break;
1992 case QGles2CommandBuffer::Command::BindShaderResources:
1993 bindShaderResources(cmd.args.bindShaderResources.maybeGraphicsPs,
1994 cmd.args.bindShaderResources.maybeComputePs,
1995 cmd.args.bindShaderResources.srb,
1996 cmd.args.bindShaderResources.dynamicOffsetPairs,
1997 cmd.args.bindShaderResources.dynamicOffsetCount);
1998 break;
1999 case QGles2CommandBuffer::Command::BindFramebuffer:
2000 if (cmd.args.bindFramebuffer.fbo) {
2001 f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo);
2002 if (caps.maxDrawBuffers > 1) {
2003 const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
2004 QVarLengthArray<GLenum, 8> bufs;
2005 for (int i = 0; i < colorAttCount; ++i)
2006 bufs.append(GL_COLOR_ATTACHMENT0 + i);
2007 f->glDrawBuffers(colorAttCount, bufs.constData());
2008 }
2009 } else {
2010 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2011 if (caps.maxDrawBuffers > 1) {
2012 GLenum bufs = GL_BACK;
2013 f->glDrawBuffers(1, &bufs);
2014 }
2015 }
2016 if (caps.srgbCapableDefaultFramebuffer) {
2017 if (cmd.args.bindFramebuffer.srgb)
2018 f->glEnable(GL_FRAMEBUFFER_SRGB);
2019 else
2020 f->glDisable(GL_FRAMEBUFFER_SRGB);
2021 }
2022 break;
2023 case QGles2CommandBuffer::Command::Clear:
2024 f->glDisable(GL_SCISSOR_TEST);
2025 if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
2026 f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2027 f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
2028 }
2029 if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
2030 f->glDepthMask(GL_TRUE);
2031 f->glClearDepthf(cmd.args.clear.d);
2032 }
2033 if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT)
2034 f->glClearStencil(cmd.args.clear.s);
2035 f->glClear(cmd.args.clear.mask);
2036 break;
2037 case QGles2CommandBuffer::Command::BufferSubData:
2038 f->glBindBuffer(cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
2039 f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
2040 cmd.args.bufferSubData.data);
2041 break;
2042 case QGles2CommandBuffer::Command::CopyTex:
2043 {
2044 GLuint fbo;
2045 f->glGenFramebuffers(1, &fbo);
2046 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2047 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2048 cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
2049 f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
2050 f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
2051 cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
2052 cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
2053 cmd.args.copyTex.w, cmd.args.copyTex.h);
2054 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2055 f->glDeleteFramebuffers(1, &fbo);
2056 }
2057 break;
2058 case QGles2CommandBuffer::Command::ReadPixels:
2059 {
2060 QRhiReadbackResult *result = cmd.args.readPixels.result;
2061 GLuint tex = cmd.args.readPixels.texture;
2062 GLuint fbo = 0;
2063 if (tex) {
2064 result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
2065 result->format = cmd.args.readPixels.format;
2066 f->glGenFramebuffers(1, &fbo);
2067 f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2068 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
2069 cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level);
2070 } else {
2071 result->pixelSize = currentSwapChain->pixelSize;
2072 result->format = QRhiTexture::RGBA8;
2073 // readPixels handles multisample resolving implicitly
2074 }
2075 result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4);
2076 // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
2077 f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(),
2078 GL_RGBA, GL_UNSIGNED_BYTE,
2079 result->data.data());
2080 if (fbo) {
2081 f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
2082 f->glDeleteFramebuffers(1, &fbo);
2083 }
2084 if (result->completed)
2085 result->completed();
2086 }
2087 break;
2088 case QGles2CommandBuffer::Command::SubImage:
2089 f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
2090 if (cmd.args.subImage.rowStartAlign != 4)
2091 f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
2092 f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
2093 cmd.args.subImage.dx, cmd.args.subImage.dy,
2094 cmd.args.subImage.w, cmd.args.subImage.h,
2095 cmd.args.subImage.glformat, cmd.args.subImage.gltype,
2096 cmd.args.subImage.data);
2097 if (cmd.args.subImage.rowStartAlign != 4)
2098 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
2099 break;
2100 case QGles2CommandBuffer::Command::CompressedImage:
2101 f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
2102 f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
2103 cmd.args.compressedImage.glintformat,
2104 cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0,
2105 cmd.args.compressedImage.size, cmd.args.compressedImage.data);
2106 break;
2107 case QGles2CommandBuffer::Command::