Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrhicustommaterialsystem.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6
8
9#include <QtQuick3DUtils/private/qssgutils_p.h>
10
12#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
19#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgrenderhelpers_p.h>
24#include <qtquick3d_tracepoints_p.h>
25
26#include <QtCore/qbitarray.h>
27
29
30Q_TRACE_POINT(qtquick3d, QSSG_generateShader_entry)
31Q_TRACE_POINT(qtquick3d, QSSG_generateShader_exit)
32
33QSSGCustomMaterialSystem::QSSGCustomMaterialSystem() = default;
34
35QSSGCustomMaterialSystem::~QSSGCustomMaterialSystem()
36{
37}
38
39bool QSSGCustomMaterialSystem::prepareForRender(const QSSGRenderModel &,
40 const QSSGRenderSubset &,
41 QSSGRenderCustomMaterial &inMaterial)
42{
43 return inMaterial.isDirty();
44}
45
46void QSSGCustomMaterialSystem::setRenderContextInterface(QSSGRenderContextInterface *inContext)
47{
48 context = inContext;
49}
50
51void QSSGCustomMaterialSystem::releaseCachedResources()
52{
53 shaderMap.clear();
54}
55
56QSSGRhiShaderPipelinePtr QSSGCustomMaterialSystem::shadersForCustomMaterial(QSSGRhiGraphicsPipelineState *ps,
57 const QSSGRenderCustomMaterial &material,
58 QSSGSubsetRenderable &renderable,
59 const QSSGShaderDefaultMaterialKeyProperties &defaultMaterialShaderKeyProperties,
60 const QSSGShaderFeatures &featureSet,
61 const QSSGUserShaderAugmentation &shaderAugmentation)
62{
63 QElapsedTimer timer;
64 timer.start();
65
66 QSSGRhiShaderPipelinePtr shaderPipeline;
67
68 const bool multiView = featureSet.isSet(QSSGShaderFeatures::Feature::DisableMultiView)
69 ? false
70 : defaultMaterialShaderKeyProperties.m_viewCount.getValue(renderable.shaderDescription) >= 2;
71 const QByteArray shaderPathKey = material.m_shaderPathKey[multiView ? QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex
72 : QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] + shaderAugmentation.preamble + shaderAugmentation.body;
73
74 // This just references inFeatureSet and inRenderable.shaderDescription -
75 // cheap to construct and is good enough for the find(). This is the first
76 // level, fast lookup. (equivalent to what
77 // QSSGRenderer::getShaderPipelineForDefaultMaterial does for the
78 // default/principled material)
79 QSSGShaderMapKey skey = QSSGShaderMapKey(shaderPathKey,
80 featureSet,
81 renderable.shaderDescription);
82 auto it = shaderMap.find(skey);
83 if (it == shaderMap.end()) {
84 // NB this key calculation must replicate exactly what the generator does in generateMaterialRhiShader()
85 QByteArray shaderString = shaderPathKey;
86 QSSGShaderDefaultMaterialKey matKey(renderable.shaderDescription);
87 matKey.toString(shaderString, defaultMaterialShaderKeyProperties);
88
89 // Try the persistent (disk-based) cache.
90 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
91 shaderPipeline = context->shaderCache()->tryNewPipelineFromPersistentCache(qsbcKey, shaderPathKey, featureSet);
92
93 if (!shaderPipeline) {
94 // Have to generate the shaders and send it all through the shader conditioning pipeline.
95 Q_TRACE_SCOPE(QSSG_generateShader);
96 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
97 QSSGMaterialVertexPipeline vertexPipeline(*context->shaderProgramGenerator(),
98 defaultMaterialShaderKeyProperties,
99 material.adapter);
100
101 shaderPipeline = QSSGMaterialShaderGenerator::generateMaterialRhiShader(shaderPathKey,
102 vertexPipeline,
103 renderable.shaderDescription,
104 defaultMaterialShaderKeyProperties,
105 featureSet,
106 material,
107 *context->shaderLibraryManager(),
108 *context->shaderCache(),
109 shaderAugmentation);
110 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, material.profilingId);
111 }
112
113 // make skey useable as a key for the QHash (makes a copy of the materialKey, instead of just referencing)
114 skey.detach();
115 // insert it no matter what, no point in trying over and over again
116 shaderMap.insert(skey, shaderPipeline);
117 } else {
118 shaderPipeline = it.value();
119 }
120
121 if (shaderPipeline) {
122 QSSGRhiGraphicsPipelineStatePrivate::setShaderPipeline(*ps, shaderPipeline.get());
123 shaderPipeline->resetExtraTextures();
124 }
125
126 QSSGRhiContextStats::get(*context->rhiContext()).registerMaterialShaderGenerationTime(timer.elapsed());
127
128 return shaderPipeline;
129}
130
131void QSSGCustomMaterialSystem::updateUniformsForCustomMaterial(QSSGRhiShaderPipeline &shaderPipeline,
132 QSSGRhiContext *rhiCtx,
133 const QSSGLayerRenderData &inData,
134 char *ubufData,
135 QSSGRhiGraphicsPipelineState *ps,
136 const QSSGRenderCustomMaterial &material,
137 QSSGSubsetRenderable &renderable,
138 const QSSGRenderCameraList &cameras,
139 const QVector2D *depthAdjust,
140 const QMatrix4x4 *alteredModelViewProjection)
141{
142 QSSGRenderMvpArray alteredMvpList;
143 if (alteredModelViewProjection)
144 alteredMvpList[0] = *alteredModelViewProjection;
145
146 const QMatrix4x4 clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
147 QRhiTexture *lightmapTexture = inData.getLightmapTexture(renderable.modelContext);
148
149 const auto &modelNode = renderable.modelContext.model;
150 const auto &[localInstanceTransform, globalInstanceTransform] = inData.getInstanceTransforms(modelNode);
151
152 const auto &defaultMaterialShaderKeyProperties = inData.getDefaultMaterialPropertyTable();
153
154 const QMatrix4x4 &modelMatrix(modelNode.usesBoneTexture() ? QMatrix4x4() : renderable.modelContext.globalTransform);
155
156 QSSGMaterialShaderGenerator::setRhiMaterialProperties(*context,
157 shaderPipeline,
158 ubufData,
159 ps,
160 material,
161 renderable.shaderDescription,
162 defaultMaterialShaderKeyProperties,
163 cameras,
164 alteredModelViewProjection ? alteredMvpList : renderable.modelContext.modelViewProjections,
165 renderable.modelContext.normalMatrix,
166 modelMatrix,
167 clipSpaceCorrMatrix,
168 localInstanceTransform,
169 globalInstanceTransform,
170 toDataView(modelNode.morphWeights),
171 renderable.firstImage,
172 renderable.opacity,
173 inData,
174 renderable.lights,
175 renderable.reflectionProbe,
176 true,
177 renderable.renderableFlags.receivesReflections(),
178 depthAdjust,
179 lightmapTexture);
180}
181
183 QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
184
185void QSSGCustomMaterialSystem::rhiPrepareRenderable(QSSGRhiGraphicsPipelineState *ps,
186 QSSGPassKey passKey,
187 QSSGSubsetRenderable &renderable,
188 const QSSGShaderFeatures &featureSet,
189 const QSSGRenderCustomMaterial &material,
190 const QSSGLayerRenderData &layerData,
191 QRhiRenderPassDescriptor *renderPassDescriptor,
192 int samples,
193 int viewCount,
194 QSSGRenderCamera *alteredCamera,
195 QSSGRenderTextureCubeFace cubeFace,
196 QMatrix4x4 *alteredModelViewProjection,
197 QSSGReflectionMapEntry *entry,
198 bool oit)
199{
200 QSSGRhiContext *rhiCtx = context->rhiContext().get();
201
202 QRhiGraphicsPipeline::TargetBlend blend; // no blending by default
203 if (material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Blending)) {
204 blend.enable = true;
205 blend.srcColor = material.m_srcBlend;
206 blend.srcAlpha = material.m_srcAlphaBlend;
207 blend.dstColor = material.m_dstBlend;
208 blend.dstAlpha = material.m_dstAlphaBlend;
209 }
210
211 const QSSGCullFaceMode cullMode = material.m_cullMode;
212
213 const auto &defaultMaterialShaderKeyProperties = layerData.getDefaultMaterialPropertyTable();
214
215 const bool blendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(renderable.shaderDescription);
216
217 const auto &shaderPipeline = shadersForCustomMaterial(ps, material, renderable, defaultMaterialShaderKeyProperties, featureSet);
218
219 if (shaderPipeline) {
220 QSSGRhiShaderResourceBindingList bindings;
221 const auto &modelNode = renderable.modelContext.model;
222
223 // NOTE:
224 // - entryIdx should 0 for QSSGRenderTextureCubeFaceNone.
225 // In all other cases the entryIdx is a combination of the cubeface idx and the subset offset, where the lower bits
226 // are the cubeface idx.
227 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
228 const quintptr entryIdx = quintptr(cubeFace != QSSGRenderTextureCubeFaceNone) * (cubeFaceIdx + (quintptr(renderable.subset.offset) << 3));
229 // As the entry might be null we create an entry key consisting of the entry and the material.
230 const auto entryPartA = reinterpret_cast<quintptr>(&material);
231 const auto entryPartB = reinterpret_cast<quintptr>(entry);
232 const void *entryKey = reinterpret_cast<const void *>(entryPartA ^ entryPartB);
233
234 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiCtx)->drawCallData({ passKey, &modelNode, entryKey, entryIdx });
235
236 shaderPipeline->ensureCombinedUniformBuffer(&dcd.ubuf);
237 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
238 if (!alteredCamera) {
239 updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, layerData, ubufData, ps, material, renderable, layerData.renderedCameras, nullptr, nullptr);
240 } else {
241 QSSGRenderCameraList cameras({ alteredCamera });
242 updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, layerData, ubufData, ps, material, renderable, cameras, nullptr, alteredModelViewProjection);
243 }
244 if (blendParticles)
245 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &renderable.modelContext.model, renderable.subset.offset);
246 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
247
248 if (blendParticles)
249 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &renderable.modelContext.model);
250 bool instancing = false;
251 if (!alteredCamera) {
252 const QSSGRenderCameraDataList &cameraDatas(*layerData.renderedCameraData);
253 instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, &renderable, cameraDatas[0].direction, cameraDatas[0].position, renderable.instancingLodMin, renderable.instancingLodMax);
254 } else {
255 const auto &altCamTransform = layerData.getGlobalTransform(*alteredCamera);
256 instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, &renderable, QSSGRenderNode::getScalingCorrectDirection(altCamTransform), QSSGRenderNode::getGlobalPos(altCamTransform), renderable.instancingLodMin, renderable.instancingLodMax);
257 }
258
259 ps->samples = samples;
260 ps->viewCount = viewCount;
261
262 ps->cullMode = QSSGRhiHelpers::toCullMode(cullMode);
263
264 if (!oit)
265 ps->targetBlend[0] = blend;
266
267 auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
268
269 ia = renderable.subset.rhi.ia;
270
271 //### Copied code from default materials
272 int instanceBufferBinding = 0;
273 if (instancing) {
274 // Need to setup new bindings for instanced buffers
275 const quint32 stride = renderable.modelContext.model.instanceTable->stride();
276 QVarLengthArray<QRhiVertexInputBinding, 8> bindings;
277 std::copy(ia.inputLayout.cbeginBindings(),
278 ia.inputLayout.cendBindings(),
279 std::back_inserter(bindings));
280 bindings.append({ stride, QRhiVertexInputBinding::PerInstance });
281 instanceBufferBinding = bindings.size() - 1;
282 ia.inputLayout.setBindings(bindings.cbegin(), bindings.cend());
283 }
284
285 QSSGRhiHelpers::bakeVertexInputLocations(&ia, *shaderPipeline, instanceBufferBinding);
286
287 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
288 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
289 QRhiTexture *dummyTexture3D = rhiCtx->dummyTexture(QRhiTexture::ThreeDimensional, resourceUpdates);
290 QRhiTexture *dummyCubeTexture = rhiCtx->dummyTexture(QRhiTexture::CubeMap, resourceUpdates);
291 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
292
293 bindings.addUniformBuffer(0, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline->ub0Size());
294 bindings.addUniformBuffer(1, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, shaderPipeline->ub0LightDataOffset(), sizeof(QSSGShaderLightsUniformData));
295 bindings.addUniformBuffer(2, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, shaderPipeline->ub0DirectionalLightDataOffset(), sizeof(QSSGShaderDirectionalLightsUniformData));
296
297 QVector<QShaderDescription::InOutVariable> samplerVars =
298 shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
299 for (const QShaderDescription::InOutVariable &var : shaderPipeline->vertexStage()->shader().description().combinedImageSamplers()) {
300 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(),
301 [&var](const QShaderDescription::InOutVariable &v) { return var.binding == v.binding; });
302 if (it == samplerVars.cend())
303 samplerVars.append(var);
304 }
305
306 int maxSamplerBinding = -1;
307 for (const QShaderDescription::InOutVariable &var : samplerVars)
308 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
309
310 // Will need to set unused image-samplers to something dummy
311 // because the shader code contains all custom property textures,
312 // and not providing a binding for all of them is invalid with some
313 // graphics APIs (and will need a real texture because setting a
314 // null handle or similar is not permitted with some of them so the
315 // srb does not accept null QRhiTextures either; but first let's
316 // figure out what bindings are unused in this frame)
317 QBitArray samplerBindingsSpecified(maxSamplerBinding + 1);
318
319 if (blendParticles)
320 samplerBindingsSpecified.setBit(shaderPipeline->bindingForTexture("qt_particleTexture"));
321
322 // Skinning
323 if (QRhiTexture *boneTexture = layerData.getBonemapTexture(renderable.modelContext)) {
324 int binding = shaderPipeline->bindingForTexture("qt_boneTexture");
325 if (binding >= 0) {
326 QRhiSampler *boneSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
327 QRhiSampler::Nearest,
328 QRhiSampler::None,
329 QRhiSampler::ClampToEdge,
330 QRhiSampler::ClampToEdge,
331 QRhiSampler::Repeat
332 });
333 bindings.addTexture(binding,
334 QRhiShaderResourceBinding::VertexStage,
335 boneTexture,
336 boneSampler);
337 samplerBindingsSpecified.setBit(binding);
338 }
339 }
340
341 // Morphing
342 auto *targetsTexture = renderable.subset.rhi.targetsTexture;
343 if (targetsTexture) {
344 int binding = shaderPipeline->bindingForTexture("qt_morphTargetTexture");
345 if (binding >= 0) {
346 QRhiSampler *targetsSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
347 QRhiSampler::Nearest,
348 QRhiSampler::None,
349 QRhiSampler::ClampToEdge,
350 QRhiSampler::ClampToEdge,
351 QRhiSampler::ClampToEdge
352 });
353 bindings.addTexture(binding, QRhiShaderResourceBinding::VertexStage, renderable.subset.rhi.targetsTexture, targetsSampler);
354 samplerBindingsSpecified.setBit(binding);
355 }
356 }
357
358 // Prioritize reflection texture over Light Probe texture because
359 // reflection texture also contains the irradiance and pre filtered
360 // values for the light probe.
361 if (featureSet.isSet(QSSGShaderFeatures::Feature::ReflectionProbe)) {
362 int reflectionSampler = shaderPipeline->bindingForTexture("qt_reflectionMap");
363 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear,
364 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
365 QRhiTexture* reflectionTexture = layerData.getReflectionMapManager()->reflectionMapEntry(renderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
366 if (reflectionSampler >= 0 && reflectionTexture) {
367 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
368 samplerBindingsSpecified.setBit(reflectionSampler);
369 }
370 } else if (shaderPipeline->lightProbeTexture()) {
371 int binding = shaderPipeline->bindingForTexture("qt_lightProbe", int(QSSGRhiSamplerBindingHints::LightProbe));
372 if (binding >= 0) {
373 samplerBindingsSpecified.setBit(binding);
374 QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> tiling = shaderPipeline->lightProbeTiling();
375 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear, // enables mipmapping
376 QSSGRhiHelpers::toRhi(tiling.first), QSSGRhiHelpers::toRhi(tiling.second), QRhiSampler::Repeat });
377 bindings.addTexture(binding,
378 QRhiShaderResourceBinding::FragmentStage,
379 shaderPipeline->lightProbeTexture(), sampler);
380 } // else ignore, not an error (for example, an unshaded material's fragment shader will not have this sampler)
381 }
382
383 if (shaderPipeline->screenTexture()) {
384 const int screenTextureBinding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
385 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture("qt_screenTextureArray", int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
386 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
387 // linear min/mag, mipmap filtering depends on the
388 // texture, with SCREEN_TEXTURE there are no mipmaps, but
389 // once SCREEN_MIP_TEXTURE is seen the texture (the same
390 // one) has mipmaps generated.
391 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
392 ? QRhiSampler::Linear : QRhiSampler::None;
393 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, mipFilter,
394 QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
395 if (screenTextureBinding >= 0) {
396 samplerBindingsSpecified.setBit(screenTextureBinding);
397 bindings.addTexture(screenTextureBinding,
398 QRhiShaderResourceBinding::FragmentStage,
399 shaderPipeline->screenTexture(), sampler);
400 }
401 if (screenTextureArrayBinding >= 0) {
402 samplerBindingsSpecified.setBit(screenTextureArrayBinding);
403 bindings.addTexture(screenTextureArrayBinding,
404 QRhiShaderResourceBinding::FragmentStage,
405 shaderPipeline->screenTexture(), sampler);
406 }
407 } // else ignore, not an error
408 }
409
410 if (shaderPipeline->depthTexture()) {
411 const int depthTextureBinding = shaderPipeline->bindingForTexture("qt_depthTexture", int(QSSGRhiSamplerBindingHints::DepthTexture));
412 const int depthTextureArrayBinding = shaderPipeline->bindingForTexture("qt_depthTextureArray", int(QSSGRhiSamplerBindingHints::DepthTextureArray));
413 if (depthTextureBinding >= 0 || depthTextureArrayBinding >= 0) {
414 // nearest min/mag, no mipmap
415 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
416 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
417 if (depthTextureBinding >= 0) {
418 samplerBindingsSpecified.setBit(depthTextureBinding);
419 bindings.addTexture(depthTextureBinding,
420 CUSTOM_MATERIAL_VISIBILITY_ALL,
421 shaderPipeline->depthTexture(), sampler);
422 }
423 if (depthTextureArrayBinding >= 0) {
424 samplerBindingsSpecified.setBit(depthTextureArrayBinding);
425 bindings.addTexture(depthTextureArrayBinding,
426 CUSTOM_MATERIAL_VISIBILITY_ALL,
427 shaderPipeline->depthTexture(), sampler);
428 }
429 } // else ignore, not an error
430 }
431
432 if (shaderPipeline->normalTexture()) {
433 const int normalTextureBinding = shaderPipeline->bindingForTexture("qt_normalTexture", int(QSSGRhiSamplerBindingHints::NormalTexture));
434 if (normalTextureBinding >= 0) {
435 // nearest min/mag, no mipmap
436 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
437 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
438 samplerBindingsSpecified.setBit(normalTextureBinding);
439 bindings.addTexture(normalTextureBinding, CUSTOM_MATERIAL_VISIBILITY_ALL, shaderPipeline->normalTexture(), sampler);
440 } // else ignore, not an error
441 }
442
443 if (shaderPipeline->ssaoTexture()) {
444 const int ssaoTextureBinding = shaderPipeline->bindingForTexture("qt_aoTexture", int(QSSGRhiSamplerBindingHints::AoTexture));
445 const int ssaoTextureArrayBinding = shaderPipeline->bindingForTexture("qt_aoTextureArray", int(QSSGRhiSamplerBindingHints::AoTextureArray));
446 if (ssaoTextureBinding >= 0 || ssaoTextureArrayBinding >= 0) {
447 // linear min/mag, no mipmap
448 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
449 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
450 if (ssaoTextureBinding >= 0) {
451 samplerBindingsSpecified.setBit(ssaoTextureBinding);
452 bindings.addTexture(ssaoTextureBinding,
453 QRhiShaderResourceBinding::FragmentStage,
454 shaderPipeline->ssaoTexture(), sampler);
455 }
456 if (ssaoTextureArrayBinding >= 0) {
457 samplerBindingsSpecified.setBit(ssaoTextureArrayBinding);
458 bindings.addTexture(ssaoTextureArrayBinding,
459 QRhiShaderResourceBinding::FragmentStage,
460 shaderPipeline->ssaoTexture(), sampler);
461 }
462 } // else ignore, not an error
463 }
464
465 if (shaderPipeline->lightmapTexture()) {
466 int binding = shaderPipeline->bindingForTexture("qt_lightmap", int(QSSGRhiSamplerBindingHints::LightmapTexture));
467 if (binding >= 0) {
468 samplerBindingsSpecified.setBit(binding);
469 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
470 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
471 bindings.addTexture(binding,
472 QRhiShaderResourceBinding::FragmentStage,
473 shaderPipeline->lightmapTexture(), sampler);
474 } // else ignore, not an error
475 }
476
477 if (shaderPipeline->MotionVectorTexture()) {
478 int binding = shaderPipeline->bindingForTexture("qt_motionVectorTexture", int(QSSGRhiSamplerBindingHints::MotionVectorTexture));
479 if (binding >= 0) {
480 samplerBindingsSpecified.setBit(binding);
481 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
482 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
483 bindings.addTexture(binding,
484 QRhiShaderResourceBinding::FragmentStage,
485 shaderPipeline->MotionVectorTexture(), sampler);
486
487 }
488 }
489
490 // Shadow map atlas
491 auto shadowMapAtlas = shaderPipeline->shadowMapAtlasTexture();
492 if (shadowMapAtlas) {
493 int binding = shaderPipeline->bindingForTexture("qt_shadowmap_texture");
494 if (binding >= 0) {
495 samplerBindingsSpecified.setBit(binding);
496 QRhiTexture *texture = shadowMapAtlas;
497 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
498 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
499 Q_ASSERT(texture && sampler);
500 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
501 }
502 }
503
504 // Shadow map blue noise
505 if (auto shadowMapBlueNoise = shaderPipeline->shadowMapBlueNoiseTexture()) {
506 int binding = shaderPipeline->bindingForTexture("qt_shadowmap_blue_noise_texture");
507 if (binding >= 0) {
508 samplerBindingsSpecified.setBit(binding);
509 QRhiTexture *texture = shadowMapBlueNoise;
510 QRhiSampler *sampler = rhiCtx->sampler(
511 { QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
512 Q_ASSERT(texture && sampler);
513 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
514 }
515 }
516
517 QSSGRenderableImage *renderableImage = renderable.firstImage;
518 while (renderableImage) {
519 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
520 const int samplerHint = int(renderableImage->m_mapType);
521 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
522 if (samplerBinding >= 0) {
523 QRhiTexture *texture = renderableImage->m_texture.m_texture;
524 if (samplerBinding >= 0 && texture) {
525 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
526 QSSGRhiSamplerDescription samplerDesc = {
527 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
528 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
529 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
530 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
531 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
532 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
533 };
534 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
535 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
536 samplerBindingsSpecified.setBit(samplerBinding);
537 bindings.addTexture(samplerBinding,
538 CUSTOM_MATERIAL_VISIBILITY_ALL,
539 texture, sampler);
540 }
541 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
542 renderableImage = renderableImage->m_nextImage;
543 }
544
545 if (maxSamplerBinding >= 0) {
546 // custom property textures
547 int customTexCount = shaderPipeline->extraTextureCount();
548 for (int i = 0; i < customTexCount; ++i) {
549 QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
550 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
551 if (samplerBinding >= 0) {
552 samplerBindingsSpecified.setBit(samplerBinding);
553 rhiCtx->checkAndAdjustForNPoT(t.texture, &t.samplerDesc);
554 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
555 bindings.addTexture(samplerBinding,
556 CUSTOM_MATERIAL_VISIBILITY_ALL,
557 t.texture,
558 sampler);
559 }
560 }
561
562 // use a dummy texture for the unused samplers in the shader
563 QRhiSampler *dummySampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
564 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
565
566 for (const QShaderDescription::InOutVariable &var : samplerVars) {
567 if (!samplerBindingsSpecified.testBit(var.binding)) {
568 QRhiTexture *t = nullptr;
569 if (var.type == QShaderDescription::SamplerCube)
570 t = dummyCubeTexture;
571 else if (var.type == QShaderDescription::Sampler3D)
572 t = dummyTexture3D;
573 else
574 t = dummyTexture;
575 bindings.addTexture(var.binding, CUSTOM_MATERIAL_VISIBILITY_ALL, t, dummySampler);
576 }
577 }
578 }
579
580 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
581
582 if (oit && layerData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
583 RenderHelpers::addAccumulatorImageBindings(shaderPipeline.get(), bindings);
584
585 // do the same srb lookup acceleration as default materials
586 QRhiShaderResourceBindings *&srb = dcd.srb;
587 bool srbChanged = false;
588 if (!srb || bindings != dcd.bindings) {
589 srb = rhiCtxD->srb(bindings);
590 rhiCtxD->releaseCachedSrb(dcd.bindings);
591 dcd.bindings = bindings;
592 srbChanged = true;
593 }
594
595 if (cubeFace == QSSGRenderTextureCubeFaceNone)
596 renderable.rhiRenderData.mainPass.srb = srb;
597 else
598 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
599
600 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
601 if (dcd.pipeline
602 && !srbChanged
603 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
604 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
605 && dcd.ps == *ps)
606 {
607 if (cubeFace == QSSGRenderTextureCubeFaceNone)
608 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
609 else
610 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
611 } else {
612 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
613 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
614 renderPassDescriptor,
615 srb);
616 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
617 } else {
618 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
619 renderPassDescriptor,
620 srb);
621 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
622 }
623
624 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
625 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
626 dcd.ps = *ps;
627 }
628 }
629}
630
631void QSSGCustomMaterialSystem::setShaderResources(char *ubufData,
632 const QSSGRenderCustomMaterial &inMaterial,
633 const QByteArray &inPropertyName,
634 const QVariant &propertyValue,
635 QSSGRenderShaderValue::Type inPropertyType,
636 QSSGRhiShaderPipeline &shaderPipeline)
637{
638 Q_UNUSED(inMaterial);
639
640 if (inPropertyType == QSSGRenderShaderValue::Texture) {
641 QSSGRenderCustomMaterial::TextureProperty *textureProperty =
642 reinterpret_cast<QSSGRenderCustomMaterial::TextureProperty *>(propertyValue.value<void *>());
643 QSSGRenderImage *image = textureProperty->texImage;
644 if (image) {
645 const auto &theBufferManager(context->bufferManager());
646 const QSSGRenderImageTexture texture = theBufferManager->loadRenderImage(image);
647 if (texture.m_texture) {
648 const QSSGRhiTexture t = {
649 inPropertyName,
650 texture.m_texture,
651 { QSSGRhiHelpers::toRhi(textureProperty->minFilterType),
652 QSSGRhiHelpers::toRhi(textureProperty->magFilterType),
653 textureProperty->mipFilterType != QSSGRenderTextureFilterOp::None ? QSSGRhiHelpers::toRhi(textureProperty->mipFilterType) : QRhiSampler::None,
654 QSSGRhiHelpers::toRhi(textureProperty->horizontalClampType),
655 QSSGRhiHelpers::toRhi(textureProperty->verticalClampType),
656 QSSGRhiHelpers::toRhi(textureProperty->zClampType)
657 }
658 };
659 shaderPipeline.addExtraTexture(t);
660 }
661 }
662 } else {
663 shaderPipeline.setUniformValue(ubufData, inPropertyName, propertyValue, inPropertyType);
664 }
665}
666
667void QSSGCustomMaterialSystem::applyRhiShaderPropertyValues(char *ubufData,
668 const QSSGRenderCustomMaterial &material,
669 QSSGRhiShaderPipeline &shaderPipeline)
670{
671 const auto &properties = material.m_properties;
672 for (const auto &prop : properties)
673 setShaderResources(ubufData, material, prop.name, prop.value, prop.shaderDataType, shaderPipeline);
674
675 const auto textProps = material.m_textureProperties;
676 for (const auto &prop : textProps)
677 setShaderResources(ubufData, material, prop.name, QVariant::fromValue((void *)&prop), prop.shaderDataType, shaderPipeline);
678}
679
680void QSSGCustomMaterialSystem::rhiRenderRenderable(QSSGRhiContext *rhiCtx,
681 QSSGSubsetRenderable &renderable,
682 bool *needsSetViewport,
683 QSSGRenderTextureCubeFace cubeFace,
684 const QSSGRhiGraphicsPipelineState &state,
685 qsizetype userPassIndex)
686{
687 QRhiGraphicsPipeline *ps = (userPassIndex >= 0) ? renderable.rhiRenderData.userPassData[userPassIndex].pipeline : renderable.rhiRenderData.mainPass.pipeline;
688 QRhiShaderResourceBindings *srb = (userPassIndex >= 0) ? renderable.rhiRenderData.userPassData[userPassIndex].srb : renderable.rhiRenderData.mainPass.srb;
689
690 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
691 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
692 ps = renderable.rhiRenderData.reflectionPass.pipeline;
693 srb = renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx];
694 }
695
696 if (!ps || !srb)
697 return;
698
699 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
700 QRhiBuffer *vertexBuffer = renderable.subset.rhi.vertexBuffer->buffer();
701 QRhiBuffer *indexBuffer = renderable.subset.rhi.indexBuffer ? renderable.subset.rhi.indexBuffer->buffer() : nullptr;
702
703 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
704 cb->setGraphicsPipeline(ps);
705 cb->setShaderResources(srb);
706
707 if (*needsSetViewport) {
708 cb->setViewport(state.viewport);
709 *needsSetViewport = false;
710 }
711
712 QRhiCommandBuffer::VertexInput vertexBuffers[2];
713 int vertexBufferCount = 1;
714 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
715 quint32 instances = 1;
716 if (renderable.modelContext.model.instancing()) {
717 instances = renderable.modelContext.model.instanceCount();
718 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(renderable.instanceBuffer, 0);
719 vertexBufferCount = 2;
720 }
721 if (indexBuffer) {
722 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, renderable.subset.rhi.indexBuffer->indexFormat());
723 cb->drawIndexed(renderable.subset.lodCount(renderable.subsetLevelOfDetail), instances, renderable.subset.lodOffset(renderable.subsetLevelOfDetail));
724 QSSGRHICTX_STAT(rhiCtx, drawIndexed(renderable.subset.lodCount(renderable.subsetLevelOfDetail), instances));
725 } else {
726 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
727 cb->draw(renderable.subset.count, instances, renderable.subset.offset);
728 QSSGRHICTX_STAT(rhiCtx, draw(renderable.subset.count, instances));
729 }
730 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (renderable.subset.count | quint64(instances) << 32),
731 QVector<int>({renderable.modelContext.model.profilingId,
732 renderable.material.profilingId}));
733}
734
735QT_END_NAMESPACE
Combined button and popup list for selecting options.
static const QRhiShaderResourceBinding::StageFlags CUSTOM_MATERIAL_VISIBILITY_ALL