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 const QVector<QShaderDescription::StorageBlock> storageBlocks = stage.shader().description().storageBlocks();
450 for (const QShaderDescription::StorageBlock &blk : storageBlocks) {
451 if (blk.binding > 0)
452 m_storageBuffers[blk.blockName] = blk;
453 }
454
455 const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = stage.shader().description().combinedImageSamplers();
456 for (const QShaderDescription::InOutVariable &var : combinedImageSamplers)
457 m_combinedImageSamplers[var.name] = var;
458
459 const QVector<QShaderDescription::InOutVariable> storageImages = stage.shader().description().storageImages() + stage.shader().description().separateImages();
460 for (const QShaderDescription::InOutVariable &var : storageImages)
461 m_storageImages[var.name] = var;
462
463 std::fill(m_materialImageSamplerBindings,
464 m_materialImageSamplerBindings + size_t(QSSGRhiSamplerBindingHints::BindingMapSize),
465 -1);
466}
467
468int QSSGRhiShaderPipeline::ub0DirectionalLightDataOffset() const
469{
470 return m_ub0NextUBufOffset + m_context.rhi()->ubufAligned(sizeof(QSSGShaderLightsUniformData));
471}
472
473void QSSGRhiShaderPipeline::addStorageBufferBinding(const QByteArray &name, QRhiShaderResourceBinding::Type type, QRhiBuffer *buffer, int binding)
474{
475 QSSG_ASSERT(type >= QRhiShaderResourceBinding::BufferLoad && type <= QRhiShaderResourceBinding::BufferLoadStore, return);
476 QSSG_ASSERT(buffer->usage().testFlag(QRhiBuffer::StorageBuffer), return);
477 m_storageBufferBindings.insert(binding, StorageResourceBinding(binding, name, type, buffer));
478}
479
480void QSSGRhiShaderPipeline::addStorageImageBinding(const QByteArray &name, QRhiShaderResourceBinding::Type type, QRhiTexture *texture, int binding)
481{
482 QSSG_ASSERT(type >= QRhiShaderResourceBinding::ImageLoad && type <= QRhiShaderResourceBinding::ImageLoadStore, return);
483 QSSG_ASSERT(texture->flags().testFlags(QRhiTexture::UsedWithLoadStore), return);
484 m_storageImageBindings.insert(binding, StorageResourceBinding(binding, name, type, texture));
485}
486
487void QSSGRhiShaderPipeline::applyAndClearStorageBindings(QRhiShaderResourceBinding::StageFlags stage, QSSGRhiShaderResourceBindingList &bindings)
488{
489 for (const auto &binding : std::as_const(m_storageBufferBindings)) {
490 if (binding.type == QRhiShaderResourceBinding::BufferLoad)
491 bindings.addStorageBufferLoad(binding.binding, stage, binding.buffer);
492 else if (binding.type == QRhiShaderResourceBinding::BufferStore)
493 bindings.addStorageBufferStore(binding.binding, stage, binding.buffer);
494 else
495 bindings.addStorageBufferLoadStore(binding.binding, stage, binding.buffer);
496 }
497 for (const auto &binding : std::as_const(m_storageImageBindings)) {
498 if (binding.type == QRhiShaderResourceBinding::ImageLoad)
499 bindings.addImageLoad(binding.binding, stage, binding.texture, 0);
500 else if (binding.type == QRhiShaderResourceBinding::ImageStore)
501 bindings.addImageStore(binding.binding, stage, binding.texture, 0);
502 else
503 bindings.addImageLoadStore(binding.binding, stage, binding.texture, 0);
504 }
505 m_storageBufferBindings.clear();
506 m_storageImageBindings.clear();
507}
508
509void QSSGRhiShaderPipeline::setUniformValue(char *ubufData, const char *name, const QVariant &inValue, QSSGRenderShaderValue::Type inType)
510{
511 using namespace QSSGRenderShaderValue;
512 switch (inType) {
513 case QSSGRenderShaderValue::Integer:
514 {
515 const qint32 v = inValue.toInt();
516 setUniform(ubufData, name, &v, sizeof(qint32));
517 }
518 break;
519 case QSSGRenderShaderValue::IntegerVec2:
520 {
521 const ivec2 v = inValue.value<ivec2>();
522 setUniform(ubufData, name, &v, 2 * sizeof(qint32));
523 }
524 break;
525 case QSSGRenderShaderValue::IntegerVec3:
526 {
527 const ivec3 v = inValue.value<ivec3>();
528 setUniform(ubufData, name, &v, 3 * sizeof(qint32));
529 }
530 break;
531 case QSSGRenderShaderValue::IntegerVec4:
532 {
533 const ivec4 v = inValue.value<ivec4>();
534 setUniform(ubufData, name, &v, 4 * sizeof(qint32));
535 }
536 break;
537 case QSSGRenderShaderValue::Boolean:
538 {
539 // whatever bool is does not matter, what matters is that the GLSL bool is 4 bytes
540 const qint32 v = inValue.value<bool>();
541 setUniform(ubufData, name, &v, sizeof(qint32));
542 }
543 break;
544 case QSSGRenderShaderValue::BooleanVec2:
545 {
546 const bvec2 b = inValue.value<bvec2>();
547 const ivec2 v(b.x, b.y);
548 setUniform(ubufData, name, &v, 2 * sizeof(qint32));
549 }
550 break;
551 case QSSGRenderShaderValue::BooleanVec3:
552 {
553 const bvec3 b = inValue.value<bvec3>();
554 const ivec3 v(b.x, b.y, b.z);
555 setUniform(ubufData, name, &v, 3 * sizeof(qint32));
556 }
557 break;
558 case QSSGRenderShaderValue::BooleanVec4:
559 {
560 const bvec4 b = inValue.value<bvec4>();
561 const ivec4 v(b.x, b.y, b.z, b.w);
562 setUniform(ubufData, name, &v, 4 * sizeof(qint32));
563 }
564 break;
565 case QSSGRenderShaderValue::Float:
566 {
567 const float v = inValue.value<float>();
568 setUniform(ubufData, name, &v, sizeof(float));
569 }
570 break;
571 case QSSGRenderShaderValue::Vec2:
572 {
573 const QVector2D v = inValue.value<QVector2D>();
574 setUniform(ubufData, name, &v, 2 * sizeof(float));
575 }
576 break;
577 case QSSGRenderShaderValue::Vec3:
578 {
579 const QVector3D v = inValue.value<QVector3D>();
580 setUniform(ubufData, name, &v, 3 * sizeof(float));
581 }
582 break;
583 case QSSGRenderShaderValue::Vec4:
584 {
585 const QVector4D v = inValue.value<QVector4D>();
586 setUniform(ubufData, name, &v, 4 * sizeof(float));
587 }
588 break;
589 case QSSGRenderShaderValue::Rgba:
590 {
591 const QVector4D v = QSSGUtils::color::sRGBToLinear(inValue.value<QColor>());
592 setUniform(ubufData, name, &v, 4 * sizeof(float));
593 }
594 break;
595 case QSSGRenderShaderValue::UnsignedInteger:
596 {
597 const quint32 v = inValue.value<quint32>();
598 setUniform(ubufData, name, &v, sizeof(quint32));
599 }
600 break;
601 case QSSGRenderShaderValue::UnsignedIntegerVec2:
602 {
603 const uvec2 v = inValue.value<uvec2>();
604 setUniform(ubufData, name, &v, 2 * sizeof(quint32));
605 }
606 break;
607 case QSSGRenderShaderValue::UnsignedIntegerVec3:
608 {
609 const uvec3 v = inValue.value<uvec3>();
610 setUniform(ubufData, name, &v, 3 * sizeof(quint32));
611 }
612 break;
613 case QSSGRenderShaderValue::UnsignedIntegerVec4:
614 {
615 const uvec4 v = inValue.value<uvec4>();
616 setUniform(ubufData, name, &v, 4 * sizeof(quint32));
617 }
618 break;
619 case QSSGRenderShaderValue::Matrix3x3:
620 {
621 const QMatrix3x3 m = inValue.value<QMatrix3x3>();
622 setUniform(ubufData, name, m.constData(), 12 * sizeof(float), nullptr, QSSGRhiShaderPipeline::UniformFlag::Mat3);
623 }
624 break;
625 case QSSGRenderShaderValue::Matrix4x4:
626 {
627 const QMatrix4x4 v = inValue.value<QMatrix4x4>();
628 setUniform(ubufData, name, v.constData(), 16 * sizeof(float));
629 }
630 break;
631 case QSSGRenderShaderValue::Size:
632 {
633 const QSize s = inValue.value<QSize>();
634 float v[2] = { float(s.width()), float(s.height()) };
635 setUniform(ubufData, name, v, 2 * sizeof(float));
636 }
637 break;
638 case QSSGRenderShaderValue::SizeF:
639 {
640 const QSizeF s = inValue.value<QSizeF>();
641 float v[2] = { float(s.width()), float(s.height()) };
642 setUniform(ubufData, name, v, 2 * sizeof(float));
643 }
644 break;
645 case QSSGRenderShaderValue::Point:
646 {
647 const QPoint p = inValue.value<QPoint>();
648 float v[2] = { float(p.x()), float(p.y()) };
649 setUniform(ubufData, name, v, 2 * sizeof(float));
650 }
651 break;
652 case QSSGRenderShaderValue::PointF:
653 {
654 const QPointF p = inValue.value<QPointF>();
655 float v[2] = { float(p.x()), float(p.y()) };
656 setUniform(ubufData, name, v, 2 * sizeof(float));
657 }
658 break;
659 case QSSGRenderShaderValue::Rect:
660 {
661 const QRect r = inValue.value<QRect>();
662 float v[4] = { float(r.x()), float(r.y()), float(r.width()), float(r.height()) };
663 setUniform(ubufData, name, v, 4 * sizeof(float));
664 }
665 break;
666 case QSSGRenderShaderValue::RectF:
667 {
668 const QRectF r = inValue.value<QRectF>();
669 float v[4] = { float(r.x()), float(r.y()), float(r.width()), float(r.height()) };
670 setUniform(ubufData, name, v, 4 * sizeof(float));
671 }
672 break;
673 case QSSGRenderShaderValue::Quaternion:
674 {
675 const QQuaternion q = inValue.value<QQuaternion>();
676 float v[4] = { float(q.x()), float(q.y()), float(q.z()), float(q.scalar()) };
677 setUniform(ubufData, name, v, 4 * sizeof(float));
678 }
679 break;
680 default:
681 qWarning("Attempted to set uniform %s value with unsupported data type %i",
682 name, int(inType));
683 break;
684 }
685}
686
687int QSSGRhiShaderPipeline::offsetOfUniform(const QByteArray &name)
688{
689 auto iter = m_ub0.constFind(name);
690 if (iter != m_ub0.cend())
691 return iter->offset;
692 return -1;
693}
694
695static QString getUBMemberSizeWarning(QLatin1StringView name, qsizetype correctedSize, qsizetype requestedSize)
696{
697 return QStringLiteral("Uniform block member '%1' got %2 bytes whereas the true size is %3").arg(name, QString::number(correctedSize), QString::number(requestedSize));
698}
699
700void QSSGRhiShaderPipeline::setUniform(char *ubufData, const char *name, const void *data, size_t size, int *storeIndex, UniformFlags flags)
701{
702 int index = -1;
703 if (!storeIndex || *storeIndex == -1) {
704 const QByteArray ba = QByteArray::fromRawData(name, strlen(name));
705 auto it = m_uniformIndex.constFind(ba);
706 if (it != m_uniformIndex.cend()) {
707 index = int(*it);
708 } else if (ba.size() < qsizetype(sizeof(QSSGRhiShaderUniform::name))) {
709 QSSGRhiShaderUniform u;
710 memcpy(u.name, name, ba.size() + 1);
711 u.size = size;
712
713 const int new_idx = m_uniforms.size();
714 m_uniformIndex[name] = new_idx; // key is name, not ba, this has to be a deep copy QByteArray
715 m_uniforms.push_back(u);
716 index = new_idx;
717 } else {
718 qWarning("Attempted to set uniform with too long name: %s", name);
719 return;
720 }
721 if (storeIndex)
722 *storeIndex = index;
723 } else {
724 index = *storeIndex;
725 }
726
727 Q_ASSERT(index >= 0);
728 QSSGRhiShaderUniform &u = m_uniforms[index];
729 if (size <= u.size) {
730 if (u.offset == SIZE_MAX && u.maybeExists) {
731 auto it = m_ub0.constFind(QByteArray::fromRawData(u.name, strlen(u.name)));
732 if (it != m_ub0.constEnd()) {
733 u.offset = it->offset;
734 QSSG_ASSERT_X(QSSG_DEBUG_COND(int(u.size) == it->size), qPrintable(getUBMemberSizeWarning(QLatin1StringView(it->name), u.size, it->size)), return);
735 }
736 }
737 if (u.offset == SIZE_MAX) {
738 // must silently ignore uniforms that are not in the actual shader
739 u.maybeExists = false; // but do not try again
740 return;
741 }
742
743 char *dst = ubufData + u.offset;
744 if (flags.testFlag(UniformFlag::Mat3)) {
745 // mat3 is still 4 floats per column in the uniform buffer (but there
746 // is no 4th column), so 48 bytes altogether, not 36 or 64.
747 const float *src = static_cast<const float *>(data);
748 memcpy(dst, src, 3 * sizeof(float));
749 memcpy(dst + 4 * sizeof(float), src + 3, 3 * sizeof(float));
750 memcpy(dst + 8 * sizeof(float), src + 6, 3 * sizeof(float));
751 } else {
752 memcpy(dst, data, size);
753 }
754 } else {
755 qWarning("Attempted to set %u bytes to uniform %s with size %u", uint(size), name, uint(u.size));
756 }
757}
758
759// Quick3D uniform buffer is std140 type and all array data should be stored in this rule.
760// You can check it in glspec45.core.pdf's 7.6.2.2.(4)
761// https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf
762void QSSGRhiShaderPipeline::setUniformArray(char *ubufData, const char *name, const void *data, size_t itemCount, QSSGRenderShaderValue::Type type, int *storeIndex)
763{
764 using namespace QSSGRenderShaderValue;
765
766 QSSGRhiShaderUniformArray *ua = nullptr;
767 constexpr size_t std140BaseTypeSize = 4 * sizeof(float);
768
769 static const auto checkSize = [std140BaseTypeSize](QSSGRhiShaderUniformArray *ua) -> bool {
770 Q_UNUSED(std140BaseTypeSize); // Silence clang warning about unneeded lambda capture (MSVC requires it be captrued).
771 const size_t uniformSize = std140BaseTypeSize < ua->typeSize ? ua->typeSize * ua->itemCount : std140BaseTypeSize * ua->itemCount;
772 QSSG_ASSERT_X(uniformSize == ua->size, qPrintable(getUBMemberSizeWarning(QLatin1StringView(ua->name), uniformSize, ua->size)), return false);
773 return true;
774 };
775
776 if (!storeIndex || *storeIndex == -1) {
777 int index = -1;
778 const QByteArray ba = QByteArray::fromRawData(name, strlen(name));
779 auto it = m_uniformIndex.constFind(ba);
780 if (it != m_uniformIndex.cend()) {
781 index = int(*it);
782 ua = &m_uniformArrays[index];
783 } else if (ba.size() < qsizetype(sizeof(QSSGRhiShaderUniformArray::name))) {
784 index = m_uniformArrays.size();
785 m_uniformArrays.push_back(QSSGRhiShaderUniformArray());
786 m_uniformIndex[name] = index; // key needs deep copy
787 ua = &m_uniformArrays.last();
788 memcpy(ua->name, name, ba.size() + 1);
789 } else {
790 qWarning("Attempted to set uniform array with too long name: %s", name);
791 return;
792 }
793 if (storeIndex)
794 *storeIndex = index;
795 } else {
796 ua = &m_uniformArrays[*storeIndex];
797 }
798
799 if (!ua)
800 return;
801
802 if (ua->offset == SIZE_MAX && ua->maybeExists) {
803 auto it = m_ub0.constFind(QByteArray::fromRawData(ua->name, strlen(ua->name)));
804 if (it != m_ub0.constEnd()) {
805 ua->offset = it->offset;
806 ua->size = it->size;
807 }
808 }
809 if (ua->offset == SIZE_MAX) {
810 // must silently ignore uniforms that are not in the actual shader
811 ua->maybeExists = false; // but do not try again
812 return;
813 }
814
815 char *p = ubufData + ua->offset;
816
817 switch (type) {
818 case QSSGRenderShaderValue::Integer:
819 {
820 const qint32 *v = static_cast<const qint32 *>(data);
821 if (sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
822 ua->typeSize = sizeof(qint32);
823 ua->itemCount = itemCount;
824 }
825
826 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
827
828 for (size_t i = 0; i < itemCount; ++i)
829 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
830 }
831 break;
832 case QSSGRenderShaderValue::IntegerVec2:
833 {
834 const ivec2 *v = static_cast<const ivec2 *>(data);
835 if (2 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
836 ua->typeSize = 2 * sizeof(qint32);
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::IntegerVec3:
847 {
848 const QSSGRenderShaderValue::ivec3 *v = static_cast<const QSSGRenderShaderValue::ivec3 *>(data);
849 if (3 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
850 ua->typeSize = 3 * sizeof(qint32);
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::IntegerVec4:
861 {
862 const ivec4 *v = static_cast<const ivec4 *>(data);
863 if (4 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
864 ua->typeSize = 4 * sizeof(qint32);
865 ua->itemCount = itemCount;
866 }
867
868 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
869
870 memcpy(p, v, ua->typeSize * ua->itemCount);
871 }
872 break;
873 case QSSGRenderShaderValue::Float:
874 {
875 const float *v = static_cast<const float *>(data);
876 if (sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
877 ua->typeSize = sizeof(float);
878 ua->itemCount = itemCount;
879 }
880
881 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
882
883 for (size_t i = 0; i < itemCount; ++i)
884 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
885 }
886 break;
887 case QSSGRenderShaderValue::Vec2:
888 {
889 const QVector2D *v = static_cast<const QVector2D *>(data);
890 if (2 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
891 ua->typeSize = 2 * 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 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
899 }
900 break;
901 case QSSGRenderShaderValue::Vec3:
902 {
903 const QVector3D *v = static_cast<const QVector3D *>(data);
904 if (3 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
905 ua->typeSize = 3 * sizeof(float);
906 ua->itemCount = itemCount;
907 }
908
909 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
910
911 for (size_t i = 0; i < itemCount; ++i)
912 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
913 }
914 break;
915 case QSSGRenderShaderValue::Vec4:
916 {
917 const QVector4D *v = static_cast<const QVector4D *>(data);
918 if (4 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
919 ua->typeSize = 4 * sizeof(float);
920 ua->itemCount = itemCount;
921 }
922
923 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
924
925 memcpy(p, v, ua->typeSize * ua->itemCount);
926 }
927 break;
928 case QSSGRenderShaderValue::Rgba:
929 {
930 const QColor *v = static_cast<const QColor *>(data);
931 if (4 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
932 ua->typeSize = 4 * sizeof(float);
933 ua->itemCount = itemCount;
934 }
935
936 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
937
938 for (size_t i = 0; i < itemCount; ++i) {
939 const QVector4D vi = QSSGUtils::color::sRGBToLinear(v[i]);
940 memcpy(p + i * std140BaseTypeSize, &vi, ua->typeSize);
941 }
942 }
943 break;
944 case QSSGRenderShaderValue::UnsignedInteger:
945 {
946 const quint32 *v = static_cast<const quint32 *>(data);
947 if (sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
948 ua->typeSize = sizeof(quint32);
949 ua->itemCount = itemCount;
950 }
951
952 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
953
954 for (size_t i = 0; i < itemCount; ++i)
955 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
956 }
957 break;
958 case QSSGRenderShaderValue::UnsignedIntegerVec2:
959 {
960 const uvec2 *v = static_cast<const uvec2 *>(data);
961 if (2 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
962 ua->typeSize = 2 * sizeof(quint32);
963 ua->itemCount = itemCount;
964 }
965
966 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
967
968 for (size_t i = 0; i < itemCount; ++i)
969 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
970 }
971 break;
972 case QSSGRenderShaderValue::UnsignedIntegerVec3:
973 {
974 const uvec3 *v = static_cast<const uvec3 *>(data);
975 if (3 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
976 ua->typeSize = 3 * sizeof(quint32);
977 ua->itemCount = itemCount;
978 }
979
980 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
981
982 for (size_t i = 0; i < itemCount; ++i)
983 memcpy(p + i * std140BaseTypeSize, &v[i], ua->typeSize);
984 }
985 break;
986 case QSSGRenderShaderValue::UnsignedIntegerVec4:
987 {
988 const uvec4 *v = static_cast<const uvec4 *>(data);
989 if (4 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
990 ua->typeSize = 4 * sizeof(quint32);
991 ua->itemCount = itemCount;
992 }
993
994 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
995
996 memcpy(p, v, ua->typeSize * ua->itemCount);
997 }
998 break;
999 case QSSGRenderShaderValue::Matrix3x3:
1000 {
1001 const QMatrix3x3 *v = static_cast<const QMatrix3x3 *>(data);
1002 if (12 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
1003 ua->typeSize = 12 * sizeof(float);
1004 ua->itemCount = itemCount;
1005 }
1006
1007 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
1008
1009 for (uint i = 0; i < ua->itemCount; ++i) {
1010 memcpy(p + i * ua->typeSize, v[i].constData(), 3 * sizeof(float));
1011 memcpy(p + i * ua->typeSize + 4 * sizeof(float), v[i].constData() + 3, 3 * sizeof(float));
1012 memcpy(p + i * ua->typeSize + 8 * sizeof(float), v[i].constData() + 6, 3 * sizeof(float));
1013 }
1014 }
1015 break;
1016 case QSSGRenderShaderValue::Matrix4x4:
1017 {
1018 const QMatrix4x4 *v = static_cast<const QMatrix4x4 *>(data);
1019 if (16 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
1020 ua->typeSize = 16 * sizeof(float);
1021 ua->itemCount = itemCount;
1022 }
1023
1024 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
1025
1026 for (uint i = 0; i < ua->itemCount; ++i)
1027 memcpy(p + i * ua->typeSize, &v[i] , ua->typeSize);
1028 }
1029 break;
1030 case QSSGRenderShaderValue::Boolean:
1031 case QSSGRenderShaderValue::BooleanVec2:
1032 case QSSGRenderShaderValue::BooleanVec3:
1033 case QSSGRenderShaderValue::BooleanVec4:
1034 case QSSGRenderShaderValue::Size:
1035 case QSSGRenderShaderValue::SizeF:
1036 case QSSGRenderShaderValue::Point:
1037 case QSSGRenderShaderValue::PointF:
1038 case QSSGRenderShaderValue::Rect:
1039 case QSSGRenderShaderValue::RectF:
1040 case QSSGRenderShaderValue::Quaternion:
1041 default:
1042 qWarning("Attempted to set uniform %s value with type %d that is unsupported for uniform arrays",
1043 name, int(type));
1044 break;
1045 }
1046}
1047
1048void QSSGRhiShaderPipeline::ensureCombinedUniformBuffer(QRhiBuffer **ubuf)
1049{
1050 const quint32 alignedLightsSize = m_context.rhi()->ubufAligned(sizeof(QSSGShaderLightsUniformData));
1051 const quint32 alignedDirectionalLightsSize = m_context.rhi()->ubufAligned(sizeof(QSSGShaderDirectionalLightsUniformData));
1052 const quint32 totalBufferSize = m_ub0NextUBufOffset + alignedLightsSize + alignedDirectionalLightsSize;
1053 if (!*ubuf) {
1054 *ubuf = m_context.rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalBufferSize);
1055 (*ubuf)->create();
1056 }
1057 if ((*ubuf)->size() < totalBufferSize) {
1058 (*ubuf)->setSize(totalBufferSize);
1059 (*ubuf)->create();
1060 }
1061}
1062
1063void QSSGRhiShaderPipeline::ensureUniformBuffer(QRhiBuffer **ubuf)
1064{
1065 if (!*ubuf) {
1066 *ubuf = m_context.rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, m_ub0Size);
1067 (*ubuf)->create();
1068 }
1069}
1070
1071int QSSGRhiShaderPipeline::bindingForTexture(const char *name, int hint)
1072{
1073 if (hint >= 0) {
1074 const int binding = m_materialImageSamplerBindings[hint];
1075 if (binding >= 0)
1076 return binding;
1077 }
1078
1079 auto it = m_combinedImageSamplers.constFind(QByteArray::fromRawData(name, strlen(name)));
1080 const int binding = it != m_combinedImageSamplers.cend() ? it->binding : -1;
1081 if (hint >= 0)
1082 m_materialImageSamplerBindings[hint] = binding;
1083
1084 return binding;
1085}
1086
1087static inline QByteArray asLookupKey(QByteArrayView name)
1088{
1089 return QByteArray::fromRawData(name.data(), name.size());
1090}
1091
1092int QSSGRhiShaderPipeline::bindingForStorageImage(QByteArrayView name)
1093{
1094 auto it = m_storageImages.constFind(asLookupKey(name));
1095 return it != m_storageImages.cend() ? it->binding : -1;
1096}
1097
1098int QSSGRhiShaderPipeline::bindingForStorageBuffer(QByteArrayView name)
1099{
1100 auto storageIt = m_storageBuffers.constFind(asLookupKey(name));
1101 return storageIt != m_storageBuffers.cend() ? storageIt->binding : -1;
1102}
1103
1104void QSSGRhiShaderPipeline::setShaderResources(char *ubufData,
1105 QSSGBufferManager &theBufferManager,
1106 const QByteArray &inPropertyName,
1107 const QVariant &propertyValue,
1108 QSSGRenderShaderValue::Type inPropertyType)
1109{
1110 if (inPropertyType == QSSGRenderShaderValue::Texture) {
1111 QSSGRenderImage *image = propertyValue.value<QSSGRenderImage *>();
1112 if (image) {
1113 const QSSGRenderImageTexture texture = theBufferManager.loadRenderImage(image);
1114 if (texture.m_texture) {
1115 const QSSGRhiTexture t = {
1116 inPropertyName,
1117 texture.m_texture,
1118 { QSSGRhiHelpers::toRhi(image->m_minFilterType),
1119 QSSGRhiHelpers::toRhi(image->m_magFilterType),
1120 image->m_mipFilterType != QSSGRenderTextureFilterOp::None ? QSSGRhiHelpers::toRhi(image->m_mipFilterType) : QRhiSampler::None,
1121 QSSGRhiHelpers::toRhi(image->m_horizontalTilingMode),
1122 QSSGRhiHelpers::toRhi(image->m_verticalTilingMode),
1123 QSSGRhiHelpers::toRhi(image->m_depthTilingMode)
1124 }
1125 };
1126 addExtraTexture(t);
1127 }
1128 }
1129 } else {
1130 setUniformValue(ubufData, inPropertyName, propertyValue, inPropertyType);
1131 }
1132}
1133
1134
1135/*!
1136 \internal
1137 */
1138QSSGRhiContext::QSSGRhiContext(QRhi *rhi)
1139 : d_ptr(new QSSGRhiContextPrivate(*this, rhi))
1140{
1141 Q_ASSERT(rhi);
1142 Q_STATIC_ASSERT(int(QSSGRhiSamplerBindingHints::LightProbe) > int(QSSGRenderableImage::Type::Occlusion));
1143}
1144
1145/*!
1146 \internal
1147 */
1148QSSGRhiContext::~QSSGRhiContext()
1149{
1150 Q_D(QSSGRhiContext);
1151 d->releaseCachedResources();
1152
1153 qDeleteAll(d->m_textures);
1154 qDeleteAll(d->m_meshes);
1155}
1156
1157/*!
1158 \return The QRhi object used by the Qt Quick 3D renderer.
1159 */
1160QRhi *QSSGRhiContext::rhi() const
1161{
1162 Q_D(const QSSGRhiContext);
1163 return d->m_rhi;
1164}
1165
1166/*!
1167 \return true if the renderer is initialized successfully.
1168 */
1169bool QSSGRhiContext::isValid() const
1170{
1171 Q_D(const QSSGRhiContext);
1172 return d->m_rhi != nullptr;
1173}
1174
1175void QSSGRhiContextPrivate::setMainRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc)
1176{
1177 m_mainRpDesc = rpDesc;
1178}
1179
1180/*!
1181 \return The QRhiRenderPassDescriptor used by the main render pass of the Qt
1182 Quick 3D renderer.
1183 */
1184QRhiRenderPassDescriptor *QSSGRhiContext::mainRenderPassDescriptor() const
1185{
1186 Q_D(const QSSGRhiContext);
1187 return d->m_mainRpDesc;
1188}
1189
1190void QSSGRhiContextPrivate::setCommandBuffer(QRhiCommandBuffer *cb)
1191{
1192 m_cb = cb;
1193}
1194
1195/*!
1196 \return The current frame's command buffer used by the Qt Quick 3D renderer.
1197 */
1198QRhiCommandBuffer *QSSGRhiContext::commandBuffer() const
1199{
1200 Q_D(const QSSGRhiContext);
1201 return d->m_cb;
1202}
1203
1204void QSSGRhiContextPrivate::setRenderTarget(QRhiRenderTarget *rt)
1205{
1206 m_rt = rt;
1207}
1208
1209/*!
1210 \return The render target the Qt Quick 3D renderer uses for its main render
1211 pass in the current frame.
1212
1213 This can effectively be a render target from a swapchain, if the \l View3D
1214 uses a renderMode other than Offscreen. More commonly, the render target
1215 refers to a texture (i.e., is a QRhiTextureRenderTarget), e.g. because the
1216 renderMode is the default Offscreen, or because post-processing effects are
1217 in use.
1218 */
1219QRhiRenderTarget *QSSGRhiContext::renderTarget() const
1220{
1221 Q_D(const QSSGRhiContext);
1222 return d->m_rt;
1223}
1224
1225void QSSGRhiContextPrivate::setMainPassSampleCount(int samples)
1226{
1227 m_mainSamples = samples;
1228}
1229
1230/*!
1231 Returns the sample count used in the main render pass.
1232 */
1233int QSSGRhiContext::mainPassSampleCount() const
1234{
1235 Q_D(const QSSGRhiContext);
1236 return d->m_mainSamples;
1237}
1238
1239void QSSGRhiContextPrivate::setMainPassViewCount(int viewCount)
1240{
1241 m_mainViewCount = viewCount;
1242}
1243
1244/*!
1245 Returns the multiview count used in the main render pass. This is either 2,
1246 when multiview rendering is in use, or 1 (no multiview).
1247 */
1248int QSSGRhiContext::mainPassViewCount() const
1249{
1250 Q_D(const QSSGRhiContext);
1251 return d->m_mainViewCount;
1252}
1253
1254void QSSGRhiContextPrivate::releaseCachedResources()
1255{
1256 for (QSSGRhiDrawCallData &dcd : m_drawCallData) {
1257 // We don't call releaseDrawCallData() here, since we're anyways
1258 // are going to delete the non-owned resources further down and
1259 // there's no point in removing each of those one-by-one, as is
1260 // the case with releaseDrawCallData().
1261 // This speeds up the release of cached resources greatly when there
1262 // are many entries in the map, also at application shutdown.
1263 dcd.reset();
1264 }
1265
1266 m_drawCallData.clear();
1267
1268 qDeleteAll(m_pipelines);
1269 qDeleteAll(m_computePipelines);
1270 qDeleteAll(m_srbCache);
1271 qDeleteAll(m_dummyTextures);
1272
1273 m_pipelines.clear();
1274 m_computePipelines.clear();
1275 m_srbCache.clear();
1276 m_dummyTextures.clear();
1277
1278 for (const auto &samplerInfo : std::as_const(m_samplers))
1279 delete samplerInfo.second;
1280
1281 m_samplers.clear();
1282
1283 for (const auto &particleData : std::as_const(m_particleData))
1284 delete particleData.texture;
1285
1286 m_particleData.clear();
1287
1288 for (const auto &instanceData : std::as_const(m_instanceBuffers)) {
1289 if (instanceData.owned)
1290 delete instanceData.buffer;
1291 }
1292
1293 m_instanceBuffers.clear();
1294
1295 for (const auto &instanceData : std::as_const(m_instanceBuffersLod)) {
1296 if (instanceData.owned)
1297 delete instanceData.buffer;
1298 }
1299
1300 m_instanceBuffersLod.clear();
1301}
1302
1303QRhiShaderResourceBindings *QSSGRhiContextPrivate::srb(const QSSGRhiShaderResourceBindingList &bindings)
1304{
1305 auto it = m_srbCache.constFind(bindings);
1306 if (it != m_srbCache.constEnd())
1307 return *it;
1308
1309 QRhiShaderResourceBindings *srb = m_rhi->newShaderResourceBindings();
1310 srb->setBindings(bindings.v, bindings.v + bindings.p);
1311 if (srb->create()) {
1312 m_srbCache.insert(bindings, srb);
1313 } else {
1314 qWarning("Failed to build srb");
1315 delete srb;
1316 srb = nullptr;
1317 }
1318 return srb;
1319}
1320
1321void QSSGRhiContextPrivate::releaseCachedSrb(QSSGRhiShaderResourceBindingList &bindings)
1322{
1323 auto srb = m_srbCache.take(bindings);
1324 delete srb;
1325}
1326
1327void QSSGRhiContextPrivate::releaseDrawCallData(QSSGRhiDrawCallData &dcd)
1328{
1329 delete dcd.ubuf;
1330 dcd.ubuf = nullptr;
1331 auto srb = m_srbCache.take(dcd.bindings);
1332 QSSG_CHECK(srb == dcd.srb);
1333 delete srb;
1334 dcd.srb = nullptr;
1335 dcd.pipeline = nullptr;
1336}
1337
1338QRhiGraphicsPipeline *QSSGRhiContextPrivate::pipeline(const QSSGRhiGraphicsPipelineState &ps,
1339 QRhiRenderPassDescriptor *rpDesc,
1340 QRhiShaderResourceBindings *srb)
1341{
1342 return pipeline(QSSGGraphicsPipelineStateKey::create(ps, rpDesc, srb), rpDesc, srb);
1343}
1344
1345QRhiComputePipeline *QSSGRhiContextPrivate::computePipeline(const QShader &shader,
1346 QRhiShaderResourceBindings *srb)
1347{
1348 return computePipeline(QSSGComputePipelineStateKey::create(shader, srb), srb);
1349}
1350
1351QSSGRhiDrawCallData &QSSGRhiContextPrivate::drawCallData(const QSSGRhiDrawCallDataKey &key)
1352{
1353 return m_drawCallData[key];
1354}
1355
1356using SamplerInfo = QPair<QSSGRhiSamplerDescription, QRhiSampler*>;
1357
1358/*!
1359 \return a sampler with the filter and tiling modes specified in \a samplerDescription.
1360
1361 The generated QRhiSampler objects are cached and reused. Thus this is a
1362 convenient way to gain access to a QRhiSampler with the given settings,
1363 without having to create a new, dedicated object all the time.
1364
1365 The ownership of the returned QRhiSampler stays with Qt Quick 3D.
1366 */
1367QRhiSampler *QSSGRhiContext::sampler(const QSSGRhiSamplerDescription &samplerDescription)
1368{
1369 Q_D(QSSGRhiContext);
1370 auto compareSampler = [samplerDescription](const SamplerInfo &info){ return info.first == samplerDescription; };
1371 auto &samplers = d->m_samplers;
1372 const auto found = std::find_if(samplers.cbegin(), samplers.cend(), compareSampler);
1373 if (found != samplers.cend())
1374 return found->second;
1375
1376 QRhiSampler *newSampler = d->m_rhi->newSampler(samplerDescription.magFilter,
1377 samplerDescription.minFilter,
1378 samplerDescription.mipmap,
1379 samplerDescription.hTiling,
1380 samplerDescription.vTiling,
1381 samplerDescription.zTiling);
1382 if (!newSampler->create()) {
1383 qWarning("Failed to build image sampler");
1384 delete newSampler;
1385 return nullptr;
1386 }
1387 samplers << SamplerInfo{samplerDescription, newSampler};
1388 return newSampler;
1389}
1390
1391/*!
1392 Adjusts \a samplerDescription's tiling and filtering modes based on the
1393 pixel size of \a texture.
1394
1395 In most cases, \a samplerDescription is not changed. With older, legacy 3D
1396 APIs in use, there is however a chance that tiling modes such as
1397 \l{QRhiSampler::Repeat} are not supported for textures with a
1398 non-power-of-two width or height.
1399
1400 This convenience function helps creating robust applications that can still
1401 function even when features such as \l{QRhi::NPOTTextureRepeat} are not
1402 supported by an OpenGL ES 2.0 or WebGL 1 implementation at run time.
1403 */
1404void QSSGRhiContext::checkAndAdjustForNPoT(QRhiTexture *texture, QSSGRhiSamplerDescription *samplerDescription)
1405{
1406 Q_D(const QSSGRhiContext);
1407 if (samplerDescription->mipmap != QRhiSampler::None
1408 || samplerDescription->hTiling != QRhiSampler::ClampToEdge
1409 || samplerDescription->vTiling != QRhiSampler::ClampToEdge
1410 || samplerDescription->zTiling != QRhiSampler::ClampToEdge)
1411 {
1412 if (d->m_rhi->isFeatureSupported(QRhi::NPOTTextureRepeat))
1413 return;
1414
1415 const QSize pixelSize = texture->pixelSize();
1416 const int w = qNextPowerOfTwo(pixelSize.width() - 1);
1417 const int h = qNextPowerOfTwo(pixelSize.height() - 1);
1418 if (w != pixelSize.width() || h != pixelSize.height()) {
1419 static bool warnShown = false;
1420 if (!warnShown) {
1421 warnShown = true;
1422 qWarning("Attempted to use an unsupported filtering or wrap mode, "
1423 "this is likely due to lacking proper support for non-power-of-two textures on this platform.\n"
1424 "If this is with WebGL, try updating the application to use QQuick3D::idealSurfaceFormat() in main() "
1425 "in order to ensure WebGL 2 is used.");
1426 }
1427 samplerDescription->mipmap = QRhiSampler::None;
1428 samplerDescription->hTiling = QRhiSampler::ClampToEdge;
1429 samplerDescription->vTiling = QRhiSampler::ClampToEdge;
1430 samplerDescription->zTiling = QRhiSampler::ClampToEdge;
1431 }
1432 }
1433}
1434
1435void QSSGRhiContextPrivate::registerTexture(QRhiTexture *texture)
1436{
1437 m_textures.insert(texture);
1438}
1439
1440void QSSGRhiContextPrivate::releaseTexture(QRhiTexture *texture)
1441{
1442 m_textures.remove(texture);
1443 delete texture;
1444}
1445
1446void QSSGRhiContextPrivate::registerMesh(QSSGRenderMesh *mesh)
1447{
1448 m_meshes.insert(mesh);
1449}
1450
1451void QSSGRhiContextPrivate::releaseMesh(QSSGRenderMesh *mesh)
1452{
1453 if (mesh) {
1454 for (const auto &subset : std::as_const(mesh->subsets)) {
1455 if (subset.rhi.targetsTexture) {
1456 // If there is a morph targets texture, it should be the same for
1457 // all subsets, so just release and break
1458 releaseTexture(subset.rhi.targetsTexture);
1459 break;
1460 }
1461 }
1462 }
1463 m_meshes.remove(mesh);
1464 delete mesh;
1465}
1466
1467void QSSGRhiContextPrivate::cleanupDrawCallData(const QSSGRenderModel *model)
1468{
1469 // Find all QSSGRhiUniformBufferSet that reference model
1470 // and delete them
1471 const void *modelNode = model;
1472 auto it = m_drawCallData.begin();
1473 while (it != m_drawCallData.end()) {
1474 if (it.key().model == modelNode) {
1475 releaseDrawCallData(*it);
1476 it = m_drawCallData.erase(it);
1477 } else {
1478 ++it;
1479 }
1480 }
1481}
1482
1483void QSSGRhiContextPrivate::cleanupDrawCallDataForResource(const void *resource)
1484{
1485 // Find all QSSGRhiUniformBufferSet that were keyed on resource (typically
1486 // a material that is being destroyed) and delete them.
1487 auto it = m_drawCallData.begin();
1488 while (it != m_drawCallData.end()) {
1489 if (it.key().resource == resource) {
1490 releaseDrawCallData(*it);
1491 it = m_drawCallData.erase(it);
1492 } else {
1493 ++it;
1494 }
1495 }
1496}
1497
1498void QSSGRhiContextPrivate::cleanupDrawCallDataForCid(const void *cid)
1499{
1500 // Find all QSSGRhiUniformBufferSet that were keyed on cid (a pass, or a
1501 // pass-like owner such as a render extension or an effect system, that is
1502 // being destroyed) and delete them.
1503 auto it = m_drawCallData.begin();
1504 while (it != m_drawCallData.end()) {
1505 if (it.key().cid == cid) {
1506 releaseDrawCallData(*it);
1507 it = m_drawCallData.erase(it);
1508 } else {
1509 ++it;
1510 }
1511 }
1512}
1513
1514void QSSGRhiContextPrivate::cleanupDrawCallDataForSkyMaterial(QSSGRenderSkyMaterial *skyMaterial)
1515{
1516 // Not a material type-wise, but it keys its uniform buffers the
1517 // same way, and the srbs for the environment cube and background
1518 // passes are cached with the material's binding lists as the key.
1519 releaseCachedSrb(skyMaterial->bindings);
1520 releaseCachedSrb(skyMaterial->backgroundBindings);
1521 cleanupDrawCallDataForResource(skyMaterial);
1522}
1523
1524/*!
1525 \return a texture that has the specified \a flags and pixel \a size.
1526
1527 This is intended to efficiently gain access to a "dummy" texture filled
1528 with a given \a fillColor, and reused in various places in the rendering
1529 stack.
1530
1531 \a rub must be a valid QRhiResourceUpdateBatch since this function will
1532 create a new texture and generate content for it, if a suitable cached
1533 object is not found. The necessary upload operations are then enqueued on
1534 this given update batch.
1535
1536 When \a arraySize is 2 or more, a 2D texture array is returned.
1537
1538 Note that the function currently returns plain cube map instead of array
1539 cube map if requested due to missing support in rhi.
1540
1541 The ownership of the returned texture stays with Qt Quick 3D.
1542 */
1543QRhiTexture *QSSGRhiContext::dummyTexture(QRhiTexture::Flags flags, QRhiResourceUpdateBatch *rub,
1544 const QSize &size, const QColor &fillColor, int arraySize)
1545{
1546 Q_D(QSSGRhiContext);
1547 // The rhi doesn't have cubemap array view type so best we can do is plain cubemap
1548 // Once they are supported, remove arraySize = 0 and update the fill loop for all faces
1549 const bool isCube = flags.testFlags(QRhiTexture::CubeMap);
1550 if (isCube)
1551 arraySize = 0;
1552 auto it = d->m_dummyTextures.constFind({flags, size, fillColor, arraySize});
1553 if (it != d->m_dummyTextures.constEnd())
1554 return *it;
1555
1556 QRhiTexture *t = arraySize < 2
1557 ? d->m_rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags)
1558 : d->m_rhi->newTextureArray(QRhiTexture::RGBA8, arraySize, size, 1, flags);
1559 if (t->create()) {
1560 QImage image(t->pixelSize(), QImage::Format_RGBA8888);
1561 image.fill(fillColor);
1562 rub->uploadTexture(t, image);
1563 // Must fill all cubemap faces. Otherwise the result is undefined.
1564 const int layerCount = isCube ? 6 : arraySize;
1565 for (int layer = 1; layer < layerCount; ++layer)
1566 rub->uploadTexture(t, QRhiTextureUploadDescription(QRhiTextureUploadEntry(layer, 0, QRhiTextureSubresourceUploadDescription(image))));
1567 } else {
1568 qWarning("Failed to build dummy texture");
1569 }
1570
1571 d->m_dummyTextures.insert({flags, size, fillColor, arraySize}, t);
1572 return t;
1573}
1574
1575QSSGRhiInstanceBufferData &QSSGRhiContextPrivate::instanceBufferData(QSSGRenderInstanceTable *instanceTable)
1576{
1577 return m_instanceBuffers[instanceTable];
1578}
1579
1580void QSSGRhiContextPrivate::releaseInstanceBuffer(QSSGRenderInstanceTable *instanceTable)
1581{
1582 auto it = m_instanceBuffers.constFind(instanceTable);
1583 if (it != m_instanceBuffers.constEnd()) {
1584 it->buffer->destroy();
1585 m_instanceBuffers.erase(it);
1586 }
1587}
1588
1589QSSGRhiInstanceBufferData &QSSGRhiContextPrivate::instanceBufferData(const QSSGRenderModel *model)
1590{
1591 return m_instanceBuffersLod[model];
1592}
1593
1594QSSGRhiParticleData &QSSGRhiContextPrivate::particleData(const QSSGRenderGraphObject *particlesOrModel)
1595{
1596 return m_particleData[particlesOrModel];
1597}
1598
1599void QSSGRhiContextStats::start(QSSGRenderLayer *layer)
1600{
1601 layerKey = layer;
1602 PerLayerInfo &info(perLayerInfo[layerKey]);
1603 info.renderPasses.clear();
1604 info.externalRenderPass = {};
1605 info.currentRenderPassIndex = -1;
1606}
1607
1608void QSSGRhiContextStats::stop(QSSGRenderLayer *layer)
1609{
1610 if (rendererDebugEnabled()) {
1611 PerLayerInfo &info(perLayerInfo[layer]);
1612 const int rpCount = info.renderPasses.size();
1613 qDebug("%d render passes in 3D renderer %p", rpCount, layer);
1614 for (int i = 0; i < rpCount; ++i) {
1615 const RenderPassInfo &rp(info.renderPasses[i]);
1616 qDebug("Render pass %d: rt name='%s' target size %dx%d pixels",
1617 i, rp.rtName.constData(), rp.pixelSize.width(), rp.pixelSize.height());
1618 printRenderPass(rp);
1619 }
1620 if (info.externalRenderPass.indexedDraws.callCount || info.externalRenderPass.instancedIndexedDraws.callCount
1621 || info.externalRenderPass.draws.callCount || info.externalRenderPass.instancedDraws.callCount)
1622 {
1623 qDebug("Within external render passes:");
1624 printRenderPass(info.externalRenderPass);
1625 }
1626 }
1627
1628 // a new start() may preceed stop() for the previous View3D, must handle this gracefully
1629 if (layerKey == layer)
1630 layerKey = nullptr;
1631
1632 // The data must stay valid until the next start() with the same key, the
1633 // QQuick3DRenderStats and DebugView may read it.
1634}
1635
1636void QSSGRhiContextStats::cleanupLayerInfo(QSSGRenderLayer *layer)
1637{
1638 perLayerInfo.remove(layer);
1639 dynamicDataSources.remove(layer);
1640}
1641
1642void QSSGRhiContextStats::beginRenderPass(QRhiTextureRenderTarget *rt)
1643{
1644 PerLayerInfo &info(perLayerInfo[layerKey]);
1645 Q_TRACE(QSSG_renderPass_entry, QString::fromUtf8(rt->name()));
1646 info.renderPasses.append({ rt->name(), rt->pixelSize(), {}, {}, {}, {} });
1647 info.currentRenderPassIndex = info.renderPasses.size() - 1;
1648}
1649
1650void QSSGRhiContextStats::endRenderPass()
1651{
1652 Q_TRACE(QSSG_renderPass_exit);
1653 PerLayerInfo &info(perLayerInfo[layerKey]);
1654 info.currentRenderPassIndex = -1;
1655}
1656
1657QSSGRhiContextStats &QSSGRhiContextStats::get(QSSGRhiContext &rhiCtx)
1658{
1659 return QSSGRhiContextPrivate::get(&rhiCtx)->m_stats;
1660}
1661
1662const QSSGRhiContextStats &QSSGRhiContextStats::get(const QSSGRhiContext &rhiCtx)
1663{
1664 return QSSGRhiContextPrivate::get(&rhiCtx)->m_stats;
1665}
1666
1667bool QSSGRhiContextStats::profilingEnabled()
1668{
1669 static bool enabled = Q_QUICK3D_PROFILING_ENABLED;
1670 return enabled;
1671}
1672
1673bool QSSGRhiContextStats::rendererDebugEnabled()
1674{
1675 static bool enabled = qgetenv("QSG_RENDERER_DEBUG").contains(QByteArrayLiteral("render"));
1676 return enabled;
1677}
1678
1679bool QSSGRhiContextStats::isEnabled() const
1680{
1681 return !dynamicDataSources.isEmpty() || profilingEnabled() || rendererDebugEnabled()
1682 || Q_TRACE_ENABLED(QSSG_draw);
1683}
1684
1685void QSSGRhiContextStats::drawIndexed(quint32 indexCount, quint32 instanceCount)
1686{
1687 Q_TRACE(QSSG_drawIndexed, indexCount, instanceCount);
1688 PerLayerInfo &info(perLayerInfo[layerKey]);
1689 RenderPassInfo &rp(info.currentRenderPassIndex >= 0 ? info.renderPasses[info.currentRenderPassIndex] : info.externalRenderPass);
1690 if (instanceCount > 1) {
1691 rp.instancedIndexedDraws.callCount += 1;
1692 rp.instancedIndexedDraws.vertexOrIndexCount += indexCount;
1693 rp.instancedIndexedDraws.instanceCount += instanceCount;
1694 } else {
1695 rp.indexedDraws.callCount += 1;
1696 rp.indexedDraws.vertexOrIndexCount += indexCount;
1697 }
1698}
1699
1700void QSSGRhiContextStats::draw(quint32 vertexCount, quint32 instanceCount)
1701{
1702 Q_TRACE(QSSG_draw, vertexCount, instanceCount);
1703 PerLayerInfo &info(perLayerInfo[layerKey]);
1704 RenderPassInfo &rp(info.currentRenderPassIndex >= 0 ? info.renderPasses[info.currentRenderPassIndex] : info.externalRenderPass);
1705 if (instanceCount > 1) {
1706 rp.instancedDraws.callCount += 1;
1707 rp.instancedDraws.vertexOrIndexCount += vertexCount;
1708 rp.instancedDraws.instanceCount += instanceCount;
1709 } else {
1710 rp.draws.callCount += 1;
1711 rp.draws.vertexOrIndexCount += vertexCount;
1712 }
1713}
1714
1715void QSSGRhiContextStats::printRenderPass(const QSSGRhiContextStats::RenderPassInfo &rp)
1716{
1717 qDebug("%llu indexed draw calls with %llu indices in total, "
1718 "%llu non-indexed draw calls with %llu vertices in total",
1719 rp.indexedDraws.callCount, rp.indexedDraws.vertexOrIndexCount,
1720 rp.draws.callCount, rp.draws.vertexOrIndexCount);
1721 if (rp.instancedIndexedDraws.callCount || rp.instancedDraws.callCount) {
1722 qDebug("%llu instanced indexed draw calls with %llu indices and %llu instances in total, "
1723 "%llu instanced non-indexed draw calls with %llu indices and %llu instances in total",
1724 rp.instancedIndexedDraws.callCount, rp.instancedIndexedDraws.vertexOrIndexCount, rp.instancedIndexedDraws.instanceCount,
1725 rp.instancedDraws.callCount, rp.instancedDraws.vertexOrIndexCount, rp.instancedDraws.instanceCount);
1726 }
1727}
1728
1729QRhiShaderResourceBinding::Data *QSSGRhiShaderResourceBindingList::newBinding()
1730{
1731#ifdef QT_DEBUG
1732 if (p == MAX_SIZE) {
1733 qWarning("Out of shader resource bindings slots (max is %d)", MAX_SIZE);
1734 return nullptr;
1735 }
1736#endif
1737 return QRhiImplementation::shaderResourceBindingData(v[p++]);
1738}
1739
1740void QSSGRhiShaderResourceBindingList::fillImage(QRhiShaderResourceBinding::Data *d, QRhiShaderResourceBinding::Type t, int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1741{
1742 d->binding = binding;
1743 d->stage = stage;
1744 d->type = t;
1745 d->u.simage.tex = tex;
1746 d->u.simage.level = level;
1747 h ^= qintptr(tex) ^ qintptr(level);
1748}
1749
1750void QSSGRhiShaderResourceBindingList::fillStorageBuffer(QRhiShaderResourceBinding::Data *d, QRhiShaderResourceBinding::Type t, int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1751{
1752 d->binding = binding;
1753 d->stage = stage;
1754 d->type = t;
1755 d->u.sbuf.buf = buf;
1756 d->u.sbuf.offset = offset;
1757 d->u.sbuf.maybeSize = size; // 0 = all
1758 h ^= qintptr(buf);
1759}
1760
1761void QSSGRhiShaderResourceBindingList::addUniformBuffer(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size, bool hasDynamicOffset)
1762{
1763 auto *d = newBinding();
1764 if (!d)
1765 return;
1766 h ^= qintptr(buf);
1767 d->binding = binding;
1768 d->stage = stage;
1769 d->type = QRhiShaderResourceBinding::UniformBuffer;
1770 d->u.ubuf.buf = buf;
1771 d->u.ubuf.offset = offset;
1772 d->u.ubuf.maybeSize = size; // 0 = all
1773 d->u.ubuf.hasDynamicOffset = hasDynamicOffset;
1774}
1775
1776void QSSGRhiShaderResourceBindingList::addStorageBufferLoadStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1777{
1778 auto *d = newBinding();
1779 if (!d)
1780 return;
1781 fillStorageBuffer(d, QRhiShaderResourceBinding::BufferLoadStore, binding, stage, buf, offset, size);
1782}
1783
1784void QSSGRhiShaderResourceBindingList::addStorageBufferLoad(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1785{
1786 auto *d = newBinding();
1787 if (!d)
1788 return;
1789 fillStorageBuffer(d, QRhiShaderResourceBinding::BufferLoad, binding, stage, buf, offset, size);
1790}
1791
1792void QSSGRhiShaderResourceBindingList::addStorageBufferStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
1793{
1794 auto *d = newBinding();
1795 if (!d)
1796 return;
1797 fillStorageBuffer(d, QRhiShaderResourceBinding::BufferStore, binding, stage, buf, offset, size);
1798}
1799
1800void QSSGRhiShaderResourceBindingList::addTexture(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
1801{
1802 auto *d = newBinding();
1803 if (!d)
1804 return;
1805 h ^= qintptr(tex) ^ qintptr(sampler);
1806 d->binding = binding;
1807 d->stage = stage;
1808 d->type = QRhiShaderResourceBinding::SampledTexture;
1809 d->stex.texSamplers.resize(1);
1810 d->stex.texSamplers[0].tex = tex;
1811 d->stex.texSamplers[0].sampler = sampler;
1812}
1813
1814void QSSGRhiShaderResourceBindingList::addImageLoad(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1815{
1816 auto *d = newBinding();
1817 if (!d)
1818 return;
1819 fillImage(d, QRhiShaderResourceBinding::ImageLoad, binding, stage, tex, level);
1820}
1821
1822void QSSGRhiShaderResourceBindingList::addImageStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1823{
1824 auto *d = newBinding();
1825 if (!d)
1826 return;
1827 fillImage(d, QRhiShaderResourceBinding::ImageStore, binding, stage, tex, level);
1828}
1829
1830void QSSGRhiShaderResourceBindingList::addImageLoadStore(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, int level)
1831{
1832 auto *d = newBinding();
1833 if (!d)
1834 return;
1835 fillImage(d, QRhiShaderResourceBinding::ImageLoadStore, binding, stage, tex, level);
1836}
1837
1838bool QSSGRhiContextPrivate::shaderDebuggingEnabled()
1839{
1840 static const bool isSet = (qEnvironmentVariableIntValue("QT_RHI_SHADER_DEBUG") != 0);
1841 return isSet;
1842}
1843
1844bool QSSGRhiContextPrivate::editorMode()
1845{
1846 static const bool isSet = (qEnvironmentVariableIntValue("QT_QUICK3D_EDITORMODE") != 0);
1847 return isSet;
1848}
1849
1850QRhiGraphicsPipeline *QSSGRhiContextPrivate::pipeline(const QSSGGraphicsPipelineStateKey &key,
1851 QRhiRenderPassDescriptor *rpDesc,
1852 QRhiShaderResourceBindings *srb)
1853{
1854 auto it = m_pipelines.constFind(key);
1855 if (it != m_pipelines.constEnd())
1856 return it.value();
1857
1858 // Build a new one. This is potentially expensive.
1859 QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
1860 const auto &ia = QSSGRhiInputAssemblerStatePrivate::get(key.state);
1861
1862 const auto *shaderPipeline = QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(key.state);
1863 ps->setShaderStages(shaderPipeline->cbeginStages(), shaderPipeline->cendStages());
1864 ps->setVertexInputLayout(ia.inputLayout);
1865 ps->setShaderResourceBindings(srb);
1866 ps->setRenderPassDescriptor(rpDesc);
1867
1868 QRhiGraphicsPipeline::Flags flags;
1869 if (key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::UsesScissor))
1870 flags |= QRhiGraphicsPipeline::UsesScissor;
1871
1872 static const bool shaderDebugInfo = qEnvironmentVariableIntValue("QT_QUICK3D_SHADER_DEBUG_INFO");
1873 if (shaderDebugInfo)
1874 flags |= QRhiGraphicsPipeline::CompileShadersWithDebugInfo;
1875 ps->setFlags(flags);
1876
1877 ps->setTopology(ia.topology);
1878 ps->setCullMode(key.state.cullMode);
1879 if (ia.topology == QRhiGraphicsPipeline::Lines || ia.topology == QRhiGraphicsPipeline::LineStrip)
1880 ps->setLineWidth(key.state.lineWidth);
1881
1882 const bool blendEnabled = key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled);
1883 QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 8> targetBlends(key.state.colorAttachmentCount);
1884 for (int i = 0; i < key.state.colorAttachmentCount; ++i) {
1885 targetBlends[i] = key.state.targetBlend[i];
1886 targetBlends[i].enable = blendEnabled;
1887 }
1888 ps->setTargetBlends(targetBlends.cbegin(), targetBlends.cend());
1889
1890 ps->setSampleCount(key.state.samples);
1891 ps->setMultiViewCount(key.state.viewCount);
1892
1893 ps->setDepthTest(key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled));
1894 ps->setDepthWrite(key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled));
1895 ps->setDepthOp(key.state.depthFunc);
1896
1897 ps->setDepthBias(key.state.depthBias);
1898 ps->setSlopeScaledDepthBias(key.state.slopeScaledDepthBias);
1899 ps->setPolygonMode(key.state.polygonMode);
1900
1901 const bool usesStencilRef = (key.state.flags.testFlag(QSSGRhiGraphicsPipelineState::Flag::UsesStencilRef));
1902 if (usesStencilRef)
1903 flags |= QRhiGraphicsPipeline::UsesStencilRef;
1904 ps->setFlags(flags);
1905 ps->setStencilFront(key.state.stencilOpFrontState);
1906 ps->setStencilTest(usesStencilRef);
1907 ps->setStencilWriteMask(key.state.stencilWriteMask);
1908
1909 if (!ps->create()) {
1910 qWarning("Failed to build graphics pipeline state");
1911 delete ps;
1912 return nullptr;
1913 }
1914
1915 m_pipelines.insert(key, ps);
1916 return ps;
1917}
1918
1919QRhiComputePipeline *QSSGRhiContextPrivate::computePipeline(const QSSGComputePipelineStateKey &key, QRhiShaderResourceBindings *srb)
1920{
1921 auto it = m_computePipelines.constFind(key);
1922 if (it != m_computePipelines.constEnd())
1923 return it.value();
1924
1925 QRhiComputePipeline *computePipeline = m_rhi->newComputePipeline();
1926 computePipeline->setShaderResourceBindings(srb);
1927 computePipeline->setShaderStage({ QRhiShaderStage::Compute, key.shader });
1928 if (!computePipeline->create()) {
1929 qWarning("Failed to build compute pipeline");
1930 delete computePipeline;
1931 return nullptr;
1932 }
1933 m_computePipelines.insert(key, computePipeline);
1934 return computePipeline;
1935}
1936
1937/*!
1938 \return The recommended flags when calling QRhiCommandBuffer::beginPass().
1939 */
1940QRhiCommandBuffer::BeginPassFlags QSSGRhiContext::commonPassFlags() const
1941{
1942 return {};
1943}
1944
1946{
1947 for (auto &tex : textures)
1948 tex->invalidate();
1949}
1950
1951/*!
1952 \internal
1953
1954 \fn void QSSGRhiRenderableTextureV2::setDescription(QRhi *rhi, QRhiTextureRenderTargetDescription rtDesc)
1955
1956 Takes ownership of the textures in \a rtDesc and creates a render target.
1957 Once set the textures should be assume owned by this object references to
1958 individual textures can be gotten via the textures() method.
1959 */
1960void QSSGRhiRenderableTextureV2::setDescription(QRhi *rhi, QRhiTextureRenderTargetDescription rtDesc, QRhiTextureRenderTarget::Flags flags)
1961{
1962 Q_ASSERT(rhi);
1963
1964 textures.clear();
1965 const size_t colorAttachmentCount = rtDesc.colorAttachmentCount();
1966 for (size_t i = 0; i < colorAttachmentCount; ++i)
1967 textures.push_back(std::make_unique<QSSGManagedRhiTexture>(m_manager, rtDesc.colorAttachmentAt(i)->texture(), QSSGManagedRhiTexture::Private::Initialize));
1968
1969 if (rtDesc.depthTexture()) {
1970 depthTexture = std::make_unique<QSSGManagedRhiTexture>(m_manager, rtDesc.depthTexture(), QSSGManagedRhiTexture::Private::Initialize);
1971 depthStencil.reset();
1972 } else if (rtDesc.depthStencilBuffer()) {
1973 depthStencil.reset(rtDesc.depthStencilBuffer());
1974 depthTexture.reset();
1975 } else {
1976 depthTexture.reset();
1977 depthStencil = nullptr;
1978 }
1979
1980 dirty = ColorTextureDirty | DepthTextureDirty | DepthStencilDirty;
1981
1982 rt.reset(rhi->newTextureRenderTarget(rtDesc));
1983 rt->setName(rtName);
1984 rt->setFlags(flags);
1985 rpDesc.reset(rt->newCompatibleRenderPassDescriptor());
1986 rt->setRenderPassDescriptor(rpDesc.get());
1987 if (!rt->create()) {
1988 qWarning("Failed to create renderable texture render target");
1989 rt.reset();
1990 return;
1991 }
1992}
1993
1995{
1996 rt.reset();
1997 rpDesc.reset();
1998}
1999
2001{
2002 if (rt)
2003 rt->setDescription({/* empty */});
2004
2006
2007 textures.clear();
2008
2009 depthStencil = nullptr;
2010 depthTexture.reset();
2011}
2012
2013// Called by the manager when it's being destroyed and since it owns the resource
2014// it will release them.
2015void QSSGRhiRenderableTextureV2::invalidate()
2016{
2017 // NOTE: This does not delete the texture wrappers, just releases the underlying QRhiTexture.
2018 for (auto &tex : textures)
2019 tex->invalidate();
2020
2021 if (depthTexture)
2022 depthTexture->invalidate();
2023
2024 reset();
2025}
2026
2027QSSGManagedRhiTexture::QSSGManagedRhiTexture(const std::shared_ptr<QSSGUserRenderPassManager> &manager, QRhiTexture *texture, Private)
2028 : m_manager(manager)
2029 , m_texture(texture)
2030{
2031 Q_ASSERT(m_manager != nullptr);
2032 m_manager->registerManagedTexture(this);
2033}
2034
2035QSSGManagedRhiTexture::QSSGManagedRhiTexture(const std::shared_ptr<QSSGUserRenderPassManager> &manager, std::unique_ptr<QRhiTexture> texture)
2036 : m_manager(manager)
2037 , m_texture(std::move(texture))
2038{
2039 Q_ASSERT(m_manager != nullptr);
2040 m_manager->registerManagedTexture(this);
2041}
2042
2043QSSGManagedRhiTexture::~QSSGManagedRhiTexture()
2044{
2045 invalidate();
2046}
2047
2048void QSSGManagedRhiTexture::invalidate()
2049{
2050 if (m_manager)
2051 m_manager->unregisterManagedTexture(this);
2052
2053 if (Q_UNLIKELY(!m_manager && m_texture)) {
2054 qWarning("QSSGManagedRhiTexture: No manager for tracked resource!");
2055 delete m_texture.release();
2056 }
2057
2058 // Under normal circumstances, the manager is responsible for deleting the texture.
2059 (void)m_texture.release();
2060}
2061
2062QT_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)
static QByteArray asLookupKey(QByteArrayView name)