185void QSSGCustomMaterialSystem::rhiPrepareRenderable(QSSGRhiGraphicsPipelineState *ps,
187 QSSGSubsetRenderable &renderable,
188 const QSSGShaderFeatures &featureSet,
189 const QSSGRenderCustomMaterial &material,
190 const QSSGLayerRenderData &layerData,
191 QRhiRenderPassDescriptor *renderPassDescriptor,
195 QSSGRenderCamera *alteredCamera,
196 QSSGRenderTextureCubeFace cubeFace,
197 QMatrix4x4 *alteredModelViewProjection,
198 QSSGReflectionMapEntry *entry,
201 QSSGRhiContext *rhiCtx = context->rhiContext().get();
203 QRhiGraphicsPipeline::TargetBlend blend;
204 if (material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Blending)) {
206 blend.srcColor = material.m_srcBlend;
207 blend.srcAlpha = material.m_srcAlphaBlend;
208 blend.dstColor = material.m_dstBlend;
209 blend.dstAlpha = material.m_dstAlphaBlend;
212 const QSSGCullFaceMode cullMode = material.m_cullMode;
214 const auto &defaultMaterialShaderKeyProperties = layerData.getDefaultMaterialPropertyTable();
216 const bool blendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(renderable.shaderDescription);
218 const auto &shaderPipeline = shadersForCustomMaterial(ps, material, renderable, defaultMaterialShaderKeyProperties, featureSet);
220 if (shaderPipeline) {
221 QSSGRhiShaderResourceBindingList bindings;
222 const auto &modelNode = renderable.modelContext.model;
228 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
229 const quintptr entryIdx = quintptr(cubeFace != QSSGRenderTextureCubeFaceNone) * (cubeFaceIdx + (quintptr(renderable.subset.offset) << 3));
231 const auto entryPartA =
reinterpret_cast<quintptr>(&material);
232 const auto entryPartB =
reinterpret_cast<quintptr>(entry);
233 const void *entryKey =
reinterpret_cast<
const void *>(entryPartA ^ entryPartB);
235 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiCtx)->drawCallData({ passKey, &modelNode, entryKey, entryIdx });
237 shaderPipeline->ensureCombinedUniformBuffer(&dcd.ubuf);
238 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
239 if (!alteredCamera) {
240 updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, layerData, ubufData, ps, material, renderable, layerData.renderedCameras,
nullptr,
nullptr);
242 QSSGRenderCameraList cameras({ alteredCamera });
243 updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, layerData, ubufData, ps, material, renderable, cameras,
nullptr, alteredModelViewProjection);
246 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &renderable.modelContext.model, renderable.subset.offset);
247 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
250 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &renderable.modelContext.model);
251 bool instancing =
false;
252 if (!alteredCamera) {
253 const QSSGRenderCameraDataList &cameraDatas(*layerData.renderedCameraData);
254 instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, &renderable, cameraDatas[0].direction, cameraDatas[0].position, renderable.instancingLodMin, renderable.instancingLodMax);
256 const auto &altCamTransform = layerData.getGlobalTransform(*alteredCamera);
257 instancing = QSSGLayerRenderData::prepareInstancing(rhiCtx, &renderable, QSSGRenderNode::getScalingCorrectDirection(altCamTransform), QSSGRenderNode::getGlobalPos(altCamTransform), renderable.instancingLodMin, renderable.instancingLodMax);
260 ps->samples = samples;
261 ps->viewCount = viewCount;
263 ps->cullMode = QSSGRhiHelpers::toCullMode(cullMode);
266 ps->targetBlend[0] = blend;
268 auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
270 ia = renderable.subset.rhi.ia;
273 int instanceBufferBinding = 0;
276 const quint32 stride = renderable.modelContext.model.instanceTable->stride();
277 QVarLengthArray<QRhiVertexInputBinding, 8> bindings;
278 std::copy(ia.inputLayout.cbeginBindings(),
279 ia.inputLayout.cendBindings(),
280 std::back_inserter(bindings));
281 bindings.append({ stride, QRhiVertexInputBinding::PerInstance });
282 instanceBufferBinding = bindings.size() - 1;
283 ia.inputLayout.setBindings(bindings.cbegin(), bindings.cend());
286 QSSGRhiHelpers::bakeVertexInputLocations(&ia, *shaderPipeline, instanceBufferBinding);
288 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
289 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
290 QRhiTexture *dummyTexture3D = rhiCtx->dummyTexture(QRhiTexture::ThreeDimensional, resourceUpdates);
291 QRhiTexture *dummyCubeTexture = rhiCtx->dummyTexture(QRhiTexture::CubeMap, resourceUpdates);
292 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
294 bindings.addUniformBuffer(0, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline->ub0Size());
295 bindings.addUniformBuffer(1, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, shaderPipeline->ub0LightDataOffset(),
sizeof(QSSGShaderLightsUniformData));
296 bindings.addUniformBuffer(2, CUSTOM_MATERIAL_VISIBILITY_ALL, dcd.ubuf, shaderPipeline->ub0DirectionalLightDataOffset(),
sizeof(QSSGShaderDirectionalLightsUniformData));
298 QVector<QShaderDescription::InOutVariable> samplerVars =
299 shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
300 const auto combinedSamplers = shaderPipeline->vertexStage()->shader().description().combinedImageSamplers();
301 for (
const QShaderDescription::InOutVariable &var : combinedSamplers) {
302 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(),
303 [&var](
const QShaderDescription::InOutVariable &v) {
return var.binding == v.binding; });
304 if (it == samplerVars.cend())
305 samplerVars.append(var);
308 int maxSamplerBinding = -1;
309 for (
const QShaderDescription::InOutVariable &var : std::as_const(samplerVars))
310 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
319 QBitArray samplerBindingsSpecified(maxSamplerBinding + 1);
322 samplerBindingsSpecified.setBit(shaderPipeline->bindingForTexture(
"qt_particleTexture"));
325 if (QRhiTexture *boneTexture = layerData.getBonemapTexture(renderable.modelContext)) {
326 int binding = shaderPipeline->bindingForTexture(
"qt_boneTexture");
328 QRhiSampler *boneSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
329 QRhiSampler::Nearest,
331 QRhiSampler::ClampToEdge,
332 QRhiSampler::ClampToEdge,
335 bindings.addTexture(binding,
336 QRhiShaderResourceBinding::VertexStage,
339 samplerBindingsSpecified.setBit(binding);
344 auto *targetsTexture = renderable.subset.rhi.targetsTexture;
345 if (targetsTexture) {
346 int binding = shaderPipeline->bindingForTexture(
"qt_morphTargetTexture");
348 QRhiSampler *targetsSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
349 QRhiSampler::Nearest,
351 QRhiSampler::ClampToEdge,
352 QRhiSampler::ClampToEdge,
353 QRhiSampler::ClampToEdge
355 bindings.addTexture(binding, QRhiShaderResourceBinding::VertexStage, renderable.subset.rhi.targetsTexture, targetsSampler);
356 samplerBindingsSpecified.setBit(binding);
363 if (featureSet.isSet(QSSGShaderFeatures::Feature::ReflectionProbe)) {
364 int reflectionSampler = shaderPipeline->bindingForTexture(
"qt_reflectionMap");
365 QRhiTexture* reflectionTexture = layerData.getReflectionMapManager()->reflectionMapEntry(renderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
366 const auto mipMapFilter = reflectionTexture && reflectionTexture->flags().testFlag(QRhiTexture::Flag::MipMapped)
367 ? QRhiSampler::Linear
369 QRhiSampler *sampler = rhiCtx->sampler(
370 { QRhiSampler::Linear, QRhiSampler::Linear, mipMapFilter, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
371 if (reflectionSampler >= 0 && reflectionTexture) {
372 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
373 samplerBindingsSpecified.setBit(reflectionSampler);
377 if (
int binding = shaderPipeline->bindingForTexture(
"qt_lightProbe",
int(QSSGRhiSamplerBindingHints::LightProbe));
379 auto texture = shaderPipeline->lightProbeTexture();
381 texture = rhiCtx->dummyTexture(QRhiTexture::CubeMap, resourceUpdates);
384 samplerBindingsSpecified.setBit(binding);
385 QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> tiling = shaderPipeline->lightProbeTiling();
386 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear,
389 QSSGRhiHelpers::toRhi(tiling.first),
390 QSSGRhiHelpers::toRhi(tiling.second),
391 QRhiSampler::Repeat });
392 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
395 if (shaderPipeline->screenTexture()) {
396 const int screenTextureBinding = shaderPipeline->bindingForTexture(
"qt_screenTexture",
int(QSSGRhiSamplerBindingHints::ScreenTexture));
397 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture(
"qt_screenTextureArray",
int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
398 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
399 QRhiTexture *screenTexture = shaderPipeline->screenTexture();
402 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
404 const QRhiTexture::Flags flags = screenTexture->flags();
406 screenTexture = rhiCtx->dummyTexture(flags, resourceUpdates);
407 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
413 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
414 ? QRhiSampler::Linear : QRhiSampler::None;
415 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, mipFilter,
416 QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
417 if (screenTextureBinding >= 0) {
418 samplerBindingsSpecified.setBit(screenTextureBinding);
419 bindings.addTexture(screenTextureBinding,
420 QRhiShaderResourceBinding::FragmentStage,
421 screenTexture, sampler);
423 if (screenTextureArrayBinding >= 0) {
424 samplerBindingsSpecified.setBit(screenTextureArrayBinding);
425 bindings.addTexture(screenTextureArrayBinding,
426 QRhiShaderResourceBinding::FragmentStage,
427 screenTexture, sampler);
432 if (shaderPipeline->depthTexture()) {
433 const int depthTextureBinding = shaderPipeline->bindingForTexture(
"qt_depthTexture",
int(QSSGRhiSamplerBindingHints::DepthTexture));
434 const int depthTextureArrayBinding = shaderPipeline->bindingForTexture(
"qt_depthTextureArray",
int(QSSGRhiSamplerBindingHints::DepthTextureArray));
435 if (depthTextureBinding >= 0 || depthTextureArrayBinding >= 0) {
437 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
438 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
439 if (depthTextureBinding >= 0) {
440 samplerBindingsSpecified.setBit(depthTextureBinding);
441 bindings.addTexture(depthTextureBinding,
442 CUSTOM_MATERIAL_VISIBILITY_ALL,
443 shaderPipeline->depthTexture(), sampler);
445 if (depthTextureArrayBinding >= 0) {
446 samplerBindingsSpecified.setBit(depthTextureArrayBinding);
447 bindings.addTexture(depthTextureArrayBinding,
448 CUSTOM_MATERIAL_VISIBILITY_ALL,
449 shaderPipeline->depthTexture(), sampler);
454 if (shaderPipeline->normalTexture()) {
455 const int normalTextureBinding = shaderPipeline->bindingForTexture(
"qt_normalTexture",
int(QSSGRhiSamplerBindingHints::NormalTexture));
456 if (normalTextureBinding >= 0) {
458 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
459 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
460 samplerBindingsSpecified.setBit(normalTextureBinding);
461 bindings.addTexture(normalTextureBinding, CUSTOM_MATERIAL_VISIBILITY_ALL, shaderPipeline->normalTexture(), sampler);
465 if (shaderPipeline->ssaoTexture()) {
466 const int ssaoTextureBinding = shaderPipeline->bindingForTexture(
"qt_aoTexture",
int(QSSGRhiSamplerBindingHints::AoTexture));
467 const int ssaoTextureArrayBinding = shaderPipeline->bindingForTexture(
"qt_aoTextureArray",
int(QSSGRhiSamplerBindingHints::AoTextureArray));
468 if (ssaoTextureBinding >= 0 || ssaoTextureArrayBinding >= 0) {
470 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
471 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
472 if (ssaoTextureBinding >= 0) {
473 samplerBindingsSpecified.setBit(ssaoTextureBinding);
474 bindings.addTexture(ssaoTextureBinding,
475 QRhiShaderResourceBinding::FragmentStage,
476 shaderPipeline->ssaoTexture(), sampler);
478 if (ssaoTextureArrayBinding >= 0) {
479 samplerBindingsSpecified.setBit(ssaoTextureArrayBinding);
480 bindings.addTexture(ssaoTextureArrayBinding,
481 QRhiShaderResourceBinding::FragmentStage,
482 shaderPipeline->ssaoTexture(), sampler);
487 if (shaderPipeline->lightmapTexture()) {
488 int binding = shaderPipeline->bindingForTexture(
"qt_lightmap",
int(QSSGRhiSamplerBindingHints::LightmapTexture));
490 samplerBindingsSpecified.setBit(binding);
491 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
492 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
493 bindings.addTexture(binding,
494 QRhiShaderResourceBinding::FragmentStage,
495 shaderPipeline->lightmapTexture(), sampler);
499 if (shaderPipeline->MotionVectorTexture()) {
500 int binding = shaderPipeline->bindingForTexture(
"qt_motionVectorTexture",
int(QSSGRhiSamplerBindingHints::MotionVectorTexture));
502 samplerBindingsSpecified.setBit(binding);
503 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
504 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
505 bindings.addTexture(binding,
506 QRhiShaderResourceBinding::FragmentStage,
507 shaderPipeline->MotionVectorTexture(), sampler);
513 auto shadowMapAtlas = shaderPipeline->shadowMapAtlasTexture();
514 if (shadowMapAtlas) {
515 int binding = shaderPipeline->bindingForTexture(
"qt_shadowmap_texture");
517 samplerBindingsSpecified.setBit(binding);
518 QRhiTexture *texture = shadowMapAtlas;
519 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
520 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
521 Q_ASSERT(texture && sampler);
522 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
527 if (
auto shadowMapBlueNoise = shaderPipeline->shadowMapBlueNoiseTexture()) {
528 int binding = shaderPipeline->bindingForTexture(
"qt_shadowmap_blue_noise_texture");
530 samplerBindingsSpecified.setBit(binding);
531 QRhiTexture *texture = shadowMapBlueNoise;
532 QRhiSampler *sampler = rhiCtx->sampler(
533 { QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
534 Q_ASSERT(texture && sampler);
535 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
539 QSSGRenderableImage *renderableImage = renderable.firstImage;
540 while (renderableImage) {
541 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
542 const int samplerHint =
int(renderableImage->m_mapType);
543 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
544 if (samplerBinding >= 0) {
545 QRhiTexture *texture = renderableImage->m_texture.m_texture;
546 if (samplerBinding >= 0 && texture) {
547 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
548 QSSGRhiSamplerDescription samplerDesc = {
549 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
550 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
551 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
552 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
553 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
554 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
556 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
557 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
558 samplerBindingsSpecified.setBit(samplerBinding);
559 bindings.addTexture(samplerBinding,
560 CUSTOM_MATERIAL_VISIBILITY_ALL,
564 renderableImage = renderableImage->m_nextImage;
567 if (maxSamplerBinding >= 0) {
569 int customTexCount = shaderPipeline->extraTextureCount();
570 for (
int i = 0; i < customTexCount; ++i) {
571 QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
572 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
573 if (samplerBinding >= 0) {
574 samplerBindingsSpecified.setBit(samplerBinding);
575 rhiCtx->checkAndAdjustForNPoT(t.texture, &t.samplerDesc);
576 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
577 bindings.addTexture(samplerBinding,
578 CUSTOM_MATERIAL_VISIBILITY_ALL,
585 QRhiSampler *dummySampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
586 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
588 for (
const QShaderDescription::InOutVariable &var : std::as_const(samplerVars)) {
589 if (!samplerBindingsSpecified.testBit(var.binding)) {
590 QRhiTexture *t =
nullptr;
591 if (var.type == QShaderDescription::SamplerCube)
592 t = dummyCubeTexture;
593 else if (var.type == QShaderDescription::Sampler3D)
597 bindings.addTexture(var.binding, CUSTOM_MATERIAL_VISIBILITY_ALL, t, dummySampler);
602 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
604 if (oit && layerData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
605 RenderHelpers::addAccumulatorImageBindings(shaderPipeline.get(), bindings);
608 QRhiShaderResourceBindings *&srb = dcd.srb;
609 bool srbChanged =
false;
610 if (!srb || bindings != dcd.bindings) {
611 srb = rhiCtxD->srb(bindings);
612 rhiCtxD->releaseCachedSrb(dcd.bindings);
613 dcd.bindings = bindings;
617 if (cubeFace == QSSGRenderTextureCubeFaceNone)
618 renderable.rhiRenderData.mainPass.srb = srb;
620 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
622 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
625 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
626 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
629 if (cubeFace == QSSGRenderTextureCubeFaceNone)
630 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
632 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
634 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
635 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
636 renderPassDescriptor,
638 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
640 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
641 renderPassDescriptor,
643 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
646 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
647 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;