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