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