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