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