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 bool screenMapPass,
195 QSSGRenderCamera *alteredCamera,
196 QSSGRenderTextureCubeFace cubeFace,
197 QMatrix4x4 *alteredModelViewProjection,
198 QSSGReflectionMapEntry *entry,
199 bool oit)
200{
201 QSSGRhiContext *rhiCtx = context->rhiContext().get();
202
203 QRhiGraphicsPipeline::TargetBlend blend; // no blending by default
204 if (material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Blending)) {
205 blend.enable = true;
206 blend.srcColor = material.m_srcBlend;
207 blend.srcAlpha = material.m_srcAlphaBlend;
208 blend.dstColor = material.m_dstBlend;
209 blend.dstAlpha = material.m_dstAlphaBlend;
210 }
211
212 const QSSGCullFaceMode cullMode = material.m_cullMode;
213
214 const auto &defaultMaterialShaderKeyProperties = layerData.getDefaultMaterialPropertyTable();
215
216 const bool blendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(renderable.shaderDescription);
217
218 const auto &shaderPipeline = shadersForCustomMaterial(ps, material, renderable, defaultMaterialShaderKeyProperties, featureSet);
219
220 if (shaderPipeline) {
221 QSSGRhiShaderResourceBindingList bindings;
222 const auto &modelNode = renderable.modelContext.model;
223
224 // NOTE:
225 // - entryIdx should 0 for QSSGRenderTextureCubeFaceNone.
226 // In all other cases the entryIdx is a combination of the cubeface idx and the subset offset, where the lower bits
227 // are the cubeface idx.
228 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
229 const quintptr entryIdx = quintptr(cubeFace != QSSGRenderTextureCubeFaceNone) * (cubeFaceIdx + (quintptr(renderable.subset.offset) << 3));
230 // As the entry might be null we create an entry key consisting of the entry and the material.
231 const auto entryPartA = reinterpret_cast<quintptr>(&material);
232 const auto entryPartB = reinterpret_cast<quintptr>(entry);
233 const void *entryKey = reinterpret_cast<const void *>(entryPartA ^ entryPartB);
234
235 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiCtx)->drawCallData({ passKey, &modelNode, entryKey, entryIdx });
236
237 shaderPipeline->ensureCombinedUniformBuffer(&dcd.ubuf);
238 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
239 if (!alteredCamera) {
240 updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, layerData, ubufData, ps, material, renderable, layerData.renderedCameras, nullptr, nullptr);
241 } else {
242 QSSGRenderCameraList cameras({ alteredCamera });
243 updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, layerData, ubufData, ps, material, renderable, cameras, nullptr, alteredModelViewProjection);
244 }
245 if (blendParticles)
246 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &renderable.modelContext.model, renderable.subset.offset);
247 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
248
249 if (blendParticles)
250 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &renderable.modelContext.model);
251 bool instancing = false;
252 if (!alteredCamera) {
253 const QSSGRenderCameraDataList &cameraDatas(*layerData.renderedCameraData);
254 instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, &renderable, cameraDatas[0].direction, cameraDatas[0].position, renderable.instancingLodMin, renderable.instancingLodMax);
255 } else {
256 const auto &altCamTransform = layerData.getGlobalTransform(*alteredCamera);
257 instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, &renderable, QSSGRenderNode::getScalingCorrectDirection(altCamTransform), QSSGRenderNode::getGlobalPos(altCamTransform), renderable.instancingLodMin, renderable.instancingLodMax);
258 }
259
260 ps->samples = samples;
261 ps->viewCount = viewCount;
262
263 ps->cullMode = QSSGRhiHelpers::toCullMode(cullMode);
264
265 if (!oit)
266 ps->targetBlend[0] = blend;
267
268 auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
269
270 ia = renderable.subset.rhi.ia;
271
272 //### Copied code from default materials
273 int instanceBufferBinding = 0;
274 if (instancing) {
275 // Need to setup new bindings for instanced buffers
276 const quint32 stride = renderable.modelContext.model.instanceTable->stride();
277 QVarLengthArray<QRhiVertexInputBinding, 8> bindings;
278 std::copy(ia.inputLayout.cbeginBindings(),
279 ia.inputLayout.cendBindings(),
280 std::back_inserter(bindings));
281 bindings.append({ stride, QRhiVertexInputBinding::PerInstance });
282 instanceBufferBinding = bindings.size() - 1;
283 ia.inputLayout.setBindings(bindings.cbegin(), bindings.cend());
284 }
285
286 QSSGRhiHelpers::bakeVertexInputLocations(&ia, *shaderPipeline, instanceBufferBinding);
287
288 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
289 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
290 QRhiTexture *dummyTexture3D = rhiCtx->dummyTexture(QRhiTexture::ThreeDimensional, resourceUpdates);
291 QRhiTexture *dummyCubeTexture = rhiCtx->dummyTexture(QRhiTexture::CubeMap, resourceUpdates);
292 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
293
294 bindings.addUniformBuffer(0, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline->ub0Size());
295 bindings.addUniformBuffer(1, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, shaderPipeline->ub0LightDataOffset(), sizeof(QSSGShaderLightsUniformData));
296 bindings.addUniformBuffer(2, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, shaderPipeline->ub0DirectionalLightDataOffset(), sizeof(QSSGShaderDirectionalLightsUniformData));
297
298 QVector<QShaderDescription::InOutVariable> samplerVars =
299 shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
300 for (const QShaderDescription::InOutVariable &var : shaderPipeline->vertexStage()->shader().description().combinedImageSamplers()) {
301 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(),
302 [&var](const QShaderDescription::InOutVariable &v) { return var.binding == v.binding; });
303 if (it == samplerVars.cend())
304 samplerVars.append(var);
305 }
306
307 int maxSamplerBinding = -1;
308 for (const QShaderDescription::InOutVariable &var : samplerVars)
309 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
310
311 // Will need to set unused image-samplers to something dummy
312 // because the shader code contains all custom property textures,
313 // and not providing a binding for all of them is invalid with some
314 // graphics APIs (and will need a real texture because setting a
315 // null handle or similar is not permitted with some of them so the
316 // srb does not accept null QRhiTextures either; but first let's
317 // figure out what bindings are unused in this frame)
318 QBitArray samplerBindingsSpecified(maxSamplerBinding + 1);
319
320 if (blendParticles)
321 samplerBindingsSpecified.setBit(shaderPipeline->bindingForTexture("qt_particleTexture"));
322
323 // Skinning
324 if (QRhiTexture *boneTexture = layerData.getBonemapTexture(renderable.modelContext)) {
325 int binding = shaderPipeline->bindingForTexture("qt_boneTexture");
326 if (binding >= 0) {
327 QRhiSampler *boneSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
328 QRhiSampler::Nearest,
329 QRhiSampler::None,
330 QRhiSampler::ClampToEdge,
331 QRhiSampler::ClampToEdge,
332 QRhiSampler::Repeat
333 });
334 bindings.addTexture(binding,
335 QRhiShaderResourceBinding::VertexStage,
336 boneTexture,
337 boneSampler);
338 samplerBindingsSpecified.setBit(binding);
339 }
340 }
341
342 // Morphing
343 auto *targetsTexture = renderable.subset.rhi.targetsTexture;
344 if (targetsTexture) {
345 int binding = shaderPipeline->bindingForTexture("qt_morphTargetTexture");
346 if (binding >= 0) {
347 QRhiSampler *targetsSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
348 QRhiSampler::Nearest,
349 QRhiSampler::None,
350 QRhiSampler::ClampToEdge,
351 QRhiSampler::ClampToEdge,
352 QRhiSampler::ClampToEdge
353 });
354 bindings.addTexture(binding, QRhiShaderResourceBinding::VertexStage, renderable.subset.rhi.targetsTexture, targetsSampler);
355 samplerBindingsSpecified.setBit(binding);
356 }
357 }
358
359 // Prioritize reflection texture over Light Probe texture because
360 // reflection texture also contains the irradiance and pre filtered
361 // values for the light probe.
362 if (featureSet.isSet(QSSGShaderFeatures::Feature::ReflectionProbe)) {
363 int reflectionSampler = shaderPipeline->bindingForTexture("qt_reflectionMap");
364 QRhiTexture* reflectionTexture = layerData.getReflectionMapManager()->reflectionMapEntry(renderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
365 const auto mipMapFilter = reflectionTexture && reflectionTexture->flags().testFlag(QRhiTexture::Flag::MipMapped)
366 ? QRhiSampler::Linear
367 : QRhiSampler::None;
368 QRhiSampler *sampler = rhiCtx->sampler(
369 { QRhiSampler::Linear, QRhiSampler::Linear, mipMapFilter, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
370 if (reflectionSampler >= 0 && reflectionTexture) {
371 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
372 samplerBindingsSpecified.setBit(reflectionSampler);
373 }
374 } else if (shaderPipeline->lightProbeTexture()) {
375 int binding = shaderPipeline->bindingForTexture("qt_lightProbe", int(QSSGRhiSamplerBindingHints::LightProbe));
376 if (binding >= 0) {
377 samplerBindingsSpecified.setBit(binding);
378 QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> tiling = shaderPipeline->lightProbeTiling();
379 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear, // enables mipmapping
380 QSSGRhiHelpers::toRhi(tiling.first), QSSGRhiHelpers::toRhi(tiling.second), QRhiSampler::Repeat });
381 bindings.addTexture(binding,
382 QRhiShaderResourceBinding::FragmentStage,
383 shaderPipeline->lightProbeTexture(), sampler);
384 } // else ignore, not an error (for example, an unshaded material's fragment shader will not have this sampler)
385 }
386
387 if (shaderPipeline->screenTexture()) {
388 const int screenTextureBinding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
389 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture("qt_screenTextureArray", int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
390 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
391 QRhiTexture *screenTexture = shaderPipeline->screenTexture();
392 // If we're called as part of the screen map pass there's obviously no screen texture available, but the shader may still expect it, so bind a dummy texture.
393 if (screenMapPass) {
394 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
395 // Just mirror what is set up by the ScreenMap pass.
396 const QRhiTexture::Flags flags = screenTexture->flags();
397 // and set the dummy texture so there's something for the shader to sample.
398 screenTexture = rhiCtx->dummyTexture(flags, resourceUpdates);
399 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
400 }
401 // linear min/mag, mipmap filtering depends on the
402 // texture, with SCREEN_TEXTURE there are no mipmaps, but
403 // once SCREEN_MIP_TEXTURE is seen the texture (the same
404 // one) has mipmaps generated.
405 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
406 ? QRhiSampler::Linear : QRhiSampler::None;
407 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, mipFilter,
408 QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
409 if (screenTextureBinding >= 0) {
410 samplerBindingsSpecified.setBit(screenTextureBinding);
411 bindings.addTexture(screenTextureBinding,
412 QRhiShaderResourceBinding::FragmentStage,
413 screenTexture, sampler);
414 }
415 if (screenTextureArrayBinding >= 0) {
416 samplerBindingsSpecified.setBit(screenTextureArrayBinding);
417 bindings.addTexture(screenTextureArrayBinding,
418 QRhiShaderResourceBinding::FragmentStage,
419 screenTexture, sampler);
420 }
421 } // else ignore, not an error
422 }
423
424 if (shaderPipeline->depthTexture()) {
425 const int depthTextureBinding = shaderPipeline->bindingForTexture("qt_depthTexture", int(QSSGRhiSamplerBindingHints::DepthTexture));
426 const int depthTextureArrayBinding = shaderPipeline->bindingForTexture("qt_depthTextureArray", int(QSSGRhiSamplerBindingHints::DepthTextureArray));
427 if (depthTextureBinding >= 0 || depthTextureArrayBinding >= 0) {
428 // nearest min/mag, no mipmap
429 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
430 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
431 if (depthTextureBinding >= 0) {
432 samplerBindingsSpecified.setBit(depthTextureBinding);
433 bindings.addTexture(depthTextureBinding,
434 CUSTOM_MATERIAL_VISIBILITY_ALL,
435 shaderPipeline->depthTexture(), sampler);
436 }
437 if (depthTextureArrayBinding >= 0) {
438 samplerBindingsSpecified.setBit(depthTextureArrayBinding);
439 bindings.addTexture(depthTextureArrayBinding,
440 CUSTOM_MATERIAL_VISIBILITY_ALL,
441 shaderPipeline->depthTexture(), sampler);
442 }
443 } // else ignore, not an error
444 }
445
446 if (shaderPipeline->normalTexture()) {
447 const int normalTextureBinding = shaderPipeline->bindingForTexture("qt_normalTexture", int(QSSGRhiSamplerBindingHints::NormalTexture));
448 if (normalTextureBinding >= 0) {
449 // nearest min/mag, no mipmap
450 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
451 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
452 samplerBindingsSpecified.setBit(normalTextureBinding);
453 bindings.addTexture(normalTextureBinding, CUSTOM_MATERIAL_VISIBILITY_ALL, shaderPipeline->normalTexture(), sampler);
454 } // else ignore, not an error
455 }
456
457 if (shaderPipeline->ssaoTexture()) {
458 const int ssaoTextureBinding = shaderPipeline->bindingForTexture("qt_aoTexture", int(QSSGRhiSamplerBindingHints::AoTexture));
459 const int ssaoTextureArrayBinding = shaderPipeline->bindingForTexture("qt_aoTextureArray", int(QSSGRhiSamplerBindingHints::AoTextureArray));
460 if (ssaoTextureBinding >= 0 || ssaoTextureArrayBinding >= 0) {
461 // linear min/mag, no mipmap
462 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
463 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
464 if (ssaoTextureBinding >= 0) {
465 samplerBindingsSpecified.setBit(ssaoTextureBinding);
466 bindings.addTexture(ssaoTextureBinding,
467 QRhiShaderResourceBinding::FragmentStage,
468 shaderPipeline->ssaoTexture(), sampler);
469 }
470 if (ssaoTextureArrayBinding >= 0) {
471 samplerBindingsSpecified.setBit(ssaoTextureArrayBinding);
472 bindings.addTexture(ssaoTextureArrayBinding,
473 QRhiShaderResourceBinding::FragmentStage,
474 shaderPipeline->ssaoTexture(), sampler);
475 }
476 } // else ignore, not an error
477 }
478
479 if (shaderPipeline->lightmapTexture()) {
480 int binding = shaderPipeline->bindingForTexture("qt_lightmap", int(QSSGRhiSamplerBindingHints::LightmapTexture));
481 if (binding >= 0) {
482 samplerBindingsSpecified.setBit(binding);
483 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
484 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
485 bindings.addTexture(binding,
486 QRhiShaderResourceBinding::FragmentStage,
487 shaderPipeline->lightmapTexture(), sampler);
488 } // else ignore, not an error
489 }
490
491 if (shaderPipeline->MotionVectorTexture()) {
492 int binding = shaderPipeline->bindingForTexture("qt_motionVectorTexture", int(QSSGRhiSamplerBindingHints::MotionVectorTexture));
493 if (binding >= 0) {
494 samplerBindingsSpecified.setBit(binding);
495 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
496 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
497 bindings.addTexture(binding,
498 QRhiShaderResourceBinding::FragmentStage,
499 shaderPipeline->MotionVectorTexture(), sampler);
500
501 }
502 }
503
504 // Shadow map atlas
505 auto shadowMapAtlas = shaderPipeline->shadowMapAtlasTexture();
506 if (shadowMapAtlas) {
507 int binding = shaderPipeline->bindingForTexture("qt_shadowmap_texture");
508 if (binding >= 0) {
509 samplerBindingsSpecified.setBit(binding);
510 QRhiTexture *texture = shadowMapAtlas;
511 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
512 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
513 Q_ASSERT(texture && sampler);
514 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
515 }
516 }
517
518 // Shadow map blue noise
519 if (auto shadowMapBlueNoise = shaderPipeline->shadowMapBlueNoiseTexture()) {
520 int binding = shaderPipeline->bindingForTexture("qt_shadowmap_blue_noise_texture");
521 if (binding >= 0) {
522 samplerBindingsSpecified.setBit(binding);
523 QRhiTexture *texture = shadowMapBlueNoise;
524 QRhiSampler *sampler = rhiCtx->sampler(
525 { QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
526 Q_ASSERT(texture && sampler);
527 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
528 }
529 }
530
531 QSSGRenderableImage *renderableImage = renderable.firstImage;
532 while (renderableImage) {
533 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
534 const int samplerHint = int(renderableImage->m_mapType);
535 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
536 if (samplerBinding >= 0) {
537 QRhiTexture *texture = renderableImage->m_texture.m_texture;
538 if (samplerBinding >= 0 && texture) {
539 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
540 QSSGRhiSamplerDescription samplerDesc = {
541 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
542 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
543 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
544 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
545 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
546 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
547 };
548 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
549 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
550 samplerBindingsSpecified.setBit(samplerBinding);
551 bindings.addTexture(samplerBinding,
552 CUSTOM_MATERIAL_VISIBILITY_ALL,
553 texture, sampler);
554 }
555 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
556 renderableImage = renderableImage->m_nextImage;
557 }
558
559 if (maxSamplerBinding >= 0) {
560 // custom property textures
561 int customTexCount = shaderPipeline->extraTextureCount();
562 for (int i = 0; i < customTexCount; ++i) {
563 QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
564 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
565 if (samplerBinding >= 0) {
566 samplerBindingsSpecified.setBit(samplerBinding);
567 rhiCtx->checkAndAdjustForNPoT(t.texture, &t.samplerDesc);
568 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
569 bindings.addTexture(samplerBinding,
570 CUSTOM_MATERIAL_VISIBILITY_ALL,
571 t.texture,
572 sampler);
573 }
574 }
575
576 // use a dummy texture for the unused samplers in the shader
577 QRhiSampler *dummySampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
578 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
579
580 for (const QShaderDescription::InOutVariable &var : samplerVars) {
581 if (!samplerBindingsSpecified.testBit(var.binding)) {
582 QRhiTexture *t = nullptr;
583 if (var.type == QShaderDescription::SamplerCube)
584 t = dummyCubeTexture;
585 else if (var.type == QShaderDescription::Sampler3D)
586 t = dummyTexture3D;
587 else
588 t = dummyTexture;
589 bindings.addTexture(var.binding, CUSTOM_MATERIAL_VISIBILITY_ALL, t, dummySampler);
590 }
591 }
592 }
593
594 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
595
596 if (oit && layerData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
597 RenderHelpers::addAccumulatorImageBindings(shaderPipeline.get(), bindings);
598
599 // do the same srb lookup acceleration as default materials
600 QRhiShaderResourceBindings *&srb = dcd.srb;
601 bool srbChanged = false;
602 if (!srb || bindings != dcd.bindings) {
603 srb = rhiCtxD->srb(bindings);
604 rhiCtxD->releaseCachedSrb(dcd.bindings);
605 dcd.bindings = bindings;
606 srbChanged = true;
607 }
608
609 if (cubeFace == QSSGRenderTextureCubeFaceNone)
610 renderable.rhiRenderData.mainPass.srb = srb;
611 else
612 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
613
614 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
615 if (dcd.pipeline
616 && !srbChanged
617 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
618 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
619 && dcd.ps == *ps)
620 {
621 if (cubeFace == QSSGRenderTextureCubeFaceNone)
622 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
623 else
624 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
625 } else {
626 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
627 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
628 renderPassDescriptor,
629 srb);
630 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
631 } else {
632 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
633 renderPassDescriptor,
634 srb);
635 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
636 }
637
638 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
639 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
640 dcd.ps = *ps;
641 }
642 }
643}
644
645void QSSGCustomMaterialSystem::setShaderResources(char *ubufData,
646 const QSSGRenderCustomMaterial &inMaterial,
647 const QByteArray &inPropertyName,
648 const QVariant &propertyValue,
649 QSSGRenderShaderValue::Type inPropertyType,
650 QSSGRhiShaderPipeline &shaderPipeline)
651{
652 Q_UNUSED(inMaterial);
653
654 if (inPropertyType == QSSGRenderShaderValue::Texture) {
655 QSSGRenderCustomMaterial::TextureProperty *textureProperty =
656 reinterpret_cast<QSSGRenderCustomMaterial::TextureProperty *>(propertyValue.value<void *>());
657 QSSGRenderImage *image = textureProperty->texImage;
658 if (image) {
659 const auto &theBufferManager(context->bufferManager());
660 const QSSGRenderImageTexture texture = theBufferManager->loadRenderImage(image);
661 if (texture.m_texture) {
662 const QSSGRhiTexture t = {
663 inPropertyName,
664 texture.m_texture,
665 { QSSGRhiHelpers::toRhi(textureProperty->minFilterType),
666 QSSGRhiHelpers::toRhi(textureProperty->magFilterType),
667 textureProperty->mipFilterType != QSSGRenderTextureFilterOp::None ? QSSGRhiHelpers::toRhi(textureProperty->mipFilterType) : QRhiSampler::None,
668 QSSGRhiHelpers::toRhi(textureProperty->horizontalClampType),
669 QSSGRhiHelpers::toRhi(textureProperty->verticalClampType),
670 QSSGRhiHelpers::toRhi(textureProperty->zClampType)
671 }
672 };
673 shaderPipeline.addExtraTexture(t);
674 }
675 }
676 } else {
677 shaderPipeline.setUniformValue(ubufData, inPropertyName, propertyValue, inPropertyType);
678 }
679}
680
681void QSSGCustomMaterialSystem::applyRhiShaderPropertyValues(char *ubufData,
682 const QSSGRenderCustomMaterial &material,
683 QSSGRhiShaderPipeline &shaderPipeline)
684{
685 const auto &properties = material.m_properties;
686 for (const auto &prop : properties)
687 setShaderResources(ubufData, material, prop.name, prop.value, prop.shaderDataType, shaderPipeline);
688
689 const auto textProps = material.m_textureProperties;
690 for (const auto &prop : textProps)
691 setShaderResources(ubufData, material, prop.name, QVariant::fromValue((void *)&prop), prop.shaderDataType, shaderPipeline);
692}
693
694void QSSGCustomMaterialSystem::rhiRenderRenderable(QSSGRhiContext *rhiCtx,
695 QSSGSubsetRenderable &renderable,
696 bool *needsSetViewport,
697 QSSGRenderTextureCubeFace cubeFace,
698 const QSSGRhiGraphicsPipelineState &state,
699 qsizetype userPassIndex)
700{
701 QRhiGraphicsPipeline *ps = (userPassIndex >= 0) ? renderable.rhiRenderData.userPassData[userPassIndex].pipeline : renderable.rhiRenderData.mainPass.pipeline;
702 QRhiShaderResourceBindings *srb = (userPassIndex >= 0) ? renderable.rhiRenderData.userPassData[userPassIndex].srb : renderable.rhiRenderData.mainPass.srb;
703
704 if (cubeFace != QSSGRenderTextureCubeFaceNone) {
705 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
706 ps = renderable.rhiRenderData.reflectionPass.pipeline;
707 srb = renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx];
708 }
709
710 if (!ps || !srb)
711 return;
712
713 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
714 QRhiBuffer *vertexBuffer = renderable.subset.rhi.vertexBuffer->buffer();
715 QRhiBuffer *indexBuffer = renderable.subset.rhi.indexBuffer ? renderable.subset.rhi.indexBuffer->buffer() : nullptr;
716
717 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
718 cb->setGraphicsPipeline(ps);
719 cb->setShaderResources(srb);
720
721 if (*needsSetViewport) {
722 cb->setViewport(state.viewport);
723 *needsSetViewport = false;
724 }
725
726 QRhiCommandBuffer::VertexInput vertexBuffers[2];
727 int vertexBufferCount = 1;
728 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
729 quint32 instances = 1;
730 if (renderable.modelContext.model.instancing()) {
731 instances = renderable.modelContext.model.instanceCount();
732 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(renderable.instanceBuffer, 0);
733 vertexBufferCount = 2;
734 }
735 if (indexBuffer) {
736 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, renderable.subset.rhi.indexBuffer->indexFormat());
737 cb->drawIndexed(renderable.subset.lodCount(renderable.subsetLevelOfDetail), instances, renderable.subset.lodOffset(renderable.subsetLevelOfDetail));
738 QSSGRHICTX_STAT(rhiCtx, drawIndexed(renderable.subset.lodCount(renderable.subsetLevelOfDetail), instances));
739 } else {
740 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
741 cb->draw(renderable.subset.count, instances, renderable.subset.offset);
742 QSSGRHICTX_STAT(rhiCtx, draw(renderable.subset.count, instances));
743 }
744 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (renderable.subset.count | quint64(instances) << 32),
745 QVector<int>({renderable.modelContext.model.profilingId,
746 renderable.material.profilingId}));
747}
748
749QT_END_NAMESPACE
Combined button and popup list for selecting options.
static const QRhiShaderResourceBinding::StageFlags CUSTOM_MATERIAL_VISIBILITY_ALL