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

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