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 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | Q_TRACE_POINT(qtquick3d, QSSG_renderPass_entry, const QString &renderPass); |
17 | Q_TRACE_POINT(qtquick3d, QSSG_renderPass_exit); |
18 | Q_TRACE_POINT(qtquick3d, QSSG_drawIndexed, int indexCount, int instanceCount); |
19 | Q_TRACE_POINT(qtquick3d, QSSG_draw, int vertexCount, int instanceCount); |
20 | |
21 | QSSGRhiBuffer::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 | |
37 | QSSGRhiBuffer::~QSSGRhiBuffer() |
38 | { |
39 | delete m_buffer; |
40 | } |
41 | |
42 | QRhiVertexInputAttribute::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 | |
88 | QRhiGraphicsPipeline::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 | |
110 | void 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 | |
163 | QRhiGraphicsPipeline::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 | |
182 | void 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 | |
253 | void 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 | |
431 | int 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 | |
439 | static 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 | |
444 | void 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 |
506 | void 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 | |
792 | void 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 | |
805 | void 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 | |
813 | int 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 | |
829 | QSSGRhiContext::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 | |
837 | QSSGRhiContext::~QSSGRhiContext() |
838 | { |
839 | releaseCachedResources(); |
840 | |
841 | qDeleteAll(c: m_textures); |
842 | qDeleteAll(c: m_meshes); |
843 | } |
844 | |
845 | void 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 | |
894 | void QSSGRhiContext::initialize(QRhi *rhi) |
895 | { |
896 | Q_ASSERT(rhi && !m_rhi); |
897 | m_rhi = rhi; |
898 | } |
899 | |
900 | QRhiShaderResourceBindings *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 | |
918 | void 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 | |
929 | QRhiGraphicsPipeline *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 | |
993 | QRhiComputePipeline *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 | |
1012 | using SamplerInfo = QPair<QSSGRhiSamplerDescription, QRhiSampler*>; |
1013 | |
1014 | QRhiSampler *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 | |
1036 | void 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 | |
1066 | void QSSGRhiContext::registerTexture(QRhiTexture *texture) |
1067 | { |
1068 | m_textures.insert(value: texture); |
1069 | } |
1070 | |
1071 | void QSSGRhiContext::releaseTexture(QRhiTexture *texture) |
1072 | { |
1073 | m_textures.remove(value: texture); |
1074 | delete texture; |
1075 | } |
1076 | |
1077 | void QSSGRhiContext::registerMesh(QSSGRenderMesh *mesh) |
1078 | { |
1079 | m_meshes.insert(value: mesh); |
1080 | } |
1081 | |
1082 | void 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 | |
1093 | void 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 | |
1109 | QRhiTexture *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 | |
1129 | bool QSSGRhiContext::shaderDebuggingEnabled() |
1130 | { |
1131 | static const bool isSet = (qEnvironmentVariableIntValue(varName: "QT_RHI_SHADER_DEBUG" ) != 0); |
1132 | return isSet; |
1133 | } |
1134 | |
1135 | bool QSSGRhiContext::editorMode() |
1136 | { |
1137 | static const bool isSet = (qEnvironmentVariableIntValue(varName: "QT_QUICK3D_EDITORMODE" ) != 0); |
1138 | return isSet; |
1139 | } |
1140 | |
1141 | void 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 | |
1150 | void 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 | |
1178 | void QSSGRhiContextStats::cleanupLayerInfo(QSSGRenderLayer *layer) |
1179 | { |
1180 | perLayerInfo.remove(key: layer); |
1181 | dynamicDataSources.remove(value: layer); |
1182 | } |
1183 | |
1184 | void 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 | |
1192 | void QSSGRhiContextStats::endRenderPass() |
1193 | { |
1194 | Q_TRACE(QSSG_renderPass_exit); |
1195 | PerLayerInfo &info(perLayerInfo[layerKey]); |
1196 | info.currentRenderPassIndex = -1; |
1197 | } |
1198 | |
1199 | bool QSSGRhiContextStats::isEnabled() const |
1200 | { |
1201 | return !dynamicDataSources.isEmpty() || profilingEnabled() || rendererDebugEnabled() |
1202 | || Q_TRACE_ENABLED(QSSG_draw); |
1203 | } |
1204 | |
1205 | void 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 | |
1220 | void 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 | |
1235 | void 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 | |
1249 | QT_END_NAMESPACE |
1250 | |