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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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