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 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | struct QSSGRenderLayer; |
27 | struct QSSGRenderInstanceTable; |
28 | struct QSSGRenderModel; |
29 | struct QSSGRenderMesh; |
30 | class QSSGRenderGraphObject; |
31 | |
32 | struct 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 | |
39 | using QSSGRhiInputAssemblerState = QSSGRhiInputAssemblerStatePrivate::InputAssemblerState; |
40 | |
41 | struct 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 | |
54 | namespace QSSGRhiHelpers |
55 | { |
56 | |
57 | inline 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 | |
71 | inline 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 | |
87 | inline 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 | |
106 | QRhiVertexInputAttribute::Format toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps); |
107 | QRhiGraphicsPipeline::Topology toTopology(QSSGRenderDrawMode drawMode); |
108 | // Fills out inputLayout.attributes[].location based on |
109 | // inputLayoutInputNames and the provided shader reflection info. |
110 | void bakeVertexInputLocations(QSSGRhiInputAssemblerState *ia, const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding = 0); |
111 | |
112 | } // namespace QSSGRhiHelpers |
113 | |
114 | inline 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 | |
145 | inline bool operator!=(const QSSGRhiGraphicsPipelineState &a, const QSSGRhiGraphicsPipelineState &b) Q_DECL_NOTHROW |
146 | { |
147 | return !(a == b); |
148 | } |
149 | |
150 | inline 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 | // |
176 | struct 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 | |
184 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiBuffer |
185 | { |
186 | Q_DISABLE_COPY(QSSGRhiBuffer) |
187 | public: |
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 | |
206 | private: |
207 | QSSGRhiContext &m_context; |
208 | QRhiBuffer *m_buffer = nullptr; |
209 | quint32 m_stride; |
210 | QRhiCommandBuffer::IndexFormat m_indexFormat; |
211 | }; |
212 | |
213 | struct QSSGRhiShaderUniform |
214 | { |
215 | char name[64]; |
216 | size_t size = 0; |
217 | |
218 | private: |
219 | size_t offset = SIZE_MAX; |
220 | bool maybeExists = true; |
221 | friend class QSSGRhiShaderPipeline; |
222 | }; |
223 | |
224 | struct QSSGRhiShaderUniformArray |
225 | { |
226 | char name[64]; |
227 | size_t typeSize = 0; |
228 | size_t itemCount = 0; |
229 | |
230 | private: |
231 | size_t offset = SIZE_MAX; |
232 | size_t size = 0; |
233 | bool maybeExists = true; |
234 | friend class QSSGRhiShaderPipeline; |
235 | }; |
236 | |
237 | using QSSGRhiBufferPtr = std::shared_ptr<QSSGRhiBuffer>; |
238 | |
239 | inline 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 | |
246 | inline bool operator!=(const QSSGRhiSamplerDescription &a, const QSSGRhiSamplerDescription &b) Q_DECL_NOTHROW |
247 | { |
248 | return !(a == b); |
249 | } |
250 | |
251 | struct QSSGRhiTexture |
252 | { |
253 | QByteArray name; |
254 | QRhiTexture *texture = nullptr; |
255 | QSSGRhiSamplerDescription samplerDesc; |
256 | }; |
257 | |
258 | enum 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 |
279 | struct 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 | |
293 | struct 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 |
303 | struct 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 | |
323 | struct 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. |
331 | struct QSSGRhiShadowMapProperties |
332 | { |
333 | QRhiTexture *shadowMapTexture = nullptr; |
334 | QByteArray shadowMapTextureUniformName; |
335 | int cachedBinding = -1; // -1 == invalid |
336 | }; |
337 | |
338 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderPipeline |
339 | { |
340 | Q_DISABLE_COPY(QSSGRhiShaderPipeline) |
341 | public: |
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 () { m_extraTextures.clear(); } |
499 | void (const QSSGRhiTexture &t) { m_extraTextures.append(t); } |
500 | int () const { return m_extraTextures.size(); } |
501 | const QSSGRhiTexture &(int index) const { return m_extraTextures[index]; } |
502 | QSSGRhiTexture &(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 | |
510 | private: |
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> ; |
537 | }; |
538 | |
539 | Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::StageFlags) |
540 | Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::UniformFlags) |
541 | |
542 | using QSSGRhiShaderPipelinePtr = std::shared_ptr<QSSGRhiShaderPipeline>; |
543 | |
544 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderResourceBindingList |
545 | { |
546 | public: |
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 | |
580 | inline 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 | |
593 | inline bool operator!=(const QSSGRhiShaderResourceBindingList &a, const QSSGRhiShaderResourceBindingList &b) Q_DECL_NOTHROW |
594 | { |
595 | return !(a == b); |
596 | } |
597 | |
598 | inline size_t qHash(const QSSGRhiShaderResourceBindingList &bl, size_t seed) Q_DECL_NOTHROW |
599 | { |
600 | return bl.h ^ seed; |
601 | } |
602 | |
603 | struct 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 | |
622 | struct 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 | |
645 | struct QSSGRhiSortData |
646 | { |
647 | float d = 0.0f; |
648 | int indexOrOffset = -1; |
649 | }; |
650 | |
651 | struct 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 | |
664 | struct 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 | |
675 | class QSSGComputePipelineStateKey |
676 | { |
677 | public: |
678 | QShader shader; |
679 | QVector<quint32> srbLayoutDescription; |
680 | struct { |
681 | size_t srbLayoutDescriptionHash; |
682 | } ; |
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 | |
691 | inline bool operator==(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW |
692 | { |
693 | return a.shader == b.shader && a.srbLayoutDescription == b.srbLayoutDescription; |
694 | } |
695 | |
696 | inline bool operator!=(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW |
697 | { |
698 | return !(a == b); |
699 | } |
700 | |
701 | inline 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 | |
706 | struct QSSGRhiDummyTextureKey |
707 | { |
708 | QRhiTexture::Flags flags; |
709 | QSize size; |
710 | QColor color; |
711 | int arraySize; |
712 | }; |
713 | |
714 | inline 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 | |
721 | inline 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 | |
726 | inline bool operator!=(const QSSGRhiDummyTextureKey &a, const QSSGRhiDummyTextureKey &b) Q_DECL_NOTHROW |
727 | { |
728 | return !(a == b); |
729 | } |
730 | |
731 | class QSSGGraphicsPipelineStateKey |
732 | { |
733 | public: |
734 | QSSGRhiGraphicsPipelineState state; |
735 | QVector<quint32> renderTargetDescription; |
736 | QVector<quint32> srbLayoutDescription; |
737 | struct { |
738 | size_t renderTargetDescriptionHash; |
739 | size_t srbLayoutDescriptionHash; |
740 | } ; |
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 | |
751 | inline 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 | |
758 | inline bool operator!=(const QSSGGraphicsPipelineStateKey &a, const QSSGGraphicsPipelineStateKey &b) Q_DECL_NOTHROW |
759 | { |
760 | return !(a == b); |
761 | } |
762 | |
763 | inline 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 | |
774 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContextStats |
775 | { |
776 | public: |
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 | |
897 | class 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 | |
907 | public: |
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 | |
983 | inline 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 | |
988 | inline bool operator!=(const QSSGRhiDrawCallDataKey &a, const QSSGRhiDrawCallDataKey &b) noexcept |
989 | { |
990 | return !(a == b); |
991 | } |
992 | |
993 | inline 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 | |
1001 | QT_END_NAMESPACE |
1002 | |
1003 | #endif // QSSGRHICONTEXT_P_H |
1004 | |