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