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

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