1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef QSSGRHICONTEXT_P_H
5#define QSSGRHICONTEXT_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtGui/rhi/qrhi.h>
19
20#include <QtQuick3DRuntimeRender/qtquick3druntimerenderexports.h>
21#include <QtQuick3DUtils/private/qssgrenderbasetypes_p.h>
22#include <ssg/qssgrhicontext.h>
23
24QT_BEGIN_NAMESPACE
25
26struct QSSGRenderLayer;
27struct QSSGRenderInstanceTable;
28struct QSSGRenderModel;
29struct QSSGRenderMesh;
30class QSSGRenderGraphObject;
31
32struct QSSGRhiInputAssemblerStatePrivate
33{
34 using InputAssemblerState = QSSGRhiGraphicsPipelineState::InputAssemblerState;
35 static const InputAssemblerState &get(const QSSGRhiGraphicsPipelineState &ps) { return ps.ia; }
36 static InputAssemblerState &get(QSSGRhiGraphicsPipelineState &ps) { return ps.ia; }
37};
38
39using QSSGRhiInputAssemblerState = QSSGRhiInputAssemblerStatePrivate::InputAssemblerState;
40
41struct QSSGRhiGraphicsPipelineStatePrivate
42{
43 static void setShaderPipeline(QSSGRhiGraphicsPipelineState &ps, const QSSGRhiShaderPipeline *pipeline)
44 {
45 ps.shaderPipeline = pipeline;
46 }
47
48 static constexpr const QSSGRhiShaderPipeline *getShaderPipeline(const QSSGRhiGraphicsPipelineState &ps)
49 {
50 return ps.shaderPipeline;
51 }
52};
53
54namespace QSSGRhiHelpers
55{
56
57inline QRhiSampler::Filter toRhi(QSSGRenderTextureFilterOp op)
58{
59 switch (op) {
60 case QSSGRenderTextureFilterOp::Nearest:
61 return QRhiSampler::Nearest;
62 case QSSGRenderTextureFilterOp::Linear:
63 return QRhiSampler::Linear;
64 case QSSGRenderTextureFilterOp::None:
65 return QRhiSampler::Linear;
66 }
67
68 Q_UNREACHABLE_RETURN(QRhiSampler::Linear);
69}
70
71inline QRhiSampler::AddressMode toRhi(QSSGRenderTextureCoordOp tiling)
72{
73 switch (tiling) {
74 case QSSGRenderTextureCoordOp::Repeat:
75 return QRhiSampler::Repeat;
76 case QSSGRenderTextureCoordOp::MirroredRepeat:
77 return QRhiSampler::Mirror;
78 case QSSGRenderTextureCoordOp::ClampToEdge:
79 return QRhiSampler::ClampToEdge;
80 case QSSGRenderTextureCoordOp::Unknown:
81 return QRhiSampler::ClampToEdge;
82 }
83
84 Q_UNREACHABLE_RETURN(QRhiSampler::ClampToEdge);
85}
86
87inline QRhiGraphicsPipeline::CullMode toCullMode(QSSGCullFaceMode cullFaceMode)
88{
89 switch (cullFaceMode) {
90 case QSSGCullFaceMode::Back:
91 return QRhiGraphicsPipeline::Back;
92 case QSSGCullFaceMode::Front:
93 return QRhiGraphicsPipeline::Front;
94 case QSSGCullFaceMode::Disabled:
95 return QRhiGraphicsPipeline::None;
96 case QSSGCullFaceMode::FrontAndBack:
97 qWarning(msg: "FrontAndBack cull mode not supported");
98 return QRhiGraphicsPipeline::None;
99 case QSSGCullFaceMode::Unknown:
100 return QRhiGraphicsPipeline::None;
101 }
102
103 Q_UNREACHABLE_RETURN(QRhiGraphicsPipeline::None);
104}
105
106QRhiVertexInputAttribute::Format toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps);
107QRhiGraphicsPipeline::Topology toTopology(QSSGRenderDrawMode drawMode);
108// Fills out inputLayout.attributes[].location based on
109// inputLayoutInputNames and the provided shader reflection info.
110void bakeVertexInputLocations(QSSGRhiInputAssemblerState *ia, const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding = 0);
111
112} // namespace QSSGRhiHelpers
113
114inline bool operator==(const QSSGRhiGraphicsPipelineState &a, const QSSGRhiGraphicsPipelineState &b) Q_DECL_NOTHROW
115{
116 const auto &ia_a = QSSGRhiInputAssemblerStatePrivate::get(ps: a);
117 const auto &ia_b = QSSGRhiInputAssemblerStatePrivate::get(ps: b);
118 return QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(ps: a) == QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(ps: b)
119 && a.samples == b.samples
120 && a.flags == b.flags
121 && a.stencilRef == b.stencilRef
122 && (std::memcmp(s1: &a.stencilOpFrontState, s2: &b.stencilOpFrontState, n: sizeof(QRhiGraphicsPipeline::StencilOpState)) == 0)
123 && a.stencilWriteMask == b.stencilWriteMask
124 && a.depthFunc == b.depthFunc
125 && a.cullMode == b.cullMode
126 && a.depthBias == b.depthBias
127 && a.slopeScaledDepthBias == b.slopeScaledDepthBias
128 && a.viewport == b.viewport
129 && a.scissor == b.scissor
130 && ia_a.topology == ia_b.topology
131 && ia_a.inputLayout == ia_b.inputLayout
132 && a.targetBlend.colorWrite == b.targetBlend.colorWrite
133 && a.targetBlend.srcColor == b.targetBlend.srcColor
134 && a.targetBlend.dstColor == b.targetBlend.dstColor
135 && a.targetBlend.opColor == b.targetBlend.opColor
136 && a.targetBlend.srcAlpha == b.targetBlend.srcAlpha
137 && a.targetBlend.dstAlpha == b.targetBlend.dstAlpha
138 && a.targetBlend.opAlpha == b.targetBlend.opAlpha
139 && a.colorAttachmentCount == b.colorAttachmentCount
140 && a.lineWidth == b.lineWidth
141 && a.polygonMode == b.polygonMode
142 && a.viewCount == b.viewCount;
143}
144
145inline bool operator!=(const QSSGRhiGraphicsPipelineState &a, const QSSGRhiGraphicsPipelineState &b) Q_DECL_NOTHROW
146{
147 return !(a == b);
148}
149
150inline size_t qHash(const QSSGRhiGraphicsPipelineState &s, size_t seed) Q_DECL_NOTHROW
151{
152 // do not bother with all fields
153 return qHash(key: QSSGRhiGraphicsPipelineStatePrivate::getShaderPipeline(ps: s), seed)
154 ^ qHash(key: s.samples)
155 ^ qHash(key: s.viewCount)
156 ^ qHash(e: s.targetBlend.dstColor)
157 ^ qHash(e: s.depthFunc)
158 ^ qHash(e: s.cullMode)
159 ^ qHash(key: s.colorAttachmentCount)
160 ^ qHash(key: s.lineWidth)
161 ^ qHash(e: s.polygonMode)
162 ^ qHashBits(p: &s.stencilOpFrontState, size: sizeof(QRhiGraphicsPipeline::StencilOpState))
163 ^ (s.flags)
164 ^ (s.stencilRef << 6)
165 ^ (s.stencilWriteMask << 7);
166}
167
168// The lookup keys can be somewhat complicated due to having to handle cases
169// like "render a model in a shared scene between multiple View3Ds" (here both
170// the View3D ('layer/cid') and the model ('model') act as the lookup key since
171// while the model is the same, we still want different uniform buffers per
172// View3D), or the case of shadow maps where the shadow map (there can be as
173// many as lights) is taken into account too ('entry') together with an entry index
174// where more resolution is needed (e.g., cube maps).
175//
176struct QSSGRhiDrawCallDataKey
177{
178 const void *cid = nullptr; // Usually the sub-pass (see usage of QSSGPassKey)
179 const void *model = nullptr;
180 const void *entry = nullptr;
181 quintptr entryIdx = 0;
182};
183
184class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiBuffer
185{
186 Q_DISABLE_COPY(QSSGRhiBuffer)
187public:
188 QSSGRhiBuffer(QSSGRhiContext &context,
189 QRhiBuffer::Type type,
190 QRhiBuffer::UsageFlags usageMask,
191 quint32 stride,
192 qsizetype size,
193 QRhiCommandBuffer::IndexFormat indexFormat = QRhiCommandBuffer::IndexUInt16);
194
195 virtual ~QSSGRhiBuffer();
196
197 QRhiBuffer *buffer() const { return m_buffer; }
198 quint32 stride() const { return m_stride; }
199 quint32 numVertices() const {
200 const quint32 sz = quint32(m_buffer->size());
201 Q_ASSERT((sz % m_stride) == 0);
202 return sz / m_stride;
203 }
204 QRhiCommandBuffer::IndexFormat indexFormat() const { return m_indexFormat; }
205
206private:
207 QSSGRhiContext &m_context;
208 QRhiBuffer *m_buffer = nullptr;
209 quint32 m_stride;
210 QRhiCommandBuffer::IndexFormat m_indexFormat;
211};
212
213struct QSSGRhiShaderUniform
214{
215 char name[64];
216 size_t size = 0;
217
218private:
219 size_t offset = SIZE_MAX;
220 bool maybeExists = true;
221 friend class QSSGRhiShaderPipeline;
222};
223
224struct QSSGRhiShaderUniformArray
225{
226 char name[64];
227 size_t typeSize = 0;
228 size_t itemCount = 0;
229
230private:
231 size_t offset = SIZE_MAX;
232 size_t size = 0;
233 bool maybeExists = true;
234 friend class QSSGRhiShaderPipeline;
235};
236
237using QSSGRhiBufferPtr = std::shared_ptr<QSSGRhiBuffer>;
238
239inline bool operator==(const QSSGRhiSamplerDescription &a, const QSSGRhiSamplerDescription &b) Q_DECL_NOTHROW
240{
241 return a.hTiling == b.hTiling && a.vTiling == b.vTiling && a.zTiling == b.zTiling
242 && a.minFilter == b.minFilter && a.magFilter == b.magFilter
243 && a.mipmap == b.mipmap;
244}
245
246inline bool operator!=(const QSSGRhiSamplerDescription &a, const QSSGRhiSamplerDescription &b) Q_DECL_NOTHROW
247{
248 return !(a == b);
249}
250
251struct QSSGRhiTexture
252{
253 QByteArray name;
254 QRhiTexture *texture = nullptr;
255 QSSGRhiSamplerDescription samplerDesc;
256};
257
258enum class QSSGRhiSamplerBindingHints
259{
260 LightProbe = 64, // must be larger than the largest value in SSGRenderableImage::Type
261 ScreenTexture,
262 DepthTexture,
263 AoTexture,
264 LightmapTexture,
265 DepthTextureArray,
266 ScreenTextureArray,
267 AoTextureArray,
268
269 BindingMapSize
270};
271
272// these are our current shader limits
273#define QSSG_MAX_NUM_LIGHTS 15
274#define QSSG_REDUCED_MAX_NUM_LIGHTS 5
275#define QSSG_MAX_NUM_SHADOW_MAPS 8
276
277// note this struct must exactly match the memory layout of the uniform block in
278// funcSampleLightVars.glsllib
279struct QSSGShaderLightData
280{
281 float position[4];
282 float direction[4]; // Specifies the light direction in world coordinates.
283 float diffuse[4];
284 float specular[4];
285 float coneAngle; // Specifies the outer cone angle of the spot light.
286 float innerConeAngle; // Specifies the inner cone angle of the spot light.
287 float constantAttenuation; // Specifies the constant light attenuation factor.
288 float linearAttenuation; // Specifies the linear light attenuation factor.
289 float quadraticAttenuation; // Specifies the quadratic light attenuation factor.
290 float padding[3]; // the next light array element must start at a vec4-aligned offset
291};
292
293struct QSSGShaderLightsUniformData
294{
295 qint32 count = -1;
296 float padding[3]; // first element must start at a vec4-aligned offset
297 QSSGShaderLightData lightData[QSSG_MAX_NUM_LIGHTS];
298
299};
300
301// note this struct must exactly match the memory layout of the uniform block in
302// funcSampleLightVars.glsllib
303struct QSSGShaderShadowData {
304 float matrices[4][16];
305 float dimensionsInverted[4][4];
306 float csmSplits[4];
307 float csmActive[4];
308
309 float bias;
310 float factor;
311 float isYUp;
312 float clipNear;
313
314 float shadowMapFar;
315 qint32 layerIndex;
316 qint32 csmNumSplits;
317 float csmBlendRatio;
318
319 float pcfFactor;
320 float padding[3];
321};
322
323struct QSSGShaderShadowsUniformData
324{
325 qint32 count = -1;
326 float padding[3]; // first element must start at a vec4-aligned offset
327 QSSGShaderShadowData shadowData[QSSG_MAX_NUM_SHADOW_MAPS];
328};
329
330// Default materials work with a regular combined image sampler for each shadowmap.
331struct QSSGRhiShadowMapProperties
332{
333 QRhiTexture *shadowMapTexture = nullptr;
334 QByteArray shadowMapTextureUniformName;
335 int cachedBinding = -1; // -1 == invalid
336};
337
338class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderPipeline
339{
340 Q_DISABLE_COPY(QSSGRhiShaderPipeline)
341public:
342 explicit QSSGRhiShaderPipeline(QSSGRhiContext &context) : m_context(context) { }
343
344 QSSGRhiContext &context() const { return m_context; }
345 bool isNull() const { return m_stages.isEmpty(); }
346
347 enum StageFlag {
348 // Indicates that this shaderpipeline object is not going to be used with
349 // a QSSGRhiInputAssemblerState, i.e. bakeVertexInputLocations() will
350 // not be called.
351 UsedWithoutIa = 0x01
352 };
353 Q_DECLARE_FLAGS(StageFlags, StageFlag)
354
355 void addStage(const QRhiShaderStage &stage, StageFlags flags = {});
356 const QRhiShaderStage *cbeginStages() const { return m_stages.cbegin(); }
357 const QRhiShaderStage *cendStages() const { return m_stages.cend(); }
358
359 const QRhiShaderStage *vertexStage() const {
360 for (const QRhiShaderStage &s : m_stages) {
361 if (s.type() == QRhiShaderStage::Vertex)
362 return &s;
363 }
364 return nullptr;
365 }
366 const QRhiShaderStage *fragmentStage() const {
367 for (const QRhiShaderStage &s : m_stages) {
368 if (s.type() == QRhiShaderStage::Fragment)
369 return &s;
370 }
371 return nullptr;
372 }
373
374 int ub0Size() const { return m_ub0Size; }
375 int ub0LightDataOffset() const { return m_ub0NextUBufOffset; }
376 int ub0LightDataSize() const
377 {
378 return int(4 * sizeof(qint32) + m_lightsUniformData.count * sizeof(QSSGShaderLightData));
379 }
380 int ub0ShadowDataOffset() const;
381 int ub0ShadowDataSize() const
382 {
383 return int(4 * sizeof(qint32) + m_shadowsUniformData.count * sizeof(QSSGShaderShadowData));
384 }
385
386 const QHash<QSSGRhiInputAssemblerState::InputSemantic, QShaderDescription::InOutVariable> &vertexInputs() const { return m_vertexInputs; }
387
388 // This struct is used purely for performance. It is used to quickly store
389 // and index common uniform names using the storeIndex argument in the
390 // setUniform method.
391 struct CommonUniformIndices
392 {
393 int cameraPositionIdx = -1;
394 int cameraDirectionIdx = -1;
395 int viewProjectionMatrixIdx = -1;
396 int projectionMatrixIdx = -1;
397 int inverseProjectionMatrixIdx = -1;
398 int viewMatrixIdx = -1;
399 int modelViewProjectionIdx = -1;
400 int normalMatrixIdx = -1;
401 int modelMatrixIdx = -1;
402 int lightProbeOrientationIdx = -1;
403 int lightProbePropertiesIdx = -1;
404 int material_emissiveColorIdx = -1;
405 int material_baseColorIdx = -1;
406 int material_specularIdx = -1;
407 int cameraPropertiesIdx = -1;
408 int light_ambient_totalIdx = -1;
409 int material_propertiesIdx = -1;
410 int material_properties2Idx = -1;
411 int material_properties3Idx = -1;
412 int material_properties4Idx = -1;
413 int material_properties5Idx = -1;
414 int material_attenuationIdx = -1;
415 int thicknessFactorIdx = -1;
416 int clearcoatNormalStrengthIdx = -1;
417 int clearcoatFresnelPowerIdx = -1;
418 int rhiPropertiesIdx = -1;
419 int displaceAmountIdx = -1;
420 int boneTransformsIdx = -1;
421 int boneNormalTransformsIdx = -1;
422 int shadowDepthAdjustIdx = -1;
423 int pointSizeIdx = -1;
424 int morphWeightsIdx = -1;
425 int reflectionProbeCubeMapCenter = -1;
426 int reflectionProbeBoxMax = -1;
427 int reflectionProbeBoxMin = -1;
428 int reflectionProbeCorrection = -1;
429 int specularAAIdx = -1;
430 int fogColorIdx = -1;
431 int fogSunColorIdx = -1;
432 int fogDepthPropertiesIdx = -1;
433 int fogHeightPropertiesIdx = -1;
434 int fogTransmitPropertiesIdx = -1;
435
436 struct ImageIndices
437 {
438 int imageRotationsUniformIndex = -1;
439 int imageOffsetsUniformIndex = -1;
440 };
441 QVarLengthArray<ImageIndices, 16> imageIndices;
442 } commonUniformIndices;
443
444 struct InstanceLocations {
445 int transform0 = -1;
446 int transform1 = -1;
447 int transform2 = -1;
448 int color = -1;
449 int data = -1;
450 } instanceLocations;
451
452 enum class UniformFlag {
453 Mat3 = 0x01
454 };
455 Q_DECLARE_FLAGS(UniformFlags, UniformFlag)
456
457 void setUniformValue(char *ubufData, const char *name, const QVariant &value, QSSGRenderShaderValue::Type type);
458 void setUniform(char *ubufData, const char *name, const void *data, size_t size, int *storeIndex = nullptr, UniformFlags flags = {});
459 void setUniformArray(char *ubufData, const char *name, const void *data, size_t itemCount, QSSGRenderShaderValue::Type type, int *storeIndex = nullptr);
460 int bindingForTexture(const char *name, int hint = -1);
461
462 void setLightsEnabled(bool enable) { m_lightsEnabled = enable; }
463 bool isLightingEnabled() const { return m_lightsEnabled; }
464
465 void resetShadowMaps() { m_shadowMaps.clear(); }
466 QSSGRhiShadowMapProperties &addShadowMap() { m_shadowMaps.append(t: QSSGRhiShadowMapProperties()); return m_shadowMaps.last(); }
467 int shadowMapCount() const { return m_shadowMaps.size(); }
468 const QSSGRhiShadowMapProperties &shadowMapAt(int index) const { return m_shadowMaps[index]; }
469 QSSGRhiShadowMapProperties &shadowMapAt(int index) { return m_shadowMaps[index]; }
470
471 void ensureCombinedUniformBuffer(QRhiBuffer **ubuf);
472 void ensureUniformBuffer(QRhiBuffer **ubuf);
473
474 void setLightProbeTexture(QRhiTexture *texture,
475 QSSGRenderTextureCoordOp hTile = QSSGRenderTextureCoordOp::ClampToEdge,
476 QSSGRenderTextureCoordOp vTile = QSSGRenderTextureCoordOp::ClampToEdge)
477 {
478 m_lightProbeTexture = texture; m_lightProbeHorzTile = hTile; m_lightProbeVertTile = vTile;
479 }
480 QRhiTexture *lightProbeTexture() const { return m_lightProbeTexture; }
481 QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> lightProbeTiling() const
482 {
483 return {m_lightProbeHorzTile, m_lightProbeVertTile};
484 }
485
486 void setScreenTexture(QRhiTexture *texture) { m_screenTexture = texture; }
487 QRhiTexture *screenTexture() const { return m_screenTexture; }
488
489 void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; }
490 QRhiTexture *depthTexture() const { return m_depthTexture; }
491
492 void setSsaoTexture(QRhiTexture *texture) { m_ssaoTexture = texture; }
493 QRhiTexture *ssaoTexture() const { return m_ssaoTexture; }
494
495 void setLightmapTexture(QRhiTexture *texture) { m_lightmapTexture = texture; }
496 QRhiTexture *lightmapTexture() const { return m_lightmapTexture; }
497
498 void resetExtraTextures() { m_extraTextures.clear(); }
499 void addExtraTexture(const QSSGRhiTexture &t) { m_extraTextures.append(t); }
500 int extraTextureCount() const { return m_extraTextures.size(); }
501 const QSSGRhiTexture &extraTextureAt(int index) const { return m_extraTextures[index]; }
502 QSSGRhiTexture &extraTextureAt(int index) { return m_extraTextures[index]; }
503
504 QSSGShaderLightsUniformData &lightsUniformData() { return m_lightsUniformData; }
505 QSSGShaderShadowsUniformData &shadowsUniformData() { return m_shadowsUniformData; }
506 InstanceLocations instanceBufferLocations() const { return instanceLocations; }
507
508 int offsetOfUniform(const QByteArray &name);
509
510private:
511 QSSGRhiContext &m_context;
512 QVarLengthArray<QRhiShaderStage, 2> m_stages;
513 int m_ub0Size = 0;
514 int m_ub0NextUBufOffset = 0;
515 QHash<QByteArray, QShaderDescription::BlockVariable> m_ub0;
516 QHash<QSSGRhiInputAssemblerState::InputSemantic, QShaderDescription::InOutVariable> m_vertexInputs;
517 QHash<QByteArray, QShaderDescription::InOutVariable> m_combinedImageSamplers;
518 int m_materialImageSamplerBindings[size_t(QSSGRhiSamplerBindingHints::BindingMapSize)];
519
520 QVarLengthArray<QSSGRhiShaderUniform, 32> m_uniforms; // members of the main (binding 0) uniform buffer
521 QVarLengthArray<QSSGRhiShaderUniformArray, 8> m_uniformArrays;
522 QHash<QByteArray, size_t> m_uniformIndex; // Maps uniform name to index in m_uniforms and m_uniformArrays
523
524 // transient (per-object) data; pointers are all non-owning
525 bool m_lightsEnabled = false;
526 QSSGShaderLightsUniformData m_lightsUniformData;
527 QSSGShaderShadowsUniformData m_shadowsUniformData;
528 QVarLengthArray<QSSGRhiShadowMapProperties, QSSG_MAX_NUM_SHADOW_MAPS> m_shadowMaps;
529 QRhiTexture *m_lightProbeTexture = nullptr;
530 QSSGRenderTextureCoordOp m_lightProbeHorzTile = QSSGRenderTextureCoordOp::ClampToEdge;
531 QSSGRenderTextureCoordOp m_lightProbeVertTile = QSSGRenderTextureCoordOp::ClampToEdge;
532 QRhiTexture *m_screenTexture = nullptr;
533 QRhiTexture *m_depthTexture = nullptr;
534 QRhiTexture *m_ssaoTexture = nullptr;
535 QRhiTexture *m_lightmapTexture = nullptr;
536 QVarLengthArray<QSSGRhiTexture, 8> m_extraTextures;
537};
538
539Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::StageFlags)
540Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::UniformFlags)
541
542using QSSGRhiShaderPipelinePtr = std::shared_ptr<QSSGRhiShaderPipeline>;
543
544class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderResourceBindingList
545{
546public:
547 static const int MAX_SIZE = 32;
548
549 int p = 0;
550 size_t h = 0;
551 QRhiShaderResourceBinding v[MAX_SIZE];
552
553 void clear() { p = 0; h = 0; }
554
555 QSSGRhiShaderResourceBindingList() { }
556
557 QSSGRhiShaderResourceBindingList(const QSSGRhiShaderResourceBindingList &other)
558 : p(other.p),
559 h(other.h)
560 {
561 for (int i = 0; i < p; ++i)
562 v[i] = other.v[i];
563 }
564
565 QSSGRhiShaderResourceBindingList &operator=(const QSSGRhiShaderResourceBindingList &other) Q_DECL_NOTHROW
566 {
567 if (this != &other) {
568 p = other.p;
569 h = other.h;
570 for (int i = 0; i < p; ++i)
571 v[i] = other.v[i];
572 }
573 return *this;
574 }
575
576 void addUniformBuffer(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset = 0 , int size = 0);
577 void addTexture(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
578};
579
580inline bool operator==(const QSSGRhiShaderResourceBindingList &a, const QSSGRhiShaderResourceBindingList &b) Q_DECL_NOTHROW
581{
582 if (a.h != b.h)
583 return false;
584 if (a.p != b.p)
585 return false;
586 for (int i = 0; i < a.p; ++i) {
587 if (a.v[i] != b.v[i])
588 return false;
589 }
590 return true;
591}
592
593inline bool operator!=(const QSSGRhiShaderResourceBindingList &a, const QSSGRhiShaderResourceBindingList &b) Q_DECL_NOTHROW
594{
595 return !(a == b);
596}
597
598inline size_t qHash(const QSSGRhiShaderResourceBindingList &bl, size_t seed) Q_DECL_NOTHROW
599{
600 return bl.h ^ seed;
601}
602
603struct QSSGRhiDrawCallData
604{
605 QRhiBuffer *ubuf = nullptr; // owned
606 QRhiShaderResourceBindings *srb = nullptr; // not owned
607 QSSGRhiShaderResourceBindingList bindings;
608 QRhiGraphicsPipeline *pipeline = nullptr; // not owned
609 size_t renderTargetDescriptionHash = 0;
610 QVector<quint32> renderTargetDescription;
611 QSSGRhiGraphicsPipelineState ps;
612
613 void reset()
614 {
615 delete ubuf;
616 ubuf = nullptr;
617 srb = nullptr;
618 pipeline = nullptr;
619 }
620};
621
622struct QSSGRhiRenderableTexture
623{
624 QRhiTexture *texture = nullptr;
625 QRhiRenderBuffer *depthStencil = nullptr;
626 QRhiTexture *depthTexture = nullptr; // either depthStencil or depthTexture are valid, never both
627 QRhiRenderPassDescriptor *rpDesc = nullptr;
628 QRhiTextureRenderTarget *rt = nullptr;
629 bool isValid() const { return texture && rpDesc && rt; }
630 void resetRenderTarget() {
631 delete rt;
632 rt = nullptr;
633 delete rpDesc;
634 rpDesc = nullptr;
635 }
636 void reset() {
637 resetRenderTarget();
638 delete texture;
639 delete depthStencil;
640 delete depthTexture;
641 *this = QSSGRhiRenderableTexture();
642 }
643};
644
645struct QSSGRhiSortData
646{
647 float d = 0.0f;
648 int indexOrOffset = -1;
649};
650
651struct QSSGRhiInstanceBufferData
652{
653 QRhiBuffer *buffer = nullptr;
654 QByteArray sortedData;
655 QList<QSSGRhiSortData> sortData;
656 QVector3D sortedCameraDirection;
657 QVector3D cameraPosition;
658 QByteArray lodData;
659 int serial = -1;
660 bool owned = true;
661 bool sorting = false;
662};
663
664struct QSSGRhiParticleData
665{
666 QRhiTexture *texture = nullptr;
667 QByteArray sortedData;
668 QByteArray convertData;
669 QList<QSSGRhiSortData> sortData;
670 int particleCount = 0;
671 int serial = -1;
672 bool sorting = false;
673};
674
675class QSSGComputePipelineStateKey
676{
677public:
678 QShader shader;
679 QVector<quint32> srbLayoutDescription;
680 struct {
681 size_t srbLayoutDescriptionHash;
682 } extra;
683 static QSSGComputePipelineStateKey create(const QShader &shader,
684 const QRhiShaderResourceBindings *srb)
685 {
686 const QVector<quint32> srbDesc = srb->serializedLayoutDescription();
687 return { .shader: shader, .srbLayoutDescription: srbDesc, .extra: { .srbLayoutDescriptionHash: qHash(key: srbDesc) } };
688 }
689};
690
691inline bool operator==(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW
692{
693 return a.shader == b.shader && a.srbLayoutDescription == b.srbLayoutDescription;
694}
695
696inline bool operator!=(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW
697{
698 return !(a == b);
699}
700
701inline size_t qHash(const QSSGComputePipelineStateKey &k, size_t seed = 0) Q_DECL_NOTHROW
702{
703 return qHash(s: k.shader, seed) ^ k.extra.srbLayoutDescriptionHash;
704}
705
706struct QSSGRhiDummyTextureKey
707{
708 QRhiTexture::Flags flags;
709 QSize size;
710 QColor color;
711 int arraySize;
712};
713
714inline size_t qHash(const QSSGRhiDummyTextureKey &k, size_t seed) Q_DECL_NOTHROW
715{
716 return qHash(flags: k.flags, seed)
717 ^ qHash(key: k.size.width() ^ k.size.height() ^ k.color.red() ^ k.color.green()
718 ^ k.color.blue() ^ k.color.alpha() ^ k.arraySize);
719}
720
721inline bool operator==(const QSSGRhiDummyTextureKey &a, const QSSGRhiDummyTextureKey &b) Q_DECL_NOTHROW
722{
723 return a.flags == b.flags && a.size == b.size && a.color == b.color && a.arraySize == b.arraySize;
724}
725
726inline bool operator!=(const QSSGRhiDummyTextureKey &a, const QSSGRhiDummyTextureKey &b) Q_DECL_NOTHROW
727{
728 return !(a == b);
729}
730
731class QSSGGraphicsPipelineStateKey
732{
733public:
734 QSSGRhiGraphicsPipelineState state;
735 QVector<quint32> renderTargetDescription;
736 QVector<quint32> srbLayoutDescription;
737 struct {
738 size_t renderTargetDescriptionHash;
739 size_t srbLayoutDescriptionHash;
740 } extra;
741 static QSSGGraphicsPipelineStateKey create(const QSSGRhiGraphicsPipelineState &state,
742 const QRhiRenderPassDescriptor *rpDesc,
743 const QRhiShaderResourceBindings *srb)
744 {
745 const QVector<quint32> rtDesc = rpDesc->serializedFormat();
746 const QVector<quint32> srbDesc = srb->serializedLayoutDescription();
747 return { .state: state, .renderTargetDescription: rtDesc, .srbLayoutDescription: srbDesc, .extra: { .renderTargetDescriptionHash: qHash(key: rtDesc), .srbLayoutDescriptionHash: qHash(key: srbDesc) } };
748 }
749};
750
751inline bool operator==(const QSSGGraphicsPipelineStateKey &a, const QSSGGraphicsPipelineStateKey &b) Q_DECL_NOTHROW
752{
753 return a.state == b.state
754 && a.renderTargetDescription == b.renderTargetDescription
755 && a.srbLayoutDescription == b.srbLayoutDescription;
756}
757
758inline bool operator!=(const QSSGGraphicsPipelineStateKey &a, const QSSGGraphicsPipelineStateKey &b) Q_DECL_NOTHROW
759{
760 return !(a == b);
761}
762
763inline size_t qHash(const QSSGGraphicsPipelineStateKey &k, size_t seed) Q_DECL_NOTHROW
764{
765 return qHash(s: k.state, seed)
766 ^ k.extra.renderTargetDescriptionHash
767 ^ k.extra.srbLayoutDescriptionHash;
768}
769
770#define QSSGRHICTX_STAT(ctx, f) \
771 for (bool qssgrhictxlog_enabled = QSSGRhiContextStats::get(*ctx).isEnabled(); qssgrhictxlog_enabled; qssgrhictxlog_enabled = false) \
772 QSSGRhiContextStats::get(*ctx).f
773
774class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContextStats
775{
776public:
777 [[nodiscard]] static QSSGRhiContextStats &get(QSSGRhiContext &rhiCtx);
778 [[nodiscard]] static const QSSGRhiContextStats &get(const QSSGRhiContext &rhiCtx);
779
780 struct DrawInfo {
781 quint64 callCount = 0;
782 quint64 vertexOrIndexCount = 0;
783 };
784 struct InstancedDrawInfo {
785 quint64 callCount = 0;
786 quint64 vertexOrIndexCount = 0;
787 quint64 instanceCount = 0;
788 };
789 struct RenderPassInfo {
790 QByteArray rtName;
791 QSize pixelSize;
792 DrawInfo indexedDraws;
793 DrawInfo draws;
794 InstancedDrawInfo instancedIndexedDraws;
795 InstancedDrawInfo instancedDraws;
796 };
797 struct PerLayerInfo {
798 PerLayerInfo()
799 {
800 externalRenderPass.rtName = QByteArrayLiteral("Qt Quick");
801 }
802
803 // The main render pass if renderMode==Offscreen, plus render passes
804 // for shadow maps, postprocessing effects, etc.
805 QVector<RenderPassInfo> renderPasses;
806
807 // An Underlay/Overlay/Inline renderMode will make the View3D add stuff
808 // to a render pass managed by Qt Quick. (external == not under the
809 // control of Qt Quick 3D)
810 RenderPassInfo externalRenderPass;
811
812 int currentRenderPassIndex = -1;
813 };
814 struct GlobalInfo { // global as in per QSSGRhiContext which is per-QQuickWindow
815 quint64 meshDataSize = 0;
816 quint64 imageDataSize = 0;
817 qint64 materialGenerationTime = 0;
818 qint64 effectGenerationTime = 0;
819 };
820
821 QHash<QSSGRenderLayer *, PerLayerInfo> perLayerInfo;
822 GlobalInfo globalInfo;
823
824 QSSGRhiContextStats(QSSGRhiContext &context)
825 : rhiCtx(&context)
826 {
827 }
828
829 // The data collected have four consumers:
830 //
831 // - Printed on debug output when QSG_RENDERER_DEBUG has the relevant key.
832 // (this way the debug output from the 2D scenegraph renderer and these 3D
833 // statistics appear nicely intermixed)
834 // - Passed on to the QML profiler when profiling is enabled.
835 // - DebugView via QQuick3DRenderStats.
836 // - When tracing is enabled
837 //
838 // The first two are enabled globally, but DebugView needs a dynamic
839 // enable/disable since we want to collect data when a DebugView item
840 // becomes visible, but not otherwise.
841
842 static bool profilingEnabled();
843 static bool rendererDebugEnabled();
844
845 bool isEnabled() const;
846 void drawIndexed(quint32 indexCount, quint32 instanceCount);
847 void draw(quint32 vertexCount, quint32 instanceCount);
848
849 void meshDataSizeChanges(quint64 newSize) // can be called outside start-stop
850 {
851 globalInfo.meshDataSize = newSize;
852 }
853
854 void imageDataSizeChanges(quint64 newSize) // can be called outside start-stop
855 {
856 globalInfo.imageDataSize = newSize;
857 }
858
859 void registerMaterialShaderGenerationTime(qint64 ms)
860 {
861 globalInfo.materialGenerationTime += ms;
862 }
863
864 void registerEffectShaderGenerationTime(qint64 ms)
865 {
866 globalInfo.effectGenerationTime += ms;
867 }
868
869 static quint64 totalDrawCallCountForPass(const QSSGRhiContextStats::RenderPassInfo &pass)
870 {
871 return pass.draws.callCount
872 + pass.indexedDraws.callCount
873 + pass.instancedDraws.callCount
874 + pass.instancedIndexedDraws.callCount;
875 }
876
877 static quint64 totalVertexCountForPass(const QSSGRhiContextStats::RenderPassInfo &pass)
878 {
879 return pass.draws.vertexOrIndexCount
880 + pass.indexedDraws.vertexOrIndexCount
881 + pass.instancedDraws.vertexOrIndexCount
882 + pass.instancedIndexedDraws.vertexOrIndexCount;
883 }
884
885 void start(QSSGRenderLayer *layer);
886 void stop(QSSGRenderLayer *layer);
887 void beginRenderPass(QRhiTextureRenderTarget *rt);
888 void endRenderPass();
889 void printRenderPass(const RenderPassInfo &rp);
890 void cleanupLayerInfo(QSSGRenderLayer *layer);
891
892 QSSGRhiContext *rhiCtx;
893 QSSGRenderLayer *layerKey = nullptr;
894 QSet<QSSGRenderLayer *> dynamicDataSources;
895};
896
897class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContextPrivate
898{
899 Q_DECLARE_PUBLIC(QSSGRhiContext)
900
901 explicit QSSGRhiContextPrivate(QSSGRhiContext &rhiCtx, QRhi *rhi_)
902 : q_ptr(&rhiCtx)
903 , m_rhi(rhi_)
904 , m_stats(rhiCtx)
905 {}
906
907public:
908 using Textures = QSet<QRhiTexture *>;
909 using Meshes = QSet<QSSGRenderMesh *>;
910
911 [[nodiscard]] static QSSGRhiContextPrivate *get(QSSGRhiContext *q) { return q->d_ptr.get(); }
912 [[nodiscard]] static const QSSGRhiContextPrivate *get(const QSSGRhiContext *q) { return q->d_ptr.get(); }
913
914 [[nodiscard]] static bool shaderDebuggingEnabled();
915 [[nodiscard]] static bool editorMode();
916
917 void setMainRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc);
918 void setCommandBuffer(QRhiCommandBuffer *cb);
919 void setRenderTarget(QRhiRenderTarget *rt);
920 void setMainPassSampleCount(int samples);
921 void setMainPassViewCount(int viewCount);
922
923 void releaseCachedResources();
924
925 void registerTexture(QRhiTexture *texture);
926 void releaseTexture(QRhiTexture *texture);
927
928 void registerMesh(QSSGRenderMesh *mesh);
929 void releaseMesh(QSSGRenderMesh *mesh);
930
931 QRhiShaderResourceBindings *srb(const QSSGRhiShaderResourceBindingList &bindings);
932 void releaseCachedSrb(QSSGRhiShaderResourceBindingList &bindings);
933
934 QRhiGraphicsPipeline *pipeline(const QSSGRhiGraphicsPipelineState &ps,
935 QRhiRenderPassDescriptor *rpDesc,
936 QRhiShaderResourceBindings *srb);
937
938 QRhiGraphicsPipeline *pipeline(const QSSGGraphicsPipelineStateKey &key,
939 QRhiRenderPassDescriptor *rpDesc,
940 QRhiShaderResourceBindings *srb);
941
942 QRhiComputePipeline *computePipeline(const QShader &shader,
943 QRhiShaderResourceBindings *srb);
944
945 QRhiComputePipeline *computePipeline(const QSSGComputePipelineStateKey &key,
946 QRhiShaderResourceBindings *srb);
947
948 QSSGRhiDrawCallData &drawCallData(const QSSGRhiDrawCallDataKey &key);
949 void releaseDrawCallData(QSSGRhiDrawCallData &dcd);
950 void cleanupDrawCallData(const QSSGRenderModel *model);
951
952 QSSGRhiInstanceBufferData &instanceBufferData(QSSGRenderInstanceTable *instanceTable);
953 void releaseInstanceBuffer(QSSGRenderInstanceTable *instanceTable);
954
955 QSSGRhiInstanceBufferData &instanceBufferData(const QSSGRenderModel *model);
956
957 QSSGRhiParticleData &particleData(const QSSGRenderGraphObject *particlesOrModel);
958
959 QSSGRhiContext *q_ptr = nullptr;
960 QRhi *m_rhi = nullptr;
961
962 QRhiRenderPassDescriptor *m_mainRpDesc = nullptr;
963 QRhiCommandBuffer *m_cb = nullptr;
964 QRhiRenderTarget *m_rt = nullptr;
965 Textures m_textures;
966 Meshes m_meshes;
967 int m_mainSamples = 1;
968 int m_mainViewCount = 1;
969
970 QVector<QPair<QSSGRhiSamplerDescription, QRhiSampler*>> m_samplers;
971
972 QHash<QSSGRhiDrawCallDataKey, QSSGRhiDrawCallData> m_drawCallData;
973 QHash<QSSGRhiShaderResourceBindingList, QRhiShaderResourceBindings *> m_srbCache;
974 QHash<QSSGGraphicsPipelineStateKey, QRhiGraphicsPipeline *> m_pipelines;
975 QHash<QSSGComputePipelineStateKey, QRhiComputePipeline *> m_computePipelines;
976 QHash<QSSGRhiDummyTextureKey, QRhiTexture *> m_dummyTextures;
977 QHash<QSSGRenderInstanceTable *, QSSGRhiInstanceBufferData> m_instanceBuffers;
978 QHash<const QSSGRenderModel *, QSSGRhiInstanceBufferData> m_instanceBuffersLod;
979 QHash<const QSSGRenderGraphObject *, QSSGRhiParticleData> m_particleData;
980 QSSGRhiContextStats m_stats;
981};
982
983inline bool operator==(const QSSGRhiDrawCallDataKey &a, const QSSGRhiDrawCallDataKey &b) noexcept
984{
985 return a.cid == b.cid && a.model == b.model && a.entry == b.entry && a.entryIdx == b.entryIdx;
986}
987
988inline bool operator!=(const QSSGRhiDrawCallDataKey &a, const QSSGRhiDrawCallDataKey &b) noexcept
989{
990 return !(a == b);
991}
992
993inline size_t qHash(const QSSGRhiDrawCallDataKey &k, size_t seed = 0) noexcept
994{
995 return qHash(key: quintptr(k.cid)
996 ^ quintptr(k.model)
997 ^ quintptr(k.entry)
998 ^ quintptr(k.entryIdx), seed);
999}
1000
1001QT_END_NAMESPACE
1002
1003#endif // QSSGRHICONTEXT_P_H
1004

source code of qtquick3d/src/runtimerender/qssgrhicontext_p.h