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