1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "submissioncontext_p.h"
5
6#include <Qt3DCore/private/qbuffer_p.h>
7#include <Qt3DRender/qgraphicsapifilter.h>
8#include <Qt3DRender/qparameter.h>
9#include <Qt3DRender/qcullface.h>
10#include <Qt3DRender/private/renderlogging_p.h>
11#include <Qt3DRender/private/shader_p.h>
12#include <Qt3DRender/private/material_p.h>
13#include <Qt3DRender/private/buffer_p.h>
14#include <Qt3DRender/private/attribute_p.h>
15#include <Qt3DRender/private/renderstates_p.h>
16#include <Qt3DRender/private/renderstateset_p.h>
17#include <Qt3DRender/private/rendertarget_p.h>
18#include <Qt3DRender/private/nodemanagers_p.h>
19#include <Qt3DRender/private/buffermanager_p.h>
20#include <Qt3DRender/private/managers_p.h>
21#include <Qt3DRender/private/attachmentpack_p.h>
22#include <Qt3DRender/private/stringtoint_p.h>
23#include <gltexture_p.h>
24#include <rendercommand_p.h>
25#include <graphicshelperinterface_p.h>
26#include <renderer_p.h>
27#include <glresourcemanagers_p.h>
28#include <renderbuffer_p.h>
29#include <glshader_p.h>
30#include <openglvertexarrayobject_p.h>
31#include <QOpenGLShaderProgram>
32
33#if !QT_CONFIG(opengles2)
34#include <QOpenGLFunctions_2_0>
35#include <QOpenGLFunctions_3_2_Core>
36#include <QOpenGLFunctions_3_3_Core>
37#include <QOpenGLFunctions_4_3_Core>
38#include <graphicshelpergl2_p.h>
39#include <graphicshelpergl3_2_p.h>
40#include <graphicshelpergl3_3_p.h>
41#include <graphicshelpergl4_p.h>
42#endif
43#include <graphicshelperes2_p.h>
44#include <graphicshelperes3_p.h>
45
46#include <private/qdebug_p.h>
47#include <QSurface>
48#include <QWindow>
49#include <QOpenGLTexture>
50#ifdef QT_OPENGL_LIB
51#include <QtOpenGL/QOpenGLDebugLogger>
52#endif
53
54QT_BEGIN_NAMESPACE
55
56#ifndef GL_READ_FRAMEBUFFER
57#define GL_READ_FRAMEBUFFER 0x8CA8
58#endif
59
60#ifndef GL_DRAW_FRAMEBUFFER
61#define GL_DRAW_FRAMEBUFFER 0x8CA9
62#endif
63
64namespace Qt3DRender {
65namespace Render {
66namespace OpenGL {
67
68
69static QHash<unsigned int, SubmissionContext*> static_contexts;
70
71unsigned int nextFreeContextId()
72{
73 for (unsigned int i=0; i < 0xffff; ++i) {
74 if (!static_contexts.contains(key: i))
75 return i;
76 }
77
78 qFatal(msg: "Couldn't find free context ID");
79 return 0;
80}
81
82namespace {
83
84GLBuffer::Type attributeTypeToGLBufferType(Qt3DCore::QAttribute::AttributeType type)
85{
86 switch (type) {
87 case Qt3DCore::QAttribute::VertexAttribute:
88 return GLBuffer::ArrayBuffer;
89 case Qt3DCore::QAttribute::IndexAttribute:
90 return GLBuffer::IndexBuffer;
91 case Qt3DCore::QAttribute::DrawIndirectAttribute:
92 return GLBuffer::DrawIndirectBuffer;
93 default:
94 Q_UNREACHABLE();
95 }
96}
97
98void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width, uint height, QAbstractTexture::TextureFormat format)
99{
100 switch (format) {
101 case QAbstractTexture::RGBA32F:
102 {
103 uchar *srcScanline = (uchar *)srcData + stride * (height - 1);
104 for (uint i = 0; i < height; ++i) {
105 uchar *dstScanline = img.scanLine(i);
106 float *pSrc = (float*)srcScanline;
107 for (uint j = 0; j < width; j++) {
108 *dstScanline++ = (uchar)(255.0f * qBound(min: 0.0f, val: pSrc[4*j+2], max: 1.0f));
109 *dstScanline++ = (uchar)(255.0f * qBound(min: 0.0f, val: pSrc[4*j+1], max: 1.0f));
110 *dstScanline++ = (uchar)(255.0f * qBound(min: 0.0f, val: pSrc[4*j+0], max: 1.0f));
111 *dstScanline++ = (uchar)(255.0f * qBound(min: 0.0f, val: pSrc[4*j+3], max: 1.0f));
112 }
113 srcScanline -= stride;
114 }
115 } break;
116 default:
117 {
118 uchar* srcScanline = (uchar *)srcData + stride * (height - 1);
119 for (uint i = 0; i < height; ++i) {
120 memcpy(dest: img.scanLine(i), src: srcScanline, n: stride);
121 srcScanline -= stride;
122 }
123 } break;
124 }
125}
126
127// Render States Helpers
128template<typename GenericState>
129void applyStateHelper(const GenericState *state, SubmissionContext *gc)
130{
131 Q_UNUSED(state);
132 Q_UNUSED(gc);
133}
134
135template<>
136void applyStateHelper<AlphaFunc>(const AlphaFunc *state, SubmissionContext *gc)
137{
138 const auto values = state->values();
139 gc->alphaTest(mode1: std::get<0>(t: values), mode2: std::get<1>(t: values));
140}
141
142template<>
143void applyStateHelper<BlendEquationArguments>(const BlendEquationArguments *state, SubmissionContext *gc)
144{
145 const auto values = state->values();
146 // Un-indexed BlendEquationArguments -> Use normal GL1.0 functions
147 if (std::get<5>(t: values) < 0) {
148 if (std::get<4>(t: values)) {
149 gc->openGLContext()->functions()->glEnable(GL_BLEND);
150 gc->openGLContext()->functions()->glBlendFuncSeparate(srcRGB: std::get<0>(t: values), dstRGB: std::get<1>(t: values), srcAlpha: std::get<2>(t: values), dstAlpha: std::get<3>(t: values));
151 } else {
152 gc->openGLContext()->functions()->glDisable(GL_BLEND);
153 }
154 }
155 // BlendEquationArguments for a particular Draw Buffer. Different behaviours for
156 // (1) 3.0-3.3: only enablei/disablei supported.
157 // (2) 4.0+: all operations supported.
158 // We just ignore blend func parameter for (1), so no warnings get
159 // printed.
160 else {
161 if (std::get<4>(t: values)) {
162 gc->enablei(GL_BLEND, index: std::get<5>(t: values));
163 if (gc->supportsDrawBuffersBlend()) {
164 gc->blendFuncSeparatei(buf: std::get<5>(t: values), sRGB: std::get<0>(t: values), dRGB: std::get<1>(t: values), sAlpha: std::get<2>(t: values), dAlpha: std::get<3>(t: values));
165 }
166 } else {
167 gc->disablei(GL_BLEND, index: std::get<5>(t: values));
168 }
169 }
170}
171
172template<>
173void applyStateHelper<BlendEquation>(const BlendEquation *state, SubmissionContext *gc)
174{
175 gc->blendEquation(mode: std::get<0>(t: state->values()));
176}
177
178template<>
179void applyStateHelper<MSAAEnabled>(const MSAAEnabled *state, SubmissionContext *gc)
180{
181 gc->setMSAAEnabled(std::get<0>(t: state->values()));
182}
183
184template<>
185void applyStateHelper<DepthRange>(const DepthRange *state, SubmissionContext *gc)
186{
187 const auto values = state->values();
188 gc->depthRange(nearValue: std::get<0>(t: values), farValue: std::get<1>(t: values));
189}
190
191template<>
192void applyStateHelper<DepthTest>(const DepthTest *state, SubmissionContext *gc)
193{
194 gc->depthTest(mode: std::get<0>(t: state->values()));
195}
196
197template<>
198void applyStateHelper<RasterMode>(const RasterMode *state, SubmissionContext *gc)
199{
200 gc->rasterMode(faceMode: std::get<0>(t: state->values()), rasterMode: std::get<1>(t: state->values()));
201}
202
203template<>
204void applyStateHelper<NoDepthMask>(const NoDepthMask *state, SubmissionContext *gc)
205{
206 gc->depthMask(mode: std::get<0>(t: state->values()));
207}
208
209template<>
210void applyStateHelper<CullFace>(const CullFace *state, SubmissionContext *gc)
211{
212 const auto values = state->values();
213 if (std::get<0>(t: values) == QCullFace::NoCulling) {
214 gc->openGLContext()->functions()->glDisable(GL_CULL_FACE);
215 } else {
216 gc->openGLContext()->functions()->glEnable(GL_CULL_FACE);
217 gc->openGLContext()->functions()->glCullFace(mode: std::get<0>(t: values));
218 }
219}
220
221template<>
222void applyStateHelper<FrontFace>(const FrontFace *state, SubmissionContext *gc)
223{
224 gc->frontFace(mode: std::get<0>(t: state->values()));
225}
226
227template<>
228void applyStateHelper<ScissorTest>(const ScissorTest *state, SubmissionContext *gc)
229{
230 const auto values = state->values();
231 gc->openGLContext()->functions()->glEnable(GL_SCISSOR_TEST);
232 gc->openGLContext()->functions()->glScissor(x: std::get<0>(t: values), y: std::get<1>(t: values), width: std::get<2>(t: values), height: std::get<3>(t: values));
233}
234
235template<>
236void applyStateHelper<StencilTest>(const StencilTest *state, SubmissionContext *gc)
237{
238 const auto values = state->values();
239 gc->openGLContext()->functions()->glEnable(GL_STENCIL_TEST);
240 gc->openGLContext()->functions()->glStencilFuncSeparate(GL_FRONT, func: std::get<0>(t: values), ref: std::get<1>(t: values), mask: std::get<2>(t: values));
241 gc->openGLContext()->functions()->glStencilFuncSeparate(GL_BACK, func: std::get<3>(t: values), ref: std::get<4>(t: values), mask: std::get<5>(t: values));
242}
243
244template<>
245void applyStateHelper<AlphaCoverage>(const AlphaCoverage *, SubmissionContext *gc)
246{
247 gc->setAlphaCoverageEnabled(true);
248}
249
250template<>
251void applyStateHelper<PointSize>(const PointSize *state, SubmissionContext *gc)
252{
253 const auto values = state->values();
254 gc->pointSize(programmable: std::get<0>(t: values), value: std::get<1>(t: values));
255}
256
257
258template<>
259void applyStateHelper<PolygonOffset>(const PolygonOffset *state, SubmissionContext *gc)
260{
261 const auto values = state->values();
262 gc->openGLContext()->functions()->glEnable(GL_POLYGON_OFFSET_FILL);
263 gc->openGLContext()->functions()->glPolygonOffset(factor: std::get<0>(t: values), units: std::get<1>(t: values));
264}
265
266template<>
267void applyStateHelper<ColorMask>(const ColorMask *state, SubmissionContext *gc)
268{
269 const auto values = state->values();
270 gc->openGLContext()->functions()->glColorMask(red: std::get<0>(t: values), green: std::get<1>(t: values), blue: std::get<2>(t: values), alpha: std::get<3>(t: values));
271}
272
273template<>
274void applyStateHelper<ClipPlane>(const ClipPlane *state, SubmissionContext *gc)
275{
276 const auto values = state->values();
277 gc->enableClipPlane(clipPlane: std::get<0>(t: values));
278 gc->setClipPlane(clipPlane: std::get<0>(t: values), normal: std::get<1>(t: values), distance: std::get<2>(t: values));
279}
280
281template<>
282void applyStateHelper<SeamlessCubemap>(const SeamlessCubemap *, SubmissionContext *gc)
283{
284 gc->setSeamlessCubemap(true);
285}
286
287template<>
288void applyStateHelper<StencilOp>(const StencilOp *state, SubmissionContext *gc)
289{
290 const auto values = state->values();
291 gc->openGLContext()->functions()->glStencilOpSeparate(GL_FRONT, fail: std::get<0>(t: values), zfail: std::get<1>(t: values), zpass: std::get<2>(t: values));
292 gc->openGLContext()->functions()->glStencilOpSeparate(GL_BACK, fail: std::get<3>(t: values), zfail: std::get<4>(t: values), zpass: std::get<5>(t: values));
293}
294
295template<>
296void applyStateHelper<StencilMask>(const StencilMask *state, SubmissionContext *gc)
297{
298 const auto values = state->values();
299 gc->openGLContext()->functions()->glStencilMaskSeparate(GL_FRONT, mask: std::get<0>(t: values));
300 gc->openGLContext()->functions()->glStencilMaskSeparate(GL_BACK, mask: std::get<1>(t: values));
301}
302
303template<>
304void applyStateHelper<Dithering>(const Dithering *, SubmissionContext *gc)
305{
306 gc->openGLContext()->functions()->glEnable(GL_DITHER);
307}
308
309#ifndef GL_LINE_SMOOTH
310#define GL_LINE_SMOOTH 0x0B20
311#endif
312
313template<>
314void applyStateHelper<LineWidth>(const LineWidth *state, SubmissionContext *gc)
315{
316 const auto values = state->values();
317 if (std::get<1>(t: values))
318 gc->openGLContext()->functions()->glEnable(GL_LINE_SMOOTH);
319 else
320 gc->openGLContext()->functions()->glDisable(GL_LINE_SMOOTH);
321
322 gc->openGLContext()->functions()->glLineWidth(width: std::get<0>(t: values));
323}
324
325GLint glAttachmentPoint(const QRenderTargetOutput::AttachmentPoint &attachmentPoint)
326{
327 if (attachmentPoint <= QRenderTargetOutput::Color15)
328 return GL_COLOR_ATTACHMENT0 + attachmentPoint;
329
330 switch (attachmentPoint) {
331 case QRenderTargetOutput::Depth:
332 case QRenderTargetOutput::DepthStencil:
333 return GL_DEPTH_ATTACHMENT;
334 case QRenderTargetOutput::Stencil:
335 return GL_STENCIL_ATTACHMENT;
336 default:
337 Q_UNREACHABLE_RETURN(GL_NONE);
338 }
339}
340
341} // anonymous
342
343
344SubmissionContext::SubmissionContext()
345 : GraphicsContext()
346 , m_ownCurrent(true)
347 , m_id(nextFreeContextId())
348 , m_surface(nullptr)
349 , m_activeShader(nullptr)
350 , m_renderTargetFormat(QAbstractTexture::NoFormat)
351 , m_currClearStencilValue(0)
352 , m_currClearDepthValue(1.f)
353 , m_currClearColorValue(0,0,0,0)
354 , m_material(nullptr)
355 , m_activeFBO(0)
356 , m_boundArrayBuffer(nullptr)
357 , m_stateSet(nullptr)
358 , m_renderer(nullptr)
359 , m_uboTempArray(QByteArray(1024, 0))
360{
361 static_contexts[m_id] = this;
362}
363
364SubmissionContext::~SubmissionContext()
365{
366 releaseOpenGL();
367
368 Q_ASSERT(static_contexts[m_id] == this);
369 static_contexts.remove(key: m_id);
370}
371
372void SubmissionContext::initialize()
373{
374 GraphicsContext::initialize();
375 m_textureContext.initialize(context: this);
376 m_imageContext.initialize(context: this);
377}
378
379void SubmissionContext::resolveRenderTargetFormat()
380{
381 const QSurfaceFormat format = m_gl->format();
382 const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize();
383 const uint r = format.redBufferSize();
384 const uint g = format.greenBufferSize();
385 const uint b = format.blueBufferSize();
386
387#define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18))
388
389 const uint bits = RGBA_BITS(r,g,b,a);
390 switch (bits) {
391 case RGBA_BITS(8,8,8,8):
392 m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm;
393 break;
394 case RGBA_BITS(8,8,8,0):
395 m_renderTargetFormat = QAbstractTexture::RGB8_UNorm;
396 break;
397 case RGBA_BITS(5,6,5,0):
398 m_renderTargetFormat = QAbstractTexture::R5G6B5;
399 break;
400 }
401#undef RGBA_BITS
402}
403
404bool SubmissionContext::beginDrawing(QSurface *surface)
405{
406 Q_ASSERT(surface);
407 Q_ASSERT(m_gl);
408
409 m_surface = surface;
410
411 // TO DO: Find a way to make to pause work if the window is not exposed
412 // if (m_surface && m_surface->surfaceClass() == QSurface::Window) {
413 // qDebug() << Q_FUNC_INFO << 1;
414 // if (!static_cast<QWindow *>(m_surface)->isExposed())
415 // return false;
416 // qDebug() << Q_FUNC_INFO << 2;
417 // }
418
419 // Makes the surface current on the OpenGLContext
420 // and sets the right glHelper
421 m_ownCurrent = !(m_gl->surface() == m_surface);
422 if (m_ownCurrent && !makeCurrent(surface: m_surface))
423 return false;
424
425 // TODO: cache surface format somewhere rather than doing this every time render surface changes
426 resolveRenderTargetFormat();
427
428#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
429 GLint err = m_gl->functions()->glGetError();
430 if (err != 0) {
431 qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err;
432 }
433#endif
434
435 if (!isInitialized())
436 initialize();
437 initializeHelpers(surface: m_surface);
438
439 // need to reset these values every frame, may get overwritten elsewhere
440 resetState();
441
442 if (m_activeShader) {
443 m_activeShader = nullptr;
444 }
445
446 m_boundArrayBuffer = nullptr;
447
448 // Record the default FBO value as there's no guarantee it remains constant over time
449 m_defaultFBO = m_gl->defaultFramebufferObject();
450
451 return true;
452}
453
454void SubmissionContext::endDrawing(bool swapBuffers)
455{
456 if (swapBuffers)
457 m_gl->swapBuffers(surface: m_surface);
458 if (m_ownCurrent)
459 m_gl->doneCurrent();
460 m_textureContext.endDrawing();
461 m_imageContext.endDrawing();
462}
463
464void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId)
465{
466 GLuint fboId = defaultFboId; // Default FBO
467 resolveRenderTargetFormat(); // Reset m_renderTargetFormat based on the default FBO
468
469 if (renderTargetNodeId) {
470 // New RenderTarget
471 if (!m_renderTargets.contains(key: renderTargetNodeId)) {
472 if (m_defaultFBO && fboId == m_defaultFBO) {
473 // this is the default fbo that some platforms create (iOS), we never
474 // register it
475 } else {
476 fboId = createRenderTarget(renderTargetNodeId, attachments);
477 }
478 } else {
479 fboId = updateRenderTarget(renderTargetNodeId, attachments, isActiveRenderTarget: true); // Overwrites m_renderTargetFormat based on custom FBO
480 }
481 }
482
483 m_activeFBO = fboId;
484 m_activeFBONodeId = renderTargetNodeId;
485 m_glHelper->bindFrameBufferObject(frameBufferId: m_activeFBO, mode: GraphicsHelperInterface::FBODraw);
486 // Set active drawBuffers
487 activateDrawBuffers(attachments);
488}
489
490void SubmissionContext::releaseRenderTarget(const Qt3DCore::QNodeId id)
491{
492 if (m_renderTargets.contains(key: id)) {
493 const RenderTargetInfo targetInfo = m_renderTargets.take(key: id);
494 const GLuint fboId = targetInfo.fboId;
495 m_glHelper->releaseFrameBufferObject(frameBufferId: fboId);
496 }
497}
498
499GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments)
500{
501 const GLuint fboId = m_glHelper->createFrameBufferObject();
502 if (fboId) {
503 // The FBO is created and its attachments are set once
504 // Bind FBO
505 m_glHelper->bindFrameBufferObject(frameBufferId: fboId, mode: GraphicsHelperInterface::FBODraw);
506 // Insert FBO into hash
507 const RenderTargetInfo info = bindFrameBufferAttachmentHelper(fboId, attachments);
508 m_renderTargets.insert(key: renderTargetNodeId, value: info);
509 } else {
510 qCritical(msg: "Failed to create FBO");
511 }
512 return fboId;
513}
514
515GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget)
516{
517 const RenderTargetInfo fboInfo = m_renderTargets.value(key: renderTargetNodeId);
518 const GLuint fboId =fboInfo.fboId;
519
520 // We need to check if one of the attachnent have changed QTBUG-64757
521 bool needsRebuild = attachments != fboInfo.attachments;
522
523 // Even if the attachment packs are the same, one of the inner texture might have
524 // been resized or recreated, we need to check for that
525 if (!needsRebuild) {
526 // render target exists, has attachment been resized?
527 GLTextureManager *glTextureManager = m_renderer->glResourceManagers()->glTextureManager();
528 const QSize s = fboInfo.size;
529
530 const auto attachments_ = attachments.attachments();
531 for (const Attachment &attachment : attachments_) {
532 const bool textureWasUpdated = m_updateTextureIds.contains(t: attachment.m_textureUuid);
533 GLTexture *rTex = glTextureManager->lookupResource(id: attachment.m_textureUuid);
534 if (rTex) {
535 const bool sizeHasChanged = rTex->size() != s;
536 needsRebuild |= sizeHasChanged;
537 if (isActiveRenderTarget && attachment.m_point == QRenderTargetOutput::Color0)
538 m_renderTargetFormat = rTex->properties().format;
539 }
540 needsRebuild |= textureWasUpdated;
541 }
542 }
543
544 if (needsRebuild) {
545 m_glHelper->bindFrameBufferObject(frameBufferId: fboId, mode: GraphicsHelperInterface::FBODraw);
546 const RenderTargetInfo updatedInfo = bindFrameBufferAttachmentHelper(fboId, attachments);
547 // Update our stored Render Target Info
548 m_renderTargets.insert(key: renderTargetNodeId, value: updatedInfo);
549 }
550
551 return fboId;
552}
553
554void SubmissionContext::releaseRenderTargets()
555{
556 const auto keys = m_renderTargets.keys();
557 for (Qt3DCore::QNodeId renderTargetId : keys)
558 releaseRenderTarget(id: renderTargetId);
559}
560
561QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const
562{
563 QSize renderTargetSize;
564 if (m_activeFBO != m_defaultFBO) {
565 // For external FBOs we may not have a m_renderTargets entry.
566 if (m_renderTargets.contains(key: m_activeFBONodeId)) {
567 renderTargetSize = m_renderTargets[m_activeFBONodeId].size;
568 } else if (surfaceSize.isValid()) {
569 renderTargetSize = surfaceSize;
570 } else {
571 // External FBO (when used with QtQuick2 Scene3D)
572
573 // Query FBO color attachment 0 size
574 GLint attachmentObjectType = GL_NONE;
575 GLint attachment0Name = 0;
576 m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
577 GL_COLOR_ATTACHMENT0,
578 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
579 params: &attachmentObjectType);
580 m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
581 GL_COLOR_ATTACHMENT0,
582 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
583 params: &attachment0Name);
584
585 if (attachmentObjectType == GL_RENDERBUFFER && m_glHelper->supportsFeature(feature: GraphicsHelperInterface::RenderBufferDimensionRetrieval))
586 renderTargetSize = m_glHelper->getRenderBufferDimensions(renderBufferId: attachment0Name);
587 else if (attachmentObjectType == GL_TEXTURE && m_glHelper->supportsFeature(feature: GraphicsHelperInterface::TextureDimensionRetrieval))
588 // Assumes texture level 0 and GL_TEXTURE_2D target
589 renderTargetSize = m_glHelper->getTextureDimensions(textureId: attachment0Name, GL_TEXTURE_2D);
590 else
591 return renderTargetSize;
592 }
593 } else {
594 renderTargetSize = m_surface->size().isValid() ? m_surface->size() : surfaceSize;
595 if (m_surface->surfaceClass() == QSurface::Window) {
596 const float dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio();
597 renderTargetSize *= dpr;
598 }
599 }
600 return renderTargetSize;
601}
602
603QImage SubmissionContext::readFramebuffer(const QRect &rect)
604{
605 QImage img;
606 const unsigned int area = rect.width() * rect.height();
607 unsigned int bytes;
608 GLenum format, type;
609 QImage::Format imageFormat;
610 uint stride;
611
612 // m_renderTargetFormat is set when the current RV FBO is set in activateRenderTarget
613 /* format value should match GL internalFormat */
614 GLenum internalFormat = m_renderTargetFormat;
615
616 switch (m_renderTargetFormat) {
617 case QAbstractTexture::RGBAFormat:
618 case QAbstractTexture::RGBA8_SNorm:
619 case QAbstractTexture::RGBA8_UNorm:
620 case QAbstractTexture::RGBA8U:
621 case QAbstractTexture::SRGB8_Alpha8:
622#if QT_CONFIG(opengles2)
623 format = GL_RGBA;
624 imageFormat = QImage::Format_RGBA8888_Premultiplied;
625#else
626 format = GL_BGRA;
627 imageFormat = QImage::Format_ARGB32_Premultiplied;
628 internalFormat = GL_RGBA8;
629#endif
630 type = GL_UNSIGNED_BYTE;
631 bytes = area * 4;
632 stride = rect.width() * 4;
633 break;
634 case QAbstractTexture::SRGB8:
635 case QAbstractTexture::RGBFormat:
636 case QAbstractTexture::RGB8U:
637 case QAbstractTexture::RGB8_UNorm:
638#if QT_CONFIG(opengles2)
639 format = GL_RGBA;
640 imageFormat = QImage::Format_RGBX8888;
641#else
642 format = GL_BGRA;
643 imageFormat = QImage::Format_RGB32;
644 internalFormat = GL_RGB8;
645#endif
646 type = GL_UNSIGNED_BYTE;
647 bytes = area * 4;
648 stride = rect.width() * 4;
649 break;
650#if !QT_CONFIG(opengles2)
651 case QAbstractTexture::RG11B10F:
652 bytes = area * 4;
653 format = GL_RGB;
654 type = GL_UNSIGNED_INT_10F_11F_11F_REV;
655 imageFormat = QImage::Format_RGB30;
656 stride = rect.width() * 4;
657 break;
658 case QAbstractTexture::RGB10A2:
659 bytes = area * 4;
660 format = GL_RGBA;
661 type = GL_UNSIGNED_INT_2_10_10_10_REV;
662 imageFormat = QImage::Format_A2BGR30_Premultiplied;
663 stride = rect.width() * 4;
664 break;
665 case QAbstractTexture::R5G6B5:
666 bytes = area * 2;
667 format = GL_RGB;
668 type = GL_UNSIGNED_SHORT;
669 internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV;
670 imageFormat = QImage::Format_RGB16;
671 stride = rect.width() * 2;
672 break;
673 case QAbstractTexture::RGBA16F:
674 case QAbstractTexture::RGBA16U:
675 case QAbstractTexture::RGBA32F:
676 case QAbstractTexture::RGBA32U:
677 bytes = area * 16;
678 format = GL_RGBA;
679 type = GL_FLOAT;
680 imageFormat = QImage::Format_ARGB32_Premultiplied;
681 stride = rect.width() * 16;
682 break;
683#endif
684 default:
685 auto warning = qWarning();
686 warning << "Unable to convert";
687 QtDebugUtils::formatQEnum(debug&: warning, value: m_renderTargetFormat);
688 warning << "render target texture format to QImage.";
689 return img;
690 }
691
692 GLint samples = 0;
693 m_gl->functions()->glGetIntegerv(GL_SAMPLES, params: &samples);
694 if (samples > 0 && !m_glHelper->supportsFeature(feature: GraphicsHelperInterface::BlitFramebuffer)) {
695 qCWarning(Backend) << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; "
696 "Required feature BlitFramebuffer is missing.";
697 return img;
698 }
699
700 img = QImage(rect.width(), rect.height(), imageFormat);
701
702 QScopedArrayPointer<uchar> data(new uchar [bytes]);
703
704 if (samples > 0) {
705 // resolve multisample-framebuffer to renderbuffer and read pixels from it
706 GLuint fbo, rb;
707 QOpenGLFunctions *gl = m_gl->functions();
708 gl->glGenFramebuffers(n: 1, framebuffers: &fbo);
709 gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer: fbo);
710 gl->glGenRenderbuffers(n: 1, renderbuffers: &rb);
711 gl->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: rb);
712 gl->glRenderbufferStorage(GL_RENDERBUFFER, internalformat: internalFormat, width: rect.width(), height: rect.height());
713 gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer: rb);
714
715 const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
716 if (status != GL_FRAMEBUFFER_COMPLETE) {
717 gl->glDeleteRenderbuffers(n: 1, renderbuffers: &rb);
718 gl->glDeleteFramebuffers(n: 1, framebuffers: &fbo);
719 qCWarning(Backend) << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status;
720 return img;
721 }
722
723 m_glHelper->blitFramebuffer(srcX0: rect.x(), srcY0: rect.y(), srcX1: rect.x() + rect.width(), srcY1: rect.y() + rect.height(),
724 dstX0: 0, dstY0: 0, dstX1: rect.width(), dstY1: rect.height(),
725 GL_COLOR_BUFFER_BIT, GL_NEAREST);
726 gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer: fbo);
727 gl->glReadPixels(x: 0,y: 0,width: rect.width(), height: rect.height(), format, type, pixels: data.data());
728
729 copyGLFramebufferDataToImage(img, srcData: data.data(), stride, width: rect.width(), height: rect.height(), format: m_renderTargetFormat);
730
731 gl->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: rb);
732 gl->glDeleteRenderbuffers(n: 1, renderbuffers: &rb);
733 gl->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_activeFBO);
734 gl->glDeleteFramebuffers(n: 1, framebuffers: &fbo);
735 } else {
736 // read pixels directly from framebuffer
737 m_gl->functions()->glReadPixels(x: rect.x(), y: rect.y(), width: rect.width(), height: rect.height(), format, type, pixels: data.data());
738 copyGLFramebufferDataToImage(img, srcData: data.data(), stride, width: rect.width(), height: rect.height(), format: m_renderTargetFormat);
739 }
740
741 return img;
742}
743
744void SubmissionContext::setViewport(const QRectF &viewport, const QSize &surfaceSize)
745{
746 // // save for later use; this has nothing to do with the viewport but it is
747 // // here that we get to know the surfaceSize from the RenderView.
748 m_surfaceSize = surfaceSize;
749
750 m_viewport = viewport;
751 QSize size = renderTargetSize(surfaceSize);
752
753 // Check that the returned size is before calling glViewport
754 if (size.isEmpty())
755 return;
756
757 // Qt3D 0------------------> 1 OpenGL 1^
758 // | |
759 // | |
760 // | |
761 // V |
762 // 1 0---------------------> 1
763 // The Viewport is defined between 0 and 1 which allows us to automatically
764 // scale to the size of the provided window surface
765 m_gl->functions()->glViewport(x: m_viewport.x() * size.width(),
766 y: (1.0 - m_viewport.y() - m_viewport.height()) * size.height(),
767 width: m_viewport.width() * size.width(),
768 height: m_viewport.height() * size.height());
769}
770
771void SubmissionContext::releaseOpenGL()
772{
773 m_renderBufferHash.clear();
774
775 // Stop and destroy the OpenGL logger
776#ifdef QT_OPENGL_LIB
777 if (m_debugLogger) {
778 m_debugLogger->stopLogging();
779 m_debugLogger.reset(other: nullptr);
780 }
781#endif
782}
783
784// The OpenGLContext is not current on any surface at this point
785void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx)
786{
787 Q_ASSERT(ctx);
788
789 releaseOpenGL();
790 m_gl = ctx;
791}
792
793// Called only from RenderThread
794bool SubmissionContext::activateShader(GLShader *shader)
795{
796 if (shader->shaderProgram() != m_activeShader) {
797 // Ensure material uniforms are re-applied
798 m_material = nullptr;
799
800 m_activeShader = shader->shaderProgram();
801 if (Q_LIKELY(m_activeShader != nullptr)) {
802 m_activeShader->bind();
803 } else {
804 m_glHelper->useProgram(programId: 0);
805 qWarning() << "No shader program found";
806 return false;
807 }
808 }
809 return true;
810}
811
812SubmissionContext::RenderTargetInfo SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments)
813{
814 // Set FBO attachments. These are normally textures, except that on Open GL
815 // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is
816 // desired since this cannot be achieved neither with a single texture (not
817 // before GLES 3.2) nor with separate textures (no suitable format for
818 // stencil before 3.1 with the appropriate extension).
819
820 QSize fboSize;
821 GLTextureManager *glTextureManager = m_renderer->glResourceManagers()->glTextureManager();
822 const auto attachments_ = attachments.attachments();
823 for (const Attachment &attachment : attachments_) {
824 GLTexture *rTex = glTextureManager->lookupResource(id: attachment.m_textureUuid);
825 if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) {
826 QOpenGLTexture *glTex = rTex ? rTex->getGLTexture() : nullptr;
827 if (glTex != nullptr) {
828 // The texture can not be rendered simultaniously by another renderer
829 Q_ASSERT(!rTex->isExternalRenderingEnabled());
830 if (fboSize.isEmpty())
831 fboSize = QSize(glTex->width(), glTex->height());
832 else
833 fboSize = QSize(qMin(a: fboSize.width(), b: glTex->width()), qMin(a: fboSize.height(), b: glTex->height()));
834 m_glHelper->bindFrameBufferAttachment(texture: glTex, attachment);
835 }
836 } else {
837 RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr;
838 if (renderBuffer) {
839 if (fboSize.isEmpty())
840 fboSize = QSize(renderBuffer->width(), renderBuffer->height());
841 else
842 fboSize = QSize(qMin(a: fboSize.width(), b: renderBuffer->width()), qMin(a: fboSize.height(), b: renderBuffer->height()));
843 m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment);
844 }
845 }
846 }
847 return {.fboId: fboId, .size: fboSize, .attachments: attachments};
848}
849
850void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments)
851{
852 const std::vector<int> &activeDrawBuffers = attachments.getGlDrawBuffers();
853
854 if (m_glHelper->checkFrameBufferComplete()) {
855 if (activeDrawBuffers.size() > 1) {// We need MRT
856 if (m_glHelper->supportsFeature(feature: GraphicsHelperInterface::MRT)) {
857 // Set up MRT, glDrawBuffers...
858 m_glHelper->drawBuffers(n: GLsizei(activeDrawBuffers.size()), bufs: activeDrawBuffers.data());
859 }
860 }
861 } else {
862 qCWarning(Backend) << "FBO incomplete";
863 }
864}
865
866
867void SubmissionContext::setActiveMaterial(Material *rmat)
868{
869 if (m_material == rmat)
870 return;
871
872 m_textureContext.deactivateTexturesWithScope(ts: TextureSubmissionContext::TextureScopeMaterial);
873 m_imageContext.deactivateImages();
874 m_material = rmat;
875}
876
877void SubmissionContext::setCurrentStateSet(RenderStateSet *ss)
878{
879 if (ss == m_stateSet)
880 return;
881 if (ss)
882 applyStateSet(ss);
883 m_stateSet = ss;
884}
885
886RenderStateSet *SubmissionContext::currentStateSet() const
887{
888 return m_stateSet;
889}
890
891void SubmissionContext::applyState(const StateVariant &stateVariant)
892{
893 switch (stateVariant.type) {
894
895 case AlphaCoverageStateMask: {
896 applyStateHelper<AlphaCoverage>(static_cast<const AlphaCoverage *>(stateVariant.constState()), gc: this);
897 break;
898 }
899 case AlphaTestMask: {
900 applyStateHelper<AlphaFunc>(state: static_cast<const AlphaFunc *>(stateVariant.constState()), gc: this);
901 break;
902 }
903 case BlendStateMask: {
904 applyStateHelper<BlendEquation>(state: static_cast<const BlendEquation *>(stateVariant.constState()), gc: this);
905 break;
906 }
907 case BlendEquationArgumentsMask: {
908 applyStateHelper<BlendEquationArguments>(state: static_cast<const BlendEquationArguments *>(stateVariant.constState()), gc: this);
909 break;
910 }
911 case MSAAEnabledStateMask: {
912 applyStateHelper<MSAAEnabled>(state: static_cast<const MSAAEnabled *>(stateVariant.constState()), gc: this);
913 break;
914 }
915
916 case CullFaceStateMask: {
917 applyStateHelper<CullFace>(state: static_cast<const CullFace *>(stateVariant.constState()), gc: this);
918 break;
919 }
920
921 case DepthWriteStateMask: {
922 applyStateHelper<NoDepthMask>(state: static_cast<const NoDepthMask *>(stateVariant.constState()), gc: this);
923 break;
924 }
925
926 case DepthTestStateMask: {
927 applyStateHelper<DepthTest>(state: static_cast<const DepthTest *>(stateVariant.constState()), gc: this);
928 break;
929 }
930
931 case DepthRangeMask: {
932 applyStateHelper<DepthRange>(state: static_cast<const DepthRange *>(stateVariant.constState()), gc: this);
933 break;
934 }
935
936 case RasterModeMask: {
937 applyStateHelper<RasterMode>(state: static_cast<const RasterMode *>(stateVariant.constState()), gc: this);
938 break;
939 }
940
941 case FrontFaceStateMask: {
942 applyStateHelper<FrontFace>(state: static_cast<const FrontFace *>(stateVariant.constState()), gc: this);
943 break;
944 }
945
946 case ScissorStateMask: {
947 applyStateHelper<ScissorTest>(state: static_cast<const ScissorTest *>(stateVariant.constState()), gc: this);
948 break;
949 }
950
951 case StencilTestStateMask: {
952 applyStateHelper<StencilTest>(state: static_cast<const StencilTest *>(stateVariant.constState()), gc: this);
953 break;
954 }
955
956 case PointSizeMask: {
957 applyStateHelper<PointSize>(state: static_cast<const PointSize *>(stateVariant.constState()), gc: this);
958 break;
959 }
960
961 case PolygonOffsetStateMask: {
962 applyStateHelper<PolygonOffset>(state: static_cast<const PolygonOffset *>(stateVariant.constState()), gc: this);
963 break;
964 }
965
966 case ColorStateMask: {
967 applyStateHelper<ColorMask>(state: static_cast<const ColorMask *>(stateVariant.constState()), gc: this);
968 break;
969 }
970
971 case ClipPlaneMask: {
972 applyStateHelper<ClipPlane>(state: static_cast<const ClipPlane *>(stateVariant.constState()), gc: this);
973 break;
974 }
975
976 case SeamlessCubemapMask: {
977 applyStateHelper<SeamlessCubemap>(static_cast<const SeamlessCubemap *>(stateVariant.constState()), gc: this);
978 break;
979 }
980
981 case StencilOpMask: {
982 applyStateHelper<StencilOp>(state: static_cast<const StencilOp *>(stateVariant.constState()), gc: this);
983 break;
984 }
985
986 case StencilWriteStateMask: {
987 applyStateHelper<StencilMask>(state: static_cast<const StencilMask *>(stateVariant.constState()), gc: this);
988 break;
989 }
990
991 case DitheringStateMask: {
992 applyStateHelper<Dithering>(static_cast<const Dithering *>(stateVariant.constState()), gc: this);
993 break;
994 }
995
996 case LineWidthMask: {
997 applyStateHelper<LineWidth>(state: static_cast<const LineWidth *>(stateVariant.constState()), gc: this);
998 break;
999 }
1000 default:
1001 Q_UNREACHABLE();
1002 }
1003}
1004
1005void SubmissionContext::resetState()
1006{
1007 QOpenGLFunctions *f = m_gl->functions();
1008 f->glActiveTexture(GL_TEXTURE0);
1009 f->glBindTexture(GL_TEXTURE_2D, texture: 0);
1010
1011 f->glDisable(GL_SCISSOR_TEST);
1012
1013 f->glColorMask(red: true, green: true, blue: true, alpha: true);
1014 f->glClearColor(red: m_currClearColorValue.redF(), green: m_currClearColorValue.greenF(), blue: m_currClearColorValue.blueF(), alpha: m_currClearColorValue.alphaF());
1015
1016 f->glEnable(GL_DEPTH_TEST);
1017 f->glDepthMask(flag: true);
1018 f->glDepthFunc(GL_LESS);
1019 f->glClearDepthf(depth: m_currClearDepthValue);
1020
1021 f->glDisable(GL_STENCIL_TEST);
1022 f->glStencilMask(mask: 0xff);
1023 f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1024 f->glStencilFunc(GL_ALWAYS, ref: 0, mask: 0xff);
1025 f->glClearStencil(s: m_currClearStencilValue);
1026
1027 f->glDisable(GL_BLEND);
1028 f->glBlendFunc(GL_ONE, GL_ZERO);
1029
1030 f->glUseProgram(program: 0);
1031}
1032
1033void SubmissionContext::resetMasked(qint64 maskOfStatesToReset)
1034{
1035 // TO DO -> Call gcHelper methods instead of raw GL
1036 // QOpenGLFunctions shouldn't be used here directly
1037 QOpenGLFunctions *funcs = m_gl->functions();
1038
1039 if (maskOfStatesToReset & ScissorStateMask)
1040 funcs->glDisable(GL_SCISSOR_TEST);
1041
1042 if (maskOfStatesToReset & BlendStateMask)
1043 funcs->glDisable(GL_BLEND);
1044
1045 if (maskOfStatesToReset & StencilWriteStateMask)
1046 funcs->glStencilMask(mask: 0);
1047
1048 if (maskOfStatesToReset & StencilTestStateMask)
1049 funcs->glDisable(GL_STENCIL_TEST);
1050
1051 if (maskOfStatesToReset & DepthRangeMask)
1052 depthRange(nearValue: 0.0f, farValue: 1.0f);
1053
1054 if (maskOfStatesToReset & DepthTestStateMask)
1055 funcs->glDisable(GL_DEPTH_TEST);
1056
1057 if (maskOfStatesToReset & DepthWriteStateMask)
1058 funcs->glDepthMask(GL_TRUE); // reset to default
1059
1060 if (maskOfStatesToReset & FrontFaceStateMask)
1061 funcs->glFrontFace(GL_CCW); // reset to default
1062
1063 if (maskOfStatesToReset & CullFaceStateMask)
1064 funcs->glDisable(GL_CULL_FACE);
1065
1066 if (maskOfStatesToReset & DitheringStateMask)
1067 funcs->glDisable(GL_DITHER);
1068
1069 if (maskOfStatesToReset & AlphaCoverageStateMask)
1070 setAlphaCoverageEnabled(false);
1071
1072 if (maskOfStatesToReset & PointSizeMask)
1073 pointSize(programmable: false, value: 1.0f); // reset to default
1074
1075 if (maskOfStatesToReset & PolygonOffsetStateMask)
1076 funcs->glDisable(GL_POLYGON_OFFSET_FILL);
1077
1078 if (maskOfStatesToReset & ColorStateMask)
1079 funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1080
1081 if (maskOfStatesToReset & ClipPlaneMask) {
1082 GLint max = maxClipPlaneCount();
1083 for (GLint i = 0; i < max; ++i)
1084 disableClipPlane(clipPlane: i);
1085 }
1086
1087 if (maskOfStatesToReset & SeamlessCubemapMask)
1088 setSeamlessCubemap(false);
1089
1090 if (maskOfStatesToReset & StencilOpMask)
1091 funcs->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1092
1093 if (maskOfStatesToReset & LineWidthMask)
1094 funcs->glLineWidth(width: 1.0f);
1095
1096#if !QT_CONFIG(opengles2)
1097 if (maskOfStatesToReset & RasterModeMask)
1098 m_glHelper->rasterMode(GL_FRONT_AND_BACK, GL_FILL);
1099#endif
1100}
1101
1102void SubmissionContext::applyStateSet(RenderStateSet *ss)
1103{
1104 RenderStateSet* previousStates = currentStateSet();
1105
1106 const StateMaskSet invOurState = ~ss->stateMask();
1107 // generate a mask for each set bit in previous, where we do not have
1108 // the corresponding bit set.
1109
1110 StateMaskSet stateToReset = 0;
1111 if (previousStates) {
1112 stateToReset = previousStates->stateMask() & invOurState;
1113 qCDebug(RenderStates) << "previous states " << QString::number(previousStates->stateMask(), base: 2);
1114 }
1115 qCDebug(RenderStates) << " current states " << QString::number(ss->stateMask(), base: 2) << "inverse " << QString::number(invOurState, base: 2) << " -> states to change: " << QString::number(stateToReset, base: 2);
1116
1117 // Reset states that aren't active in the current state set
1118 resetMasked(maskOfStatesToReset: stateToReset);
1119
1120 // Apply states that weren't in the previous state or that have
1121 // different values
1122 const std::vector<StateVariant> statesToSet = ss->states();
1123 for (const StateVariant &ds : statesToSet) {
1124 if (previousStates && previousStates->contains(ds))
1125 continue;
1126 applyState(stateVariant: ds);
1127 }
1128}
1129
1130void SubmissionContext::clearColor(const QColor &color)
1131{
1132 if (m_currClearColorValue != color) {
1133 m_currClearColorValue = color;
1134 m_gl->functions()->glClearColor(red: color.redF(), green: color.greenF(), blue: color.blueF(), alpha: color.alphaF());
1135 }
1136}
1137
1138void SubmissionContext::clearDepthValue(float depth)
1139{
1140 if (m_currClearDepthValue != depth) {
1141 m_currClearDepthValue = depth;
1142 m_gl->functions()->glClearDepthf(depth);
1143 }
1144}
1145
1146void SubmissionContext::clearStencilValue(int stencil)
1147{
1148 if (m_currClearStencilValue != stencil) {
1149 m_currClearStencilValue = stencil;
1150 m_gl->functions()->glClearStencil(s: stencil);
1151 }
1152}
1153
1154GLFence SubmissionContext::fenceSync()
1155{
1156 return m_glHelper->fenceSync();
1157}
1158
1159void SubmissionContext::clientWaitSync(GLFence sync, GLuint64 nanoSecTimeout)
1160{
1161 qDebug() << Q_FUNC_INFO << sync;
1162 m_glHelper->clientWaitSync(sync, nanoSecTimeout);
1163}
1164
1165void SubmissionContext::waitSync(GLFence sync)
1166{
1167 qDebug() << Q_FUNC_INFO << sync;
1168 m_glHelper->waitSync(sync);
1169}
1170
1171bool SubmissionContext::wasSyncSignaled(GLFence sync)
1172{
1173 return m_glHelper->wasSyncSignaled(sync);
1174}
1175
1176void SubmissionContext::deleteSync(GLFence sync)
1177{
1178 m_glHelper->deleteSync(sync);
1179}
1180
1181void SubmissionContext::setUpdatedTexture(const Qt3DCore::QNodeIdVector &updatedTextureIds)
1182{
1183 m_updateTextureIds = updatedTextureIds;
1184}
1185
1186// It will be easier if the QGraphicContext applies the QUniformPack
1187// than the other way around
1188bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack, GLShader *shader)
1189{
1190 static const int irradianceStructId = StringToInt::lookupId(str: QLatin1String("envLight.irradiance"));
1191 static const int specularStructId = StringToInt::lookupId(str: QLatin1String("envLight.specular"));
1192 static const int irradianceId = StringToInt::lookupId(str: QLatin1String("envLightIrradiance"));
1193 static const int specularId = StringToInt::lookupId(str: QLatin1String("envLightSpecular"));
1194 // Activate textures and update TextureUniform in the pack
1195 // with the correct textureUnit
1196
1197 // Set the pinned texture of the previous material texture
1198 // to pinable so that we should easily find an available texture unit
1199 m_textureContext.deactivateTexturesWithScope(ts: TextureSubmissionContext::TextureScopeMaterial);
1200 // Update the uniforms with the correct texture unit id's
1201 PackUniformHash &uniformValues = parameterPack.uniforms();
1202
1203 // Fill Texture Uniform Value with proper texture units
1204 // so that they can be applied as regular uniforms in a second step
1205 for (size_t i = 0; i < parameterPack.textures().size(); ++i) {
1206 const ShaderParameterPack::NamedResource &namedTex = parameterPack.textures().at(n: i);
1207 // Given a Texture QNodeId, we retrieve the associated shared GLTexture
1208 if (uniformValues.contains(key: namedTex.glslNameId)) {
1209 GLTexture *t = m_renderer->glResourceManagers()->glTextureManager()->lookupResource(id: namedTex.nodeId);
1210 if (t != nullptr) {
1211 UniformValue &texUniform = uniformValues.value(key: namedTex.glslNameId);
1212 if (texUniform.valueType() == UniformValue::TextureValue) {
1213 const int texUnit = m_textureContext.activateTexture(scope: TextureSubmissionContext::TextureScopeMaterial, gl: m_gl, tex: t);
1214 texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit;
1215 if (texUnit == -1) {
1216 if (namedTex.glslNameId != irradianceId &&
1217 namedTex.glslNameId != specularId &&
1218 namedTex.glslNameId != irradianceStructId &&
1219 namedTex.glslNameId != specularStructId) {
1220 // Only return false if we are not dealing with env light textures
1221 qCWarning(Backend) << "Unable to find suitable Texture Unit for" << StringToInt::lookupString(idx: namedTex.glslNameId);
1222 return false;
1223 }
1224 }
1225 }
1226 }
1227 }
1228 }
1229
1230 // Set the pinned images of the previous material
1231 // to pinable so that we should easily find an available image unit
1232 m_imageContext.deactivateImages();
1233
1234 // Fill Image Uniform Value with proper image units
1235 // so that they can be applied as regular uniforms in a second step
1236 for (size_t i = 0; i < parameterPack.images().size(); ++i) {
1237 const ShaderParameterPack::NamedResource &namedTex = parameterPack.images().at(n: i);
1238 // Given a Texture QNodeId, we retrieve the associated shared GLTexture
1239 if (uniformValues.contains(key: namedTex.glslNameId)) {
1240 ShaderImage *img = m_renderer->nodeManagers()->shaderImageManager()->lookupResource(id: namedTex.nodeId);
1241 if (img != nullptr) {
1242 GLTexture *t = m_renderer->glResourceManagers()->glTextureManager()->lookupResource(id: img->textureId());
1243 if (t == nullptr) {
1244 qCWarning(Backend) << "Shader Image referencing invalid texture";
1245 continue;
1246 } else {
1247 UniformValue &imgUniform = uniformValues.value(key: namedTex.glslNameId);
1248 if (imgUniform.valueType() == UniformValue::ShaderImageValue) {
1249 const int imgUnit = m_imageContext.activateImage(image: img, tex: t);
1250 imgUniform.data<int>()[namedTex.uniformArrayIndex] = imgUnit;
1251 if (imgUnit == -1) {
1252 qCWarning(Backend) << "Unable to bind Image to Texture";
1253 return false;
1254 }
1255 }
1256 }
1257 }
1258 }
1259 }
1260
1261 QOpenGLShaderProgram *glShader = activeShader();
1262
1263 // TO DO: We could cache the binding points somehow and only do the binding when necessary
1264 // for SSBO and UBO
1265
1266 // Bind Shader Storage block to SSBO and update SSBO
1267 const std::vector<BlockToSSBO> &blockToSSBOs = parameterPack.shaderStorageBuffers();
1268 for (const BlockToSSBO &b : blockToSSBOs) {
1269 Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(id: b.m_bufferID);
1270 GLBuffer *ssbo = glBufferForRenderBuffer(buf: cpuBuffer);
1271 // bindShaderStorageBlock
1272 // This is currently not required as we are introspecting the bindingIndex
1273 // value from the shaders and not replacing them, making such a call useless
1274 // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
1275 bindShaderStorageBlock(programId: glShader->programId(), shaderStorageBlockIndex: b.m_blockIndex, shaderStorageBlockBinding: b.m_bindingIndex);
1276 // Needed to avoid conflict where the buffer would already
1277 // be bound as a VertexArray
1278 bindGLBuffer(buffer: ssbo, type: GLBuffer::ShaderStorageBuffer);
1279 ssbo->bindBufferBase(ctx: this, bindingPoint: b.m_bindingIndex, t: GLBuffer::ShaderStorageBuffer);
1280 // TO DO: Make sure that there's enough binding points
1281 }
1282
1283 // Bind UniformBlocks to UBO and update UBO from Buffer
1284 // TO DO: Convert ShaderData to Buffer so that we can use that generic process
1285 const std::vector<BlockToUBO> &blockToUBOs = parameterPack.uniformBuffers();
1286 int uboIndex = 0;
1287 for (const BlockToUBO &b : blockToUBOs) {
1288 Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(id: b.m_bufferID);
1289 GLBuffer *ubo = glBufferForRenderBuffer(buf: cpuBuffer);
1290 bindUniformBlock(programId: glShader->programId(), uniformBlockIndex: b.m_blockIndex, uniformBlockBinding: uboIndex);
1291 // Needed to avoid conflict where the buffer would already
1292 // be bound as a VertexArray
1293 bindGLBuffer(buffer: ubo, type: GLBuffer::UniformBuffer);
1294 ubo->bindBufferBase(ctx: this, bindingPoint: uboIndex++, t: GLBuffer::UniformBuffer);
1295 // TO DO: Make sure that there's enough binding points
1296 }
1297
1298 // Update uniforms in the Default Uniform Block
1299 const PackUniformHash& values = parameterPack.uniforms();
1300 const auto &activeUniformsIndices = parameterPack.submissionUniformIndices();
1301 const std::vector<ShaderUniform> &shaderUniforms = shader->uniforms();
1302
1303 for (const int shaderUniformIndex : activeUniformsIndices) {
1304 const ShaderUniform &uniform = shaderUniforms[shaderUniformIndex];
1305 values.apply(key: uniform.m_nameId, func: [&] (const UniformValue& v) {
1306 // skip invalid textures/images
1307 if (!((v.valueType() == UniformValue::TextureValue ||
1308 v.valueType() == UniformValue::ShaderImageValue) &&
1309 *v.constData<int>() == -1))
1310 applyUniform(description: uniform, v);
1311 });
1312
1313 }
1314 // if not all data is valid, the next frame will be rendered immediately
1315 return true;
1316}
1317
1318void SubmissionContext::enableAttribute(const VAOVertexAttribute &attr)
1319{
1320 // Bind buffer within the current VAO
1321 GLBuffer *buf = m_renderer->glResourceManagers()->glBufferManager()->data(handle: attr.bufferHandle);
1322 Q_ASSERT(buf);
1323 bindGLBuffer(buffer: buf, type: attr.attributeType);
1324
1325 // Don't use QOpenGLShaderProgram::setAttributeBuffer() because of QTBUG-43199.
1326 // Use the introspection data and set the attribute explicitly
1327 m_glHelper->enableVertexAttributeArray(location: attr.location);
1328 m_glHelper->vertexAttributePointer(shaderDataType: attr.shaderDataType,
1329 index: attr.location,
1330 size: attr.vertexSize,
1331 type: attr.dataType,
1332 GL_TRUE, // TODO: Support normalization property on QAttribute
1333 stride: attr.byteStride,
1334 pointer: reinterpret_cast<const void *>(qintptr(attr.byteOffset)));
1335
1336
1337 // Done by the helper if it supports it
1338 if (attr.divisor != 0)
1339 m_glHelper->vertexAttribDivisor(index: attr.location, divisor: attr.divisor);
1340}
1341
1342void SubmissionContext::disableAttribute(const SubmissionContext::VAOVertexAttribute &attr)
1343{
1344 QOpenGLShaderProgram *prog = activeShader();
1345 prog->disableAttributeArray(location: attr.location);
1346}
1347
1348// Note: needs to be called while VAO is bound
1349void SubmissionContext::specifyAttribute(const Attribute *attribute,
1350 Buffer *buffer,
1351 const ShaderAttribute *attributeDescription)
1352{
1353 const int location = attributeDescription->m_location;
1354 if (location < 0) {
1355 qCWarning(Backend) << "failed to resolve location for attribute:" << attribute->name();
1356 return;
1357 }
1358
1359 const GLint attributeDataType = glDataTypeFromAttributeDataType(dataType: attribute->vertexBaseType());
1360 const HGLBuffer glBufferHandle = m_renderer->glResourceManagers()->glBufferManager()->lookupHandle(id: buffer->peerId());
1361 Q_ASSERT(!glBufferHandle.isNull());
1362 const GLBuffer::Type attributeType = attributeTypeToGLBufferType(type: attribute->attributeType());
1363
1364 int typeSize = 0;
1365 int attrCount = 0;
1366
1367 if (attribute->vertexSize() >= 1 && attribute->vertexSize() <= 4) {
1368 attrCount = 1;
1369 } else if (attribute->vertexSize() == 9) {
1370 typeSize = byteSizeFromType(type: attributeDataType);
1371 attrCount = 3;
1372 } else if (attribute->vertexSize() == 16) {
1373 typeSize = byteSizeFromType(type: attributeDataType);
1374 attrCount = 4;
1375 } else {
1376 Q_UNREACHABLE();
1377 }
1378
1379 Q_ASSERT(!glBufferHandle.isNull());
1380 VAOVertexAttribute attr;
1381 attr.bufferHandle = glBufferHandle;
1382 attr.attributeType = attributeType;
1383 attr.dataType = attributeDataType;
1384 attr.divisor = attribute->divisor();
1385 attr.vertexSize = attribute->vertexSize() / attrCount;
1386 attr.byteStride = (attribute->byteStride() != 0) ? attribute->byteStride() : (attrCount * attrCount * typeSize);
1387 attr.shaderDataType = attributeDescription->m_type;
1388
1389 for (int i = 0; i < attrCount; i++) {
1390 attr.location = location + i;
1391 attr.byteOffset = attribute->byteOffset() + (i * attrCount * typeSize);
1392
1393 enableAttribute(attr);
1394
1395 // Save this in the current emulated VAO
1396 if (m_currentVAO)
1397 m_currentVAO->saveVertexAttribute(attr);
1398 }
1399}
1400
1401void SubmissionContext::specifyIndices(Buffer *buffer)
1402{
1403 GLBuffer *buf = glBufferForRenderBuffer(buf: buffer);
1404 if (!bindGLBuffer(buffer: buf, type: GLBuffer::IndexBuffer))
1405 qCWarning(Backend) << Q_FUNC_INFO << "binding index buffer failed";
1406
1407 // bound within the current VAO
1408 // Save this in the current emulated VAO
1409 if (m_currentVAO)
1410 m_currentVAO->saveIndexAttribute(glBufferHandle: m_renderer->glResourceManagers()->glBufferManager()->lookupHandle(id: buffer->peerId()));
1411}
1412
1413void SubmissionContext::updateBuffer(Buffer *buffer)
1414{
1415 const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(key: buffer->peerId());
1416 if (it != m_renderBufferHash.end())
1417 uploadDataToGLBuffer(buffer, b: m_renderer->glResourceManagers()->glBufferManager()->data(handle: it.value()));
1418}
1419
1420QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer)
1421{
1422 const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(key: buffer->peerId());
1423 if (it != m_renderBufferHash.end())
1424 return downloadDataFromGLBuffer(buffer, b: m_renderer->glResourceManagers()->glBufferManager()->data(handle: it.value()));
1425 return QByteArray();
1426}
1427
1428void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId)
1429{
1430 auto it = m_renderBufferHash.find(key: bufferId);
1431 if (it != m_renderBufferHash.end()) {
1432 HGLBuffer glBuffHandle = it.value();
1433 GLBuffer *glBuff = m_renderer->glResourceManagers()->glBufferManager()->data(handle: glBuffHandle);
1434
1435 Q_ASSERT(glBuff);
1436 // Destroy the GPU resource
1437 glBuff->destroy(ctx: this);
1438 // Destroy the GLBuffer instance
1439 m_renderer->glResourceManagers()->glBufferManager()->releaseResource(id: bufferId);
1440 // Remove Id - HGLBuffer entry
1441 m_renderBufferHash.erase(it);
1442 }
1443}
1444
1445bool SubmissionContext::hasGLBufferForBuffer(Buffer *buffer)
1446{
1447 const QHash<Qt3DCore::QNodeId, HGLBuffer>::iterator it = m_renderBufferHash.find(key: buffer->peerId());
1448 return (it != m_renderBufferHash.end());
1449}
1450
1451GLBuffer *SubmissionContext::glBufferForRenderBuffer(Buffer *buf)
1452{
1453 if (!m_renderBufferHash.contains(key: buf->peerId()))
1454 m_renderBufferHash.insert(key: buf->peerId(), value: createGLBufferFor(buffer: buf));
1455 return m_renderer->glResourceManagers()->glBufferManager()->data(handle: m_renderBufferHash.value(key: buf->peerId()));
1456}
1457
1458HGLBuffer SubmissionContext::createGLBufferFor(Buffer *buffer)
1459{
1460 GLBuffer *b = m_renderer->glResourceManagers()->glBufferManager()->getOrCreateResource(id: buffer->peerId());
1461 // b.setUsagePattern(static_cast<QOpenGLBuffer::UsagePattern>(buffer->usage()));
1462 Q_ASSERT(b);
1463 if (!b->create(ctx: this))
1464 qCWarning(Io) << Q_FUNC_INFO << "buffer creation failed";
1465
1466 return m_renderer->glResourceManagers()->glBufferManager()->lookupHandle(id: buffer->peerId());
1467}
1468
1469bool SubmissionContext::bindGLBuffer(GLBuffer *buffer, GLBuffer::Type type)
1470{
1471 if (type == GLBuffer::ArrayBuffer && buffer == m_boundArrayBuffer)
1472 return true;
1473
1474 if (buffer->bind(ctx: this, t: type)) {
1475 if (type == GLBuffer::ArrayBuffer)
1476 m_boundArrayBuffer = buffer;
1477 return true;
1478 }
1479 return false;
1480}
1481
1482void SubmissionContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer)
1483{
1484 if (!bindGLBuffer(buffer: b, type: GLBuffer::ArrayBuffer)) // We're uploading, the type doesn't matter here
1485 qCWarning(Io) << Q_FUNC_INFO << "buffer bind failed";
1486 // If the buffer is dirty (hence being called here)
1487 // there are two possible cases
1488 // * setData was called changing the whole data or functor (or the usage pattern)
1489 // * partial buffer updates where received
1490
1491 // TO DO: Handle usage pattern
1492 std::vector<Qt3DCore::QBufferUpdate> updates = Qt3DCore::moveAndClear(data&: buffer->pendingBufferUpdates());
1493 for (auto it = updates.begin(); it != updates.end(); ++it) {
1494 auto update = it;
1495 // We have a partial update
1496 if (update->offset >= 0) {
1497 //accumulate sequential updates as single one
1498 qsizetype bufferSize = update->data.size();
1499 auto it2 = it + 1;
1500 while ((it2 != updates.end())
1501 && (it2->offset - update->offset == bufferSize)) {
1502 bufferSize += it2->data.size();
1503 ++it2;
1504 }
1505 update->data.resize(size: bufferSize);
1506 while (it + 1 != it2) {
1507 ++it;
1508 update->data.replace(index: it->offset - update->offset, len: it->data.size(), s: it->data);
1509 it->data.clear();
1510 }
1511 // TO DO: based on the number of updates .., it might make sense to
1512 // sometime use glMapBuffer rather than glBufferSubData
1513 b->update(ctx: this, data: update->data.constData(), size: update->data.size(), offset: update->offset);
1514 } else {
1515 // We have an update that was done by calling QBuffer::setData
1516 // which is used to resize or entirely clear the buffer
1517 // Note: we use the buffer data directly in that case
1518 const qsizetype bufferSize = buffer->data().size();
1519 b->allocate(ctx: this, size: bufferSize, dynamic: false); // orphan the buffer
1520 b->allocate(ctx: this, data: buffer->data().constData(), size: bufferSize, dynamic: false);
1521 }
1522 }
1523
1524 if (releaseBuffer) {
1525 b->release(ctx: this);
1526 m_boundArrayBuffer = nullptr;
1527 }
1528 qCDebug(Io) << "uploaded buffer size=" << buffer->data().size();
1529}
1530
1531QByteArray SubmissionContext::downloadDataFromGLBuffer(Buffer *buffer, GLBuffer *b)
1532{
1533 if (!bindGLBuffer(buffer: b, type: GLBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here
1534 qCWarning(Io) << Q_FUNC_INFO << "buffer bind failed";
1535
1536 QByteArray data = b->download(ctx: this, size: buffer->data().size());
1537 return data;
1538}
1539
1540void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
1541 Qt3DCore::QNodeId outputRenderTargetId,
1542 QRect inputRect, QRect outputRect,
1543 uint defaultFboId,
1544 QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
1545 QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
1546 QBlitFramebuffer::InterpolationMethod interpolationMethod)
1547{
1548 GLuint inputFboId = defaultFboId;
1549 bool inputBufferIsDefault = true;
1550 if (!inputRenderTargetId.isNull()) {
1551 RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(id: inputRenderTargetId);
1552 if (renderTarget) {
1553 AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager());
1554 if (m_renderTargets.contains(key: inputRenderTargetId))
1555 inputFboId = updateRenderTarget(renderTargetNodeId: inputRenderTargetId, attachments, isActiveRenderTarget: false);
1556 else
1557 inputFboId = createRenderTarget(renderTargetNodeId: inputRenderTargetId, attachments);
1558 }
1559 inputBufferIsDefault = false;
1560 }
1561
1562 GLuint outputFboId = defaultFboId;
1563 bool outputBufferIsDefault = true;
1564 if (!outputRenderTargetId.isNull()) {
1565 RenderTarget *renderTarget = m_renderer->nodeManagers()->renderTargetManager()->lookupResource(id: outputRenderTargetId);
1566 if (renderTarget) {
1567 AttachmentPack attachments(renderTarget, m_renderer->nodeManagers()->attachmentManager());
1568 if (m_renderTargets.contains(key: outputRenderTargetId))
1569 outputFboId = updateRenderTarget(renderTargetNodeId: outputRenderTargetId, attachments, isActiveRenderTarget: false);
1570 else
1571 outputFboId = createRenderTarget(renderTargetNodeId: outputRenderTargetId, attachments);
1572 }
1573 outputBufferIsDefault = false;
1574 }
1575
1576 // Up until this point the input and output rects are normal Qt rectangles.
1577 // Convert them to GL rectangles (Y at bottom).
1578 const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargets[inputRenderTargetId].size.height();
1579 const GLint srcX0 = inputRect.left();
1580 const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height());
1581 const GLint srcX1 = srcX0 + inputRect.width();
1582 const GLint srcY1 = srcY0 + inputRect.height();
1583
1584 const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() : m_renderTargets[outputRenderTargetId].size.height();
1585 const GLint dstX0 = outputRect.left();
1586 const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height());
1587 const GLint dstX1 = dstX0 + outputRect.width();
1588 const GLint dstY1 = dstY0 + outputRect.height();
1589
1590 //Get the last bounded framebuffers
1591 const GLuint lastDrawFboId = boundFrameBufferObject();
1592
1593 // Activate input framebuffer for reading
1594 bindFramebuffer(fbo: inputFboId, mode: GraphicsHelperInterface::FBORead);
1595
1596 // Activate output framebuffer for writing
1597 bindFramebuffer(fbo: outputFboId, mode: GraphicsHelperInterface::FBODraw);
1598
1599 //Bind texture
1600 if (!inputBufferIsDefault)
1601 readBuffer(mode: glAttachmentPoint(attachmentPoint: inputAttachmentPoint));
1602
1603 if (!outputBufferIsDefault) {
1604 // Note that we use glDrawBuffers, not glDrawBuffer. The
1605 // latter is not available with GLES.
1606 const int buf = outputAttachmentPoint;
1607 drawBuffers(n: 1, bufs: &buf);
1608 }
1609
1610 // Blit framebuffer
1611 const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR;
1612 m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1,
1613 dstX0, dstY0, dstX1, dstY1,
1614 GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT,
1615 filter: mode);
1616
1617 // Reset draw buffer
1618 bindFramebuffer(fbo: lastDrawFboId, mode: GraphicsHelperInterface::FBOReadAndDraw);
1619 if (outputAttachmentPoint != QRenderTargetOutput::Color0) {
1620 const int buf = QRenderTargetOutput::Color0;
1621 drawBuffers(n: 1, bufs: &buf);
1622 }
1623}
1624
1625} // namespace OpenGL
1626} // namespace Render
1627} // namespace Qt3DRender of namespace
1628
1629QT_END_NAMESPACE
1630

source code of qt3d/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp