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

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