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 QRhiTexture* reflectionTexture = layerData.getReflectionMapManager()->reflectionMapEntry(renderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
364 const auto mipMapFilter = reflectionTexture && reflectionTexture->flags().testFlag(QRhiTexture::Flag::MipMapped)
365 ? QRhiSampler::Linear
366 : QRhiSampler::None;
367 QRhiSampler *sampler = rhiCtx->sampler(
368 { QRhiSampler::Linear, QRhiSampler::Linear, mipMapFilter, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
369 if (reflectionSampler >= 0 && reflectionTexture) {
370 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
371 samplerBindingsSpecified.setBit(reflectionSampler);
372 }
373 } else if (shaderPipeline->lightProbeTexture()) {
374 int binding = shaderPipeline->bindingForTexture("qt_lightProbe", int(QSSGRhiSamplerBindingHints::LightProbe));
375 if (binding >= 0) {
376 samplerBindingsSpecified.setBit(binding);
377 QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> tiling = shaderPipeline->lightProbeTiling();
378 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear, // enables mipmapping
379 QSSGRhiHelpers::toRhi(tiling.first), QSSGRhiHelpers::toRhi(tiling.second), QRhiSampler::Repeat });
380 bindings.addTexture(binding,
381 QRhiShaderResourceBinding::FragmentStage,
382 shaderPipeline->lightProbeTexture(), sampler);
383 } // else ignore, not an error (for example, an unshaded material's fragment shader will not have this sampler)
384 }
385
386 if (shaderPipeline->screenTexture()) {
387 const int screenTextureBinding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
388 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture("qt_screenTextureArray", int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
389 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
390 // linear min/mag, mipmap filtering depends on the
391 // texture, with SCREEN_TEXTURE there are no mipmaps, but
392 // once SCREEN_MIP_TEXTURE is seen the texture (the same
393 // one) has mipmaps generated.
394 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
395 ? QRhiSampler::Linear : QRhiSampler::None;
396 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, mipFilter,
397 QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
398 if (screenTextureBinding >= 0) {
399 samplerBindingsSpecified.setBit(screenTextureBinding);
400 bindings.addTexture(screenTextureBinding,
401 QRhiShaderResourceBinding::FragmentStage,
402 shaderPipeline->screenTexture(), sampler);
403 }
404 if (screenTextureArrayBinding >= 0) {
405 samplerBindingsSpecified.setBit(screenTextureArrayBinding);
406 bindings.addTexture(screenTextureArrayBinding,
407 QRhiShaderResourceBinding::FragmentStage,
408 shaderPipeline->screenTexture(), sampler);
409 }
410 } // else ignore, not an error
411 }
412
413 if (shaderPipeline->depthTexture()) {
414 const int depthTextureBinding = shaderPipeline->bindingForTexture("qt_depthTexture", int(QSSGRhiSamplerBindingHints::DepthTexture));
415 const int depthTextureArrayBinding = shaderPipeline->bindingForTexture("qt_depthTextureArray", int(QSSGRhiSamplerBindingHints::DepthTextureArray));
416 if (depthTextureBinding >= 0 || depthTextureArrayBinding >= 0) {
417 // nearest min/mag, no mipmap
418 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
419 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
420 if (depthTextureBinding >= 0) {
421 samplerBindingsSpecified.setBit(depthTextureBinding);
422 bindings.addTexture(depthTextureBinding,
423 CUSTOM_MATERIAL_VISIBILITY_ALL,
424 shaderPipeline->depthTexture(), sampler);
425 }
426 if (depthTextureArrayBinding >= 0) {
427 samplerBindingsSpecified.setBit(depthTextureArrayBinding);
428 bindings.addTexture(depthTextureArrayBinding,
429 CUSTOM_MATERIAL_VISIBILITY_ALL,
430 shaderPipeline->depthTexture(), sampler);
431 }
432 } // else ignore, not an error
433 }
434
435 if (shaderPipeline->normalTexture()) {
436 const int normalTextureBinding = shaderPipeline->bindingForTexture("qt_normalTexture", int(QSSGRhiSamplerBindingHints::NormalTexture));
437 if (normalTextureBinding >= 0) {
438 // nearest min/mag, no mipmap
439 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
440 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
441 samplerBindingsSpecified.setBit(normalTextureBinding);
442 bindings.addTexture(normalTextureBinding, CUSTOM_MATERIAL_VISIBILITY_ALL, shaderPipeline->normalTexture(), sampler);
443 } // else ignore, not an error
444 }
445
446 if (shaderPipeline->ssaoTexture()) {
447 const int ssaoTextureBinding = shaderPipeline->bindingForTexture("qt_aoTexture", int(QSSGRhiSamplerBindingHints::AoTexture));
448 const int ssaoTextureArrayBinding = shaderPipeline->bindingForTexture("qt_aoTextureArray", int(QSSGRhiSamplerBindingHints::AoTextureArray));
449 if (ssaoTextureBinding >= 0 || ssaoTextureArrayBinding >= 0) {
450 // linear min/mag, no mipmap
451 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
452 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
453 if (ssaoTextureBinding >= 0) {
454 samplerBindingsSpecified.setBit(ssaoTextureBinding);
455 bindings.addTexture(ssaoTextureBinding,
456 QRhiShaderResourceBinding::FragmentStage,
457 shaderPipeline->ssaoTexture(), sampler);
458 }
459 if (ssaoTextureArrayBinding >= 0) {
460 samplerBindingsSpecified.setBit(ssaoTextureArrayBinding);
461 bindings.addTexture(ssaoTextureArrayBinding,
462 QRhiShaderResourceBinding::FragmentStage,
463 shaderPipeline->ssaoTexture(), sampler);
464 }
465 } // else ignore, not an error
466 }
467
468 if (shaderPipeline->lightmapTexture()) {
469 int binding = shaderPipeline->bindingForTexture("qt_lightmap", int(QSSGRhiSamplerBindingHints::LightmapTexture));
470 if (binding >= 0) {
471 samplerBindingsSpecified.setBit(binding);
472 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
473 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
474 bindings.addTexture(binding,
475 QRhiShaderResourceBinding::FragmentStage,
476 shaderPipeline->lightmapTexture(), sampler);
477 } // else ignore, not an error
478 }
479
480 if (shaderPipeline->MotionVectorTexture()) {
481 int binding = shaderPipeline->bindingForTexture("qt_motionVectorTexture", int(QSSGRhiSamplerBindingHints::MotionVectorTexture));
482 if (binding >= 0) {
483 samplerBindingsSpecified.setBit(binding);
484 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
485 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
486 bindings.addTexture(binding,
487 QRhiShaderResourceBinding::FragmentStage,
488 shaderPipeline->MotionVectorTexture(), sampler);
489
490 }
491 }
492
493 // Shadow map atlas
494 auto shadowMapAtlas = shaderPipeline->shadowMapAtlasTexture();
495 if (shadowMapAtlas) {
496 int binding = shaderPipeline->bindingForTexture("qt_shadowmap_texture");
497 if (binding >= 0) {
498 samplerBindingsSpecified.setBit(binding);
499 QRhiTexture *texture = shadowMapAtlas;
500 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
501 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
502 Q_ASSERT(texture && sampler);
503 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
504 }
505 }
506
507 // Shadow map blue noise
508 if (auto shadowMapBlueNoise = shaderPipeline->shadowMapBlueNoiseTexture()) {
509 int binding = shaderPipeline->bindingForTexture("qt_shadowmap_blue_noise_texture");
510 if (binding >= 0) {
511 samplerBindingsSpecified.setBit(binding);
512 QRhiTexture *texture = shadowMapBlueNoise;
513 QRhiSampler *sampler = rhiCtx->sampler(
514 { QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
515 Q_ASSERT(texture && sampler);
516 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
517 }
518 }
519
520 QSSGRenderableImage *renderableImage = renderable.firstImage;
521 while (renderableImage) {
522 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
523 const int samplerHint = int(renderableImage->m_mapType);
524 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
525 if (samplerBinding >= 0) {
526 QRhiTexture *texture = renderableImage->m_texture.m_texture;
527 if (samplerBinding >= 0 && texture) {
528 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
529 QSSGRhiSamplerDescription samplerDesc = {
530 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
531 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
532 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
533 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
534 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
535 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
536 };
537 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
538 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
539 samplerBindingsSpecified.setBit(samplerBinding);
540 bindings.addTexture(samplerBinding,
541 CUSTOM_MATERIAL_VISIBILITY_ALL,
542 texture, sampler);
543 }
544 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
545 renderableImage = renderableImage->m_nextImage;
546 }
547
548 if (maxSamplerBinding >= 0) {
549 // custom property textures
550 int customTexCount = shaderPipeline->extraTextureCount();
551 for (int i = 0; i < customTexCount; ++i) {
552 QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
553 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
554 if (samplerBinding >= 0) {
555 samplerBindingsSpecified.setBit(samplerBinding);
556 rhiCtx->checkAndAdjustForNPoT(t.texture, &t.samplerDesc);
557 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
558 bindings.addTexture(samplerBinding,
559 CUSTOM_MATERIAL_VISIBILITY_ALL,
560 t.texture,
561 sampler);
562 }
563 }
564
565 // use a dummy texture for the unused samplers in the shader
566 QRhiSampler *dummySampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
567 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
568
569 for (const QShaderDescription::InOutVariable &var : samplerVars) {
570 if (!samplerBindingsSpecified.testBit(var.binding)) {
571 QRhiTexture *t = nullptr;
572 if (var.type == QShaderDescription::SamplerCube)
573 t = dummyCubeTexture;
574 else if (var.type == QShaderDescription::Sampler3D)
575 t = dummyTexture3D;
576 else
577 t = dummyTexture;
578 bindings.addTexture(var.binding, CUSTOM_MATERIAL_VISIBILITY_ALL, t, dummySampler);
579 }
580 }
581 }
582
583 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
584
585 if (oit && layerData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
586 RenderHelpers::addAccumulatorImageBindings(shaderPipeline.get(), bindings);
587
588 // do the same srb lookup acceleration as default materials
589 QRhiShaderResourceBindings *&srb = dcd.srb;
590 bool srbChanged = false;
591 if (!srb || bindings != dcd.bindings) {
592 srb = rhiCtxD->srb(bindings);
593 rhiCtxD->releaseCachedSrb(dcd.bindings);
594 dcd.bindings = bindings;
595 srbChanged = true;
596 }
597
598 if (cubeFace == QSSGRenderTextureCubeFaceNone)
599 renderable.rhiRenderData.mainPass.srb = srb;
600 else
601 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
602
603 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
604 if (dcd.pipeline
605 && !srbChanged
606 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
607 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
608 && dcd.ps == *ps)
609 {
610 if (cubeFace == QSSGRenderTextureCubeFaceNone)
611 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
612 else
613 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
614 } else {
615 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
616 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
617 renderPassDescriptor,
618 srb);
619 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
620 } else {
621 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
622 renderPassDescriptor,
623 srb);
624 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
625 }
626
627 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
628 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
629 dcd.ps = *ps;
630 }
631 }
632}
633
634void QSSGCustomMaterialSystem::setShaderResources(char *ubufData,
635 const QSSGRenderCustomMaterial &inMaterial,
636 const QByteArray &inPropertyName,
637 const QVariant &propertyValue,
638 QSSGRenderShaderValue::Type inPropertyType,
639 QSSGRhiShaderPipeline &shaderPipeline)
640{
641 Q_UNUSED(inMaterial);
642
643 if (inPropertyType == QSSGRenderShaderValue::Texture) {
644 QSSGRenderCustomMaterial::TextureProperty *textureProperty =
645 reinterpret_cast<QSSGRenderCustomMaterial::TextureProperty *>(propertyValue.value<void *>());
646 QSSGRenderImage *image = textureProperty->texImage;
647 if (image) {
648 const auto &theBufferManager(context->bufferManager());
649 const QSSGRenderImageTexture texture = theBufferManager->loadRenderImage(image);
650 if (texture.m_texture) {
651 const QSSGRhiTexture t = {
652 inPropertyName,
653 texture.m_texture,
654 { QSSGRhiHelpers::toRhi(textureProperty->minFilterType),
655 QSSGRhiHelpers::toRhi(textureProperty->magFilterType),
656 textureProperty->mipFilterType != QSSGRenderTextureFilterOp::None ? QSSGRhiHelpers::toRhi(textureProperty->mipFilterType) : QRhiSampler::None,
657 QSSGRhiHelpers::toRhi(textureProperty->horizontalClampType),
658 QSSGRhiHelpers::toRhi(textureProperty->verticalClampType),
659 QSSGRhiHelpers::toRhi(textureProperty->zClampType)
660 }
661 };
662 shaderPipeline.addExtraTexture(t);
663 }
664 }
665 } else {
666 shaderPipeline.setUniformValue(ubufData, inPropertyName, propertyValue, inPropertyType);
667 }
668}
669
670void QSSGCustomMaterialSystem::applyRhiShaderPropertyValues(char *ubufData,
671 const QSSGRenderCustomMaterial &material,
672 QSSGRhiShaderPipeline &shaderPipeline)
673{
674 const auto &properties = material.m_properties;
675 for (const auto &prop : properties)
676 setShaderResources(ubufData, material, prop.name, prop.value, prop.shaderDataType, shaderPipeline);
677
678 const auto textProps = material.m_textureProperties;
679 for (const auto &prop : textProps)
680 setShaderResources(ubufData, material, prop.name, QVariant::fromValue((void *)&prop), prop.shaderDataType, shaderPipeline);
681}
682
683void QSSGCustomMaterialSystem::rhiRenderRenderable(QSSGRhiContext *rhiCtx,
684 QSSGSubsetRenderable &renderable,
685 bool *needsSetViewport,
686 QSSGRenderTextureCubeFace cubeFace,
687 const QSSGRhiGraphicsPipelineState &state,
688 qsizetype userPassIndex)
689{
690 QRhiGraphicsPipeline *ps = (userPassIndex >= 0) ? renderable.rhiRenderData.userPassData[userPassIndex].pipeline : renderable.rhiRenderData.mainPass.pipeline;
691 QRhiShaderResourceBindings *srb = (userPassIndex >= 0) ? renderable.rhiRenderData.userPassData[userPassIndex].srb : renderable.rhiRenderData.mainPass.srb;
692
693 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
694 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
695 ps = renderable.rhiRenderData.reflectionPass.pipeline;
696 srb = renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx];
697 }
698
699 if (!ps || !srb)
700 return;
701
702 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
703 QRhiBuffer *vertexBuffer = renderable.subset.rhi.vertexBuffer->buffer();
704 QRhiBuffer *indexBuffer = renderable.subset.rhi.indexBuffer ? renderable.subset.rhi.indexBuffer->buffer() : nullptr;
705
706 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
707 cb->setGraphicsPipeline(ps);
708 cb->setShaderResources(srb);
709
710 if (*needsSetViewport) {
711 cb->setViewport(state.viewport);
712 *needsSetViewport = false;
713 }
714
715 QRhiCommandBuffer::VertexInput vertexBuffers[2];
716 int vertexBufferCount = 1;
717 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
718 quint32 instances = 1;
719 if (renderable.modelContext.model.instancing()) {
720 instances = renderable.modelContext.model.instanceCount();
721 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(renderable.instanceBuffer, 0);
722 vertexBufferCount = 2;
723 }
724 if (indexBuffer) {
725 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, renderable.subset.rhi.indexBuffer->indexFormat());
726 cb->drawIndexed(renderable.subset.lodCount(renderable.subsetLevelOfDetail), instances, renderable.subset.lodOffset(renderable.subsetLevelOfDetail));
727 QSSGRHICTX_STAT(rhiCtx, drawIndexed(renderable.subset.lodCount(renderable.subsetLevelOfDetail), instances));
728 } else {
729 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
730 cb->draw(renderable.subset.count, instances, renderable.subset.offset);
731 QSSGRHICTX_STAT(rhiCtx, draw(renderable.subset.count, instances));
732 }
733 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (renderable.subset.count | quint64(instances) << 32),
734 QVector<int>({renderable.modelContext.model.profilingId,
735 renderable.material.profilingId}));
736}
737
738QT_END_NAMESPACE
Combined button and popup list for selecting options.
static const QRhiShaderResourceBinding::StageFlags CUSTOM_MATERIAL_VISIBILITY_ALL