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 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | class QSSGRhiContext; |
27 | class QSSGRhiBuffer; |
28 | struct QSSGShaderLightProperties; |
29 | struct QSSGRenderMesh; |
30 | struct QSSGRenderModel; |
31 | class QSSGRhiShaderPipeline; |
32 | struct QSSGRenderInstanceTable; |
33 | struct QSSGRenderLayer; |
34 | |
35 | struct 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 | |
67 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiBuffer |
68 | { |
69 | Q_DISABLE_COPY(QSSGRhiBuffer) |
70 | public: |
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 | |
89 | private: |
90 | QSSGRhiContext &m_context; |
91 | QRhiBuffer *m_buffer = nullptr; |
92 | quint32 m_stride; |
93 | QRhiCommandBuffer::IndexFormat m_indexFormat; |
94 | }; |
95 | |
96 | struct QSSGRhiShaderUniform |
97 | { |
98 | char name[64]; |
99 | size_t size = 0; |
100 | |
101 | private: |
102 | size_t offset = SIZE_MAX; |
103 | bool maybeExists = true; |
104 | friend class QSSGRhiShaderPipeline; |
105 | }; |
106 | |
107 | struct QSSGRhiShaderUniformArray |
108 | { |
109 | char name[64]; |
110 | size_t typeSize = 0; |
111 | size_t itemCount = 0; |
112 | |
113 | private: |
114 | size_t offset = SIZE_MAX; |
115 | size_t size = 0; |
116 | bool maybeExists = true; |
117 | friend class QSSGRhiShaderPipeline; |
118 | }; |
119 | |
120 | using QSSGRhiBufferPtr = std::shared_ptr<QSSGRhiBuffer>; |
121 | |
122 | QRhiSampler::Filter toRhi(QSSGRenderTextureFilterOp op); |
123 | QRhiSampler::AddressMode toRhi(QSSGRenderTextureCoordOp tiling); |
124 | |
125 | struct 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 | |
135 | inline 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 | |
142 | inline bool operator!=(const QSSGRhiSamplerDescription &a, const QSSGRhiSamplerDescription &b) Q_DECL_NOTHROW |
143 | { |
144 | return !(a == b); |
145 | } |
146 | |
147 | struct QSSGRhiTexture |
148 | { |
149 | QByteArray name; |
150 | QRhiTexture *texture; |
151 | QSSGRhiSamplerDescription samplerDesc; |
152 | }; |
153 | |
154 | enum 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 |
172 | struct 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 | |
186 | struct 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. |
194 | struct QSSGRhiShadowMapProperties |
195 | { |
196 | QRhiTexture *shadowMapTexture = nullptr; |
197 | QByteArray shadowMapTextureUniformName; |
198 | int cachedBinding = -1; // -1 == invalid |
199 | }; |
200 | |
201 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiShaderPipeline |
202 | { |
203 | Q_DISABLE_COPY(QSSGRhiShaderPipeline) |
204 | public: |
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 () { m_extraTextures.clear(); } |
355 | void (const QSSGRhiTexture &t) { m_extraTextures.append(t); } |
356 | int () const { return m_extraTextures.size(); } |
357 | const QSSGRhiTexture &(int index) const { return m_extraTextures[index]; } |
358 | QSSGRhiTexture &(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 | |
365 | private: |
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> ; |
391 | }; |
392 | |
393 | Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::StageFlags) |
394 | Q_DECLARE_OPERATORS_FOR_FLAGS(QSSGRhiShaderPipeline::UniformFlags) |
395 | |
396 | using QSSGRhiShaderPipelinePtr = std::shared_ptr<QSSGRhiShaderPipeline>; |
397 | |
398 | struct 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 | |
429 | inline 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 | |
461 | inline bool operator!=(const QSSGRhiGraphicsPipelineState &a, const QSSGRhiGraphicsPipelineState &b) Q_DECL_NOTHROW |
462 | { |
463 | return !(a == b); |
464 | } |
465 | |
466 | inline 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 | |
487 | struct QSSGGraphicsPipelineStateKey |
488 | { |
489 | QSSGRhiGraphicsPipelineState state; |
490 | QVector<quint32> renderTargetDescription; |
491 | QVector<quint32> srbLayoutDescription; |
492 | struct { |
493 | size_t renderTargetDescriptionHash; |
494 | size_t srbLayoutDescriptionHash; |
495 | } ; |
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 | |
506 | inline 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 | |
513 | inline bool operator!=(const QSSGGraphicsPipelineStateKey &a, const QSSGGraphicsPipelineStateKey &b) Q_DECL_NOTHROW |
514 | { |
515 | return !(a == b); |
516 | } |
517 | |
518 | inline 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 | |
525 | struct QSSGComputePipelineStateKey |
526 | { |
527 | QShader shader; |
528 | QVector<quint32> srbLayoutDescription; |
529 | struct { |
530 | size_t srbLayoutDescriptionHash; |
531 | } ; |
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 | |
540 | inline bool operator==(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW |
541 | { |
542 | return a.shader == b.shader && a.srbLayoutDescription == b.srbLayoutDescription; |
543 | } |
544 | |
545 | inline bool operator!=(const QSSGComputePipelineStateKey &a, const QSSGComputePipelineStateKey &b) Q_DECL_NOTHROW |
546 | { |
547 | return !(a == b); |
548 | } |
549 | |
550 | inline 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 | |
555 | struct 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 | |
590 | inline 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 | |
603 | inline bool operator!=(const QSSGRhiShaderResourceBindingList &a, const QSSGRhiShaderResourceBindingList &b) Q_DECL_NOTHROW |
604 | { |
605 | return !(a == b); |
606 | } |
607 | |
608 | inline size_t qHash(const QSSGRhiShaderResourceBindingList &bl, size_t seed) Q_DECL_NOTHROW |
609 | { |
610 | return bl.h ^ seed; |
611 | } |
612 | |
613 | inline 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 | |
633 | inline 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 | // |
660 | struct 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 | |
668 | inline 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 | |
673 | inline bool operator!=(const QSSGRhiDrawCallDataKey &a, const QSSGRhiDrawCallDataKey &b) Q_DECL_NOTHROW |
674 | { |
675 | return !(a == b); |
676 | } |
677 | |
678 | inline 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 | |
686 | struct 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 | |
705 | struct 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 | |
726 | struct QSSGRhiSortData |
727 | { |
728 | float d = 0.0f; |
729 | int indexOrOffset = -1; |
730 | }; |
731 | |
732 | struct 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 | |
745 | struct 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 | |
756 | struct QSSGRhiDummyTextureKey |
757 | { |
758 | QRhiTexture::Flags flags; |
759 | QSize size; |
760 | QColor color; |
761 | }; |
762 | |
763 | inline 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 | |
770 | inline 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 | |
775 | inline 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 | |
782 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContextStats |
783 | { |
784 | public: |
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 | |
911 | class QSSGRenderGraphObject; |
912 | |
913 | class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGRhiContext |
914 | { |
915 | Q_DISABLE_COPY(QSSGRhiContext) |
916 | public: |
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 | |
998 | private: |
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 | |
1018 | inline 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 | |
1031 | inline 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 | |
1046 | QT_END_NAMESPACE |
1047 | |
1048 | #endif |
1049 | |