Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrhicontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
7
8#include "graphobjects/qssgrenderskymaterial_p.h"
9
10#include <QtCore/qvariant.h>
11#include <QtGui/private/qrhi_p.h>
12
13#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
14#include <QtQuick3DUtils/private/qssgmesh_p.h>
15#include <QtQuick3DUtils/private/qssgassert_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
18#include <QtQuick3DRuntimeRender/private/qssguserrenderpassmanager_p.h>
19#include <QtQuick3DUtils/private/qssgutils_p.h>
20#include <QtQuick3DUtils/private/qssgassert_p.h>
21#include <qtquick3d_tracepoints_p.h>
22
23#include <QtGui/qquaternion.h>
24
25#include <memory>
26
28
29Q_TRACE_POINT(qtquick3d, QSSG_renderPass_entry, const QString &renderPass);
31Q_TRACE_POINT(qtquick3d, QSSG_drawIndexed, int indexCount, int instanceCount);
32Q_TRACE_POINT(qtquick3d, QSSG_draw, int vertexCount, int instanceCount);
33
34/*!
35 \class QSSGRhiContext
36 \inmodule QtQuick3D
37 \since 6.7
38
39 \brief QSSGRhiContext.
40 */
41
42/*!
43 \class QSSGRhiGraphicsPipelineState
44 \inmodule QtQuick3D
45 \since 6.7
46
47 \brief Graphics pipeline state for the spatial scene graph.
48
49 This class is a convenience class used by QtQuick3D to wrap relevant pipeline state from the QRhi classes,
50 like \l QRhiGraphicsPipeline. Most of the types and value used in QSSGRhiGraphicsPipelineState will
51 therefore map directly to an equivalent QRhi type or class.
52 */
53
54/*!
55 \variable QSSGRhiGraphicsPipelineState::samples
56
57 The sample count.
58
59 \note A sample count of 1 means no multisample antialiasing.
60
61 \sa QRhiSwapChain::sampleCount()
62 */
63
64/*!
65 \enum QSSGRhiGraphicsPipelineState::Flag
66 \value DepthTestEnabled
67 \value DepthWriteEnabled
68 \value BlendEnabled
69 \value UsesStencilRef
70 \value UsesScissor
71 */
72
73/*!
74 \variable QSSGRhiGraphicsPipelineState::depthFunc
75
76 The depth comparison function.
77
78 \sa QRhiGraphicsPipeline::CompareOp
79 */
80
81/*!
82 \variable QSSGRhiGraphicsPipelineState::cullMode
83
84 Specifies the culling mode.
85
86 \sa QRhiGraphicsPipeline::CullMode
87 */
88
89/*!
90 \variable QSSGRhiGraphicsPipelineState::stencilOpFrontState
91
92 Describes the stencil operation state.
93
94 \sa QRhiGraphicsPipeline::StencilOpState
95 */
96
97/*!
98 \variable QSSGRhiGraphicsPipelineState::stencilWriteMask
99
100 The stencil write mask value. The default value is \c 0xFF.
101
102 \sa QRhiGraphicsPipeline::stencilWriteMask()
103 */
104
105/*!
106 \variable QSSGRhiGraphicsPipelineState::stencilRef
107
108 The active stencil reference value.
109
110 \note Only used when \l{QSSGRhiGraphicsPipelineState::Flag::}{UsesStencilRef} is set.
111
112 \sa QRhiCommandBuffer::setStencilRef()
113 */
114
115/*!
116 \variable QSSGRhiGraphicsPipelineState::depthBias
117
118 The depth bias. The default value is 0.
119
120 \sa QRhiGraphicsPipeline::depthBias()
121 */
122
123/*!
124 \variable QSSGRhiGraphicsPipelineState::slopeScaledDepthBias
125
126 The slope scaled depth bias. The default value is 0.
127
128 \sa QRhiGraphicsPipeline::slopeScaledDepthBias()
129 */
130
131/*!
132 \variable QSSGRhiGraphicsPipelineState::targetBlend
133
134 The blend state for one color attachment.
135
136 \sa QRhiGraphicsPipeline::TargetBlend
137 */
138
139/*!
140 \variable QSSGRhiGraphicsPipelineState::colorAttachmentCount
141
142 The number of color attachments. The default is 1.
143
144 \sa QRhiTextureRenderTargetDescription::setColorAttachments(),
145 QRhiTextureRenderTargetDescription::colorAttachmentCount()
146 */
147
148/*!
149 \variable QSSGRhiGraphicsPipelineState::viewport
150
151 The viewport dimensions used for rendering.
152 */
153
154/*!
155 \variable QSSGRhiGraphicsPipelineState::scissor
156
157 The scissor rect.
158
159 \note Only used if \l{QSSGRhiGraphicsPipelineState::Flag::}{UsesScissor} is set.
160
161 \sa QRhiCommandBuffer::setScissor()
162 */
163
164/*!
165 \variable QSSGRhiGraphicsPipelineState::lineWidth
166
167 The line width used. The default is 1.0
168
169 \note For values other than 1.0 it's required that feature \l QRhi::WideLines is reported
170 as supported at runtime.
171 */
172
173/*!
174 \variable QSSGRhiGraphicsPipelineState::polygonMode
175
176 The polygon mode value. The default is \l{QRhiGraphicsPipeline::Fill}{Fill}.
177
178 \sa QRhiGraphicsPipeline::polygonMode()
179 */
180
181/*!
182 \struct QSSGRhiSamplerDescription
183 \inmodule QtQuick3D
184 \since 6.7
185
186 \brief QSSGRhiSamplerDescription.
187
188 Convenience class used to request a \l QRhiSampler from QtQuick3D internal cache.
189
190 \note Samplers are owned by QtQuick3D.
191
192 \sa QSSGRhiContext::sampler()
193*/
194
195/*!
196 \variable QSSGRhiSamplerDescription::minFilter
197
198 The minification filter mode.
199
200 \sa QRhiSampler::Filter, QRhiSampler::minFilter()
201 */
202
203/*!
204 \variable QSSGRhiSamplerDescription::magFilter
205
206 The magnification filter mode.
207
208 \sa QRhiSampler::Filter, QRhiSampler::magFilter()
209 */
210
211/*!
212 \variable QSSGRhiSamplerDescription::mipmap
213
214 The mipmap filtering mode.
215
216 \sa QRhiSampler::Filter, QRhiSampler::mipmapMode()
217 */
218
219/*!
220 \variable QSSGRhiSamplerDescription::hTiling
221
222 The horizontal wrap mode.
223
224 \sa QRhiSampler::AddressMode
225 */
226
227/*!
228 \variable QSSGRhiSamplerDescription::vTiling
229
230 The vertical wrap mode.
231
232 \sa QRhiSampler::AddressMode
233 */
234
235/*!
236 \variable QSSGRhiSamplerDescription::zTiling
237
238 The depth wrap mode.
239
240 \sa QRhiSampler::AddressMode
241 */
242
243QSSGRhiBuffer::QSSGRhiBuffer(QSSGRhiContext &context,
244 QRhiBuffer::Type type,
245 QRhiBuffer::UsageFlags usageMask,
246 quint32 stride,
247 qsizetype size,
248 QRhiCommandBuffer::IndexFormat indexFormat)
249 : m_context(context),
250 m_stride(stride),
251 m_indexFormat(indexFormat)
252{
253 QSSG_ASSERT(size >= 0, size = 0);
254 m_buffer = m_context.rhi()->newBuffer(type, usageMask, quint32(size));
255 if (!m_buffer->create())
256 qWarning("Failed to build QRhiBuffer with size %d", m_buffer->size());
257}
258
259QSSGRhiBuffer::~QSSGRhiBuffer()
260{
261 delete m_buffer;
262}
263
264namespace QSSGRhiHelpers {
265QRhiVertexInputAttribute::Format toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps)
266{
267 if (compType == QSSGRenderComponentType::Float32) {
268 switch (numComps) {
269 case 1:
270 return QRhiVertexInputAttribute::Float;
271 case 2:
272 return QRhiVertexInputAttribute::Float2;
273 case 3:
274 return QRhiVertexInputAttribute::Float3;
275 case 4:
276 return QRhiVertexInputAttribute::Float4;
277 default:
278 break;
279 }
280 } else if (compType == QSSGRenderComponentType::UnsignedInt32) {
281 switch (numComps) {
282 case 1:
283 return QRhiVertexInputAttribute::UInt;
284 case 2:
285 return QRhiVertexInputAttribute::UInt2;
286 case 3:
287 return QRhiVertexInputAttribute::UInt3;
288 case 4:
289 return QRhiVertexInputAttribute::UInt4;
290 default:
291 break;
292 }
293 } else if (compType == QSSGRenderComponentType::Int32) {
294 switch (numComps) {
295 case 1:
296 return QRhiVertexInputAttribute::SInt;
297 case 2:
298 return QRhiVertexInputAttribute::SInt2;
299 case 3:
300 return QRhiVertexInputAttribute::SInt3;
301 case 4:
302 return QRhiVertexInputAttribute::SInt4;
303 default:
304 break;
305 }
306 }
307 Q_ASSERT(false);
308 return QRhiVertexInputAttribute::Float4;
309}
310
311QRhiGraphicsPipeline::Topology toTopology(QSSGRenderDrawMode drawMode)
312{
313 switch (drawMode) {
314 case QSSGRenderDrawMode::Points:
315 return QRhiGraphicsPipeline::Points;
316 case QSSGRenderDrawMode::LineStrip:
317 return QRhiGraphicsPipeline::LineStrip;
318 case QSSGRenderDrawMode::Lines:
319 return QRhiGraphicsPipeline::Lines;
320 case QSSGRenderDrawMode::TriangleStrip:
321 return QRhiGraphicsPipeline::TriangleStrip;
322 case QSSGRenderDrawMode::TriangleFan:
323 return QRhiGraphicsPipeline::TriangleFan;
324 case QSSGRenderDrawMode::Triangles:
325 return QRhiGraphicsPipeline::Triangles;
326 case QSSGRenderDrawMode::LineLoop:
327 QSSG_ASSERT_X(false, "LineLoop draw mode is not supported", return QRhiGraphicsPipeline::Triangles);
328 }
329
330 Q_UNREACHABLE_RETURN(QRhiGraphicsPipeline::Triangles);
331}
332
333void bakeVertexInputLocations(QSSGRhiInputAssemblerState *ia, const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding)
334{
335 if (!shaders.vertexStage())
336 return;
337
338 const auto &vertexInputs = shaders.vertexInputs();
339
340 QVarLengthArray<QRhiVertexInputAttribute, 8> attrs;
341 int inputIndex = 0;
342 for (auto it = ia->inputLayout.cbeginAttributes(), itEnd = ia->inputLayout.cendAttributes(); it != itEnd; ++it) {
343 const QSSGRhiInputAssemblerState::InputSemantic sem = ia->inputs.at(inputIndex); // avoid detaching - submeshes share the same name list
344 auto vertexInputVar = vertexInputs.constFind(sem);
345 if (vertexInputVar != vertexInputs.constEnd()) {
346 attrs.append(*it);
347 attrs.last().setLocation(vertexInputVar->location);
348 } // else the mesh has an input attribute that is not declared and used in the vertex shader - that's fine
349
350 ++inputIndex;
351 }
352
353 // Add instance buffer input if necessary
354 if (instanceBufferBinding > 0) {
355 auto instanceBufferLocations = shaders.instanceBufferLocations();
356 // transform0
357 attrs.append(QRhiVertexInputAttribute(instanceBufferBinding,
358 instanceBufferLocations.transform0,
359 QRhiVertexInputAttribute::Float4,
360 0));
361 // transform1
362 attrs.append(QRhiVertexInputAttribute(instanceBufferBinding,
363 instanceBufferLocations.transform1,
364 QRhiVertexInputAttribute::Float4,
365 sizeof(float) * 4));
366 // transform2
367 attrs.append(QRhiVertexInputAttribute(instanceBufferBinding,
368 instanceBufferLocations.transform2,
369 QRhiVertexInputAttribute::Float4,
370 sizeof(float) * 4 * 2));
371 // color
372 attrs.append(QRhiVertexInputAttribute(instanceBufferBinding,
373 instanceBufferLocations.color,
374 QRhiVertexInputAttribute::Float4,
375 sizeof(float) * 4 * 3));
376 // data
377 attrs.append(QRhiVertexInputAttribute(instanceBufferBinding,
378 instanceBufferLocations.data,
379 QRhiVertexInputAttribute::Float4,
380 sizeof(float) * 4 * 4));
381 }
382
383 ia->inputLayout.setAttributes(attrs.cbegin(), attrs.cend());
384}
385
386} // namespace QSSGRhiHelpers
387
388void QSSGRhiShaderPipeline::addStage(const QRhiShaderStage &stage, StageFlags flags)
389{
390 m_stages.append(stage);
391
392 // Copy all member infos for the uniform block with binding 0 into m_ub0
393 // for faster lookup.
394 if (stage.type() == QRhiShaderStage::Vertex) {
395 // Optimize by doing it only for the vertex shader. This code path is
396 // only hit for pipelines with vertex+fragment stages and an in shaders
397 // from materials an identical uniform block is present in both
398 // shaders, so go through only one of them.
399 const QVector<QShaderDescription::UniformBlock> uniformBlocks = stage.shader().description().uniformBlocks();
400 for (const QShaderDescription::UniformBlock &blk : uniformBlocks) {
401 if (blk.binding == 0) {
402 m_ub0Size = blk.size;
403 m_ub0NextUBufOffset = m_context.rhi()->ubufAligned(m_ub0Size);
404 for (const QShaderDescription::BlockVariable &var : blk.members)
405 m_ub0[var.name] = var;
406 break;
407 }
408 }
409 // Now the same for vertex inputs.
410 if (!flags.testFlag(UsedWithoutIa)) {
411 const QVector<QShaderDescription::InOutVariable> inputs = stage.shader().description().inputVariables();
412 for (const QShaderDescription::InOutVariable &var : inputs) {
413 if (var.name == QSSGMesh::MeshInternal::getPositionAttrName()) {
414 m_vertexInputs[QSSGRhiInputAssemblerState::PositionSemantic] = var;
415 } else if (var.name == QSSGMesh::MeshInternal::getNormalAttrName()) {
416 m_vertexInputs[QSSGRhiInputAssemblerState::NormalSemantic] = var;
417 } else if (var.name == QSSGMesh::MeshInternal::getUV0AttrName()) {
418 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoord0Semantic] = var;
419 } else if (var.name == QSSGMesh::MeshInternal::getUV1AttrName()) {
420 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoord1Semantic] = var;
421 } else if (var.name == QSSGMesh::MeshInternal::getLightmapUVAttrName()) {
422 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoordLightmapSemantic] = var;
423 } else if (var.name == QSSGMesh::MeshInternal::getTexTanAttrName()) {
424 m_vertexInputs[QSSGRhiInputAssemblerState::TangentSemantic] = var;
425 } else if (var.name == QSSGMesh::MeshInternal::getTexBinormalAttrName()) {
426 m_vertexInputs[QSSGRhiInputAssemblerState::BinormalSemantic] = var;
427 } else if (var.name == QSSGMesh::MeshInternal::getColorAttrName()) {
428 m_vertexInputs[QSSGRhiInputAssemblerState::ColorSemantic] = var;
429 } else if (var.name == QSSGMesh::MeshInternal::getJointAttrName()) {
430 m_vertexInputs[QSSGRhiInputAssemblerState::JointSemantic] = var;
431 } else if (var.name == QSSGMesh::MeshInternal::getWeightAttrName()) {
432 m_vertexInputs[QSSGRhiInputAssemblerState::WeightSemantic] = var;
433 } else if (var.name == "qt_instanceTransform0") {
434 instanceLocations.transform0 = var.location;
435 } else if (var.name == "qt_instanceTransform1") {
436 instanceLocations.transform1 = var.location;
437 } else if (var.name == "qt_instanceTransform2") {
438 instanceLocations.transform2 = var.location;
439 } else if (var.name == "qt_instanceColor") {
440 instanceLocations.color = var.location;
441 } else if (var.name == "qt_instanceData") {
442 instanceLocations.data = var.location;
443 } else {
444 qWarning("Ignoring vertex input %s in shader", var.name.constData());
445 }
446 }
447 }
448 }
449
450 const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = stage.shader().description().combinedImageSamplers();
451 for (const QShaderDescription::InOutVariable &var : combinedImageSamplers)
452 m_combinedImageSamplers[var.name] = var;
453
454 const QVector<QShaderDescription::InOutVariable> storageImages = stage.shader().description().storageImages() + stage.shader().description().separateImages();
455 for (const QShaderDescription::InOutVariable &var : storageImages)
456 m_storageImages[var.name] = var;
457
458 std::fill(m_materialImageSamplerBindings,
459 m_materialImageSamplerBindings + size_t(QSSGRhiSamplerBindingHints::BindingMapSize),
460 -1);
461}
462
463int QSSGRhiShaderPipeline::ub0DirectionalLightDataOffset() const
464{
465 return m_ub0NextUBufOffset + m_context.rhi()->ubufAligned(sizeof(QSSGShaderLightsUniformData));
466}
467
468void QSSGRhiShaderPipeline::setUniformValue(char *ubufData, const char *name, const QVariant &inValue, QSSGRenderShaderValue::Type inType)
469{
470 using namespace QSSGRenderShaderValue;
471 switch (inType) {
472 case QSSGRenderShaderValue::Integer:
473 {
474 const qint32 v = inValue.toInt();
475 setUniform(ubufData, name, &v, sizeof(qint32));
476 }
477 break;
478 case QSSGRenderShaderValue::IntegerVec2:
479 {
480 const ivec2 v = inValue.value<ivec2>();
481 setUniform(ubufData, name, &v, 2 * sizeof(qint32));
482 }
483 break;
484 case QSSGRenderShaderValue::IntegerVec3:
485 {
486 const ivec3 v = inValue.value<ivec3>();
487 setUniform(ubufData, name, &v, 3 * sizeof(qint32));
488 }
489 break;
490 case QSSGRenderShaderValue::IntegerVec4:
491 {
492 const ivec4 v = inValue.value<ivec4>();
493 setUniform(ubufData, name, &v, 4 * sizeof(qint32));
494 }
495 break;
496 case QSSGRenderShaderValue::Boolean:
497 {
498 // whatever bool is does not matter, what matters is that the GLSL bool is 4 bytes
499 const qint32 v = inValue.value<bool>();
500 setUniform(ubufData, name, &v, sizeof(qint32));
501 }
502 break;
503 case QSSGRenderShaderValue::BooleanVec2:
504 {
505 const bvec2 b = inValue.value<bvec2>();
506 const ivec2 v(b.x, b.y);
507 setUniform(ubufData, name, &v, 2 * sizeof(qint32));
508 }
509 break;
510 case QSSGRenderShaderValue::BooleanVec3:
511 {
512 const bvec3 b = inValue.value<bvec3>();
513 const ivec3 v(b.x, b.y, b.z);
514 setUniform(ubufData, name, &v, 3 * sizeof(qint32));
515 }
516 break;
517 case QSSGRenderShaderValue::BooleanVec4:
518 {
519 const bvec4 b = inValue.value<bvec4>();
520 const ivec4 v(b.x, b.y, b.z, b.w);
521 setUniform(ubufData, name, &v, 4 * sizeof(qint32));
522 }
523 break;
524 case QSSGRenderShaderValue::Float:
525 {
526 const float v = inValue.value<float>();
527 setUniform(ubufData, name, &v, sizeof(float));
528 }
529 break;
530 case QSSGRenderShaderValue::Vec2:
531 {
532 const QVector2D v = inValue.value<QVector2D>();
533 setUniform(ubufData, name, &v, 2 * sizeof(float));
534 }
535 break;
536 case QSSGRenderShaderValue::Vec3:
537 {
538 const QVector3D v = inValue.value<QVector3D>();
539 setUniform(ubufData, name, &v, 3 * sizeof(float));
540 }
541 break;
542 case QSSGRenderShaderValue::Vec4:
543 {
544 const QVector4D v = inValue.value<QVector4D>();
545 setUniform(ubufData, name, &v, 4 * sizeof(float));
546 }
547 break;
548 case QSSGRenderShaderValue::Rgba:
549 {
550 const QVector4D v = QSSGUtils::color::sRGBToLinear(inValue.value<QColor>());
551 setUniform(ubufData, name, &v, 4 * sizeof(float));
552 }
553 break;
554 case QSSGRenderShaderValue::UnsignedInteger:
555 {
556 const quint32 v = inValue.value<quint32>();
557 setUniform(ubufData, name, &v, sizeof(quint32));
558 }
559 break;
560 case QSSGRenderShaderValue::UnsignedIntegerVec2:
561 {
562 const uvec2 v = inValue.value<uvec2>();
563 setUniform(ubufData, name, &v, 2 * sizeof(quint32));
564 }
565 break;
566 case QSSGRenderShaderValue::UnsignedIntegerVec3:
567 {
568 const uvec3 v = inValue.value<uvec3>();
569 setUniform(ubufData, name, &v, 3 * sizeof(quint32));
570 }
571 break;
572 case QSSGRenderShaderValue::UnsignedIntegerVec4:
573 {
574 const uvec4 v = inValue.value<uvec4>();
575 setUniform(ubufData, name, &v, 4 * sizeof(quint32));
576 }
577 break;
578 case QSSGRenderShaderValue::Matrix3x3:
579 {
580 const QMatrix3x3 m = inValue.value<QMatrix3x3>();
581 setUniform(ubufData, name, m.constData(), 12 * sizeof(float), nullptr, QSSGRhiShaderPipeline::UniformFlag::Mat3);
582 }
583 break;
584 case QSSGRenderShaderValue::Matrix4x4:
585 {
586 const QMatrix4x4 v = inValue.value<QMatrix4x4>();
587 setUniform(ubufData, name, v.constData(), 16 * sizeof(float));
588 }
589 break;
590 case QSSGRenderShaderValue::Size:
591 {
592 const QSize s = inValue.value<QSize>();
593 float v[2] = { float(s.width()), float(s.height()) };
594 setUniform(ubufData, name, v, 2 * sizeof(float));
595 }
596 break;
597 case QSSGRenderShaderValue::SizeF:
598 {
599 const QSizeF s = inValue.value<QSizeF>();
600 float v[2] = { float(s.width()), float(s.height()) };
601 setUniform(ubufData, name, v, 2 * sizeof(float));
602 }
603 break;
604 case QSSGRenderShaderValue::Point:
605 {
606 const QPoint p = inValue.value<QPoint>();
607 float v[2] = { float(p.x()), float(p.y()) };
608 setUniform(ubufData, name, v, 2 * sizeof(float));
609 }
610 break;
611 case QSSGRenderShaderValue::PointF:
612 {
613 const QPointF p = inValue.value<QPointF>();
614 float v[2] = { float(p.x()), float(p.y()) };
615 setUniform(ubufData, name, v, 2 * sizeof(float));
616 }
617 break;
618 case QSSGRenderShaderValue::Rect:
619 {
620 const QRect r = inValue.value<QRect>();
621 float v[4] = { float(r.x()), float(r.y()), float(r.width()), float(r.height()) };
622 setUniform(ubufData, name, v, 4 * sizeof(float));
623 }
624 break;
625 case QSSGRenderShaderValue::RectF:
626 {
627 const QRectF r = inValue.value<QRectF>();
628 float v[4] = { float(r.x()), float(r.y()), float(r.width()), float(r.height()) };
629 setUniform(ubufData, name, v, 4 * sizeof(float));
630 }
631 break;
632 case QSSGRenderShaderValue::Quaternion:
633 {
634 const QQuaternion q = inValue.value<QQuaternion>();
635 float v[4] = { float(q.x()), float(q.y()), float(q.z()), float(q.scalar()) };
636 setUniform(ubufData, name, v, 4 * sizeof(float));
637 }
638 break;
639 default:
640 qWarning("Attempted to set uniform %s value with unsupported data type %i",
641 name, int(inType));
642 break;
643 }
644}
645
646int QSSGRhiShaderPipeline::offsetOfUniform(const QByteArray &name)
647{
648 auto iter = m_ub0.constFind(name);
649 if (iter != m_ub0.cend())
650 return iter->offset;
651 return -1;
652}
653
654static QString getUBMemberSizeWarning(QLatin1StringView name, qsizetype correctedSize, qsizetype requestedSize)
655{
656 return QStringLiteral("Uniform block member '%1' got %2 bytes whereas the true size is %3").arg(name, QString::number(correctedSize), QString::number(requestedSize));
657}
658
659void QSSGRhiShaderPipeline::setUniform(char *ubufData, const char *name, const void *data, size_t size, int *storeIndex, UniformFlags flags)
660{
661 int index = -1;
662 if (!storeIndex || *storeIndex == -1) {
663 const QByteArray ba = QByteArray::fromRawData(name, strlen(name));
664 auto it = m_uniformIndex.constFind(ba);
665 if (it != m_uniformIndex.cend()) {
666 index = int(*it);
667 } else if (ba.size() < qsizetype(sizeof(QSSGRhiShaderUniform::name))) {
668 QSSGRhiShaderUniform u;
669 memcpy(u.name, name, ba.size() + 1);
670 u.size = size;
671
672 const int new_idx = m_uniforms.size();
673 m_uniformIndex[name] = new_idx; // key is name, not ba, this has to be a deep copy QByteArray
674 m_uniforms.push_back(u);
675 index = new_idx;
676 } else {
677 qWarning("Attempted to set uniform with too long name: %s", name);
678 return;
679 }
680 if (storeIndex)
681 *storeIndex = index;
682 } else {
683 index = *storeIndex;
684 }
685
686 Q_ASSERT(index >= 0);
687 QSSGRhiShaderUniform &u = m_uniforms[index];
688 if (size <= u.size) {
689 if (u.offset == SIZE_MAX && u.maybeExists) {
690 auto it = m_ub0.constFind(QByteArray::fromRawData(u.name, strlen(u.name)));
691 if (it != m_ub0.constEnd()) {
692 u.offset = it->offset;
693 QSSG_ASSERT_X(QSSG_DEBUG_COND(int(u.size) == it->size), qPrintable(getUBMemberSizeWarning(QLatin1StringView(it->name), u.size, it->size)), return);
694 }
695 }
696 if (u.offset == SIZE_MAX) {
697 // must silently ignore uniforms that are not in the actual shader
698 u.maybeExists = false; // but do not try again
699 return;
700 }
701
702 char *dst = ubufData + u.offset;
703 if (flags.testFlag(UniformFlag::Mat3)) {
704 // mat3 is still 4 floats per column in the uniform buffer (but there
705 // is no 4th column), so 48 bytes altogether, not 36 or 64.
706 const float *src = static_cast<const float *>(data);
707 memcpy(dst, src, 3 * sizeof(float));
708 memcpy(dst + 4 * sizeof(float), src + 3, 3 * sizeof(float));
709 memcpy(dst + 8 * sizeof(float), src + 6, 3 * sizeof(float));
710 } else {
711 memcpy(dst, data, size);
712 }
713 } else {
714 qWarning("Attempted to set %u bytes to uniform %s with size %u", uint(size), name, uint(u.size));
715 }
716}
717
718// Quick3D uniform buffer is std140 type and all array data should be stored in this rule.
719// You can check it in glspec45.core.pdf's 7.6.2.2.(4)
720// https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf
721void QSSGRhiShaderPipeline::setUniformArray(char *ubufData, const char *name, const void *data, size_t itemCount, QSSGRenderShaderValue::Type type, int *storeIndex)
722{
723 using namespace QSSGRenderShaderValue;
724
725 QSSGRhiShaderUniformArray *ua = nullptr;
726 constexpr size_t std140BaseTypeSize = 4 * sizeof(float);
727
728 static const auto checkSize = [std140BaseTypeSize](QSSGRhiShaderUniformArray *ua) -> bool {
729 Q_UNUSED(std140BaseTypeSize); // Silence clang warning about unneeded lambda capture (MSVC requires it be captrued).
730 const size_t uniformSize = std140BaseTypeSize < ua->typeSize ? ua->typeSize * ua->itemCount : std140BaseTypeSize * ua->itemCount;
731 QSSG_ASSERT_X(uniformSize == ua->size, qPrintable(getUBMemberSizeWarning(QLatin1StringView(ua->name), uniformSize, ua->size)), return false);
732 return true;
733 };
734
735 if (!storeIndex || *storeIndex == -1) {
736 int index = -1;
737 const QByteArray ba = QByteArray::fromRawData(name, strlen(name));
738 auto it = m_uniformIndex.constFind(ba);
739 if (it != m_uniformIndex.cend()) {
740 index = int(*it);
741 ua = &m_uniformArrays[index];
742 } else if (ba.size() < qsizetype(sizeof(QSSGRhiShaderUniformArray::name))) {
743 index = m_uniformArrays.size();
744 m_uniformArrays.push_back(QSSGRhiShaderUniformArray());
745 m_uniformIndex[name] = index; // key needs deep copy
746 ua = &m_uniformArrays.last();
747 memcpy(ua->name, name, ba.size() + 1);
748 } else {
749 qWarning("Attempted to set uniform array with too long name: %s", name);
750 return;
751 }
752 if (storeIndex)
753 *storeIndex = index;
754 } else {
755 ua = &m_uniformArrays[*storeIndex];
756 }
757
758 if (!ua)
759 return;
760
761 if (ua->offset == SIZE_MAX && ua->maybeExists) {
762 auto it = m_ub0.constFind(QByteArray::fromRawData(ua->name, strlen(ua->name)));
763 if (it != m_ub0.constEnd()) {
764 ua->offset = it->offset;
765 ua->size = it->size;
766 }
767 }
768 if (ua->offset == SIZE_MAX) {
769 // must silently ignore uniforms that are not in the actual shader
770 ua->maybeExists = false; // but do not try again
771 return;
772 }
773
774 char *p = ubufData + ua->offset;
775
776 switch (type) {
777 case QSSGRenderShaderValue::Integer:
778 {
779 const qint32 *v = static_cast<const qint32 *>(data);
780 if (sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
781 ua->typeSize = sizeof(qint32);
782 ua->itemCount = itemCount;
783 }
784
785 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
786
787 for (size_t i = 0; i < itemCount; ++i)
788 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
789 }
790 break;
791 case QSSGRenderShaderValue::IntegerVec2:
792 {
793 const ivec2 *v = static_cast<const ivec2 *>(data);
794 if (2 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
795 ua->typeSize = 2 * sizeof(qint32);
796 ua->itemCount = itemCount;
797 }
798
799 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
800
801 for (size_t i = 0; i < itemCount; ++i)
802 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
803 }
804 break;
805 case QSSGRenderShaderValue::IntegerVec3:
806 {
807 const QSSGRenderShaderValue::ivec3 *v = static_cast<const QSSGRenderShaderValue::ivec3 *>(data);
808 if (3 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
809 ua->typeSize = 3 * sizeof(qint32);
810 ua->itemCount = itemCount;
811 }
812
813 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
814
815 for (size_t i = 0; i < itemCount; ++i)
816 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
817 }
818 break;
819 case QSSGRenderShaderValue::IntegerVec4:
820 {
821 const ivec4 *v = static_cast<const ivec4 *>(data);
822 if (4 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
823 ua->typeSize = 4 * sizeof(qint32);
824 ua->itemCount = itemCount;
825 }
826
827 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
828
829 memcpy(p, v, ua->typeSize * ua->itemCount);
830 }
831 break;
832 case QSSGRenderShaderValue::Float:
833 {
834 const float *v = static_cast<const float *>(data);
835 if (sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
836 ua->typeSize = sizeof(float);
837 ua->itemCount = itemCount;
838 }
839
840 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
841
842 for (size_t i = 0; i < itemCount; ++i)
843 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
844 }
845 break;
846 case QSSGRenderShaderValue::Vec2:
847 {
848 const QVector2D *v = static_cast<const QVector2D *>(data);
849 if (2 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
850 ua->typeSize = 2 * sizeof(float);
851 ua->itemCount = itemCount;
852 }
853
854 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
855
856 for (size_t i = 0; i < itemCount; ++i)
857 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
858 }
859 break;
860 case QSSGRenderShaderValue::Vec3:
861 {
862 const QVector3D *v = static_cast<const QVector3D *>(data);
863 if (3 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
864 ua->typeSize = 3 * sizeof(float);
865 ua->itemCount = itemCount;
866 }
867
868 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
869
870 for (size_t i = 0; i < itemCount; ++i)
871 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
872 }
873 break;
874 case QSSGRenderShaderValue::Vec4:
875 {
876 const QVector4D *v = static_cast<const QVector4D *>(data);
877 if (4 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
878 ua->typeSize = 4 * sizeof(float);
879 ua->itemCount = itemCount;
880 }
881
882 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
883
884 memcpy(p, v, ua->typeSize * ua->itemCount);
885 }
886 break;
887 case QSSGRenderShaderValue::Rgba:
888 {
889 const QColor *v = static_cast<const QColor *>(data);
890 if (4 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
891 ua->typeSize = 4 * sizeof(float);
892 ua->itemCount = itemCount;
893 }
894
895 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
896
897 for (size_t i = 0; i < itemCount; ++i) {
898 const QVector4D vi = QSSGUtils::color::sRGBToLinear(v[i]);
899 memcpy(p + i * std140BaseTypeSize, &vi, ua->typeSize);
900 }
901 }
902 break;
903 case QSSGRenderShaderValue::UnsignedInteger:
904 {
905 const quint32 *v = static_cast<const quint32 *>(data);
906 if (sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
907 ua->typeSize = sizeof(quint32);
908 ua->itemCount = itemCount;
909 }
910
911 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
912
913 for (size_t i = 0; i < itemCount; ++i)
914 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
915 }
916 break;
917 case QSSGRenderShaderValue::UnsignedIntegerVec2:
918 {
919 const uvec2 *v = static_cast<const uvec2 *>(data);
920 if (2 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
921 ua->typeSize = 2 * sizeof(quint32);
922 ua->itemCount = itemCount;
923 }
924
925 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
926
927 for (size_t i = 0; i < itemCount; ++i)
928 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
929 }
930 break;
931 case QSSGRenderShaderValue::UnsignedIntegerVec3:
932 {
933 const uvec3 *v = static_cast<const uvec3 *>(data);
934 if (3 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
935 ua->typeSize = 3 * sizeof(quint32);
936 ua->itemCount = itemCount;
937 }
938
939 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
940
941 for (size_t i = 0; i < itemCount; ++i)
942 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
943 }
944 break;
945 case QSSGRenderShaderValue::UnsignedIntegerVec4:
946 {
947 const uvec4 *v = static_cast<const uvec4 *>(data);
948 if (4 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
949 ua->typeSize = 4 * sizeof(quint32);
950 ua->itemCount = itemCount;
951 }
952
953 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
954
955 memcpy(p, v, ua->typeSize * ua->itemCount);
956 }
957 break;
958 case QSSGRenderShaderValue::Matrix3x3:
959 {
960 const QMatrix3x3 *v = static_cast<const QMatrix3x3 *>(data);
961 if (12 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
962 ua->typeSize = 12 * sizeof(float);
963 ua->itemCount = itemCount;
964 }
965
966 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
967
968 for (uint i = 0; i < ua->itemCount; ++i) {
969 memcpy(p + i * ua->typeSize, v[i].constData(), 3 * sizeof(float));
970 memcpy(p + i * ua->typeSize + 4 * sizeof(float), v[i].constData() + 3, 3 * sizeof(float));
971 memcpy(p + i * ua->typeSize + 8 * sizeof(float), v[i].constData() + 6, 3 * sizeof(float));
972 }
973 }
974 break;
975 case QSSGRenderShaderValue::Matrix4x4:
976 {
977 const QMatrix4x4 *v = static_cast<const QMatrix4x4 *>(data);
978 if (16 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
979 ua->typeSize = 16 * sizeof(float);
980 ua->itemCount = itemCount;
981 }
982
983 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
984
985 for (uint i = 0; i < ua->itemCount; ++i)
986 memcpy(p + i * ua->typeSize, &v[i] , ua->typeSize);
987 }
988 break;
989 case QSSGRenderShaderValue::Boolean:
990 case QSSGRenderShaderValue::BooleanVec2:
991 case QSSGRenderShaderValue::BooleanVec3:
992 case QSSGRenderShaderValue::BooleanVec4:
993 case QSSGRenderShaderValue::Size:
994 case QSSGRenderShaderValue::SizeF:
995 case QSSGRenderShaderValue::Point:
996 case QSSGRenderShaderValue::PointF:
997 case QSSGRenderShaderValue::Rect:
998 case QSSGRenderShaderValue::RectF:
999 case QSSGRenderShaderValue::Quaternion:
1000 default:
1001 qWarning("Attempted to set uniform %s value with type %d that is unsupported for uniform arrays",
1002 name, int(type));
1003 break;
1004 }
1005}
1006
1007void QSSGRhiShaderPipeline::ensureCombinedUniformBuffer(QRhiBuffer **ubuf)
1008{
1009 const quint32 alignedLightsSize = m_context.rhi()->ubufAligned(sizeof(QSSGShaderLightsUniformData));
1010 const quint32 alignedDirectionalLightsSize = m_context.rhi()->ubufAligned(sizeof(QSSGShaderDirectionalLightsUniformData));
1011 const quint32 totalBufferSize = m_ub0NextUBufOffset + alignedLightsSize + alignedDirectionalLightsSize;
1012 if (!*ubuf) {
1013 *ubuf = m_context.rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalBufferSize);
1014 (*ubuf)->create();
1015 }
1016 if ((*ubuf)->size() < totalBufferSize) {
1017 (*ubuf)->setSize(totalBufferSize);
1018 (*ubuf)->create();
1019 }
1020}
1021
1022void QSSGRhiShaderPipeline::ensureUniformBuffer(QRhiBuffer **ubuf)
1023{
1024 if (!*ubuf) {
1025 *ubuf = m_context.rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, m_ub0Size);
1026 (*ubuf)->create();
1027 }
1028}
1029
1030int QSSGRhiShaderPipeline::bindingForTexture(const char *name, int hint)
1031{
1032 if (hint >= 0) {
1033 const int binding = m_materialImageSamplerBindings[hint];
1034 if (binding >= 0)
1035 return binding;
1036 }
1037
1038 auto it = m_combinedImageSamplers.constFind(QByteArray::fromRawData(name, strlen(name)));
1039 const int binding = it != m_combinedImageSamplers.cend() ? it->binding : -1;
1040 if (hint >= 0)
1041 m_materialImageSamplerBindings[hint] = binding;
1042
1043 return binding;
1044}
1045
1046int QSSGRhiShaderPipeline::bindingForImage(const char *name)
1047{
1048 auto it = m_storageImages.constFind(QByteArray::fromRawData(name, strlen(name)));
1049 const int binding = it != m_storageImages.cend() ? it->binding : -1;
1050 return binding;
1051}
1052
1053void QSSGRhiShaderPipeline::setShaderResources(char *ubufData,
1054 QSSGBufferManager &theBufferManager,
1055 const QByteArray &inPropertyName,
1056 const QVariant &propertyValue,
1057 QSSGRenderShaderValue::Type inPropertyType)
1058{
1059 if (inPropertyType == QSSGRenderShaderValue::Texture) {
1060 QSSGRenderImage *image = propertyValue.value<QSSGRenderImage *>();
1061 if (image) {
1062 const QSSGRenderImageTexture texture = theBufferManager.loadRenderImage(image);
1063 if (texture.m_texture) {
1064 const QSSGRhiTexture t = {
1065 inPropertyName,
1066 texture.m_texture,
1067 { QSSGRhiHelpers::toRhi(image->m_minFilterType),
1068 QSSGRhiHelpers::toRhi(image->m_magFilterType),
1069 image->m_mipFilterType != QSSGRenderTextureFilterOp::None ? QSSGRhiHelpers::toRhi(image->m_mipFilterType) : QRhiSampler::None,
1070 QSSGRhiHelpers::toRhi(image->m_horizontalTilingMode),
1071 QSSGRhiHelpers::toRhi(image->m_verticalTilingMode),
1072 QSSGRhiHelpers::toRhi(image->m_depthTilingMode)
1073 }
1074 };
1075 addExtraTexture(t);
1076 }
1077 }
1078 } else {
1079 setUniformValue(ubufData, inPropertyName, propertyValue, inPropertyType);
1080 }
1081}
1082
1083
1084/*!
1085 \internal
1086 */
1087QSSGRhiContext::QSSGRhiContext(QRhi *rhi)
1088 : d_ptr(new QSSGRhiContextPrivate(*this, rhi))
1089{
1090 Q_ASSERT(rhi);
1091 Q_STATIC_ASSERT(int(QSSGRhiSamplerBindingHints::LightProbe) > int(QSSGRenderableImage::Type::Occlusion));
1092}
1093
1094/*!
1095 \internal
1096 */
1097QSSGRhiContext::~QSSGRhiContext()
1098{
1099 Q_D(QSSGRhiContext);
1100 d->releaseCachedResources();
1101
1102 qDeleteAll(d->m_textures);
1103 qDeleteAll(d->m_meshes);
1104}
1105
1106/*!
1107 \return The QRhi object used by the Qt Quick 3D renderer.
1108 */
1109QRhi *QSSGRhiContext::rhi() const
1110{
1111 Q_D(const QSSGRhiContext);
1112 return d->m_rhi;
1113}
1114
1115/*!
1116 \return true if the renderer is initialized successfully.
1117 */
1118bool QSSGRhiContext::isValid() const
1119{
1120 Q_D(const QSSGRhiContext);
1121 return d->m_rhi != nullptr;
1122}
1123
1124void QSSGRhiContextPrivate::setMainRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc)
1125{
1126 m_mainRpDesc = rpDesc;
1127}
1128
1129/*!
1130 \return The QRhiRenderPassDescriptor used by the main render pass of the Qt
1131 Quick 3D renderer.
1132 */
1133QRhiRenderPassDescriptor *QSSGRhiContext::mainRenderPassDescriptor() const
1134{
1135 Q_D(const QSSGRhiContext);
1136 return d->m_mainRpDesc;
1137}
1138
1139void QSSGRhiContextPrivate::setCommandBuffer(QRhiCommandBuffer *cb)
1140{
1141 m_cb = cb;
1142}
1143
1144/*!
1145 \return The current frame's command buffer used by the Qt Quick 3D renderer.
1146 */
1147QRhiCommandBuffer *QSSGRhiContext::commandBuffer() const
1148{
1149 Q_D(const QSSGRhiContext);
1150 return d->m_cb;
1151}
1152
1153void QSSGRhiContextPrivate::setRenderTarget(QRhiRenderTarget *rt)
1154{
1155 m_rt = rt;
1156}
1157
1158/*!
1159 \return The render target the Qt Quick 3D renderer uses for its main render
1160 pass in the current frame.
1161
1162 This can effectively be a render target from a swapchain, if the \l View3D
1163 uses a renderMode other than Offscreen. More commonly, the render target
1164 refers to a texture (i.e., is a QRhiTextureRenderTarget), e.g. because the
1165 renderMode is the default Offscreen, or because post-processing effects are
1166 in use.
1167 */
1168QRhiRenderTarget *QSSGRhiContext::renderTarget() const
1169{
1170 Q_D(const QSSGRhiContext);
1171 return d->m_rt;
1172}
1173
1174void QSSGRhiContextPrivate::setMainPassSampleCount(int samples)
1175{
1176 m_mainSamples = samples;
1177}
1178
1179/*!
1180 Returns the sample count used in the main render pass.
1181 */
1182int QSSGRhiContext::mainPassSampleCount() const
1183{
1184 Q_D(const QSSGRhiContext);
1185 return d->m_mainSamples;
1186}
1187
1188void QSSGRhiContextPrivate::setMainPassViewCount(int viewCount)
1189{
1190 m_mainViewCount = viewCount;
1191}
1192
1193/*!
1194 Returns the multiview count used in the main render pass. This is either 2,
1195 when multiview rendering is in use, or 1 (no multiview).
1196 */
1197int QSSGRhiContext::mainPassViewCount() const
1198{
1199 Q_D(const QSSGRhiContext);
1200 return d->m_mainViewCount;
1201}
1202
1203void QSSGRhiContextPrivate::releaseCachedResources()
1204{
1205 for (QSSGRhiDrawCallData &dcd : m_drawCallData) {
1206 // We don't call releaseDrawCallData() here, since we're anyways
1207 // are going to delete the non-owned resources further down and
1208 // there's no point in removing each of those one-by-one, as is
1209 // the case with releaseDrawCallData().
1210 // This speeds up the release of cached resources greatly when there
1211 // are many entries in the map, also at application shutdown.
1212 dcd.reset();
1213 }
1214
1215 m_drawCallData.clear();
1216
1217 qDeleteAll(m_pipelines);
1218 qDeleteAll(m_computePipelines);
1219 qDeleteAll(m_srbCache);
1220 qDeleteAll(m_dummyTextures);
1221
1222 m_pipelines.clear();
1223 m_computePipelines.clear();
1224 m_srbCache.clear();
1225 m_dummyTextures.clear();
1226
1227 for (const auto &samplerInfo : std::as_const(m_samplers))
1228 delete samplerInfo.second;
1229
1230 m_samplers.clear();
1231
1232 for (const auto &particleData : std::as_const(m_particleData))
1233 delete particleData.texture;
1234
1235 m_particleData.clear();
1236
1237 for (const auto &instanceData : std::as_const(m_instanceBuffers)) {
1238 if (instanceData.owned)
1239 delete instanceData.buffer;
1240 }
1241
1242 m_instanceBuffers.clear();
1243
1244 for (const auto &instanceData : std::as_const(m_instanceBuffersLod)) {
1245 if (instanceData.owned)
1246 delete instanceData.buffer;
1247 }
1248
1249 m_instanceBuffersLod.clear();
1250}
1251
1252QRhiShaderResourceBindings *QSSGRhiContextPrivate::srb(const QSSGRhiShaderResourceBindingList &bindings)
1253{
1254 auto it = m_srbCache.constFind(bindings);
1255 if (it != m_srbCache.constEnd())
1256 return *it;
1257
1258 QRhiShaderResourceBindings *srb = m_rhi->newShaderResourceBindings();
1259 srb->setBindings(bindings.v, bindings.v + bindings.p);
1260 if (srb->create()) {
1261 m_srbCache.insert(bindings, srb);
1262 } else {
1263 qWarning("Failed to build srb");
1264 delete srb;
1265 srb = nullptr;
1266 }
1267 return srb;
1268}
1269
1270void QSSGRhiContextPrivate::releaseCachedSrb(QSSGRhiShaderResourceBindingList &bindings)
1271{
1272 auto srb = m_srbCache.take(bindings);
1273 delete srb;
1274}
1275
1276void QSSGRhiContextPrivate::releaseDrawCallData(QSSGRhiDrawCallData &dcd)
1277{
1278 delete dcd.ubuf;
1279 dcd.ubuf = nullptr;
1280 auto srb = m_srbCache.take(dcd.bindings);
1281 QSSG_CHECK(srb == dcd.srb);
1282 delete srb;
1283 dcd.srb = nullptr;
1284 dcd.pipeline = nullptr;
1285}
1286
1287QRhiGraphicsPipeline *QSSGRhiContextPrivate::pipeline(const QSSGRhiGraphicsPipelineState &ps,
1288 QRhiRenderPassDescriptor *rpDesc,
1289 QRhiShaderResourceBindings *srb)
1290{
1291 return pipeline(QSSGGraphicsPipelineStateKey::create(ps, rpDesc, srb), rpDesc, srb);
1292}
1293
1294QRhiComputePipeline *QSSGRhiContextPrivate::computePipeline(const QShader &shader,
1295 QRhiShaderResourceBindings *srb)
1296{
1297 return computePipeline(QSSGComputePipelineStateKey::create(shader, srb), srb);
1298}
1299
1300QSSGRhiDrawCallData &QSSGRhiContextPrivate::drawCallData(const QSSGRhiDrawCallDataKey &key)
1301{
1302 return m_drawCallData[key];
1303}
1304
1305using SamplerInfo = QPair<QSSGRhiSamplerDescription, QRhiSampler*>;
1306
1307/*!
1308 \return a sampler with the filter and tiling modes specified in \a samplerDescription.
1309
1310 The generated QRhiSampler objects are cached and reused. Thus this is a
1311 convenient way to gain access to a QRhiSampler with the given settings,
1312 without having to create a new, dedicated object all the time.
1313
1314 The ownership of the returned QRhiSampler stays with Qt Quick 3D.
1315 */
1316QRhiSampler *QSSGRhiContext::sampler(const QSSGRhiSamplerDescription &samplerDescription)
1317{
1318 Q_D(QSSGRhiContext);
1319 auto compareSampler = [samplerDescription](const SamplerInfo &info){ return info.first == samplerDescription; };
1320 auto &samplers = d->m_samplers;
1321 const auto found = std::find_if(samplers.cbegin(), samplers.cend(), compareSampler);
1322 if (found != samplers.cend())
1323 return found->second;
1324
1325 QRhiSampler *newSampler = d->m_rhi->newSampler(samplerDescription.magFilter,
1326 samplerDescription.minFilter,
1327 samplerDescription.mipmap,
1328 samplerDescription.hTiling,
1329 samplerDescription.vTiling,
1330 samplerDescription.zTiling);
1331 if (!newSampler->create()) {
1332 qWarning("Failed to build image sampler");
1333 delete newSampler;
1334 return nullptr;
1335 }
1336 samplers << SamplerInfo{samplerDescription, newSampler};
1337 return newSampler;
1338}
1339
1340/*!
1341 Adjusts \a samplerDescription's tiling and filtering modes based on the
1342 pixel size of \a texture.
1343
1344 In most cases, \a samplerDescription is not changed. With older, legacy 3D
1345 APIs in use, there is however a chance that tiling modes such as
1346 \l{QRhiSampler::Repeat} are not supported for textures with a
1347 non-power-of-two width or height.
1348
1349 This convenience function helps creating robust applications that can still
1350 function even when features such as \l{QRhi::NPOTTextureRepeat} are not
1351 supported by an OpenGL ES 2.0 or WebGL 1 implementation at run time.
1352 */
1353void QSSGRhiContext::checkAndAdjustForNPoT(QRhiTexture *texture, QSSGRhiSamplerDescription *samplerDescription)
1354{
1355 Q_D(const QSSGRhiContext);
1356 if (samplerDescription->mipmap != QRhiSampler::None
1357 || samplerDescription->hTiling != QRhiSampler::ClampToEdge
1358 || samplerDescription->vTiling != QRhiSampler::ClampToEdge
1359 || samplerDescription->zTiling != QRhiSampler::ClampToEdge)
1360 {
1361 if (d->m_rhi->isFeatureSupported(QRhi::NPOTTextureRepeat))
1362 return;
1363
1364 const QSize pixelSize = texture->pixelSize();
1365 const int w = qNextPowerOfTwo(pixelSize.width() - 1);
1366 const int h = qNextPowerOfTwo(pixelSize.height() - 1);
1367 if (w != pixelSize.width() || h != pixelSize.height()) {
1368 static bool warnShown = false;
1369 if (!warnShown) {
1370 warnShown = true;
1371 qWarning("Attempted to use an unsupported filtering or wrap mode, "
1372 "this is likely due to lacking proper support for non-power-of-two textures on this platform.\n"
1373 "If this is with WebGL, try updating the application to use QQuick3D::idealSurfaceFormat() in main() "
1374 "in order to ensure WebGL 2 is used.");
1375 }
1376 samplerDescription->mipmap = QRhiSampler::None;
1377 samplerDescription->hTiling = QRhiSampler::ClampToEdge;
1378 samplerDescription->vTiling = QRhiSampler::ClampToEdge;
1379 samplerDescription->zTiling = QRhiSampler::ClampToEdge;
1380 }
1381 }
1382}
1383
1384void QSSGRhiContextPrivate::registerTexture(QRhiTexture *texture)
1385{
1386 m_textures.insert(texture);
1387}
1388
1389void QSSGRhiContextPrivate::releaseTexture(QRhiTexture *texture)
1390{
1391 m_textures.remove(texture);
1392 delete texture;
1393}
1394
1395void QSSGRhiContextPrivate::registerMesh(QSSGRenderMesh *mesh)
1396{
1397 m_meshes.insert(mesh);
1398}
1399
1400void QSSGRhiContextPrivate::releaseMesh(QSSGRenderMesh *mesh)
1401{
1402 if (mesh) {
1403 for (const auto &subset : std::as_const(mesh->subsets)) {
1404 if (subset.rhi.targetsTexture) {
1405 // If there is a morph targets texture, it should be the same for
1406 // all subsets, so just release and break
1407 releaseTexture(subset.rhi.targetsTexture);
1408 break;
1409 }
1410 }
1411 }
1412 m_meshes.remove(mesh);
1413 delete mesh;
1414}
1415
1416void QSSGRhiContextPrivate::cleanupDrawCallData(const QSSGRenderModel *model)
1417{
1418 // Find all QSSGRhiUniformBufferSet that reference model
1419 // and delete them
1420 const void *modelNode = model;
1421 auto it = m_drawCallData.begin();
1422 while (it != m_drawCallData.end()) {
1423 if (it.key().model == modelNode) {
1424 releaseDrawCallData(*it);
1425 it = m_drawCallData.erase(it);
1426 } else {
1427 ++it;
1428 }
1429 }
1430}
1431
1432void QSSGRhiContextPrivate::cleanupDrawCallDataForResource(const void *resource)
1433{
1434 // Find all QSSGRhiUniformBufferSet that were keyed on resource (typically
1435 // a material that is being destroyed) and delete them.
1436 auto it = m_drawCallData.begin();
1437 while (it != m_drawCallData.end()) {
1438 if (it.key().resource == resource) {
1439 releaseDrawCallData(*it);
1440 it = m_drawCallData.erase(it);
1441 } else {
1442 ++it;
1443 }
1444 }
1445}
1446
1447void QSSGRhiContextPrivate::cleanupDrawCallDataForCid(const void *cid)
1448{
1449 // Find all QSSGRhiUniformBufferSet that were keyed on cid (a pass, or a
1450 // pass-like owner such as a render extension or an effect system, that is
1451 // being destroyed) and delete them.
1452 auto it = m_drawCallData.begin();
1453 while (it != m_drawCallData.end()) {
1454 if (it.key().cid == cid) {
1455 releaseDrawCallData(*it);
1456 it = m_drawCallData.erase(it);
1457 } else {
1458 ++it;
1459 }
1460 }
1461}
1462
1463void QSSGRhiContextPrivate::cleanupDrawCallDataForSkyMaterial(QSSGRenderSkyMaterial *skyMaterial)
1464{
1465 // Not a material type-wise, but it keys its uniform buffers the
1466 // same way, and the srbs for the environment cube and background
1467 // passes are cached with the material's binding lists as the key.
1468 releaseCachedSrb(skyMaterial->bindings);
1469 releaseCachedSrb(skyMaterial->backgroundBindings);
1470 cleanupDrawCallDataForResource(skyMaterial);
1471}
1472
1473/*!
1474 \return a texture that has the specified \a flags and pixel \a size.
1475
1476 This is intended to efficiently gain access to a "dummy" texture filled
1477 with a given \a fillColor, and reused in various places in the rendering
1478 stack.
1479
1480 \a rub must be a valid QRhiResourceUpdateBatch since this function will
1481 create a new texture and generate content for it, if a suitable cached
1482 object is not found. The necessary upload operations are then enqueued on
1483 this given update batch.
1484
1485 When \a arraySize is 2 or more, a 2D texture array is returned.
1486
1487 Note that the function currently returns plain cube map instead of array
1488 cube map if requested due to missing support in rhi.
1489
1490 The ownership of the returned texture stays with Qt Quick 3D.
1491 */
1492QRhiTexture *QSSGRhiContext::dummyTexture(QRhiTexture::Flags flags, QRhiResourceUpdateBatch *rub,
1493 const QSize &size, const QColor &fillColor, int arraySize)
1494{
1495 Q_D(QSSGRhiContext);
1496 // The rhi doesn't have cubemap array view type so best we can do is plain cubemap
1497 // Once they are supported, remove arraySize = 0 and update the fill loop for all faces
1498 const bool isCube = flags.testFlags(QRhiTexture::CubeMap);
1499 if (isCube)
1500 arraySize = 0;
1501 auto it = d->m_dummyTextures.constFind({flags, size, fillColor, arraySize});
1502 if (it != d->m_dummyTextures.constEnd())
1503 return *it;
1504
1505 QRhiTexture *t = arraySize < 2
1506 ? d->m_rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags)
1507 : d->m_rhi->newTextureArray(QRhiTexture::RGBA8, arraySize, size, 1, flags);
1508 if (t->create()) {
1509 QImage image(t->pixelSize(), QImage::Format_RGBA8888);
1510 image.fill(fillColor);
1511 rub->uploadTexture(t, image);
1512 // Must fill all cubemap faces. Otherwise the result is undefined.
1513 const int layerCount = isCube ? 6 : arraySize;
1514 for (int layer = 1; layer < layerCount; ++layer)
1515 rub->uploadTexture(t, QRhiTextureUploadDescription(QRhiTextureUploadEntry(layer, 0, QRhiTextureSubresourceUploadDescription(image))));
1516 } else {
1517 qWarning("Failed to build dummy texture");
1518 }
1519
1520 d->m_dummyTextures.insert({flags, size, fillColor, arraySize}, t);
1521 return t;
1522}
1523
1524QSSGRhiInstanceBufferData &QSSGRhiContextPrivate::instanceBufferData(QSSGRenderInstanceTable *instanceTable)
1525{
1526 return m_instanceBuffers[instanceTable];
1527}
1528
1529void QSSGRhiContextPrivate::releaseInstanceBuffer(QSSGRenderInstanceTable *instanceTable)
1530{
1531 auto it = m_instanceBuffers.constFind(instanceTable);
1532 if (it != m_instanceBuffers.constEnd()) {
1533 it->buffer->destroy();
1534 m_instanceBuffers.erase(it);
1535 }
1536}
1537
1538QSSGRhiInstanceBufferData &QSSGRhiContextPrivate::instanceBufferData(const QSSGRenderModel *model)
1539{
1540 return m_instanceBuffersLod[model];
1541}
1542
1543QSSGRhiParticleData &QSSGRhiContextPrivate::particleData(const QSSGRenderGraphObject *particlesOrModel)
1544{
1545 return m_particleData[particlesOrModel];
1546}
1547
1548void QSSGRhiContextStats::start(QSSGRenderLayer *layer)
1549{
1550 layerKey = layer;
1551 PerLayerInfo &info(perLayerInfo[layerKey]);
1552 info.renderPasses.clear();
1553 info.externalRenderPass = {};
1554 info.currentRenderPassIndex = -1;
1555}
1556
1557void QSSGRhiContextStats::stop(QSSGRenderLayer *layer)
1558{
1559 if (rendererDebugEnabled()) {
1560 PerLayerInfo &info(perLayerInfo[layer]);
1561 const int rpCount = info.renderPasses.size();
1562 qDebug("%d render passes in 3D renderer %p", rpCount, layer);
1563 for (int i = 0; i < rpCount; ++i) {
1564 const RenderPassInfo &rp(info.renderPasses[i]);
1565 qDebug("Render pass %d: rt name='%s' target size %dx%d pixels",
1566 i, rp.rtName.constData(), rp.pixelSize.width(), rp.pixelSize.height());
1567 printRenderPass(rp);
1568 }
1569 if (info.externalRenderPass.indexedDraws.callCount || info.externalRenderPass.instancedIndexedDraws.callCount
1570 || info.externalRenderPass.draws.callCount || info.externalRenderPass.instancedDraws.callCount)
1571 {
1572 qDebug("Within external render passes:");
1573 printRenderPass(info.externalRenderPass);
1574 }
1575 }
1576
1577 // a new start() may preceed stop() for the previous View3D, must handle this gracefully
1578 if (layerKey == layer)
1579 layerKey = nullptr;
1580
1581 // The data must stay valid until the next start() with the same key, the
1582 // QQuick3DRenderStats and DebugView may read it.
1583}
1584
1585void QSSGRhiContextStats::cleanupLayerInfo(QSSGRenderLayer *layer)
1586{
1587 perLayerInfo.remove(layer);
1588 dynamicDataSources.remove(layer);
1589}
1590
1591void QSSGRhiContextStats::beginRenderPass(QRhiTextureRenderTarget *rt)
1592{
1593 PerLayerInfo &info(perLayerInfo[layerKey]);
1594 Q_TRACE(QSSG_renderPass_entry, QString::fromUtf8(rt->name()));
1595 info.renderPasses.append({ rt->name(), rt->pixelSize(), {}, {}, {}, {} });
1596 info.currentRenderPassIndex = info.renderPasses.size() - 1;
1597}
1598
1599void QSSGRhiContextStats::endRenderPass()
1600{
1601 Q_TRACE(QSSG_renderPass_exit);
1602 PerLayerInfo &info(perLayerInfo[layerKey]);
1603 info.currentRenderPassIndex = -1;
1604}
1605
1606QSSGRhiContextStats &QSSGRhiContextStats::get(QSSGRhiContext &rhiCtx)
1607{
1608 return QSSGRhiContextPrivate::get(&rhiCtx)->m_stats;
1609}
1610
1611const QSSGRhiContextStats &QSSGRhiContextStats::get(const QSSGRhiContext &rhiCtx)
1612{
1613 return QSSGRhiContextPrivate::get(&rhiCtx)->m_stats;
1614}
1615
1616bool QSSGRhiContextStats::profilingEnabled()
1617{
1618 static bool enabled = Q_QUICK3D_PROFILING_ENABLED;
1619 return enabled;
1620}
1621
1622bool QSSGRhiContextStats::rendererDebugEnabled()
1623{
1624 static bool enabled = qgetenv("QSG_RENDERER_DEBUG").contains(QByteArrayLiteral("render"));
1625 return enabled;
1626}
1627
1628bool QSSGRhiContextStats::isEnabled() const
1629{
1630 return !dynamicDataSources.isEmpty() || profilingEnabled() || rendererDebugEnabled()
1631 || Q_TRACE_ENABLED(QSSG_draw);
1632}
1633
1634void QSSGRhiContextStats::drawIndexed(quint32 indexCount, quint32 instanceCount)
1635{
1636 Q_TRACE(QSSG_drawIndexed, indexCount, instanceCount);
1637 PerLayerInfo &info(perLayerInfo[layerKey]);
1638 RenderPassInfo &rp(info.currentRenderPassIndex >= 0 ? info.renderPasses[info.currentRenderPassIndex] : info.externalRenderPass);
1639 if (instanceCount > 1) {
1640 rp.instancedIndexedDraws.callCount += 1;
1641 rp.instancedIndexedDraws.vertexOrIndexCount += indexCount;
1642 rp.instancedIndexedDraws.instanceCount += instanceCount;
1643 } else {
1644 rp.indexedDraws.callCount += 1;
1645 rp.indexedDraws.vertexOrIndexCount += indexCount;
1646 }
1647}
1648
1649void QSSGRhiContextStats::draw(quint32 vertexCount, quint32 instanceCount)
1650{
1651 Q_TRACE(QSSG_draw, vertexCount, instanceCount);
1652 PerLayerInfo &info(perLayerInfo[layerKey]);
1653 RenderPassInfo &rp(info.currentRenderPassIndex >= 0 ? info.renderPasses[info.currentRenderPassIndex] : info.externalRenderPass);
1654 if (instanceCount > 1) {
1655 rp.instancedDraws.callCount += 1;
1656 rp.instancedDraws.vertexOrIndexCount += vertexCount;
1657 rp.instancedDraws.instanceCount += instanceCount;
1658 } else {
1659 rp.draws.callCount += 1;
1660 rp.draws.vertexOrIndexCount += vertexCount;
1661 }
1662}
1663
1664void QSSGRhiContextStats::printRenderPass(const QSSGRhiContextStats::RenderPassInfo &rp)
1665{
1666 qDebug("%llu indexed draw calls with %llu indices in total, "
1667 "%llu non-indexed draw calls with %llu vertices in total",
1668 rp.indexedDraws.callCount, rp.indexedDraws.vertexOrIndexCount,
1669 rp.draws.callCount, rp.draws.vertexOrIndexCount);
1670 if (rp.instancedIndexedDraws.callCount || rp.instancedDraws.callCount) {
1671 qDebug("%llu instanced indexed draw calls with %llu indices and %llu instances in total, "
1672 "%llu instanced non-indexed draw calls with %llu indices and %llu instances in total",
1673 rp.instancedIndexedDraws.callCount, rp.instancedIndexedDraws.vertexOrIndexCount, rp.instancedIndexedDraws.instanceCount,
1674 rp.instancedDraws.callCount, rp.instancedDraws.vertexOrIndexCount, rp.instancedDraws.instanceCount);
1675 }
1676}
1677
1678QRhiShaderResourceBinding::Data *QSSGRhiShaderResourceBindingList::newBinding()
1679{
1680#ifdef QT_DEBUG
1681 if (p == MAX_SIZE) {
1682 qWarning("Out of shader resource bindings slots (max is %d)", MAX_SIZE);
1683 return nullptr;
1684 }
1685#endif
1686 return QRhiImplementation::shaderResourceBindingData(v[p++]);
1687}
1688
1689void QSSGRhiShaderResourceBindingList::fillImage(QRhiShaderResourceBinding::Data *d, QRhiShaderResourceBinding::Type t, int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1690{
1691 d->binding = binding;
1692 d->stage = stage;
1693 d->type = t;
1694 d->u.simage.tex = tex;
1695 d->u.simage.level = level;
1696 h ^= qintptr(tex) ^ qintptr(level);
1697}
1698
1699void QSSGRhiShaderResourceBindingList::fillStorageBuffer(QRhiShaderResourceBinding::Data *d, QRhiShaderResourceBinding::Type t, int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1700{
1701 d->binding = binding;
1702 d->stage = stage;
1703 d->type = t;
1704 d->u.sbuf.buf = buf;
1705 d->u.sbuf.offset = offset;
1706 d->u.sbuf.maybeSize = size; // 0 = all
1707 h ^= qintptr(buf);
1708}
1709
1710void QSSGRhiShaderResourceBindingList::addUniformBuffer(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size, bool hasDynamicOffset)
1711{
1712 auto *d = newBinding();
1713 if (!d)
1714 return;
1715 h ^= qintptr(buf);
1716 d->binding = binding;
1717 d->stage = stage;
1718 d->type = QRhiShaderResourceBinding::UniformBuffer;
1719 d->u.ubuf.buf = buf;
1720 d->u.ubuf.offset = offset;
1721 d->u.ubuf.maybeSize = size; // 0 = all
1722 d->u.ubuf.hasDynamicOffset = hasDynamicOffset;
1723}
1724
1725void QSSGRhiShaderResourceBindingList::addStorageBufferLoadStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1726{
1727 auto *d = newBinding();
1728 if (!d)
1729 return;
1730 fillStorageBuffer(d, QRhiShaderResourceBinding::BufferLoadStore, binding, stage, buf, offset, size);
1731}
1732
1733void QSSGRhiShaderResourceBindingList::addStorageBufferLoad(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1734{
1735 auto *d = newBinding();
1736 if (!d)
1737 return;
1738 fillStorageBuffer(d, QRhiShaderResourceBinding::BufferLoad, binding, stage, buf, offset, size);
1739}
1740
1741void QSSGRhiShaderResourceBindingList::addStorageBufferStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1742{
1743 auto *d = newBinding();
1744 if (!d)
1745 return;
1746 fillStorageBuffer(d, QRhiShaderResourceBinding::BufferStore, binding, stage, buf, offset, size);
1747}
1748
1749void QSSGRhiShaderResourceBindingList::addTexture(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
1750{
1751 auto *d = newBinding();
1752 if (!d)
1753 return;
1754 h ^= qintptr(tex) ^ qintptr(sampler);
1755 d->binding = binding;
1756 d->stage = stage;
1757 d->type = QRhiShaderResourceBinding::SampledTexture;
1758 d->u.stex.count = 1;
1759 d->u.stex.texSamplers[0].tex = tex;
1760 d->u.stex.texSamplers[0].sampler = sampler;
1761}
1762
1763void QSSGRhiShaderResourceBindingList::addImageLoad(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1764{
1765 auto *d = newBinding();
1766 if (!d)
1767 return;
1768 fillImage(d, QRhiShaderResourceBinding::ImageLoad, binding, stage, tex, level);
1769}
1770
1771void QSSGRhiShaderResourceBindingList::addImageStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1772{
1773 auto *d = newBinding();
1774 if (!d)
1775 return;
1776 fillImage(d, QRhiShaderResourceBinding::ImageStore, binding, stage, tex, level);
1777}
1778
1779void QSSGRhiShaderResourceBindingList::addImageLoadStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1780{
1781 auto *d = newBinding();
1782 if (!d)
1783 return;
1784 fillImage(d, QRhiShaderResourceBinding::ImageLoadStore, binding, stage, tex, level);
1785}
1786
1787bool QSSGRhiContextPrivate::shaderDebuggingEnabled()
1788{
1789 static const bool isSet = (qEnvironmentVariableIntValue("QT_RHI_SHADER_DEBUG") != 0);
1790 return isSet;
1791}
1792
1793bool QSSGRhiContextPrivate::editorMode()
1794{
1795 static const bool isSet = (qEnvironmentVariableIntValue("QT_QUICK3D_EDITORMODE") != 0);
1796 return isSet;
1797}
1798
1799QRhiGraphicsPipeline *QSSGRhiContextPrivate::pipeline(const QSSGGraphicsPipelineStateKey &key,
1800 QRhiRenderPassDescriptor *rpDesc,
1801 QRhiShaderResourceBindings *srb)
1802{
1803 auto it = m_pipelines.constFind(key);
1804 if (it != m_pipelines.constEnd())
1805 return it.value();
1806
1807 // Build a new one. This is potentially expensive.
1808 QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
1809 const auto &ia = QSSGRhiInputAssemblerStatePrivate::get(key.state);
1810
1811 const auto *shaderPipeline = QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(key.state);
1812 ps->setShaderStages(shaderPipeline->cbeginStages(), shaderPipeline->cendStages());
1813 ps->setVertexInputLayout(ia.inputLayout);
1814 ps->setShaderResourceBindings(srb);
1815 ps->setRenderPassDescriptor(rpDesc);
1816
1817 QRhiGraphicsPipeline::Flags flags;
1818 if (key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::UsesScissor))
1819 flags |= QRhiGraphicsPipeline::UsesScissor;
1820
1821 static const bool shaderDebugInfo = qEnvironmentVariableIntValue("QT_QUICK3D_SHADER_DEBUG_INFO");
1822 if (shaderDebugInfo)
1823 flags |= QRhiGraphicsPipeline::CompileShadersWithDebugInfo;
1824 ps->setFlags(flags);
1825
1826 ps->setTopology(ia.topology);
1827 ps->setCullMode(key.state.cullMode);
1828 if (ia.topology == QRhiGraphicsPipeline::Lines || ia.topology == QRhiGraphicsPipeline::LineStrip)
1829 ps->setLineWidth(key.state.lineWidth);
1830
1831 const bool blendEnabled = key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled);
1832 QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 8> targetBlends(key.state.colorAttachmentCount);
1833 for (int i = 0; i < key.state.colorAttachmentCount; ++i) {
1834 targetBlends[i] = key.state.targetBlend[i];
1835 targetBlends[i].enable = blendEnabled;
1836 }
1837 ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend());
1838
1839 ps->setSampleCount(key.state.samples);
1840 ps->setMultiViewCount(key.state.viewCount);
1841
1842 ps->setDepthTest(key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled));
1843 ps->setDepthWrite(key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled));
1844 ps->setDepthOp(key.state.depthFunc);
1845
1846 ps->setDepthBias(key.state.depthBias);
1847 ps->setSlopeScaledDepthBias(key.state.slopeScaledDepthBias);
1848 ps->setPolygonMode(key.state.polygonMode);
1849
1850 const bool usesStencilRef = (key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::UsesStencilRef));
1851 if (usesStencilRef)
1852 flags |= QRhiGraphicsPipeline::UsesStencilRef;
1853 ps->setFlags(flags);
1854 ps->setStencilFront(key.state.stencilOpFrontState);
1855 ps->setStencilTest(usesStencilRef);
1856 ps->setStencilWriteMask(key.state.stencilWriteMask);
1857
1858 if (!ps->create()) {
1859 qWarning("Failed to build graphics pipeline state");
1860 delete ps;
1861 return nullptr;
1862 }
1863
1864 m_pipelines.insert(key, ps);
1865 return ps;
1866}
1867
1868QRhiComputePipeline *QSSGRhiContextPrivate::computePipeline(const QSSGComputePipelineStateKey &key, QRhiShaderResourceBindings *srb)
1869{
1870 auto it = m_computePipelines.constFind(key);
1871 if (it != m_computePipelines.constEnd())
1872 return it.value();
1873
1874 QRhiComputePipeline *computePipeline = m_rhi->newComputePipeline();
1875 computePipeline->setShaderResourceBindings(srb);
1876 computePipeline->setShaderStage({ QRhiShaderStage::Compute, key.shader });
1877 if (!computePipeline->create()) {
1878 qWarning("Failed to build compute pipeline");
1879 delete computePipeline;
1880 return nullptr;
1881 }
1882 m_computePipelines.insert(key, computePipeline);
1883 return computePipeline;
1884}
1885
1886/*!
1887 \return The recommended flags when calling QRhiCommandBuffer::beginPass().
1888 */
1889QRhiCommandBuffer::BeginPassFlags QSSGRhiContext::commonPassFlags() const
1890{
1891 return {};
1892}
1893
1895{
1896 for (auto &tex : textures)
1897 tex->invalidate();
1898}
1899
1900/*!
1901 \internal
1902
1903 \fn void QSSGRhiRenderableTextureV2::setDescription(QRhi *rhi, QRhiTextureRenderTargetDescription rtDesc)
1904
1905 Takes ownership of the textures in \a rtDesc and creates a render target.
1906 Once set the textures should be assume owned by this object references to
1907 individual textures can be gotten via the textures() method.
1908 */
1909void QSSGRhiRenderableTextureV2::setDescription(QRhi *rhi, QRhiTextureRenderTargetDescription rtDesc, QRhiTextureRenderTarget::Flags flags)
1910{
1911 Q_ASSERT(rhi);
1912
1913 textures.clear();
1914 const size_t colorAttachmentCount = rtDesc.colorAttachmentCount();
1915 for (size_t i = 0; i < colorAttachmentCount; ++i)
1916 textures.push_back(std::make_unique<QSSGManagedRhiTexture>(m_manager, rtDesc.colorAttachmentAt(i)->texture(), QSSGManagedRhiTexture::Private::Initialize));
1917
1918 if (rtDesc.depthTexture()) {
1919 depthTexture = std::make_unique<QSSGManagedRhiTexture>(m_manager, rtDesc.depthTexture(), QSSGManagedRhiTexture::Private::Initialize);
1920 depthStencil.reset();
1921 } else if (rtDesc.depthStencilBuffer()) {
1922 depthStencil.reset(rtDesc.depthStencilBuffer());
1923 depthTexture.reset();
1924 } else {
1925 depthTexture.reset();
1926 depthStencil = nullptr;
1927 }
1928
1929 dirty = ColorTextureDirty | DepthTextureDirty | DepthStencilDirty;
1930
1931 rt.reset(rhi->newTextureRenderTarget(rtDesc));
1932 rt->setName(rtName);
1933 rt->setFlags(flags);
1934 rpDesc.reset(rt->newCompatibleRenderPassDescriptor());
1935 rt->setRenderPassDescriptor(rpDesc.get());
1936 if (!rt->create()) {
1937 qWarning("Failed to create renderable texture render target");
1938 rt.reset();
1939 return;
1940 }
1941}
1942
1944{
1945 rt.reset();
1946 rpDesc.reset();
1947}
1948
1950{
1951 if (rt)
1952 rt->setDescription({/* empty */});
1953
1955
1956 textures.clear();
1957
1958 depthStencil = nullptr;
1959 depthTexture.reset();
1960}
1961
1962// Called by the manager when it's being destroyed and since it owns the resource
1963// it will release them.
1964void QSSGRhiRenderableTextureV2::invalidate()
1965{
1966 // NOTE: This does not delete the texture wrappers, just releases the underlying QRhiTexture.
1967 for (auto &tex : textures)
1968 tex->invalidate();
1969
1970 if (depthTexture)
1971 depthTexture->invalidate();
1972
1973 reset();
1974}
1975
1976QSSGManagedRhiTexture::QSSGManagedRhiTexture(const std::shared_ptr<QSSGUserRenderPassManager> &manager, QRhiTexture *texture, Private)
1977 : m_manager(manager)
1978 , m_texture(texture)
1979{
1980 Q_ASSERT(m_manager != nullptr);
1981 m_manager->registerManagedTexture(this);
1982}
1983
1984QSSGManagedRhiTexture::QSSGManagedRhiTexture(const std::shared_ptr<QSSGUserRenderPassManager> &manager, std::unique_ptr<QRhiTexture> texture)
1985 : m_manager(manager)
1986 , m_texture(std::move(texture))
1987{
1988 Q_ASSERT(m_manager != nullptr);
1989 m_manager->registerManagedTexture(this);
1990}
1991
1992QSSGManagedRhiTexture::~QSSGManagedRhiTexture()
1993{
1994 invalidate();
1995}
1996
1997void QSSGManagedRhiTexture::invalidate()
1998{
1999 if (m_manager)
2000 m_manager->unregisterManagedTexture(this);
2001
2002 if (Q_UNLIKELY(!m_manager && m_texture)) {
2003 qWarning("QSSGManagedRhiTexture: No manager for tracked resource!");
2004 delete m_texture.release();
2005 }
2006
2007 // Under normal circumstances, the manager is responsible for deleting the texture.
2008 (void)m_texture.release();
2009}
2010
2011QT_END_NAMESPACE
void setDescription(QRhi *rhi, QRhiTextureRenderTargetDescription rtDesc, QRhiTextureRenderTarget::Flags={})
QRhiVertexInputAttribute::Format toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps)
QRhiGraphicsPipeline::Topology toTopology(QSSGRenderDrawMode drawMode)
void bakeVertexInputLocations(QSSGRhiInputAssemblerState *ia, const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding)
Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_exit)
Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName)
Q_TRACE_POINT(qtquick3d, QSSG_renderFrame_entry, int width, int height)
static QString getUBMemberSizeWarning(QLatin1StringView name, qsizetype correctedSize, qsizetype requestedSize)