1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qssgrhicontext_p.h"
5#include <QtQuick3DUtils/private/qssgmesh_p.h>
6#include <QtQuick3DUtils/private/qssgassert_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
9#include <QtQuick3DUtils/private/qssgutils_p.h>
10#include <QtQuick3DUtils/private/qssgassert_p.h>
11#include <QtCore/QVariant>
12#include <qtquick3d_tracepoints_p.h>
13
14QT_BEGIN_NAMESPACE
15
16Q_TRACE_POINT(qtquick3d, QSSG_renderPass_entry, const QString &renderPass);
17Q_TRACE_POINT(qtquick3d, QSSG_renderPass_exit);
18Q_TRACE_POINT(qtquick3d, QSSG_drawIndexed, int indexCount, int instanceCount);
19Q_TRACE_POINT(qtquick3d, QSSG_draw, int vertexCount, int instanceCount);
20
21QSSGRhiBuffer::QSSGRhiBuffer(QSSGRhiContext &context,
22 QRhiBuffer::Type type,
23 QRhiBuffer::UsageFlags usageMask,
24 quint32 stride,
25 qsizetype size,
26 QRhiCommandBuffer::IndexFormat indexFormat)
27 : m_context(context),
28 m_stride(stride),
29 m_indexFormat(indexFormat)
30{
31 QSSG_ASSERT(size >= 0, size = 0);
32 m_buffer = m_context.rhi()->newBuffer(type, usage: usageMask, size: quint32(size));
33 if (!m_buffer->create())
34 qWarning(msg: "Failed to build QRhiBuffer with size %d", m_buffer->size());
35}
36
37QSSGRhiBuffer::~QSSGRhiBuffer()
38{
39 delete m_buffer;
40}
41
42QRhiVertexInputAttribute::Format QSSGRhiInputAssemblerState::toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps)
43{
44 if (compType == QSSGRenderComponentType::Float32) {
45 switch (numComps) {
46 case 1:
47 return QRhiVertexInputAttribute::Float;
48 case 2:
49 return QRhiVertexInputAttribute::Float2;
50 case 3:
51 return QRhiVertexInputAttribute::Float3;
52 case 4:
53 return QRhiVertexInputAttribute::Float4;
54 default:
55 break;
56 }
57 } else if (compType == QSSGRenderComponentType::UnsignedInt32) {
58 switch (numComps) {
59 case 1:
60 return QRhiVertexInputAttribute::UInt;
61 case 2:
62 return QRhiVertexInputAttribute::UInt2;
63 case 3:
64 return QRhiVertexInputAttribute::UInt3;
65 case 4:
66 return QRhiVertexInputAttribute::UInt4;
67 default:
68 break;
69 }
70 } else if (compType == QSSGRenderComponentType::Int32) {
71 switch (numComps) {
72 case 1:
73 return QRhiVertexInputAttribute::SInt;
74 case 2:
75 return QRhiVertexInputAttribute::SInt2;
76 case 3:
77 return QRhiVertexInputAttribute::SInt3;
78 case 4:
79 return QRhiVertexInputAttribute::SInt4;
80 default:
81 break;
82 }
83 }
84 Q_ASSERT(false);
85 return QRhiVertexInputAttribute::Float4;
86}
87
88QRhiGraphicsPipeline::Topology QSSGRhiInputAssemblerState::toTopology(QSSGRenderDrawMode drawMode)
89{
90 switch (drawMode) {
91 case QSSGRenderDrawMode::Points:
92 return QRhiGraphicsPipeline::Points;
93 case QSSGRenderDrawMode::LineStrip:
94 return QRhiGraphicsPipeline::LineStrip;
95 case QSSGRenderDrawMode::Lines:
96 return QRhiGraphicsPipeline::Lines;
97 case QSSGRenderDrawMode::TriangleStrip:
98 return QRhiGraphicsPipeline::TriangleStrip;
99 case QSSGRenderDrawMode::TriangleFan:
100 return QRhiGraphicsPipeline::TriangleFan;
101 case QSSGRenderDrawMode::Triangles:
102 return QRhiGraphicsPipeline::Triangles;
103 case QSSGRenderDrawMode::LineLoop:
104 QSSG_ASSERT_X(false, "LineLoop draw mode is not supported", return QRhiGraphicsPipeline::Triangles);
105 }
106
107 Q_UNREACHABLE_RETURN(QRhiGraphicsPipeline::Triangles);
108}
109
110void QSSGRhiInputAssemblerState::bakeVertexInputLocations(const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding)
111{
112 if (!shaders.vertexStage())
113 return;
114
115 const auto &vertexInputs = shaders.vertexInputs();
116
117 QVarLengthArray<QRhiVertexInputAttribute, 8> attrs;
118 int inputIndex = 0;
119 for (auto it = inputLayout.cbeginAttributes(), itEnd = inputLayout.cendAttributes(); it != itEnd; ++it) {
120 const QSSGRhiInputAssemblerState::InputSemantic sem = inputs.at(idx: inputIndex); // avoid detaching - submeshes share the same name list
121 auto vertexInputVar = vertexInputs.constFind(key: sem);
122 if (vertexInputVar != vertexInputs.constEnd()) {
123 attrs.append(t: *it);
124 attrs.last().setLocation(vertexInputVar->location);
125 } // else the mesh has an input attribute that is not declared and used in the vertex shader - that's fine
126
127 ++inputIndex;
128 }
129
130 // Add instance buffer input if necessary
131 if (instanceBufferBinding > 0) {
132 auto instanceBufferLocations = shaders.instanceBufferLocations();
133 // transform0
134 attrs.append(t: QRhiVertexInputAttribute(instanceBufferBinding,
135 instanceBufferLocations.transform0,
136 QRhiVertexInputAttribute::Float4,
137 0));
138 // transform1
139 attrs.append(t: QRhiVertexInputAttribute(instanceBufferBinding,
140 instanceBufferLocations.transform1,
141 QRhiVertexInputAttribute::Float4,
142 sizeof(float) * 4));
143 // transform2
144 attrs.append(t: QRhiVertexInputAttribute(instanceBufferBinding,
145 instanceBufferLocations.transform2,
146 QRhiVertexInputAttribute::Float4,
147 sizeof(float) * 4 * 2));
148 // color
149 attrs.append(t: QRhiVertexInputAttribute(instanceBufferBinding,
150 instanceBufferLocations.color,
151 QRhiVertexInputAttribute::Float4,
152 sizeof(float) * 4 * 3));
153 // data
154 attrs.append(t: QRhiVertexInputAttribute(instanceBufferBinding,
155 instanceBufferLocations.data,
156 QRhiVertexInputAttribute::Float4,
157 sizeof(float) * 4 * 4));
158 }
159
160 inputLayout.setAttributes(first: attrs.cbegin(), last: attrs.cend());
161}
162
163QRhiGraphicsPipeline::CullMode QSSGRhiGraphicsPipelineState::toCullMode(QSSGCullFaceMode cullFaceMode)
164{
165 switch (cullFaceMode) {
166 case QSSGCullFaceMode::Back:
167 return QRhiGraphicsPipeline::Back;
168 case QSSGCullFaceMode::Front:
169 return QRhiGraphicsPipeline::Front;
170 case QSSGCullFaceMode::Disabled:
171 return QRhiGraphicsPipeline::None;
172 case QSSGCullFaceMode::FrontAndBack:
173 qWarning(msg: "FrontAndBack cull mode not supported");
174 return QRhiGraphicsPipeline::None;
175 case QSSGCullFaceMode::Unknown:
176 return QRhiGraphicsPipeline::None;
177 }
178
179 Q_UNREACHABLE_RETURN(QRhiGraphicsPipeline::None);
180}
181
182void QSSGRhiShaderPipeline::addStage(const QRhiShaderStage &stage, StageFlags flags)
183{
184 m_stages.append(t: stage);
185
186 // Copy all member infos for the uniform block with binding 0 into m_ub0
187 // for faster lookup.
188 if (stage.type() == QRhiShaderStage::Vertex) {
189 // Optimize by doing it only for the vertex shader. This code path is
190 // only hit for pipelines with vertex+fragment stages and an in shaders
191 // from materials an identical uniform block is present in both
192 // shaders, so go through only one of them.
193 const QVector<QShaderDescription::UniformBlock> uniformBlocks = stage.shader().description().uniformBlocks();
194 for (const QShaderDescription::UniformBlock &blk : uniformBlocks) {
195 if (blk.binding == 0) {
196 m_ub0Size = blk.size;
197 m_ub0NextUBufOffset = m_context.rhi()->ubufAligned(v: m_ub0Size);
198 for (const QShaderDescription::BlockVariable &var : blk.members)
199 m_ub0[var.name] = var;
200 break;
201 }
202 }
203 // Now the same for vertex inputs.
204 if (!flags.testFlag(flag: UsedWithoutIa)) {
205 const QVector<QShaderDescription::InOutVariable> inputs = stage.shader().description().inputVariables();
206 for (const QShaderDescription::InOutVariable &var : inputs) {
207 if (var.name == QSSGMesh::MeshInternal::getPositionAttrName()) {
208 m_vertexInputs[QSSGRhiInputAssemblerState::PositionSemantic] = var;
209 } else if (var.name == QSSGMesh::MeshInternal::getNormalAttrName()) {
210 m_vertexInputs[QSSGRhiInputAssemblerState::NormalSemantic] = var;
211 } else if (var.name == QSSGMesh::MeshInternal::getUV0AttrName()) {
212 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoord0Semantic] = var;
213 } else if (var.name == QSSGMesh::MeshInternal::getUV1AttrName()) {
214 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoord1Semantic] = var;
215 } else if (var.name == QSSGMesh::MeshInternal::getLightmapUVAttrName()) {
216 m_vertexInputs[QSSGRhiInputAssemblerState::TexCoordLightmapSemantic] = var;
217 } else if (var.name == QSSGMesh::MeshInternal::getTexTanAttrName()) {
218 m_vertexInputs[QSSGRhiInputAssemblerState::TangentSemantic] = var;
219 } else if (var.name == QSSGMesh::MeshInternal::getTexBinormalAttrName()) {
220 m_vertexInputs[QSSGRhiInputAssemblerState::BinormalSemantic] = var;
221 } else if (var.name == QSSGMesh::MeshInternal::getColorAttrName()) {
222 m_vertexInputs[QSSGRhiInputAssemblerState::ColorSemantic] = var;
223 } else if (var.name == QSSGMesh::MeshInternal::getJointAttrName()) {
224 m_vertexInputs[QSSGRhiInputAssemblerState::JointSemantic] = var;
225 } else if (var.name == QSSGMesh::MeshInternal::getWeightAttrName()) {
226 m_vertexInputs[QSSGRhiInputAssemblerState::WeightSemantic] = var;
227 } else if (var.name == "qt_instanceTransform0") {
228 instanceLocations.transform0 = var.location;
229 } else if (var.name == "qt_instanceTransform1") {
230 instanceLocations.transform1 = var.location;
231 } else if (var.name == "qt_instanceTransform2") {
232 instanceLocations.transform2 = var.location;
233 } else if (var.name == "qt_instanceColor") {
234 instanceLocations.color = var.location;
235 } else if (var.name == "qt_instanceData") {
236 instanceLocations.data = var.location;
237 } else {
238 qWarning(msg: "Ignoring vertex input %s in shader", var.name.constData());
239 }
240 }
241 }
242 }
243
244 const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = stage.shader().description().combinedImageSamplers();
245 for (const QShaderDescription::InOutVariable &var : combinedImageSamplers)
246 m_combinedImageSamplers[var.name] = var;
247
248 std::fill(first: m_materialImageSamplerBindings,
249 last: m_materialImageSamplerBindings + size_t(QSSGRhiSamplerBindingHints::BindingMapSize),
250 value: -1);
251}
252
253void QSSGRhiShaderPipeline::setUniformValue(char *ubufData, const char *name, const QVariant &inValue, QSSGRenderShaderValue::Type inType)
254{
255 using namespace QSSGRenderShaderValue;
256 switch (inType) {
257 case QSSGRenderShaderValue::Integer:
258 {
259 const qint32 v = inValue.toInt();
260 setUniform(ubufData, name, data: &v, size: sizeof(qint32));
261 }
262 break;
263 case QSSGRenderShaderValue::IntegerVec2:
264 {
265 const ivec2 v = inValue.value<ivec2>();
266 setUniform(ubufData, name, data: &v, size: 2 * sizeof(qint32));
267 }
268 break;
269 case QSSGRenderShaderValue::IntegerVec3:
270 {
271 const ivec3 v = inValue.value<ivec3>();
272 setUniform(ubufData, name, data: &v, size: 3 * sizeof(qint32));
273 }
274 break;
275 case QSSGRenderShaderValue::IntegerVec4:
276 {
277 const ivec4 v = inValue.value<ivec4>();
278 setUniform(ubufData, name, data: &v, size: 4 * sizeof(qint32));
279 }
280 break;
281 case QSSGRenderShaderValue::Boolean:
282 {
283 // whatever bool is does not matter, what matters is that the GLSL bool is 4 bytes
284 const qint32 v = inValue.value<bool>();
285 setUniform(ubufData, name, data: &v, size: sizeof(qint32));
286 }
287 break;
288 case QSSGRenderShaderValue::BooleanVec2:
289 {
290 const bvec2 b = inValue.value<bvec2>();
291 const ivec2 v(b.x, b.y);
292 setUniform(ubufData, name, data: &v, size: 2 * sizeof(qint32));
293 }
294 break;
295 case QSSGRenderShaderValue::BooleanVec3:
296 {
297 const bvec3 b = inValue.value<bvec3>();
298 const ivec3 v(b.x, b.y, b.z);
299 setUniform(ubufData, name, data: &v, size: 3 * sizeof(qint32));
300 }
301 break;
302 case QSSGRenderShaderValue::BooleanVec4:
303 {
304 const bvec4 b = inValue.value<bvec4>();
305 const ivec4 v(b.x, b.y, b.z, b.w);
306 setUniform(ubufData, name, data: &v, size: 4 * sizeof(qint32));
307 }
308 break;
309 case QSSGRenderShaderValue::Float:
310 {
311 const float v = inValue.value<float>();
312 setUniform(ubufData, name, data: &v, size: sizeof(float));
313 }
314 break;
315 case QSSGRenderShaderValue::Vec2:
316 {
317 const QVector2D v = inValue.value<QVector2D>();
318 setUniform(ubufData, name, data: &v, size: 2 * sizeof(float));
319 }
320 break;
321 case QSSGRenderShaderValue::Vec3:
322 {
323 const QVector3D v = inValue.value<QVector3D>();
324 setUniform(ubufData, name, data: &v, size: 3 * sizeof(float));
325 }
326 break;
327 case QSSGRenderShaderValue::Vec4:
328 {
329 const QVector4D v = inValue.value<QVector4D>();
330 setUniform(ubufData, name, data: &v, size: 4 * sizeof(float));
331 }
332 break;
333 case QSSGRenderShaderValue::Rgba:
334 {
335 const QVector4D v = QSSGUtils::color::sRGBToLinear(color: inValue.value<QColor>());
336 setUniform(ubufData, name, data: &v, size: 4 * sizeof(float));
337 }
338 break;
339 case QSSGRenderShaderValue::UnsignedInteger:
340 {
341 const quint32 v = inValue.value<quint32>();
342 setUniform(ubufData, name, data: &v, size: sizeof(quint32));
343 }
344 break;
345 case QSSGRenderShaderValue::UnsignedIntegerVec2:
346 {
347 const uvec2 v = inValue.value<uvec2>();
348 setUniform(ubufData, name, data: &v, size: 2 * sizeof(quint32));
349 }
350 break;
351 case QSSGRenderShaderValue::UnsignedIntegerVec3:
352 {
353 const uvec3 v = inValue.value<uvec3>();
354 setUniform(ubufData, name, data: &v, size: 3 * sizeof(quint32));
355 }
356 break;
357 case QSSGRenderShaderValue::UnsignedIntegerVec4:
358 {
359 const uvec4 v = inValue.value<uvec4>();
360 setUniform(ubufData, name, data: &v, size: 4 * sizeof(quint32));
361 }
362 break;
363 case QSSGRenderShaderValue::Matrix3x3:
364 {
365 const QMatrix3x3 m = inValue.value<QMatrix3x3>();
366 setUniform(ubufData, name, data: m.constData(), size: 12 * sizeof(float), storeIndex: nullptr, flags: QSSGRhiShaderPipeline::UniformFlag::Mat3);
367 }
368 break;
369 case QSSGRenderShaderValue::Matrix4x4:
370 {
371 const QMatrix4x4 v = inValue.value<QMatrix4x4>();
372 setUniform(ubufData, name, data: v.constData(), size: 16 * sizeof(float));
373 }
374 break;
375 case QSSGRenderShaderValue::Size:
376 {
377 const QSize s = inValue.value<QSize>();
378 float v[2] = { float(s.width()), float(s.height()) };
379 setUniform(ubufData, name, data: v, size: 2 * sizeof(float));
380 }
381 break;
382 case QSSGRenderShaderValue::SizeF:
383 {
384 const QSizeF s = inValue.value<QSizeF>();
385 float v[2] = { float(s.width()), float(s.height()) };
386 setUniform(ubufData, name, data: v, size: 2 * sizeof(float));
387 }
388 break;
389 case QSSGRenderShaderValue::Point:
390 {
391 const QPoint p = inValue.value<QPoint>();
392 float v[2] = { float(p.x()), float(p.y()) };
393 setUniform(ubufData, name, data: v, size: 2 * sizeof(float));
394 }
395 break;
396 case QSSGRenderShaderValue::PointF:
397 {
398 const QPointF p = inValue.value<QPointF>();
399 float v[2] = { float(p.x()), float(p.y()) };
400 setUniform(ubufData, name, data: v, size: 2 * sizeof(float));
401 }
402 break;
403 case QSSGRenderShaderValue::Rect:
404 {
405 const QRect r = inValue.value<QRect>();
406 float v[4] = { float(r.x()), float(r.y()), float(r.width()), float(r.height()) };
407 setUniform(ubufData, name, data: v, size: 4 * sizeof(float));
408 }
409 break;
410 case QSSGRenderShaderValue::RectF:
411 {
412 const QRectF r = inValue.value<QRectF>();
413 float v[4] = { float(r.x()), float(r.y()), float(r.width()), float(r.height()) };
414 setUniform(ubufData, name, data: v, size: 4 * sizeof(float));
415 }
416 break;
417 case QSSGRenderShaderValue::Quaternion:
418 {
419 const QQuaternion q = inValue.value<QQuaternion>();
420 float v[4] = { float(q.x()), float(q.y()), float(q.z()), float(q.scalar()) };
421 setUniform(ubufData, name, data: v, size: 4 * sizeof(float));
422 }
423 break;
424 default:
425 qWarning(msg: "Attempted to set uniform %s value with unsupported data type %i",
426 name, int(inType));
427 break;
428 }
429}
430
431int QSSGRhiShaderPipeline::offsetOfUniform(const QByteArray &name)
432{
433 auto iter = m_ub0.constFind(key: name);
434 if (iter != m_ub0.cend())
435 return iter->offset;
436 return -1;
437}
438
439static QString getUBMemberSizeWarning(QLatin1StringView name, qsizetype correctedSize, qsizetype requestedSize)
440{
441 return QStringLiteral("Uniform block member '%1' got %2 bytes whereas the true size is %3").arg(args&: name, args: QString::number(correctedSize), args: QString::number(requestedSize));
442}
443
444void QSSGRhiShaderPipeline::setUniform(char *ubufData, const char *name, const void *data, size_t size, int *storeIndex, UniformFlags flags)
445{
446 int index = -1;
447 if (!storeIndex || *storeIndex == -1) {
448 const QByteArray ba = QByteArray::fromRawData(data: name, size: strlen(s: name));
449 auto it = m_uniformIndex.constFind(key: ba);
450 if (it != m_uniformIndex.cend()) {
451 index = int(*it);
452 } else if (ba.size() < qsizetype(sizeof(QSSGRhiShaderUniform::name))) {
453 QSSGRhiShaderUniform u;
454 memcpy(dest: u.name, src: name, n: ba.size() + 1);
455 u.size = size;
456
457 const int new_idx = m_uniforms.size();
458 m_uniformIndex[name] = new_idx; // key is name, not ba, this has to be a deep copy QByteArray
459 m_uniforms.push_back(t: u);
460 index = new_idx;
461 } else {
462 qWarning(msg: "Attempted to set uniform with too long name: %s", name);
463 return;
464 }
465 if (storeIndex)
466 *storeIndex = index;
467 } else {
468 index = *storeIndex;
469 }
470
471 Q_ASSERT(index >= 0);
472 QSSGRhiShaderUniform &u = m_uniforms[index];
473 if (size <= u.size) {
474 if (u.offset == SIZE_MAX && u.maybeExists) {
475 auto it = m_ub0.constFind(key: QByteArray::fromRawData(data: u.name, size: strlen(s: u.name)));
476 if (it != m_ub0.constEnd()) {
477 u.offset = it->offset;
478 QSSG_ASSERT_X(QSSG_DEBUG_COND(int(u.size) == it->size), qPrintable(getUBMemberSizeWarning(QLatin1StringView(it->name), u.size, it->size)), return);
479 }
480 }
481 if (u.offset == SIZE_MAX) {
482 // must silently ignore uniforms that are not in the actual shader
483 u.maybeExists = false; // but do not try again
484 return;
485 }
486
487 char *dst = ubufData + u.offset;
488 if (flags.testFlag(flag: UniformFlag::Mat3)) {
489 // mat3 is still 4 floats per column in the uniform buffer (but there
490 // is no 4th column), so 48 bytes altogether, not 36 or 64.
491 const float *src = static_cast<const float *>(data);
492 memcpy(dest: dst, src: src, n: 3 * sizeof(float));
493 memcpy(dest: dst + 4 * sizeof(float), src: src + 3, n: 3 * sizeof(float));
494 memcpy(dest: dst + 8 * sizeof(float), src: src + 6, n: 3 * sizeof(float));
495 } else {
496 memcpy(dest: dst, src: data, n: size);
497 }
498 } else {
499 qWarning(msg: "Attempted to set %u bytes to uniform %s with size %u", uint(size), name, uint(u.size));
500 }
501}
502
503// Quick3D uniform buffer is std140 type and all array data should be stored in this rule.
504// You can check it in glspec45.core.pdf's 7.6.2.2.(4)
505// https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf
506void QSSGRhiShaderPipeline::setUniformArray(char *ubufData, const char *name, const void *data, size_t itemCount, QSSGRenderShaderValue::Type type, int *storeIndex)
507{
508 using namespace QSSGRenderShaderValue;
509
510 QSSGRhiShaderUniformArray *ua = nullptr;
511 constexpr size_t std140BaseTypeSize = 4 * sizeof(float);
512
513 static const auto checkSize = [std140BaseTypeSize](QSSGRhiShaderUniformArray *ua) -> bool {
514 Q_UNUSED(std140BaseTypeSize); // Silence clang warning about unneeded lambda capture (MSVC requires it be captrued).
515 const size_t uniformSize = std140BaseTypeSize < ua->typeSize ? ua->typeSize * ua->itemCount : std140BaseTypeSize * ua->itemCount;
516 QSSG_ASSERT_X(uniformSize == ua->size, qPrintable(getUBMemberSizeWarning(QLatin1StringView(ua->name), uniformSize, ua->size)), return false);
517 return true;
518 };
519
520 if (!storeIndex || *storeIndex == -1) {
521 int index = -1;
522 const QByteArray ba = QByteArray::fromRawData(data: name, size: strlen(s: name));
523 auto it = m_uniformIndex.constFind(key: ba);
524 if (it != m_uniformIndex.cend()) {
525 index = int(*it);
526 ua = &m_uniformArrays[index];
527 } else if (ba.size() < qsizetype(sizeof(QSSGRhiShaderUniformArray::name))) {
528 index = m_uniformArrays.size();
529 m_uniformArrays.push_back(t: QSSGRhiShaderUniformArray());
530 m_uniformIndex[name] = index; // key needs deep copy
531 ua = &m_uniformArrays.last();
532 memcpy(dest: ua->name, src: name, n: ba.size() + 1);
533 } else {
534 qWarning(msg: "Attempted to set uniform array with too long name: %s", name);
535 return;
536 }
537 if (storeIndex)
538 *storeIndex = index;
539 } else {
540 ua = &m_uniformArrays[*storeIndex];
541 }
542
543 if (!ua)
544 return;
545
546 if (ua->offset == SIZE_MAX && ua->maybeExists) {
547 auto it = m_ub0.constFind(key: QByteArray::fromRawData(data: ua->name, size: strlen(s: ua->name)));
548 if (it != m_ub0.constEnd()) {
549 ua->offset = it->offset;
550 ua->size = it->size;
551 }
552 }
553 if (ua->offset == SIZE_MAX) {
554 // must silently ignore uniforms that are not in the actual shader
555 ua->maybeExists = false; // but do not try again
556 return;
557 }
558
559 char *p = ubufData + ua->offset;
560
561 switch (type) {
562 case QSSGRenderShaderValue::Integer:
563 {
564 const qint32 *v = static_cast<const qint32 *>(data);
565 if (sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
566 ua->typeSize = sizeof(qint32);
567 ua->itemCount = itemCount;
568 }
569
570 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
571
572 for (size_t i = 0; i < itemCount; ++i)
573 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
574 }
575 break;
576 case QSSGRenderShaderValue::IntegerVec2:
577 {
578 const ivec2 *v = static_cast<const ivec2 *>(data);
579 if (2 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
580 ua->typeSize = 2 * sizeof(qint32);
581 ua->itemCount = itemCount;
582 }
583
584 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
585
586 for (size_t i = 0; i < itemCount; ++i)
587 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
588 }
589 break;
590 case QSSGRenderShaderValue::IntegerVec3:
591 {
592 const QSSGRenderShaderValue::ivec3 *v = static_cast<const QSSGRenderShaderValue::ivec3 *>(data);
593 if (3 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
594 ua->typeSize = 3 * sizeof(qint32);
595 ua->itemCount = itemCount;
596 }
597
598 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
599
600 for (size_t i = 0; i < itemCount; ++i)
601 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
602 }
603 break;
604 case QSSGRenderShaderValue::IntegerVec4:
605 {
606 const ivec4 *v = static_cast<const ivec4 *>(data);
607 if (4 * sizeof(qint32) != ua->typeSize || itemCount != ua->itemCount) {
608 ua->typeSize = 4 * sizeof(qint32);
609 ua->itemCount = itemCount;
610 }
611
612 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
613
614 memcpy(dest: p, src: v, n: ua->typeSize * ua->itemCount);
615 }
616 break;
617 case QSSGRenderShaderValue::Float:
618 {
619 const float *v = static_cast<const float *>(data);
620 if (sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
621 ua->typeSize = sizeof(float);
622 ua->itemCount = itemCount;
623 }
624
625 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
626
627 for (size_t i = 0; i < itemCount; ++i)
628 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
629 }
630 break;
631 case QSSGRenderShaderValue::Vec2:
632 {
633 const QVector2D *v = static_cast<const QVector2D *>(data);
634 if (2 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
635 ua->typeSize = 2 * sizeof(float);
636 ua->itemCount = itemCount;
637 }
638
639 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
640
641 for (size_t i = 0; i < itemCount; ++i)
642 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
643 }
644 break;
645 case QSSGRenderShaderValue::Vec3:
646 {
647 const QVector3D *v = static_cast<const QVector3D *>(data);
648 if (3 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
649 ua->typeSize = 3 * sizeof(float);
650 ua->itemCount = itemCount;
651 }
652
653 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
654
655 for (size_t i = 0; i < itemCount; ++i)
656 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
657 }
658 break;
659 case QSSGRenderShaderValue::Vec4:
660 {
661 const QVector4D *v = static_cast<const QVector4D *>(data);
662 if (4 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
663 ua->typeSize = 4 * sizeof(float);
664 ua->itemCount = itemCount;
665 }
666
667 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
668
669 memcpy(dest: p, src: v, n: ua->typeSize * ua->itemCount);
670 }
671 break;
672 case QSSGRenderShaderValue::Rgba:
673 {
674 const QColor *v = static_cast<const QColor *>(data);
675 if (4 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
676 ua->typeSize = 4 * sizeof(float);
677 ua->itemCount = itemCount;
678 }
679
680 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
681
682 for (size_t i = 0; i < itemCount; ++i) {
683 const QVector4D vi = QSSGUtils::color::sRGBToLinear(color: v[i]);
684 memcpy(dest: p + i * std140BaseTypeSize, src: &vi, n: ua->typeSize);
685 }
686 }
687 break;
688 case QSSGRenderShaderValue::UnsignedInteger:
689 {
690 const quint32 *v = static_cast<const quint32 *>(data);
691 if (sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
692 ua->typeSize = sizeof(quint32);
693 ua->itemCount = itemCount;
694 }
695
696 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
697
698 for (size_t i = 0; i < itemCount; ++i)
699 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
700 }
701 break;
702 case QSSGRenderShaderValue::UnsignedIntegerVec2:
703 {
704 const uvec2 *v = static_cast<const uvec2 *>(data);
705 if (2 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
706 ua->typeSize = 2 * sizeof(quint32);
707 ua->itemCount = itemCount;
708 }
709
710 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
711
712 for (size_t i = 0; i < itemCount; ++i)
713 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
714 }
715 break;
716 case QSSGRenderShaderValue::UnsignedIntegerVec3:
717 {
718 const uvec3 *v = static_cast<const uvec3 *>(data);
719 if (3 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
720 ua->typeSize = 3 * sizeof(quint32);
721 ua->itemCount = itemCount;
722 }
723
724 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
725
726 for (size_t i = 0; i < itemCount; ++i)
727 memcpy(dest: p + i * std140BaseTypeSize, src: &v[i], n: ua->typeSize);
728 }
729 break;
730 case QSSGRenderShaderValue::UnsignedIntegerVec4:
731 {
732 const uvec4 *v = static_cast<const uvec4 *>(data);
733 if (4 * sizeof(quint32) != ua->typeSize || itemCount != ua->itemCount) {
734 ua->typeSize = 4 * sizeof(quint32);
735 ua->itemCount = itemCount;
736 }
737
738 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
739
740 memcpy(dest: p, src: v, n: ua->typeSize * ua->itemCount);
741 }
742 break;
743 case QSSGRenderShaderValue::Matrix3x3:
744 {
745 const QMatrix3x3 *v = static_cast<const QMatrix3x3 *>(data);
746 if (12 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
747 ua->typeSize = 12 * sizeof(float);
748 ua->itemCount = itemCount;
749 }
750
751 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
752
753 for (uint i = 0; i < ua->itemCount; ++i) {
754 memcpy(dest: p + i * ua->typeSize, src: v[i].constData(), n: 3 * sizeof(float));
755 memcpy(dest: p + i * ua->typeSize + 4 * sizeof(float), src: v[i].constData() + 3, n: 3 * sizeof(float));
756 memcpy(dest: p + i * ua->typeSize + 8 * sizeof(float), src: v[i].constData() + 6, n: 3 * sizeof(float));
757 }
758 }
759 break;
760 case QSSGRenderShaderValue::Matrix4x4:
761 {
762 const QMatrix4x4 *v = static_cast<const QMatrix4x4 *>(data);
763 if (16 * sizeof(float) != ua->typeSize || itemCount != ua->itemCount) {
764 ua->typeSize = 16 * sizeof(float);
765 ua->itemCount = itemCount;
766 }
767
768 QSSG_ASSERT(QSSG_DEBUG_COND(checkSize(ua)), return);
769
770 for (uint i = 0; i < ua->itemCount; ++i)
771 memcpy(dest: p + i * ua->typeSize, src: &v[i] , n: ua->typeSize);
772 }
773 break;
774 case QSSGRenderShaderValue::Boolean:
775 case QSSGRenderShaderValue::BooleanVec2:
776 case QSSGRenderShaderValue::BooleanVec3:
777 case QSSGRenderShaderValue::BooleanVec4:
778 case QSSGRenderShaderValue::Size:
779 case QSSGRenderShaderValue::SizeF:
780 case QSSGRenderShaderValue::Point:
781 case QSSGRenderShaderValue::PointF:
782 case QSSGRenderShaderValue::Rect:
783 case QSSGRenderShaderValue::RectF:
784 case QSSGRenderShaderValue::Quaternion:
785 default:
786 qWarning(msg: "Attempted to set uniform %s value with type %d that is unsupported for uniform arrays",
787 name, int(type));
788 break;
789 }
790}
791
792void QSSGRhiShaderPipeline::ensureCombinedMainLightsUniformBuffer(QRhiBuffer **ubuf)
793{
794 const quint32 totalBufferSize = m_ub0NextUBufOffset + sizeof(QSSGShaderLightsUniformData);
795 if (!*ubuf) {
796 *ubuf = m_context.rhi()->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: totalBufferSize);
797 (*ubuf)->create();
798 }
799 if ((*ubuf)->size() < totalBufferSize) {
800 (*ubuf)->setSize(totalBufferSize);
801 (*ubuf)->create();
802 }
803}
804
805void QSSGRhiShaderPipeline::ensureUniformBuffer(QRhiBuffer **ubuf)
806{
807 if (!*ubuf) {
808 *ubuf = m_context.rhi()->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: m_ub0Size);
809 (*ubuf)->create();
810 }
811}
812
813int QSSGRhiShaderPipeline::bindingForTexture(const char *name, int hint)
814{
815 if (hint >= 0) {
816 const int binding = m_materialImageSamplerBindings[hint];
817 if (binding >= 0)
818 return binding;
819 }
820
821 auto it = m_combinedImageSamplers.constFind(key: QByteArray::fromRawData(data: name, size: strlen(s: name)));
822 const int binding = it != m_combinedImageSamplers.cend() ? it->binding : -1;
823 if (hint >= 0)
824 m_materialImageSamplerBindings[hint] = binding;
825
826 return binding;
827}
828
829QSSGRhiContext::QSSGRhiContext(QRhi *rhi)
830 : m_rhi(rhi)
831 , m_stats(*this)
832{
833 Q_STATIC_ASSERT(int(QSSGRhiSamplerBindingHints::LightProbe) > int(QSSGRenderableImage::Type::Occlusion));
834}
835
836
837QSSGRhiContext::~QSSGRhiContext()
838{
839 releaseCachedResources();
840
841 qDeleteAll(c: m_textures);
842 qDeleteAll(c: m_meshes);
843}
844
845void QSSGRhiContext::releaseCachedResources()
846{
847 for (QSSGRhiDrawCallData &dcd : m_drawCallData) {
848 // We don't call releaseDrawCallData() here, since we're anyways
849 // are going to delete the non-owned resources further down and
850 // there's no point in removing each of those one-by-one, as is
851 // the case with releaseDrawCallData().
852 // This speeds up the release of cached resources greatly when there
853 // are many entries in the map, also at application shutdown.
854 dcd.reset();
855 }
856
857 m_drawCallData.clear();
858
859 qDeleteAll(c: m_pipelines);
860 qDeleteAll(c: m_computePipelines);
861 qDeleteAll(c: m_srbCache);
862 qDeleteAll(c: m_dummyTextures);
863
864 m_pipelines.clear();
865 m_computePipelines.clear();
866 m_srbCache.clear();
867 m_dummyTextures.clear();
868
869 for (const auto &samplerInfo : std::as_const(t&: m_samplers))
870 delete samplerInfo.second;
871
872 m_samplers.clear();
873
874 for (const auto &particleData : std::as_const(t&: m_particleData))
875 delete particleData.texture;
876
877 m_particleData.clear();
878
879 for (const auto &instanceData : std::as_const(t&: m_instanceBuffers)) {
880 if (instanceData.owned)
881 delete instanceData.buffer;
882 }
883
884 m_instanceBuffers.clear();
885
886 for (const auto &instanceData : std::as_const(t&: m_instanceBuffersLod)) {
887 if (instanceData.owned)
888 delete instanceData.buffer;
889 }
890
891 m_instanceBuffersLod.clear();
892}
893
894void QSSGRhiContext::initialize(QRhi *rhi)
895{
896 Q_ASSERT(rhi && !m_rhi);
897 m_rhi = rhi;
898}
899
900QRhiShaderResourceBindings *QSSGRhiContext::srb(const QSSGRhiShaderResourceBindingList &bindings)
901{
902 auto it = m_srbCache.constFind(key: bindings);
903 if (it != m_srbCache.constEnd())
904 return *it;
905
906 QRhiShaderResourceBindings *srb = m_rhi->newShaderResourceBindings();
907 srb->setBindings(first: bindings.v, last: bindings.v + bindings.p);
908 if (srb->create()) {
909 m_srbCache.insert(key: bindings, value: srb);
910 } else {
911 qWarning(msg: "Failed to build srb");
912 delete srb;
913 srb = nullptr;
914 }
915 return srb;
916}
917
918void QSSGRhiContext::releaseDrawCallData(QSSGRhiDrawCallData &dcd)
919{
920 delete dcd.ubuf;
921 dcd.ubuf = nullptr;
922 auto srb = m_srbCache.take(key: dcd.bindings);
923 QSSG_CHECK(srb == dcd.srb);
924 delete srb;
925 dcd.srb = nullptr;
926 dcd.pipeline = nullptr;
927}
928
929QRhiGraphicsPipeline *QSSGRhiContext::pipeline(const QSSGGraphicsPipelineStateKey &key,
930 QRhiRenderPassDescriptor *rpDesc,
931 QRhiShaderResourceBindings *srb)
932{
933 auto it = m_pipelines.constFind(key);
934 if (it != m_pipelines.constEnd())
935 return it.value();
936
937 // Build a new one. This is potentially expensive.
938 QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
939
940 ps->setShaderStages(first: key.state.shaderPipeline->cbeginStages(), last: key.state.shaderPipeline->cendStages());
941 ps->setVertexInputLayout(key.state.ia.inputLayout);
942 ps->setShaderResourceBindings(srb);
943 ps->setRenderPassDescriptor(rpDesc);
944
945 QRhiGraphicsPipeline::Flags flags;
946 if (key.state.scissorEnable)
947 flags |= QRhiGraphicsPipeline::UsesScissor;
948
949 static const bool shaderDebugInfo = qEnvironmentVariableIntValue(varName: "QT_QUICK3D_SHADER_DEBUG_INFO");
950 if (shaderDebugInfo)
951 flags |= QRhiGraphicsPipeline::CompileShadersWithDebugInfo;
952 ps->setFlags(flags);
953
954 ps->setTopology(key.state.ia.topology);
955 ps->setCullMode(key.state.cullMode);
956 if (key.state.ia.topology == QRhiGraphicsPipeline::Lines || key.state.ia.topology == QRhiGraphicsPipeline::LineStrip)
957 ps->setLineWidth(key.state.lineWidth);
958
959 QRhiGraphicsPipeline::TargetBlend blend = key.state.targetBlend;
960 blend.enable = key.state.blendEnable;
961 QVarLengthArray<QRhiGraphicsPipeline::TargetBlend, 8> targetBlends(key.state.colorAttachmentCount);
962 for (int i = 0; i < key.state.colorAttachmentCount; ++i)
963 targetBlends[i] = blend;
964 ps->setTargetBlends(first: targetBlends.cbegin(), last: targetBlends.cend());
965
966 ps->setSampleCount(key.state.samples);
967
968 ps->setDepthTest(key.state.depthTestEnable);
969 ps->setDepthWrite(key.state.depthWriteEnable);
970 ps->setDepthOp(key.state.depthFunc);
971
972 ps->setDepthBias(key.state.depthBias);
973 ps->setSlopeScaledDepthBias(key.state.slopeScaledDepthBias);
974 ps->setPolygonMode(key.state.polygonMode);
975
976 if (key.state.usesStencilRef)
977 flags |= QRhiGraphicsPipeline::UsesStencilRef;
978 ps->setFlags(flags);
979 ps->setStencilFront(key.state.stencilOpFrontState);
980 ps->setStencilTest(key.state.usesStencilRef);
981 ps->setStencilWriteMask(key.state.stencilWriteMask);
982
983 if (!ps->create()) {
984 qWarning(msg: "Failed to build graphics pipeline state");
985 delete ps;
986 return nullptr;
987 }
988
989 m_pipelines.insert(key, value: ps);
990 return ps;
991}
992
993QRhiComputePipeline *QSSGRhiContext::computePipeline(const QSSGComputePipelineStateKey &key,
994 QRhiShaderResourceBindings *srb)
995{
996 auto it = m_computePipelines.constFind(key);
997 if (it != m_computePipelines.constEnd())
998 return it.value();
999
1000 QRhiComputePipeline *computePipeline = m_rhi->newComputePipeline();
1001 computePipeline->setShaderResourceBindings(srb);
1002 computePipeline->setShaderStage({ QRhiShaderStage::Compute, key.shader });
1003 if (!computePipeline->create()) {
1004 qWarning(msg: "Failed to build compute pipeline");
1005 delete computePipeline;
1006 return nullptr;
1007 }
1008 m_computePipelines.insert(key, value: computePipeline);
1009 return computePipeline;
1010}
1011
1012using SamplerInfo = QPair<QSSGRhiSamplerDescription, QRhiSampler*>;
1013
1014QRhiSampler *QSSGRhiContext::sampler(const QSSGRhiSamplerDescription &samplerDescription)
1015{
1016 auto compareSampler = [samplerDescription](const SamplerInfo &info){ return info.first == samplerDescription; };
1017 const auto found = std::find_if(first: m_samplers.cbegin(), last: m_samplers.cend(), pred: compareSampler);
1018 if (found != m_samplers.cend())
1019 return found->second;
1020
1021 QRhiSampler *newSampler = m_rhi->newSampler(magFilter: samplerDescription.magFilter,
1022 minFilter: samplerDescription.minFilter,
1023 mipmapMode: samplerDescription.mipmap,
1024 addressU: samplerDescription.hTiling,
1025 addressV: samplerDescription.vTiling,
1026 addressW: samplerDescription.zTiling);
1027 if (!newSampler->create()) {
1028 qWarning(msg: "Failed to build image sampler");
1029 delete newSampler;
1030 return nullptr;
1031 }
1032 m_samplers << SamplerInfo{samplerDescription, newSampler};
1033 return newSampler;
1034}
1035
1036void QSSGRhiContext::checkAndAdjustForNPoT(QRhiTexture *texture, QSSGRhiSamplerDescription *samplerDescription)
1037{
1038 if (samplerDescription->mipmap != QRhiSampler::None
1039 || samplerDescription->hTiling != QRhiSampler::ClampToEdge
1040 || samplerDescription->vTiling != QRhiSampler::ClampToEdge
1041 || samplerDescription->zTiling != QRhiSampler::ClampToEdge)
1042 {
1043 if (m_rhi->isFeatureSupported(feature: QRhi::NPOTTextureRepeat))
1044 return;
1045
1046 const QSize pixelSize = texture->pixelSize();
1047 const int w = qNextPowerOfTwo(v: pixelSize.width() - 1);
1048 const int h = qNextPowerOfTwo(v: pixelSize.height() - 1);
1049 if (w != pixelSize.width() || h != pixelSize.height()) {
1050 static bool warnShown = false;
1051 if (!warnShown) {
1052 warnShown = true;
1053 qWarning(msg: "Attempted to use an unsupported filtering or wrap mode, "
1054 "this is likely due to lacking proper support for non-power-of-two textures on this platform.\n"
1055 "If this is with WebGL, try updating the application to use QQuick3D::idealSurfaceFormat() in main() "
1056 "in order to ensure WebGL 2 is used.");
1057 }
1058 samplerDescription->mipmap = QRhiSampler::None;
1059 samplerDescription->hTiling = QRhiSampler::ClampToEdge;
1060 samplerDescription->vTiling = QRhiSampler::ClampToEdge;
1061 samplerDescription->zTiling = QRhiSampler::ClampToEdge;
1062 }
1063 }
1064}
1065
1066void QSSGRhiContext::registerTexture(QRhiTexture *texture)
1067{
1068 m_textures.insert(value: texture);
1069}
1070
1071void QSSGRhiContext::releaseTexture(QRhiTexture *texture)
1072{
1073 m_textures.remove(value: texture);
1074 delete texture;
1075}
1076
1077void QSSGRhiContext::registerMesh(QSSGRenderMesh *mesh)
1078{
1079 m_meshes.insert(value: mesh);
1080}
1081
1082void QSSGRhiContext::releaseMesh(QSSGRenderMesh *mesh)
1083{
1084 QSSG_ASSERT(mesh, return);
1085 for (const auto &subset : std::as_const(t&: mesh->subsets)) {
1086 if (subset.rhi.targetsTexture)
1087 releaseTexture(texture: subset.rhi.targetsTexture);
1088 }
1089 m_meshes.remove(value: mesh);
1090 delete mesh;
1091}
1092
1093void QSSGRhiContext::cleanupDrawCallData(const QSSGRenderModel *model)
1094{
1095 // Find all QSSGRhiUniformBufferSet that reference model
1096 // and delete them
1097 const void *modelNode = model;
1098 auto it = m_drawCallData.begin();
1099 while (it != m_drawCallData.end()) {
1100 if (it.key().model == modelNode) {
1101 releaseDrawCallData(dcd&: *it);
1102 it = m_drawCallData.erase(it);
1103 } else {
1104 ++it;
1105 }
1106 }
1107}
1108
1109QRhiTexture *QSSGRhiContext::dummyTexture(QRhiTexture::Flags flags, QRhiResourceUpdateBatch *rub,
1110 const QSize &size, const QColor &fillColor)
1111{
1112 auto it = m_dummyTextures.constFind(key: {.flags: flags, .size: size, .color: fillColor});
1113 if (it != m_dummyTextures.constEnd())
1114 return *it;
1115
1116 QRhiTexture *t = m_rhi->newTexture(format: QRhiTexture::RGBA8, pixelSize: size, sampleCount: 1, flags);
1117 if (t->create()) {
1118 QImage image(t->pixelSize(), QImage::Format_RGBA8888);
1119 image.fill(color: fillColor);
1120 rub->uploadTexture(tex: t, image);
1121 } else {
1122 qWarning(msg: "Failed to build dummy texture");
1123 }
1124
1125 m_dummyTextures.insert(key: {.flags: flags, .size: size, .color: fillColor}, value: t);
1126 return t;
1127}
1128
1129bool QSSGRhiContext::shaderDebuggingEnabled()
1130{
1131 static const bool isSet = (qEnvironmentVariableIntValue(varName: "QT_RHI_SHADER_DEBUG") != 0);
1132 return isSet;
1133}
1134
1135bool QSSGRhiContext::editorMode()
1136{
1137 static const bool isSet = (qEnvironmentVariableIntValue(varName: "QT_QUICK3D_EDITORMODE") != 0);
1138 return isSet;
1139}
1140
1141void QSSGRhiContextStats::start(QSSGRenderLayer *layer)
1142{
1143 layerKey = layer;
1144 PerLayerInfo &info(perLayerInfo[layerKey]);
1145 info.renderPasses.clear();
1146 info.externalRenderPass = {};
1147 info.currentRenderPassIndex = -1;
1148}
1149
1150void QSSGRhiContextStats::stop(QSSGRenderLayer *layer)
1151{
1152 if (rendererDebugEnabled()) {
1153 PerLayerInfo &info(perLayerInfo[layer]);
1154 const int rpCount = info.renderPasses.size();
1155 qDebug(msg: "%d render passes in 3D renderer %p", rpCount, layer);
1156 for (int i = 0; i < rpCount; ++i) {
1157 const RenderPassInfo &rp(info.renderPasses[i]);
1158 qDebug(msg: "Render pass %d: rt name='%s' target size %dx%d pixels",
1159 i, rp.rtName.constData(), rp.pixelSize.width(), rp.pixelSize.height());
1160 printRenderPass(rp);
1161 }
1162 if (info.externalRenderPass.indexedDraws.callCount || info.externalRenderPass.instancedIndexedDraws.callCount
1163 || info.externalRenderPass.draws.callCount || info.externalRenderPass.instancedDraws.callCount)
1164 {
1165 qDebug(msg: "Within external render passes:");
1166 printRenderPass(rp: info.externalRenderPass);
1167 }
1168 }
1169
1170 // a new start() may preceed stop() for the previous View3D, must handle this gracefully
1171 if (layerKey == layer)
1172 layerKey = nullptr;
1173
1174 // The data must stay valid until the next start() with the same key, the
1175 // QQuick3DRenderStats and DebugView may read it.
1176}
1177
1178void QSSGRhiContextStats::cleanupLayerInfo(QSSGRenderLayer *layer)
1179{
1180 perLayerInfo.remove(key: layer);
1181 dynamicDataSources.remove(value: layer);
1182}
1183
1184void QSSGRhiContextStats::beginRenderPass(QRhiTextureRenderTarget *rt)
1185{
1186 PerLayerInfo &info(perLayerInfo[layerKey]);
1187 Q_TRACE(QSSG_renderPass_entry, QString::fromUtf8(rt->name()));
1188 info.renderPasses.append(t: { .rtName: rt->name(), .pixelSize: rt->pixelSize(), .indexedDraws: {}, .draws: {}, .instancedIndexedDraws: {}, .instancedDraws: {} });
1189 info.currentRenderPassIndex = info.renderPasses.size() - 1;
1190}
1191
1192void QSSGRhiContextStats::endRenderPass()
1193{
1194 Q_TRACE(QSSG_renderPass_exit);
1195 PerLayerInfo &info(perLayerInfo[layerKey]);
1196 info.currentRenderPassIndex = -1;
1197}
1198
1199bool QSSGRhiContextStats::isEnabled() const
1200{
1201 return !dynamicDataSources.isEmpty() || profilingEnabled() || rendererDebugEnabled()
1202 || Q_TRACE_ENABLED(QSSG_draw);
1203}
1204
1205void QSSGRhiContextStats::drawIndexed(quint32 indexCount, quint32 instanceCount)
1206{
1207 Q_TRACE(QSSG_drawIndexed, indexCount, instanceCount);
1208 PerLayerInfo &info(perLayerInfo[layerKey]);
1209 RenderPassInfo &rp(info.currentRenderPassIndex >= 0 ? info.renderPasses[info.currentRenderPassIndex] : info.externalRenderPass);
1210 if (instanceCount > 1) {
1211 rp.instancedIndexedDraws.callCount += 1;
1212 rp.instancedIndexedDraws.vertexOrIndexCount += indexCount;
1213 rp.instancedIndexedDraws.instanceCount += instanceCount;
1214 } else {
1215 rp.indexedDraws.callCount += 1;
1216 rp.indexedDraws.vertexOrIndexCount += indexCount;
1217 }
1218}
1219
1220void QSSGRhiContextStats::draw(quint32 vertexCount, quint32 instanceCount)
1221{
1222 Q_TRACE(QSSG_draw, vertexCount, instanceCount);
1223 PerLayerInfo &info(perLayerInfo[layerKey]);
1224 RenderPassInfo &rp(info.currentRenderPassIndex >= 0 ? info.renderPasses[info.currentRenderPassIndex] : info.externalRenderPass);
1225 if (instanceCount > 1) {
1226 rp.instancedDraws.callCount += 1;
1227 rp.instancedDraws.vertexOrIndexCount += vertexCount;
1228 rp.instancedDraws.instanceCount += instanceCount;
1229 } else {
1230 rp.draws.callCount += 1;
1231 rp.draws.vertexOrIndexCount += vertexCount;
1232 }
1233}
1234
1235void QSSGRhiContextStats::printRenderPass(const QSSGRhiContextStats::RenderPassInfo &rp)
1236{
1237 qDebug(msg: "%llu indexed draw calls with %llu indices in total, "
1238 "%llu non-indexed draw calls with %llu vertices in total",
1239 rp.indexedDraws.callCount, rp.indexedDraws.vertexOrIndexCount,
1240 rp.draws.callCount, rp.draws.vertexOrIndexCount);
1241 if (rp.instancedIndexedDraws.callCount || rp.instancedDraws.callCount) {
1242 qDebug(msg: "%llu instanced indexed draw calls with %llu indices and %llu instances in total, "
1243 "%llu instanced non-indexed draw calls with %llu indices and %llu instances in total",
1244 rp.instancedIndexedDraws.callCount, rp.instancedIndexedDraws.vertexOrIndexCount, rp.instancedIndexedDraws.instanceCount,
1245 rp.instancedDraws.callCount, rp.instancedDraws.vertexOrIndexCount, rp.instancedDraws.instanceCount);
1246 }
1247}
1248
1249QT_END_NAMESPACE
1250

source code of qtquick3d/src/runtimerender/qssgrhicontext.cpp