1// Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "submissioncontext_p.h"
5
6#include <Qt3DCore/private/qbuffer_p.h>
7#include <Qt3DRender/qgraphicsapifilter.h>
8#include <Qt3DRender/qparameter.h>
9#include <Qt3DRender/qcullface.h>
10#include <Qt3DRender/qfrontface.h>
11#include <Qt3DRender/qdepthtest.h>
12#include <Qt3DRender/qblendequation.h>
13#include <Qt3DRender/qblendequationarguments.h>
14#include <Qt3DRender/qstenciloperationarguments.h>
15#include <Qt3DRender/qstenciltestarguments.h>
16#include <Qt3DRender/private/renderlogging_p.h>
17#include <Qt3DRender/private/shader_p.h>
18#include <Qt3DRender/private/material_p.h>
19#include <Qt3DRender/private/buffer_p.h>
20#include <Qt3DRender/private/attribute_p.h>
21#include <Qt3DRender/private/renderstates_p.h>
22#include <Qt3DRender/private/renderstateset_p.h>
23#include <Qt3DRender/private/rendertarget_p.h>
24#include <Qt3DRender/private/nodemanagers_p.h>
25#include <Qt3DRender/private/buffermanager_p.h>
26#include <Qt3DRender/private/managers_p.h>
27#include <Qt3DRender/private/attachmentpack_p.h>
28#include <Qt3DRender/private/stringtoint_p.h>
29#if QT_CONFIG(qt3d_vulkan) && QT_CONFIG(vulkan)
30# include <Qt3DRender/private/vulkaninstance_p.h>
31#endif
32#include <QGuiApplication>
33#include <texture_p.h>
34#include <rendercommand_p.h>
35#include <renderer_p.h>
36#include <rhiresourcemanagers_p.h>
37#include <renderbuffer_p.h>
38#include <rhishader_p.h>
39#include <QOpenGLShaderProgram>
40
41#include <private/qdebug_p.h>
42#include <QSurface>
43#include <QWindow>
44#include <rhi/qrhi.h>
45#include <rhi/qshaderbaker.h>
46
47#include <bitset>
48
49QT_BEGIN_NAMESPACE
50
51namespace Qt3DRender {
52namespace Render {
53namespace Rhi {
54
55static QHash<unsigned int, SubmissionContext *> static_contexts;
56
57unsigned int nextFreeContextId() noexcept
58{
59 for (unsigned int i = 0; i < 0xffff; ++i) {
60 if (!static_contexts.contains(key: i))
61 return i;
62 }
63
64 qFatal(msg: "Couldn't find free context ID");
65 return 0;
66}
67
68namespace {
69
70//RHIBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) noexcept
71//{
72// switch (type) {
73// case QAttribute::VertexAttribute:
74// return RHIBuffer::ArrayBuffer;
75// case QAttribute::IndexAttribute:
76// return RHIBuffer::IndexBuffer;
77// case QAttribute::DrawIndirectAttribute:
78// return RHIBuffer::DrawIndirectBuffer;
79// default:
80// Q_UNREACHABLE();
81// }
82//}
83
84//void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width,
85// uint height, QAbstractTexture::TextureFormat format) noexcept
86//{
87// switch (format) {
88// case QAbstractTexture::RGBA32F: {
89// uchar *srcScanline = const_cast<uchar *>(srcData) + stride * (height - 1);
90// for (uint i = 0; i < height; ++i) {
91// uchar *dstScanline = img.scanLine(i);
92// float *pSrc = reinterpret_cast<float *>(srcScanline);
93// for (uint j = 0; j < width; j++) {
94// *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 2], 1.0f));
95// *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 1], 1.0f));
96// *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 0], 1.0f));
97// *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 3], 1.0f));
98// }
99// srcScanline -= stride;
100// }
101// } break;
102// default: {
103// uchar *srcScanline = (uchar *)srcData + stride * (height - 1);
104// for (uint i = 0; i < height; ++i) {
105// memcpy(img.scanLine(i), srcScanline, stride);
106// srcScanline -= stride;
107// }
108// } break;
109// }
110//}
111
112// Render States Helpers
113
114template<typename GenericState>
115void applyStateHelper(const GenericState *state, QRhiGraphicsPipeline *gp) noexcept
116{
117 Q_UNUSED(state);
118 Q_UNUSED(gp);
119 qCWarning(Backend) << "RHI Unhandled render state" << typeid(GenericState).name();
120}
121
122void applyStateHelper(const BlendEquationArguments *state, QRhiGraphicsPipeline *gp) noexcept
123{
124 const auto values = state->values();
125
126 // We assume a single color attachment
127 QRhiGraphicsPipeline::TargetBlend targetBlend {};
128
129 const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
130 if (hasTargetBlend)
131 targetBlend = *(gp->cbeginTargetBlends());
132
133 auto getRHIBlendFactor = [](int arg) {
134 switch (arg) {
135 case QBlendEquationArguments::Zero:
136 return QRhiGraphicsPipeline::Zero;
137 case QBlendEquationArguments::One:
138 return QRhiGraphicsPipeline::One;
139 case QBlendEquationArguments::SourceColor:
140 return QRhiGraphicsPipeline::SrcColor;
141 case QBlendEquationArguments::SourceAlpha:
142 return QRhiGraphicsPipeline::SrcAlpha;
143 // ### Qt 6 Fix values
144 // case QBlendEquationArguments::Source1Alpha:
145 // return QRhiGraphicsPipeline::Src1Alpha;
146 // case QBlendEquationArguments::Source1Color:
147 // return QRhiGraphicsPipeline::Src1Color;
148 case QBlendEquationArguments::DestinationColor:
149 return QRhiGraphicsPipeline::DstColor;
150 case QBlendEquationArguments::DestinationAlpha:
151 return QRhiGraphicsPipeline::DstAlpha;
152 case QBlendEquationArguments::SourceAlphaSaturate:
153 return QRhiGraphicsPipeline::SrcAlphaSaturate;
154 case QBlendEquationArguments::ConstantColor:
155 return QRhiGraphicsPipeline::ConstantColor;
156 case QBlendEquationArguments::ConstantAlpha:
157 return QRhiGraphicsPipeline::ConstantAlpha;
158 case QBlendEquationArguments::OneMinusSourceColor:
159 return QRhiGraphicsPipeline::OneMinusSrcColor;
160 case QBlendEquationArguments::OneMinusSourceAlpha:
161 return QRhiGraphicsPipeline::OneMinusSrcAlpha;
162 case QBlendEquationArguments::OneMinusDestinationAlpha:
163 return QRhiGraphicsPipeline::OneMinusDstAlpha;
164 case QBlendEquationArguments::OneMinusDestinationColor:
165 return QRhiGraphicsPipeline::OneMinusDstColor;
166 case QBlendEquationArguments::OneMinusConstantColor:
167 return QRhiGraphicsPipeline::OneMinusConstantColor;
168 case QBlendEquationArguments::OneMinusConstantAlpha:
169 return QRhiGraphicsPipeline::OneMinusConstantAlpha;
170 case QBlendEquationArguments::OneMinusSource1Alpha:
171 return QRhiGraphicsPipeline::OneMinusSrc1Alpha;
172 case QBlendEquationArguments::OneMinusSource1Color:
173 return QRhiGraphicsPipeline::OneMinusSrc1Color;
174 default:
175 qDebug() << "Unhandled blend equation argument" << arg;
176 return QRhiGraphicsPipeline::Zero;
177 }
178 };
179
180 targetBlend.srcAlpha = getRHIBlendFactor(std::get<2>(t: values));
181 targetBlend.dstAlpha = getRHIBlendFactor(std::get<3>(t: values));
182 targetBlend.srcColor = getRHIBlendFactor(std::get<0>(t: values));
183 targetBlend.dstColor = getRHIBlendFactor(std::get<1>(t: values));
184 gp->setTargetBlends({ targetBlend });
185}
186
187void applyStateHelper(const BlendEquation *state, QRhiGraphicsPipeline *gp) noexcept
188{
189 const auto values = state->values();
190 const QBlendEquation::BlendFunction equation =
191 static_cast<QBlendEquation::BlendFunction>(std::get<0>(t: values));
192
193 // We assume a single color attachment
194 QRhiGraphicsPipeline::TargetBlend targetBlend;
195
196 const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
197 if (hasTargetBlend)
198 targetBlend = *(gp->cbeginTargetBlends());
199
200 auto getRHIBlendOp = [](QBlendEquation::BlendFunction equation) {
201 switch (equation) {
202 case QBlendEquation::Add:
203 return QRhiGraphicsPipeline::Add;
204 case QBlendEquation::Subtract:
205 return QRhiGraphicsPipeline::Subtract;
206 case QBlendEquation::ReverseSubtract:
207 return QRhiGraphicsPipeline::ReverseSubtract;
208 case QBlendEquation::Min:
209 return QRhiGraphicsPipeline::Min;
210 case QBlendEquation::Max:
211 return QRhiGraphicsPipeline::Max;
212 default:
213 return QRhiGraphicsPipeline::Add;
214 }
215 };
216
217 targetBlend.enable = true;
218 targetBlend.opAlpha = getRHIBlendOp(equation);
219 gp->setTargetBlends({ targetBlend });
220}
221
222void applyStateHelper(const MSAAEnabled *state, QRhiGraphicsPipeline *gp,
223 const QSurfaceFormat &format) noexcept
224{
225 Q_UNUSED(state);
226 gp->setSampleCount(format.samples());
227}
228
229void applyStateHelper(const DepthTest *state, QRhiGraphicsPipeline *gp) noexcept
230{
231 gp->setDepthTest(true);
232 const QDepthTest::DepthFunction depthFunc =
233 static_cast<QDepthTest::DepthFunction>(std::get<0>(t: state->values()));
234 switch (depthFunc) {
235 case QDepthTest::Never:
236 gp->setDepthOp(QRhiGraphicsPipeline::Never);
237 break;
238 case QDepthTest::Always:
239 gp->setDepthOp(QRhiGraphicsPipeline::Always);
240 break;
241 case QDepthTest::Less:
242 gp->setDepthOp(QRhiGraphicsPipeline::Less);
243 break;
244 case QDepthTest::LessOrEqual:
245 gp->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
246 break;
247 case QDepthTest::Equal:
248 gp->setDepthOp(QRhiGraphicsPipeline::Equal);
249 break;
250 case QDepthTest::GreaterOrEqual:
251 gp->setDepthOp(QRhiGraphicsPipeline::GreaterOrEqual);
252 break;
253 case QDepthTest::Greater:
254 gp->setDepthOp(QRhiGraphicsPipeline::Greater);
255 break;
256 case QDepthTest::NotEqual:
257 gp->setDepthOp(QRhiGraphicsPipeline::NotEqual);
258 break;
259 }
260}
261
262void applyStateHelper(const NoDepthMask *state, QRhiGraphicsPipeline *gp) noexcept
263{
264 const auto values = state->values();
265 gp->setDepthWrite(std::get<0>(t: values));
266}
267
268void applyStateHelper(const CullFace *state, QRhiGraphicsPipeline *gp) noexcept
269{
270 const auto values = state->values();
271 const QCullFace::CullingMode cullingMode =
272 static_cast<QCullFace::CullingMode>(std::get<0>(t: values));
273 switch (cullingMode) {
274 case QCullFace::NoCulling:
275 gp->setCullMode(QRhiGraphicsPipeline::None);
276 break;
277 case QCullFace::Front:
278 gp->setCullMode(QRhiGraphicsPipeline::Front);
279 break;
280 case QCullFace::Back:
281 gp->setCullMode(QRhiGraphicsPipeline::Back);
282 break;
283 case QCullFace::FrontAndBack:
284 qCWarning(Backend) << "RHI doesn't handle FrontAndBack CullFace";
285 break;
286 }
287}
288
289void applyStateHelper(const FrontFace *state, QRhiGraphicsPipeline *gp) noexcept
290{
291 const auto values = state->values();
292 const QFrontFace::WindingDirection cullingMode =
293 static_cast<QFrontFace::WindingDirection>(std::get<0>(t: values));
294
295 switch (cullingMode) {
296 case QFrontFace::ClockWise:
297 gp->setFrontFace(QRhiGraphicsPipeline::CW);
298 break;
299 case QFrontFace::CounterClockWise:
300 gp->setFrontFace(QRhiGraphicsPipeline::CCW);
301 break;
302 }
303}
304
305void applyStateHelper(const PolygonOffset *state, QRhiGraphicsPipeline *gp)
306{
307 const auto values = state->values();
308 auto slopeScaledDepthBias = std::get<0>(t: values);
309 auto depthBias = std::get<1>(t: values);
310 gp->setSlopeScaledDepthBias(slopeScaledDepthBias);
311 gp->setDepthBias(depthBias);
312}
313
314void applyStateHelper(const StencilTest *state, QRhiGraphicsPipeline *gp) noexcept
315{
316 const auto values = state->values();
317 gp->setStencilTest(true);
318
319 auto getCompareOp = [](int compareOp) {
320 switch (compareOp) {
321 case QStencilTestArguments::Never:
322 return QRhiGraphicsPipeline::Never;
323 case QStencilTestArguments::Always:
324 return QRhiGraphicsPipeline::Always;
325 case QStencilTestArguments::Less:
326 return QRhiGraphicsPipeline::Less;
327 case QStencilTestArguments::LessOrEqual:
328 return QRhiGraphicsPipeline::LessOrEqual;
329 case QStencilTestArguments::Equal:
330 return QRhiGraphicsPipeline::Equal;
331 case QStencilTestArguments::GreaterOrEqual:
332 return QRhiGraphicsPipeline::GreaterOrEqual;
333 case QStencilTestArguments::Greater:
334 return QRhiGraphicsPipeline::Greater;
335 case QStencilTestArguments::NotEqual:
336 return QRhiGraphicsPipeline::NotEqual;
337 default:
338 qDebug() << "Unhandled stencil test argument";
339 return QRhiGraphicsPipeline::Never;
340 }
341 };
342
343 QRhiGraphicsPipeline::StencilOpState frontCompare = gp->stencilFront();
344 frontCompare.compareOp = getCompareOp(std::get<0>(t: values));
345 gp->setStencilFront(frontCompare);
346
347 QRhiGraphicsPipeline::StencilOpState backCompare = gp->stencilBack();
348 backCompare.compareOp = getCompareOp(std::get<3>(t: values));
349 gp->setStencilBack(backCompare);
350}
351
352void applyStateHelper(const ColorMask *state, QRhiGraphicsPipeline *gp) noexcept
353{
354 const auto values = state->values();
355
356 // We assume a single color attachment
357 QRhiGraphicsPipeline::TargetBlend targetBlend;
358
359 const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
360 if (hasTargetBlend)
361 targetBlend = *(gp->cbeginTargetBlends());
362
363 const bool redEnabled = std::get<0>(t: values);
364 const bool greenEnabled = std::get<1>(t: values);
365 const bool blueEnabled = std::get<2>(t: values);
366 const bool alphaEnabled = std::get<3>(t: values);
367
368 QRhiGraphicsPipeline::ColorMask colorMask;
369 if (redEnabled)
370 colorMask |= QRhiGraphicsPipeline::R;
371 if (greenEnabled)
372 colorMask |= QRhiGraphicsPipeline::G;
373 if (blueEnabled)
374 colorMask |= QRhiGraphicsPipeline::B;
375 if (alphaEnabled)
376 colorMask |= QRhiGraphicsPipeline::A;
377
378 targetBlend.colorWrite = colorMask;
379 gp->setTargetBlends({ targetBlend });
380}
381
382void applyStateHelper(const StencilOp *state, QRhiGraphicsPipeline *gp) noexcept
383{
384 const auto values = state->values();
385 auto getRHIStencilOp = [](int op) {
386 switch (op) {
387 case QStencilOperationArguments::Zero:
388 return QRhiGraphicsPipeline::StencilZero;
389 case QStencilOperationArguments::Keep:
390 return QRhiGraphicsPipeline::Keep;
391 case QStencilOperationArguments::Replace:
392 return QRhiGraphicsPipeline::Replace;
393 case QStencilOperationArguments::Increment:
394 return QRhiGraphicsPipeline::IncrementAndClamp;
395 case QStencilOperationArguments::Decrement:
396 return QRhiGraphicsPipeline::DecrementAndClamp;
397 case QStencilOperationArguments::IncrementWrap:
398 return QRhiGraphicsPipeline::IncrementAndWrap;
399 case QStencilOperationArguments::DecrementWrap:
400 return QRhiGraphicsPipeline::DecrementAndWrap;
401 case QStencilOperationArguments::Invert:
402 return QRhiGraphicsPipeline::Invert;
403 default:
404 qDebug() << "Unhandled stencil operation argument";
405 return QRhiGraphicsPipeline::StencilZero;
406 }
407 };
408
409 QRhiGraphicsPipeline::StencilOpState frontCompare = gp->stencilFront();
410 frontCompare.depthFailOp = getRHIStencilOp(std::get<1>(t: values));
411 frontCompare.failOp = getRHIStencilOp(std::get<0>(t: values));
412 frontCompare.passOp = getRHIStencilOp(std::get<2>(t: values));
413 gp->setStencilFront(frontCompare);
414
415 QRhiGraphicsPipeline::StencilOpState backCompare = gp->stencilBack();
416 backCompare.depthFailOp = getRHIStencilOp(std::get<4>(t: values));
417 backCompare.failOp = getRHIStencilOp(std::get<3>(t: values));
418 backCompare.passOp = getRHIStencilOp(std::get<5>(t: values));
419 gp->setStencilBack(backCompare);
420}
421
422void applyStateHelper(const StencilMask *state, QRhiGraphicsPipeline *gp) noexcept
423{
424 const auto values = state->values();
425 gp->setStencilWriteMask(std::get<0>(t: values));
426 gp->setStencilReadMask(std::get<1>(t: values));
427}
428
429static QShader::Stage rhiShaderStage(QShaderProgram::ShaderType type) noexcept
430{
431 switch (type) {
432 case QShaderProgram::Vertex:
433 return QShader::VertexStage;
434 case QShaderProgram::Fragment:
435 return QShader::FragmentStage;
436 case QShaderProgram::TessellationControl:
437 return QShader::TessellationControlStage;
438 case QShaderProgram::TessellationEvaluation:
439 return QShader::TessellationEvaluationStage;
440 case QShaderProgram::Geometry:
441 return QShader::GeometryStage;
442 case QShaderProgram::Compute:
443 return QShader::ComputeStage;
444 default:
445 std::abort();
446 }
447}
448
449} // anonymous
450
451SubmissionContext::SubmissionContext()
452 : m_initialized(false),
453 m_ownsRhiCtx(false),
454 m_drivenExternally(false),
455 m_id(nextFreeContextId()),
456 m_material(nullptr),
457 m_renderer(nullptr),
458 m_rhi(nullptr),
459 m_currentSwapChain(nullptr),
460 m_currentRenderPassDescriptor(nullptr),
461 m_defaultRenderTarget(nullptr),
462 m_defaultCommandBuffer(nullptr)
463#ifndef QT_NO_OPENGL
464 ,
465 m_fallbackSurface(nullptr)
466#endif
467{
468 static_contexts[m_id] = this;
469 m_contextInfo.m_api = QGraphicsApiFilter::RHI;
470
471 // We set those version numbers because QShaderGenerator wants major > 0
472 m_contextInfo.m_major = 1;
473 m_contextInfo.m_minor = 0;
474}
475
476SubmissionContext::~SubmissionContext()
477{
478 releaseResources();
479
480 Q_ASSERT(static_contexts[m_id] == this);
481 static_contexts.remove(key: m_id);
482}
483
484void SubmissionContext::initialize()
485{
486 m_initialized = true;
487
488 // If the RHI instance was already set (Scene3D)
489 // no point in continuing below;
490 if (m_rhi)
491 return;
492
493 m_ownsRhiCtx = true;
494
495 Qt3DRender::API requestedApi = Qt3DRender::API::RHI;
496 const auto userRequestedApi = qgetenv(varName: "QSG_RHI_BACKEND").toLower();
497 if (!userRequestedApi.isEmpty()) {
498 if (userRequestedApi == QByteArrayLiteral("opengl") ||
499 userRequestedApi == QByteArrayLiteral("gl") ||
500 userRequestedApi == QByteArrayLiteral("gles2")) {
501 requestedApi = Qt3DRender::API::OpenGL;
502 } else if (userRequestedApi == QByteArrayLiteral("vulkan")) {
503 requestedApi = Qt3DRender::API::Vulkan;
504 } else if (userRequestedApi == QByteArrayLiteral("metal")) {
505 requestedApi = Qt3DRender::API::Metal;
506 } else if (userRequestedApi == QByteArrayLiteral("d3d11")) {
507 requestedApi = Qt3DRender::API::DirectX;
508 } else if (userRequestedApi == QByteArrayLiteral("null")) {
509 requestedApi = Qt3DRender::API::Null;
510 }
511 }
512
513 // If nothing specified, deduce best backend API based on platform
514 if (requestedApi == Qt3DRender::API::RHI) {
515#if defined(Q_OS_WIN)
516 requestedApi = Qt3DRender::API::DirectX;
517#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
518 requestedApi = Qt3DRender::API::Metal;
519#elif QT_CONFIG(opengl)
520 requestedApi = Qt3DRender::API::OpenGL;
521#else
522 requestedApi = Qt3DRender::API::Vulkan;
523#endif
524 }
525
526 QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
527
528#if QT_CONFIG(qt3d_vulkan) && QT_CONFIG(vulkan)
529 if (requestedApi == Qt3DRender::API::Vulkan) {
530 QRhiVulkanInitParams params;
531 params.inst = &Qt3DRender::staticVulkanInstance();
532 m_rhi = QRhi::create(impl: QRhi::Vulkan, params: &params, flags: rhiFlags);
533 qCWarning(Backend) << "Initializing RHI with Vulkan backend";
534 }
535#endif
536
537#ifdef Q_OS_WIN
538 if (requestedApi == Qt3DRender::API::DirectX) {
539 QRhiD3D11InitParams params;
540 params.enableDebugLayer = true;
541 m_rhi = QRhi::create(QRhi::D3D11, &params, rhiFlags);
542 qCWarning(Backend) << "Initializing RHI with DirectX backend";
543 }
544#endif
545
546#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
547 if (requestedApi == Qt3DRender::API::Metal) {
548 QRhiMetalInitParams params;
549 m_rhi = QRhi::create(QRhi::Metal, &params, rhiFlags);
550 qCWarning(Backend) << "Initializing RHI with Metal backend";
551 }
552#endif
553 if (requestedApi == Qt3DRender::API::Null) {
554 QRhiInitParams params;
555 m_rhi = QRhi::create(impl: QRhi::Null, params: &params, flags: rhiFlags);
556 qCWarning(Backend) << "Initializing RHI with Null backend";
557 }
558
559 if (requestedApi != Qt3DRender::API::OpenGL && m_rhi == nullptr) {
560 qCWarning(Backend) << "RHI: Unable to use requested RHI Api, trying to fall back on OpenGL";
561 requestedApi = Qt3DRender::API::OpenGL;
562 }
563
564 if (requestedApi == Qt3DRender::API::OpenGL) {
565#ifndef QT_NO_OPENGL
566 m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
567 QRhiGles2InitParams params;
568 params.format = QSurfaceFormat::defaultFormat();
569 params.fallbackSurface = m_fallbackSurface;
570 m_rhi = QRhi::create(impl: QRhi::OpenGLES2, params: &params, flags: rhiFlags);
571 qCWarning(Backend) << "Initializing RHI with OpenGL backend";
572#else
573 qCWarning(Backend) << "RHI: OpenGL not supported";
574#endif
575 }
576
577 Q_ASSERT(m_rhi != nullptr);
578}
579
580void SubmissionContext::setDrivenExternally(bool drivenExternally)
581{
582 Q_ASSERT(!m_initialized);
583 m_drivenExternally = drivenExternally;
584}
585
586bool SubmissionContext::drivenExternally() const
587{
588 return m_drivenExternally;
589}
590
591bool SubmissionContext::beginDrawing(QSurface *surface)
592{
593 Q_ASSERT(surface);
594
595 Q_ASSERT(isInitialized());
596
597 // In the Scene3D case it does not make sense to create SwapChains as we
598 // can only record commands that will be used against QtQuick default's
599 // swap chain. Also when rendering through Scene3D, QtQuick takes care of
600 // beginning the frame
601 if (m_drivenExternally)
602 return true;
603
604 // Check if we have a swapchain for the Window, if not create one
605 SwapChainInfo *swapChainInfo = swapChainForSurface(surface);
606 QRhiSwapChain *swapChain = swapChainInfo->swapChain;
607
608 // Resize swapchain if needed
609 if (surface->size() != swapChain->currentPixelSize()) {
610 bool couldRebuild = swapChain->createOrResize();
611 if (!couldRebuild)
612 return false;
613 }
614
615 m_currentSwapChain = swapChain;
616 m_currentRenderPassDescriptor = swapChainInfo->renderPassDescriptor;
617
618 const auto success = m_rhi->beginFrame(swapChain: m_currentSwapChain);
619 return success == QRhi::FrameOpSuccess;
620}
621
622void SubmissionContext::endDrawing(bool swapBuffers)
623{
624 Q_UNUSED(swapBuffers);
625 const bool shouldEndFrame = !m_drivenExternally;
626 // When rendering through Scene3D, QtQuick takes care of ending the frame
627 if (shouldEndFrame)
628 m_rhi->endFrame(swapChain: m_currentSwapChain, flags: {});
629}
630
631QImage SubmissionContext::readFramebuffer(const QRect &rect)
632{
633 Q_UNUSED(rect);
634 RHI_UNIMPLEMENTED;
635 return {};
636 //* QImage img;
637 //* const unsigned int area = rect.width() * rect.height();
638 //* unsigned int bytes;
639 //* GLenum format, type;
640 //* QImage::Format imageFormat;
641 //* uint stride;
642 //*
643 //* /* format value should match GL internalFormat */
644 //* GLenum internalFormat = m_renderTargetFormat;
645 //*
646 //* switch (m_renderTargetFormat) {
647 //* case QAbstractTexture::RGBAFormat:
648 //* case QAbstractTexture::RGBA8_SNorm:
649 //* case QAbstractTexture::RGBA8_UNorm:
650 //* case QAbstractTexture::RGBA8U:
651 //* case QAbstractTexture::SRGB8_Alpha8:
652 //*#ifdef QT_OPENGL_ES_2
653 //* format = GL_RGBA;
654 //* imageFormat = QImage::Format_RGBA8888_Premultiplied;
655 //*#else
656 //* format = GL_BGRA;
657 //* imageFormat = QImage::Format_ARGB32_Premultiplied;
658 //* internalFormat = GL_RGBA8;
659 //*#endif
660 //* type = GL_UNSIGNED_BYTE;
661 //* bytes = area * 4;
662 //* stride = rect.width() * 4;
663 //* break;
664 //* case QAbstractTexture::SRGB8:
665 //* case QAbstractTexture::RGBFormat:
666 //* case QAbstractTexture::RGB8U:
667 //* case QAbstractTexture::RGB8_UNorm:
668 //*#ifdef QT_OPENGL_ES_2
669 //* format = GL_RGBA;
670 //* imageFormat = QImage::Format_RGBX8888;
671 //*#else
672 //* format = GL_BGRA;
673 //* imageFormat = QImage::Format_RGB32;
674 //* internalFormat = GL_RGB8;
675 //*#endif
676 //* type = GL_UNSIGNED_BYTE;
677 //* bytes = area * 4;
678 //* stride = rect.width() * 4;
679 //* break;
680 //*#ifndef QT_OPENGL_ES_2
681 //* case QAbstractTexture::RG11B10F:
682 //* bytes = area * 4;
683 //* format = GL_RGB;
684 //* type = GL_UNSIGNED_INT_10F_11F_11F_REV;
685 //* imageFormat = QImage::Format_RGB30;
686 //* stride = rect.width() * 4;
687 //* break;
688 //* case QAbstractTexture::RGB10A2:
689 //* bytes = area * 4;
690 //* format = GL_RGBA;
691 //* type = GL_UNSIGNED_INT_2_10_10_10_REV;
692 //* imageFormat = QImage::Format_A2BGR30_Premultiplied;
693 //* stride = rect.width() * 4;
694 //* break;
695 //* case QAbstractTexture::R5G6B5:
696 //* bytes = area * 2;
697 //* format = GL_RGB;
698 //* type = GL_UNSIGNED_SHORT;
699 //* internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV;
700 //* imageFormat = QImage::Format_RGB16;
701 //* stride = rect.width() * 2;
702 //* break;
703 //* case QAbstractTexture::RGBA16F:
704 //* case QAbstractTexture::RGBA16U:
705 //* case QAbstractTexture::RGBA32F:
706 //* case QAbstractTexture::RGBA32U:
707 //* bytes = area * 16;
708 //* format = GL_RGBA;
709 //* type = GL_FLOAT;
710 //* imageFormat = QImage::Format_ARGB32_Premultiplied;
711 //* stride = rect.width() * 16;
712 //* break;
713 //*#endif
714 //* default:
715 //* auto warning = qWarning();
716 //* warning << "Unable to convert";
717 //* QtDebugUtils::formatQEnum(warning, m_renderTargetFormat);
718 //* warning << "render target texture format to QImage.";
719 //* return img;
720 //* }
721 //*
722 //* GLint samples = 0;
723 //* m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples);
724 //* if (samples > 0 &&
725 //!m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) {
726 //* qCWarning(Backend) << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; "
727 //* "Required feature BlitFramebuffer is missing.";
728 //* return img;
729 //* }
730 //*
731 //* img = QImage(rect.width(), rect.height(), imageFormat);
732 //*
733 //* QScopedArrayPointer<uchar> data(new uchar [bytes]);
734 //*
735 //* if (samples > 0) {
736 //* // resolve multisample-framebuffer to renderbuffer and read pixels from it
737 //* GLuint fbo, rb;
738 //* QOpenGLFunctions *gl = m_gl->functions();
739 //* gl->glGenFramebuffers(1, &fbo);
740 //* gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
741 //* gl->glGenRenderbuffers(1, &rb);
742 //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
743 //* gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(),
744 //rect.height());
745 //* gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
746 //GL_RENDERBUFFER, rb);
747 //*
748 //* const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
749 //* if (status != GL_FRAMEBUFFER_COMPLETE) {
750 //* gl->glDeleteRenderbuffers(1, &rb);
751 //* gl->glDeleteFramebuffers(1, &fbo);
752 //* qCWarning(Backend) << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status;
753 //* return img;
754 //* }
755 //*
756 //* m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() +
757 //rect.height(),
758 //* 0, 0, rect.width(), rect.height(),
759 //* GL_COLOR_BUFFER_BIT, GL_NEAREST);
760 //* gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
761 //* gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data());
762 //*
763 //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(),
764 //m_renderTargetFormat);
765 //*
766 //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
767 //* gl->glDeleteRenderbuffers(1, &rb);
768 //* gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO);
769 //* gl->glDeleteFramebuffers(1, &fbo);
770 //* } else {
771 //* // read pixels directly from framebuffer
772 //* m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(),
773 //format, type, data.data());
774 //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(),
775 //m_renderTargetFormat);
776 //* }
777 //*
778 //* return img;
779}
780
781void SubmissionContext::releaseResources()
782{
783 m_renderBufferHash.clear();
784 RHI_UNIMPLEMENTED;
785
786 if (m_currentUpdates) {
787 m_currentUpdates->release();
788 m_currentUpdates = nullptr;
789 }
790
791 // Free RHI resources
792 {
793 qCDebug(Backend) << Q_FUNC_INFO;
794
795 // We must ensure no remaining resource before deleting m_rhi.
796 m_renderer->rhiResourceManagers()->releaseAllResources();
797
798 auto it = m_swapChains.begin();
799 while (it != m_swapChains.end()) {
800 SwapChainInfo &swapChainInfo = it.value();
801 delete swapChainInfo.renderPassDescriptor;
802 delete swapChainInfo.renderBuffer;
803 delete swapChainInfo.swapChain;
804 it = m_swapChains.erase(it);
805 }
806
807 // Only destroy RHI context if we created it
808 if (m_ownsRhiCtx)
809 delete m_rhi;
810 m_rhi = nullptr;
811
812#ifndef QT_NO_OPENGL
813 delete m_fallbackSurface;
814 m_fallbackSurface = nullptr;
815#endif
816 }
817
818 //* // Stop and destroy the OpenGL logger
819 //* if (m_debugLogger) {
820 //* m_debugLogger->stopLogging();
821 //* m_debugLogger.reset(nullptr);
822 //* }
823}
824
825// Called when Scene3D is used
826void SubmissionContext::setRHIContext(QRhi *ctx)
827{
828 m_rhi = ctx;
829}
830
831// Scene3D
832void SubmissionContext::setDefaultRenderTarget(QRhiRenderTarget *target)
833{
834 m_defaultRenderTarget = target;
835}
836
837// Scene3D
838void SubmissionContext::setCommandBuffer(QRhiCommandBuffer *commandBuffer)
839{
840 m_defaultCommandBuffer = commandBuffer;
841}
842
843void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId,
844 const AttachmentPack &attachments)
845{
846 Q_UNUSED(fboId);
847 Q_UNUSED(attachments);
848 RHI_UNIMPLEMENTED;
849 // Set FBO attachments. These are normally textures, except that on Open GL
850 // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is
851 // desired since this cannot be achieved neither with a single texture (not
852 // before GLES 3.2) nor with separate textures (no suitable format for
853 // stencil before 3.1 with the appropriate extension).
854
855 //* QSize fboSize;
856 //* RHITextureManager *rhiTextureManager =
857 //m_renderer->rhiResourceManagers()->rhiTextureManager();
858 //* const auto attachments_ = attachments.attachments();
859 //* for (const Attachment &attachment : attachments_) {
860 //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid);
861 //* if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) {
862 //* QOpenGLTexture *glTex = rTex ? rTex->getGLTexture() : nullptr;
863 //* if (glTex != nullptr) {
864 //* // The texture can not be rendered simultaniously by another renderer
865 //* Q_ASSERT(!rTex->isExternalRenderingEnabled());
866 //* if (fboSize.isEmpty())
867 //* fboSize = QSize(glTex->width(), glTex->height());
868 //* else
869 //* fboSize = QSize(qMin(fboSize.width(), glTex->width()),
870 //qMin(fboSize.height(), glTex->height()));
871 //* m_glHelper->bindFrameBufferAttachment(glTex, attachment);
872 //* }
873 //* } else {
874 //* RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr;
875 //* if (renderBuffer) {
876 //* if (fboSize.isEmpty())
877 //* fboSize = QSize(renderBuffer->width(), renderBuffer->height());
878 //* else
879 //* fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()),
880 //qMin(fboSize.height(), renderBuffer->height()));
881 //* m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment);
882 //* }
883 //* }
884 //* }
885 //* m_renderTargetsSize.insert(fboId, fboSize);
886}
887
888void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments)
889{
890 Q_UNUSED(attachments);
891 RHI_UNIMPLEMENTED;
892 //* const std::vector<int> activeDrawBuffers = attachments.getGlDrawBuffers();
893 //*
894 //* if (m_glHelper->checkFrameBufferComplete()) {
895 //* if (activeDrawBuffers.size() > 1) {// We need MRT
896 //* if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) {
897 //* // Set up MRT, glDrawBuffers...
898 //* m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data());
899 //* }
900 //* }
901 //* } else {
902 //* qCWarning(Backend) << "FBO incomplete";
903 //* }
904}
905
906void SubmissionContext::applyState(const StateVariant &stateVariant,
907 QRhiGraphicsPipeline *graphicsPipeline)
908{
909 switch (stateVariant.type) {
910
911 case AlphaCoverageStateMask: {
912 applyStateHelper(state: static_cast<const AlphaCoverage *>(stateVariant.constState()),
913 gp: graphicsPipeline);
914 break;
915 }
916 case AlphaTestMask: {
917 applyStateHelper(state: static_cast<const AlphaFunc *>(stateVariant.constState()),
918 gp: graphicsPipeline);
919 break;
920 }
921 case BlendStateMask: {
922 applyStateHelper(state: static_cast<const BlendEquation *>(stateVariant.constState()),
923 gp: graphicsPipeline);
924 break;
925 }
926 case BlendEquationArgumentsMask: {
927 applyStateHelper(state: static_cast<const BlendEquationArguments *>(stateVariant.constState()),
928 gp: graphicsPipeline);
929 break;
930 }
931 case MSAAEnabledStateMask: {
932 applyStateHelper(state: static_cast<const MSAAEnabled *>(stateVariant.constState()),
933 gp: graphicsPipeline, format: m_renderer->format());
934 break;
935 }
936
937 case CullFaceStateMask: {
938 applyStateHelper(state: static_cast<const CullFace *>(stateVariant.constState()),
939 gp: graphicsPipeline);
940 break;
941 }
942
943 case DepthWriteStateMask: {
944 applyStateHelper(state: static_cast<const NoDepthMask *>(stateVariant.constState()),
945 gp: graphicsPipeline);
946 break;
947 }
948
949 case DepthTestStateMask: {
950 applyStateHelper(state: static_cast<const DepthTest *>(stateVariant.constState()),
951 gp: graphicsPipeline);
952 break;
953 }
954
955 case DepthRangeMask: {
956 applyStateHelper(state: static_cast<const DepthRange *>(stateVariant.constState()),
957 gp: graphicsPipeline);
958 break;
959 }
960
961 case RasterModeMask: {
962 applyStateHelper(state: static_cast<const RasterMode *>(stateVariant.constState()),
963 gp: graphicsPipeline);
964 break;
965 }
966
967 case FrontFaceStateMask: {
968 applyStateHelper(state: static_cast<const FrontFace *>(stateVariant.constState()),
969 gp: graphicsPipeline);
970 break;
971 }
972
973 case ScissorStateMask: {
974 applyStateHelper(state: static_cast<const ScissorTest *>(stateVariant.constState()),
975 gp: graphicsPipeline);
976 break;
977 }
978
979 case StencilTestStateMask: {
980 applyStateHelper(state: static_cast<const StencilTest *>(stateVariant.constState()),
981 gp: graphicsPipeline);
982 break;
983 }
984
985 case PointSizeMask: {
986 applyStateHelper(state: static_cast<const PointSize *>(stateVariant.constState()),
987 gp: graphicsPipeline);
988 break;
989 }
990
991 case PolygonOffsetStateMask: {
992 applyStateHelper(state: static_cast<const PolygonOffset *>(stateVariant.constState()),
993 gp: graphicsPipeline);
994 break;
995 }
996
997 case ColorStateMask: {
998 applyStateHelper(state: static_cast<const ColorMask *>(stateVariant.constState()),
999 gp: graphicsPipeline);
1000 break;
1001 }
1002
1003 case ClipPlaneMask: {
1004 applyStateHelper(state: static_cast<const ClipPlane *>(stateVariant.constState()),
1005 gp: graphicsPipeline);
1006 break;
1007 }
1008
1009 case SeamlessCubemapMask: {
1010 applyStateHelper(state: static_cast<const SeamlessCubemap *>(stateVariant.constState()),
1011 gp: graphicsPipeline);
1012 break;
1013 }
1014
1015 case StencilOpMask: {
1016 applyStateHelper(state: static_cast<const StencilOp *>(stateVariant.constState()),
1017 gp: graphicsPipeline);
1018 break;
1019 }
1020
1021 case StencilWriteStateMask: {
1022 applyStateHelper(state: static_cast<const StencilMask *>(stateVariant.constState()),
1023 gp: graphicsPipeline);
1024 break;
1025 }
1026
1027 case DitheringStateMask: {
1028 applyStateHelper(state: static_cast<const Dithering *>(stateVariant.constState()),
1029 gp: graphicsPipeline);
1030 break;
1031 }
1032
1033 case LineWidthMask: {
1034 applyStateHelper(state: static_cast<const LineWidth *>(stateVariant.constState()),
1035 gp: graphicsPipeline);
1036 break;
1037 }
1038 default:
1039 Q_UNREACHABLE();
1040 }
1041}
1042
1043void SubmissionContext::applyStateSet(const RenderStateSet *ss,
1044 QRhiGraphicsPipeline *graphicsPipeline)
1045{
1046 // Set default state values on graphicsPipeline
1047 QRhiGraphicsPipeline::Flags flags{};
1048 graphicsPipeline->setFlags(flags);
1049
1050 graphicsPipeline->setDepthWrite(true);
1051 graphicsPipeline->setDepthTest(true);
1052 graphicsPipeline->setDepthOp(QRhiGraphicsPipeline::Less);
1053
1054 graphicsPipeline->setCullMode(QRhiGraphicsPipeline::Back);
1055
1056 graphicsPipeline->setSampleCount(format().samples());
1057
1058 graphicsPipeline->setStencilTest(false);
1059 graphicsPipeline->setStencilReadMask(0xff);
1060 graphicsPipeline->setStencilWriteMask(0xff);
1061
1062 QRhiGraphicsPipeline::StencilOpState stencilOp;
1063 stencilOp.failOp = QRhiGraphicsPipeline::Keep;
1064 stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
1065 stencilOp.passOp = QRhiGraphicsPipeline::Keep;
1066 stencilOp.compareOp = QRhiGraphicsPipeline::Always;
1067
1068 graphicsPipeline->setStencilFront(stencilOp);
1069 graphicsPipeline->setStencilBack(stencilOp);
1070
1071
1072 QRhiGraphicsPipeline::TargetBlend blend;
1073 blend.colorWrite = QRhiGraphicsPipeline::ColorMask(0xF); // R | G | B | A
1074 blend.enable = false;
1075 blend.srcColor = QRhiGraphicsPipeline::One;
1076 blend.dstColor = QRhiGraphicsPipeline::Zero;
1077 blend.srcAlpha = QRhiGraphicsPipeline::One;
1078 blend.dstAlpha = QRhiGraphicsPipeline::Zero;
1079
1080 graphicsPipeline->setTargetBlends({blend});
1081
1082
1083 const auto &statesToSet = ss->states();
1084 for (const StateVariant &ds : statesToSet)
1085 applyState(stateVariant: ds, graphicsPipeline);
1086}
1087
1088StateVariant *SubmissionContext::getState(RenderStateSet *ss, StateMask type) const
1089{
1090 const auto &statesToSet = ss->states();
1091 for (qsizetype i = 0, m = statesToSet.size(); i < m; ++i) {
1092 const StateVariant &ds = statesToSet.at(n: i);
1093 if (ds.type == type)
1094 return ss->states().data() + i;
1095 }
1096 return nullptr;
1097}
1098
1099SubmissionContext::SwapChainInfo *SubmissionContext::swapChainForSurface(QSurface *surface) noexcept
1100{
1101 SwapChainInfo &swapChainInfo = m_swapChains[surface];
1102 auto &swapChain = swapChainInfo.swapChain;
1103
1104 if (swapChain == nullptr) {
1105 swapChain = m_rhi->newSwapChain();
1106 Q_ASSERT(surface->surfaceClass() == QSurface::Window);
1107 QWindow *window = static_cast<QWindow *>(surface);
1108 Q_ASSERT(window != nullptr);
1109 const int samples = format().samples();
1110
1111 swapChain->setWindow(window);
1112 // Allow to read back from swap chain for RenderCapture to work
1113 swapChain->setFlags(QRhiSwapChain::Flags { QRhiSwapChain::UsedAsTransferSource });
1114 swapChain->setSampleCount(samples);
1115
1116 QRhiRenderBuffer *renderBuffer =
1117 m_rhi->newRenderBuffer(type: QRhiRenderBuffer::DepthStencil, pixelSize: QSize(), sampleCount: samples,
1118 flags: QRhiRenderBuffer::UsedWithSwapChainOnly);
1119 swapChain->setDepthStencil(renderBuffer);
1120
1121 QRhiRenderPassDescriptor *renderPassDescriptor =
1122 swapChain->newCompatibleRenderPassDescriptor();
1123 swapChain->setRenderPassDescriptor(renderPassDescriptor);
1124
1125 // Build swapChain the first time
1126 if (swapChain->createOrResize()) {
1127 swapChainInfo.swapChain = swapChain;
1128 swapChainInfo.renderBuffer = renderBuffer;
1129 swapChainInfo.renderPassDescriptor = renderPassDescriptor;
1130 } else {
1131 swapChain->deleteLater();
1132 m_swapChains.remove(key: surface);
1133 return nullptr;
1134 }
1135 }
1136 return &swapChainInfo;
1137}
1138
1139QRhiCommandBuffer *SubmissionContext::currentFrameCommandBuffer() const
1140{
1141 // When rendering with Scene3D, we have to use the Command Buffer provided by QtQuick
1142 // When Qt3D renders on its own, we use our own Command Buffer which we can
1143 // retrieve from the current Swap Chain
1144 if (m_defaultCommandBuffer)
1145 return m_defaultCommandBuffer;
1146 return m_currentSwapChain->currentFrameCommandBuffer();
1147}
1148
1149QRhiRenderTarget *SubmissionContext::currentFrameRenderTarget() const
1150{
1151 return m_currentSwapChain->currentFrameRenderTarget();
1152}
1153
1154QRhiRenderTarget *SubmissionContext::defaultRenderTarget() const
1155{
1156 return m_defaultRenderTarget;
1157}
1158
1159QRhiSwapChain *SubmissionContext::currentSwapChain() const
1160{
1161 return m_currentSwapChain;
1162}
1163
1164QRhiRenderPassDescriptor *SubmissionContext::currentRenderPassDescriptor() const
1165{
1166 return m_currentRenderPassDescriptor;
1167}
1168
1169QSurfaceFormat SubmissionContext::format() const noexcept
1170{
1171 if (this->m_rhi && this->m_rhi->backend() == QRhi::OpenGLES2) {
1172 auto rhi_gl = static_cast<const QRhiGles2NativeHandles *>(this->m_rhi->nativeHandles());
1173 return rhi_gl->context->format();
1174 }
1175 return QSurfaceFormat::defaultFormat();
1176}
1177
1178void SubmissionContext::updateBuffer(Buffer *buffer)
1179{
1180 const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
1181 m_renderBufferHash.find(key: buffer->peerId());
1182 if (it != m_renderBufferHash.end())
1183 uploadDataToRHIBuffer(
1184 buffer, b: m_renderer->rhiResourceManagers()->rhiBufferManager()->data(handle: it.value()));
1185}
1186
1187QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer)
1188{
1189 const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
1190 m_renderBufferHash.find(key: buffer->peerId());
1191 if (it != m_renderBufferHash.end())
1192 return downloadDataFromRHIBuffer(
1193 buffer, b: m_renderer->rhiResourceManagers()->rhiBufferManager()->data(handle: it.value()));
1194 return QByteArray();
1195}
1196
1197void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId)
1198{
1199 auto it = m_renderBufferHash.find(key: bufferId);
1200 if (it != m_renderBufferHash.end()) {
1201 HRHIBuffer glBuffHandle = it.value();
1202 RHIBuffer *glBuff =
1203 m_renderer->rhiResourceManagers()->rhiBufferManager()->data(handle: glBuffHandle);
1204
1205 Q_ASSERT(glBuff);
1206 // Destroy the GPU resource
1207 glBuff->destroy();
1208 // Destroy the RHIBuffer instance
1209 m_renderer->rhiResourceManagers()->rhiBufferManager()->releaseResource(id: bufferId);
1210 // Remove Id - HRHIBuffer entry
1211 m_renderBufferHash.erase(it);
1212 }
1213}
1214
1215bool SubmissionContext::hasRHIBufferForBuffer(Buffer *buffer)
1216{
1217 const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
1218 m_renderBufferHash.find(key: buffer->peerId());
1219 return (it != m_renderBufferHash.end());
1220}
1221
1222RHIBuffer *SubmissionContext::rhiBufferForRenderBuffer(Buffer *buf)
1223{
1224 if (!m_renderBufferHash.contains(key: buf->peerId()))
1225 m_renderBufferHash.insert(key: buf->peerId(), value: createRHIBufferFor(buffer: buf));
1226 return m_renderer->rhiResourceManagers()->rhiBufferManager()->data(
1227 handle: m_renderBufferHash.value(key: buf->peerId()));
1228}
1229
1230HRHIBuffer SubmissionContext::createRHIBufferFor(Buffer *buffer)
1231{
1232 m_renderer->rhiResourceManagers()->rhiBufferManager()->getOrCreateResource(id: buffer->peerId());
1233 return m_renderer->rhiResourceManagers()->rhiBufferManager()->lookupHandle(id: buffer->peerId());
1234}
1235
1236bool SubmissionContext::bindRHIBuffer(RHIBuffer *buffer, RHIBuffer::Type type)
1237{
1238 return buffer->bind(ctx: this, t: type);
1239}
1240
1241void SubmissionContext::uploadDataToRHIBuffer(Buffer *buffer, RHIBuffer *b)
1242{
1243 // If the buffer is dirty (hence being called here)
1244 // there are two possible cases
1245 // * setData was called changing the whole data or functor (or the usage pattern)
1246 // * partial buffer updates where received
1247
1248 // Note: we are only storing the updates data CPU side at this point
1249 // actually upload will be performed when the buffer will be bound
1250 // as we would otherwise need to know the usage type of the buffer
1251 auto updates = Qt3DCore::moveAndClear(data&: buffer->pendingBufferUpdates());
1252
1253 if (updates.empty())
1254 qCWarning(Backend) << "Buffer has no data to upload";
1255
1256 for (auto it = updates.begin(); it != updates.end(); ++it) {
1257 auto update = it;
1258 // We have a partial update
1259 if (update->offset >= 0) {
1260 // accumulate sequential updates as single one
1261 qsizetype bufferSize = update->data.size();
1262 auto it2 = it + 1;
1263 while ((it2 != updates.end()) && (it2->offset - update->offset == bufferSize)) {
1264 bufferSize += it2->data.size();
1265 ++it2;
1266 }
1267 update->data.resize(size: bufferSize);
1268 while (it + 1 != it2) {
1269 ++it;
1270 update->data.replace(index: it->offset - update->offset, len: it->data.size(), s: it->data);
1271 it->data.clear();
1272 }
1273 // TO DO: based on the number of updates .., it might make sense to
1274 // sometime use glMapBuffer rather than glBufferSubData
1275 b->update(data: update->data, offset: update->offset);
1276 } else {
1277 // We have an update that was done by calling QBuffer::setData
1278 // which is used to resize or entirely clear the buffer
1279 // Note: we use the buffer data directly in that case
1280 b->allocate(data: buffer->data(), dynamic: false);
1281 }
1282 }
1283
1284 qCDebug(Io) << "uploaded buffer size=" << buffer->data().size();
1285}
1286
1287QByteArray SubmissionContext::downloadDataFromRHIBuffer(Buffer *buffer, RHIBuffer *b)
1288{
1289 if (!bindRHIBuffer(buffer: b,
1290 type: RHIBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here
1291 qCWarning(Io) << Q_FUNC_INFO << "buffer bind failed";
1292
1293 return b->download(ctx: this, size: buffer->data().size());
1294}
1295
1296void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
1297 Qt3DCore::QNodeId outputRenderTargetId, QRect inputRect,
1298 QRect outputRect, uint defaultFboId,
1299 QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
1300 QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
1301 QBlitFramebuffer::InterpolationMethod interpolationMethod)
1302{
1303 Q_UNUSED(inputRenderTargetId);
1304 Q_UNUSED(outputRenderTargetId);
1305 Q_UNUSED(inputRect);
1306 Q_UNUSED(outputRect);
1307 Q_UNUSED(defaultFboId);
1308 Q_UNUSED(inputAttachmentPoint);
1309 Q_UNUSED(outputAttachmentPoint);
1310 Q_UNUSED(interpolationMethod);
1311 RHI_UNIMPLEMENTED;
1312}
1313
1314namespace {
1315template<std::size_t N>
1316constexpr int getFirstAvailableBit(const std::bitset<N> &bits)
1317{
1318 for (std::size_t i = 0; i < N; i++) {
1319 if (!bits.test(i))
1320 return int(i);
1321 }
1322 return -1;
1323}
1324// This function ensures that the shader stages all have the same bindings
1325void preprocessRHIShader(std::vector<QByteArray> &shaderCodes)
1326{
1327 // Map the variable names to bindings
1328 std::map<QByteArray, int> bindings;
1329 bindings["qt3d_render_view_uniforms"] = 0;
1330 bindings["qt3d_command_uniforms"] = 1;
1331 std::bitset<512> assignedBindings;
1332 assignedBindings.set(position: 0);
1333 assignedBindings.set(position: 1);
1334
1335 thread_local const QRegularExpression samplerRegex(
1336 QStringLiteral("binding\\s*=\\s*([0-9]+).*\\)\\s*uniform\\s*[ui]?sampler[a-zA-Z0-9]+"
1337 "\\s*([a-zA-Z0-9_]+)\\s*;"));
1338 thread_local const QRegularExpression uboRegex(
1339 QStringLiteral("(?:std140\\s*,\\s*binding\\s*=\\s*([0-9]+).*|binding\\s*=\\s*([0-9]+)"
1340 "\\s*,\\s*std140.*)\\)\\s*uniform\\s*([a-zA-Z0-9_]+)"));
1341
1342 auto replaceBinding = [&bindings, &assignedBindings](
1343 qsizetype &offset, QRegularExpressionMatch &match, QString &code,
1344 int indexCapture, int variableCapture) noexcept {
1345 int index = match.captured(nth: indexCapture).toInt();
1346 QByteArray variable = match.captured(nth: variableCapture).toUtf8();
1347
1348 auto it = bindings.find(x: variable);
1349 if (it == bindings.end()) {
1350 // 1. Check if the index is already used
1351 if (assignedBindings.test(position: index)) {
1352 index = getFirstAvailableBit(bits: assignedBindings);
1353 if (index == -1) {
1354 return;
1355 }
1356
1357 const qsizetype indexStartOffset = match.capturedStart(nth: indexCapture);
1358 const qsizetype indexEndOffset = match.capturedEnd(nth: indexCapture);
1359 const qsizetype indexLength = indexEndOffset - indexStartOffset;
1360 code.replace(i: indexStartOffset, len: indexLength, after: QByteArray::number(index));
1361 }
1362
1363 assignedBindings.set(position: index);
1364 bindings.emplace(args: std::move(variable), args&: index);
1365 } else {
1366 int indexToUse = it->second;
1367 const qsizetype indexStartOffset = match.capturedStart(nth: indexCapture);
1368 const qsizetype indexEndOffset = match.capturedEnd(nth: indexCapture);
1369 const qsizetype indexLength = indexEndOffset - indexStartOffset;
1370 code.replace(i: indexStartOffset, len: indexLength, after: QByteArray::number(indexToUse));
1371 }
1372 // This may fail in the case where the replaced offset is an incredibly long number,
1373 // which seems quite unlikely
1374 offset = match.capturedEnd(nth: 0);
1375 };
1376
1377 for (QByteArray &shaderCode : shaderCodes) {
1378 // Since QRegularExpression::match takes a QString anyway, convert once beforehand
1379 QString shaderString = shaderCode;
1380
1381 // Regex for the sampler variables
1382 qsizetype offset = 0;
1383 auto match = samplerRegex.match(subject: shaderString, offset);
1384 while (match.hasMatch()) {
1385 const int indexCapture = 1;
1386 const int variableCapture = 2;
1387 replaceBinding(offset, match, shaderString, indexCapture, variableCapture);
1388
1389 match = samplerRegex.match(subject: shaderString, offset);
1390 }
1391
1392 // Regex for the UBOs
1393 offset = 0;
1394 match = uboRegex.match(subject: shaderString, offset);
1395 while (match.hasMatch()) {
1396 const int indexCapture = !match.capturedView(nth: 1).isEmpty() ? 1 : 2;
1397 const int variableCapture = 3;
1398 replaceBinding(offset, match, shaderString, indexCapture, variableCapture);
1399
1400 match = uboRegex.match(subject: shaderString, offset);
1401 }
1402
1403 shaderCode = shaderString.toUtf8();
1404 }
1405}
1406
1407QShaderVersion glslVersionForFormat(const QSurfaceFormat &format) noexcept
1408{
1409 const int major = format.majorVersion();
1410 const int minor = format.minorVersion();
1411 const auto type = format.renderableType();
1412
1413 if (type != QSurfaceFormat::OpenGLES) {
1414 static const QHash<std::pair<int, int>, int> glVersionToGLSLVersion = {
1415 { { 4, 6 }, 460 }, { { 4, 5 }, 450 }, { { 4, 4 }, 440 }, { { 4, 3 }, 430 },
1416 { { 4, 2 }, 420 }, { { 4, 1 }, 410 }, { { 4, 0 }, 400 }, { { 3, 3 }, 330 },
1417 { { 3, 2 }, 150 }, { { 3, 2 }, 120 }, { { 3, 1 }, 120 },
1418 };
1419
1420 const auto it = glVersionToGLSLVersion.find(key: { major, minor });
1421 if (it == glVersionToGLSLVersion.end()) {
1422 if (major < 3) {
1423 return 120;
1424 } else {
1425 return major * 100 + minor * 10;
1426 }
1427 } else {
1428 return *it;
1429 }
1430 }
1431 else {
1432 static const QHash<std::pair<int, int>, int> glVersionToGLSLVersion = {
1433 { { 3, 2 }, 320 }, { { 3, 1 }, 310 }, { { 3, 0 }, 300 },
1434 };
1435
1436 const auto it = glVersionToGLSLVersion.find(key: { major, minor });
1437 if (it == glVersionToGLSLVersion.end()) {
1438 if (major < 3) {
1439 return {100, QShaderVersion::GlslEs};
1440 } else {
1441 return {major * 100 + minor * 10, QShaderVersion::GlslEs};
1442 }
1443 } else {
1444 return {*it, QShaderVersion::GlslEs};
1445 }
1446 }
1447}
1448}
1449
1450// Called by GL Command Thread
1451SubmissionContext::ShaderCreationInfo SubmissionContext::createShaderProgram(RHIShader *shader)
1452{
1453 // Compile shaders
1454 const auto &shaderCode = shader->shaderCode();
1455 QShaderBaker b;
1456 QList<QShaderBaker::GeneratedShader> generatedShaders;
1457
1458#if QT_FEATURE_vulkan
1459 if (m_rhi->backend() == QRhi::Vulkan)
1460 generatedShaders.emplace_back(args: QShader::SpirvShader, args: 100);
1461#endif
1462
1463#ifndef QT_NO_OPENGL
1464 if (m_rhi->backend() == QRhi::OpenGLES2)
1465 generatedShaders.emplace_back(args: QShader::GlslShader, args: glslVersionForFormat(format: format()));
1466#endif
1467
1468#ifdef Q_OS_WIN
1469 if (m_rhi->backend() == QRhi::D3D11)
1470 generatedShaders.emplace_back(QShader::HlslShader, QShaderVersion(50));
1471#endif
1472
1473#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
1474 if (m_rhi->backend() == QRhi::Metal)
1475 generatedShaders.emplace_back(QShader::MslShader, QShaderVersion(12));
1476#endif
1477
1478 QList<QShader::Variant> generatedShaderVariants(generatedShaders.size());
1479
1480 b.setGeneratedShaders(generatedShaders);
1481 b.setGeneratedShaderVariants(generatedShaderVariants);
1482
1483 // TODO handle caching as QShader does not have a built-in mechanism for that
1484 QString logs;
1485 bool success = true;
1486 for (size_t i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) {
1487 const QShaderProgram::ShaderType type = static_cast<QShaderProgram::ShaderType>(i);
1488 if (!shaderCode.at(n: i).isEmpty()) {
1489 // Note: logs only return the error but not all the shader code
1490 // we could append it
1491
1492 const auto rhiStage = rhiShaderStage(type);
1493 b.setSourceString(sourceString: shaderCode.at(n: i), stage: rhiStage);
1494 QShader bakedShader = b.bake();
1495 if (b.errorMessage() != QString() || !bakedShader.isValid()) {
1496 qDebug() << "Shader Error: " << b.errorMessage() << shaderCode.at(n: i).data()
1497 << rhiStage;
1498 logs += b.errorMessage();
1499 success = false;
1500 }
1501 shader->m_stages[rhiStage] = std::move(bakedShader);
1502 }
1503 }
1504
1505 // Perform shader introspection
1506 if (success)
1507 shader->introspect();
1508
1509 return { .linkSucceeded: success, .logs: logs };
1510}
1511
1512// Called by Renderer::updateResources
1513void SubmissionContext::loadShader(Shader *shaderNode, ShaderManager *shaderManager,
1514 RHIShaderManager *rhiShaderManager)
1515{
1516 const Qt3DCore::QNodeId shaderId = shaderNode->peerId();
1517 RHIShader *rhiShader = rhiShaderManager->lookupResource(shaderId);
1518
1519 // We already have a shader associated with the node
1520 if (rhiShader != nullptr) {
1521 // We need to abandon it
1522 rhiShaderManager->abandon(apiShader: rhiShader, shader: shaderNode);
1523 }
1524
1525 // We create or adopt an already created rhiShader
1526 rhiShader = rhiShaderManager->createOrAdoptExisting(shader: shaderNode);
1527
1528 const std::vector<Qt3DCore::QNodeId> &sharedShaderIds =
1529 rhiShaderManager->shaderIdsForProgram(glShader: rhiShader);
1530 if (sharedShaderIds.size() == 1) {
1531 // Shader in the cache hasn't been loaded yet
1532 // We want a copy of the QByteArray as preprocessRHIShader will
1533 // modify them
1534 std::vector<QByteArray> shaderCodes = shaderNode->shaderCode();
1535 preprocessRHIShader(shaderCodes);
1536 rhiShader->setShaderCode(shaderCodes);
1537
1538 const ShaderCreationInfo loadResult = createShaderProgram(shader: rhiShader);
1539 shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready
1540 : QShaderProgram::Error);
1541 shaderNode->setLog(loadResult.logs);
1542 // Loaded in the sense we tried to load it (and maybe it failed)
1543 rhiShader->setLoaded(true);
1544 } else {
1545 // Find an already loaded shader that shares the same QShaderProgram
1546 for (const Qt3DCore::QNodeId &sharedShaderId : sharedShaderIds) {
1547 if (sharedShaderId != shaderNode->peerId()) {
1548 Shader *refShader = shaderManager->lookupResource(id: sharedShaderId);
1549 // We only introspect once per actual OpenGL shader program
1550 // rather than once per ShaderNode.
1551 shaderNode->initializeFromReference(other: *refShader);
1552 break;
1553 }
1554 }
1555 }
1556 shaderNode->unsetDirty();
1557 // Ensure we will rebuilt material caches
1558 shaderNode->requestCacheRebuild();
1559}
1560
1561const GraphicsApiFilterData *SubmissionContext::contextInfo() const
1562{
1563 return &m_contextInfo;
1564}
1565
1566} // namespace Rhi
1567} // namespace Render
1568} // namespace Qt3DRender of namespace
1569
1570QT_END_NAMESPACE
1571

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