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 | |
44 | QT_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, ¶ms); |
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 | */ |
276 | QRhiGles2InitParams::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 | */ |
288 | QSurfaceFormat 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 | */ |
313 | QOffscreenSurface *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 | |
333 | QRhiGles2::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 | |
350 | bool 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 | |
374 | bool 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 | |
484 | void 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 | |
500 | void 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 | |
529 | QVector<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 | |
539 | int 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 | |
550 | static inline bool isPowerOfTwo(int x) |
551 | { |
552 | // Assumption: x >= 1 |
553 | return x == (x & -x); |
554 | } |
555 | |
556 | QSize 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 | |
587 | QRhiSwapChain *QRhiGles2::createSwapChain() |
588 | { |
589 | return new QGles2SwapChain(this); |
590 | } |
591 | |
592 | QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) |
593 | { |
594 | return new QGles2Buffer(this, type, usage, size); |
595 | } |
596 | |
597 | int QRhiGles2::ubufAlignment() const |
598 | { |
599 | return 256; |
600 | } |
601 | |
602 | bool QRhiGles2::isYUpInFramebuffer() const |
603 | { |
604 | return true; |
605 | } |
606 | |
607 | bool QRhiGles2::isYUpInNDC() const |
608 | { |
609 | return true; |
610 | } |
611 | |
612 | bool QRhiGles2::isClipDepthZeroToOne() const |
613 | { |
614 | return false; |
615 | } |
616 | |
617 | QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const |
618 | { |
619 | return QMatrix4x4(); // identity |
620 | } |
621 | |
622 | static 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 | |
674 | bool 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 | |
706 | bool 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 | |
749 | int 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 | |
766 | const QRhiNativeHandles *QRhiGles2::nativeHandles() |
767 | { |
768 | return &nativeHandlesStruct; |
769 | } |
770 | |
771 | void QRhiGles2::sendVMemStatsToProfiler() |
772 | { |
773 | // nothing to do here |
774 | } |
775 | |
776 | void QRhiGles2::makeThreadLocalNativeContextCurrent() |
777 | { |
778 | if (inFrame && !ofr.active) |
779 | ensureContext(currentSwapChain->surface); |
780 | else |
781 | ensureContext(); |
782 | } |
783 | |
784 | QRhiRenderBuffer *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 | |
790 | QRhiTexture *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 | |
796 | QRhiSampler *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 | |
803 | QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, |
804 | QRhiTextureRenderTarget::Flags flags) |
805 | { |
806 | return new QGles2TextureRenderTarget(this, desc, flags); |
807 | } |
808 | |
809 | QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline() |
810 | { |
811 | return new QGles2GraphicsPipeline(this); |
812 | } |
813 | |
814 | QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings() |
815 | { |
816 | return new QGles2ShaderResourceBindings(this); |
817 | } |
818 | |
819 | QRhiComputePipeline *QRhiGles2::createComputePipeline() |
820 | { |
821 | return new QGles2ComputePipeline(this); |
822 | } |
823 | |
824 | void 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 | |
843 | void 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 | |
954 | void 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 | |
996 | void 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 | |
1013 | void 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 | |
1028 | void 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 | |
1042 | void 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 | |
1054 | void 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 | |
1070 | void 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 | |
1087 | void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) |
1088 | { |
1089 | if (!debugMarkers) |
1090 | return; |
1091 | |
1092 | Q_UNUSED(cb); |
1093 | Q_UNUSED(name); |
1094 | } |
1095 | |
1096 | void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb) |
1097 | { |
1098 | if (!debugMarkers) |
1099 | return; |
1100 | |
1101 | Q_UNUSED(cb); |
1102 | } |
1103 | |
1104 | void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) |
1105 | { |
1106 | if (!debugMarkers) |
1107 | return; |
1108 | |
1109 | Q_UNUSED(cb); |
1110 | Q_UNUSED(msg); |
1111 | } |
1112 | |
1113 | const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb) |
1114 | { |
1115 | Q_UNUSED(cb); |
1116 | return nullptr; |
1117 | } |
1118 | |
1119 | void QRhiGles2::beginExternal(QRhiCommandBuffer *cb) |
1120 | { |
1121 | Q_UNUSED(cb); |
1122 | flushCommandBuffer(); // also ensures the context is current |
1123 | } |
1124 | |
1125 | void 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 | |
1134 | static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Command::Cmd type) |
1135 | { |
1136 | QGles2CommandBuffer::Command cmd; |
1137 | cmd.cmd = type; |
1138 | cb->commands.append(cmd); |
1139 | } |
1140 | |
1141 | QRhi::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 | |
1162 | QRhi::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 | |
1190 | QRhi::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 | |
1206 | QRhi::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 | |
1221 | QRhi::FrameOpResult QRhiGles2::finish() |
1222 | { |
1223 | return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess; |
1224 | } |
1225 | |
1226 | QRhi::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(¤tSwapChain->cb); |
1239 | currentSwapChain->cb.resetCommands(); |
1240 | } |
1241 | return QRhi::FrameOpSuccess; |
1242 | } |
1243 | |
1244 | void 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 | |
1265 | void 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 | |
1282 | void 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 | |
1375 | void 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 | |
1496 | static 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 | |
1515 | static 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 | |
1528 | static 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 | |
1541 | static 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 | |
1589 | static 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 | |
1608 | static 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 | |
1633 | static 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 | |
1658 | static 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 | |
1677 | static 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 | |
1690 | static 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 | |
1710 | static 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 | |
1735 | static 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 | |
1757 | static 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 | |
1766 | static 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 | |
1788 | static 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 | |
1797 | void 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 | |
1807 | void 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 | |
1817 | void 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:: |
---|