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 for (
const QShaderDescription::InOutVariable &var : shaderPipeline->vertexStage()->shader().description().combinedImageSamplers()) {
301 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(),
302 [&var](
const QShaderDescription::InOutVariable &v) {
return var.binding == v.binding; });
303 if (it == samplerVars.cend())
304 samplerVars.append(var);
307 int maxSamplerBinding = -1;
308 for (
const QShaderDescription::InOutVariable &var : samplerVars)
309 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
318 QBitArray samplerBindingsSpecified(maxSamplerBinding + 1);
321 samplerBindingsSpecified.setBit(shaderPipeline->bindingForTexture(
"qt_particleTexture"));
324 if (QRhiTexture *boneTexture = layerData.getBonemapTexture(renderable.modelContext)) {
325 int binding = shaderPipeline->bindingForTexture(
"qt_boneTexture");
327 QRhiSampler *boneSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
328 QRhiSampler::Nearest,
330 QRhiSampler::ClampToEdge,
331 QRhiSampler::ClampToEdge,
334 bindings.addTexture(binding,
335 QRhiShaderResourceBinding::VertexStage,
338 samplerBindingsSpecified.setBit(binding);
343 auto *targetsTexture = renderable.subset.rhi.targetsTexture;
344 if (targetsTexture) {
345 int binding = shaderPipeline->bindingForTexture(
"qt_morphTargetTexture");
347 QRhiSampler *targetsSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
348 QRhiSampler::Nearest,
350 QRhiSampler::ClampToEdge,
351 QRhiSampler::ClampToEdge,
352 QRhiSampler::ClampToEdge
354 bindings.addTexture(binding, QRhiShaderResourceBinding::VertexStage, renderable.subset.rhi.targetsTexture, targetsSampler);
355 samplerBindingsSpecified.setBit(binding);
362 if (featureSet.isSet(QSSGShaderFeatures::Feature::ReflectionProbe)) {
363 int reflectionSampler = shaderPipeline->bindingForTexture(
"qt_reflectionMap");
364 QRhiTexture* reflectionTexture = layerData.getReflectionMapManager()->reflectionMapEntry(renderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
365 const auto mipMapFilter = reflectionTexture && reflectionTexture->flags().testFlag(QRhiTexture::Flag::MipMapped)
366 ? QRhiSampler::Linear
368 QRhiSampler *sampler = rhiCtx->sampler(
369 { QRhiSampler::Linear, QRhiSampler::Linear, mipMapFilter, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
370 if (reflectionSampler >= 0 && reflectionTexture) {
371 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
372 samplerBindingsSpecified.setBit(reflectionSampler);
374 }
else if (shaderPipeline->lightProbeTexture()) {
375 int binding = shaderPipeline->bindingForTexture(
"qt_lightProbe",
int(QSSGRhiSamplerBindingHints::LightProbe));
377 samplerBindingsSpecified.setBit(binding);
378 QPair<QSSGRenderTextureCoordOp, QSSGRenderTextureCoordOp> tiling = shaderPipeline->lightProbeTiling();
379 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear,
380 QSSGRhiHelpers::toRhi(tiling.first), QSSGRhiHelpers::toRhi(tiling.second), QRhiSampler::Repeat });
381 bindings.addTexture(binding,
382 QRhiShaderResourceBinding::FragmentStage,
383 shaderPipeline->lightProbeTexture(), sampler);
387 if (shaderPipeline->screenTexture()) {
388 const int screenTextureBinding = shaderPipeline->bindingForTexture(
"qt_screenTexture",
int(QSSGRhiSamplerBindingHints::ScreenTexture));
389 const int screenTextureArrayBinding = shaderPipeline->bindingForTexture(
"qt_screenTextureArray",
int(QSSGRhiSamplerBindingHints::ScreenTextureArray));
390 if (screenTextureBinding >= 0 || screenTextureArrayBinding >= 0) {
391 QRhiTexture *screenTexture = shaderPipeline->screenTexture();
394 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
396 const QRhiTexture::Flags flags = screenTexture->flags();
398 screenTexture = rhiCtx->dummyTexture(flags, resourceUpdates);
399 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
405 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
406 ? QRhiSampler::Linear : QRhiSampler::None;
407 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, mipFilter,
408 QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
409 if (screenTextureBinding >= 0) {
410 samplerBindingsSpecified.setBit(screenTextureBinding);
411 bindings.addTexture(screenTextureBinding,
412 QRhiShaderResourceBinding::FragmentStage,
413 screenTexture, sampler);
415 if (screenTextureArrayBinding >= 0) {
416 samplerBindingsSpecified.setBit(screenTextureArrayBinding);
417 bindings.addTexture(screenTextureArrayBinding,
418 QRhiShaderResourceBinding::FragmentStage,
419 screenTexture, sampler);
424 if (shaderPipeline->depthTexture()) {
425 const int depthTextureBinding = shaderPipeline->bindingForTexture(
"qt_depthTexture",
int(QSSGRhiSamplerBindingHints::DepthTexture));
426 const int depthTextureArrayBinding = shaderPipeline->bindingForTexture(
"qt_depthTextureArray",
int(QSSGRhiSamplerBindingHints::DepthTextureArray));
427 if (depthTextureBinding >= 0 || depthTextureArrayBinding >= 0) {
429 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
430 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
431 if (depthTextureBinding >= 0) {
432 samplerBindingsSpecified.setBit(depthTextureBinding);
433 bindings.addTexture(depthTextureBinding,
434 CUSTOM_MATERIAL_VISIBILITY_ALL,
435 shaderPipeline->depthTexture(), sampler);
437 if (depthTextureArrayBinding >= 0) {
438 samplerBindingsSpecified.setBit(depthTextureArrayBinding);
439 bindings.addTexture(depthTextureArrayBinding,
440 CUSTOM_MATERIAL_VISIBILITY_ALL,
441 shaderPipeline->depthTexture(), sampler);
446 if (shaderPipeline->normalTexture()) {
447 const int normalTextureBinding = shaderPipeline->bindingForTexture(
"qt_normalTexture",
int(QSSGRhiSamplerBindingHints::NormalTexture));
448 if (normalTextureBinding >= 0) {
450 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
451 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
452 samplerBindingsSpecified.setBit(normalTextureBinding);
453 bindings.addTexture(normalTextureBinding, CUSTOM_MATERIAL_VISIBILITY_ALL, shaderPipeline->normalTexture(), sampler);
457 if (shaderPipeline->ssaoTexture()) {
458 const int ssaoTextureBinding = shaderPipeline->bindingForTexture(
"qt_aoTexture",
int(QSSGRhiSamplerBindingHints::AoTexture));
459 const int ssaoTextureArrayBinding = shaderPipeline->bindingForTexture(
"qt_aoTextureArray",
int(QSSGRhiSamplerBindingHints::AoTextureArray));
460 if (ssaoTextureBinding >= 0 || ssaoTextureArrayBinding >= 0) {
462 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
463 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
464 if (ssaoTextureBinding >= 0) {
465 samplerBindingsSpecified.setBit(ssaoTextureBinding);
466 bindings.addTexture(ssaoTextureBinding,
467 QRhiShaderResourceBinding::FragmentStage,
468 shaderPipeline->ssaoTexture(), sampler);
470 if (ssaoTextureArrayBinding >= 0) {
471 samplerBindingsSpecified.setBit(ssaoTextureArrayBinding);
472 bindings.addTexture(ssaoTextureArrayBinding,
473 QRhiShaderResourceBinding::FragmentStage,
474 shaderPipeline->ssaoTexture(), sampler);
479 if (shaderPipeline->lightmapTexture()) {
480 int binding = shaderPipeline->bindingForTexture(
"qt_lightmap",
int(QSSGRhiSamplerBindingHints::LightmapTexture));
482 samplerBindingsSpecified.setBit(binding);
483 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
484 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
485 bindings.addTexture(binding,
486 QRhiShaderResourceBinding::FragmentStage,
487 shaderPipeline->lightmapTexture(), sampler);
491 if (shaderPipeline->MotionVectorTexture()) {
492 int binding = shaderPipeline->bindingForTexture(
"qt_motionVectorTexture",
int(QSSGRhiSamplerBindingHints::MotionVectorTexture));
494 samplerBindingsSpecified.setBit(binding);
495 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
496 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
497 bindings.addTexture(binding,
498 QRhiShaderResourceBinding::FragmentStage,
499 shaderPipeline->MotionVectorTexture(), sampler);
505 auto shadowMapAtlas = shaderPipeline->shadowMapAtlasTexture();
506 if (shadowMapAtlas) {
507 int binding = shaderPipeline->bindingForTexture(
"qt_shadowmap_texture");
509 samplerBindingsSpecified.setBit(binding);
510 QRhiTexture *texture = shadowMapAtlas;
511 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
512 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
513 Q_ASSERT(texture && sampler);
514 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
519 if (
auto shadowMapBlueNoise = shaderPipeline->shadowMapBlueNoiseTexture()) {
520 int binding = shaderPipeline->bindingForTexture(
"qt_shadowmap_blue_noise_texture");
522 samplerBindingsSpecified.setBit(binding);
523 QRhiTexture *texture = shadowMapBlueNoise;
524 QRhiSampler *sampler = rhiCtx->sampler(
525 { QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::Repeat, QRhiSampler::Repeat, QRhiSampler::Repeat });
526 Q_ASSERT(texture && sampler);
527 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
531 QSSGRenderableImage *renderableImage = renderable.firstImage;
532 while (renderableImage) {
533 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
534 const int samplerHint =
int(renderableImage->m_mapType);
535 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
536 if (samplerBinding >= 0) {
537 QRhiTexture *texture = renderableImage->m_texture.m_texture;
538 if (samplerBinding >= 0 && texture) {
539 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
540 QSSGRhiSamplerDescription samplerDesc = {
541 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
542 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
543 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
544 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
545 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
546 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
548 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
549 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
550 samplerBindingsSpecified.setBit(samplerBinding);
551 bindings.addTexture(samplerBinding,
552 CUSTOM_MATERIAL_VISIBILITY_ALL,
556 renderableImage = renderableImage->m_nextImage;
559 if (maxSamplerBinding >= 0) {
561 int customTexCount = shaderPipeline->extraTextureCount();
562 for (
int i = 0; i < customTexCount; ++i) {
563 QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
564 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
565 if (samplerBinding >= 0) {
566 samplerBindingsSpecified.setBit(samplerBinding);
567 rhiCtx->checkAndAdjustForNPoT(t.texture, &t.samplerDesc);
568 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
569 bindings.addTexture(samplerBinding,
570 CUSTOM_MATERIAL_VISIBILITY_ALL,
577 QRhiSampler *dummySampler = rhiCtx->sampler({ QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
578 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::Repeat });
580 for (
const QShaderDescription::InOutVariable &var : samplerVars) {
581 if (!samplerBindingsSpecified.testBit(var.binding)) {
582 QRhiTexture *t =
nullptr;
583 if (var.type == QShaderDescription::SamplerCube)
584 t = dummyCubeTexture;
585 else if (var.type == QShaderDescription::Sampler3D)
589 bindings.addTexture(var.binding, CUSTOM_MATERIAL_VISIBILITY_ALL, t, dummySampler);
594 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
596 if (oit && layerData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
597 RenderHelpers::addAccumulatorImageBindings(shaderPipeline.get(), bindings);
600 QRhiShaderResourceBindings *&srb = dcd.srb;
601 bool srbChanged =
false;
602 if (!srb || bindings != dcd.bindings) {
603 srb = rhiCtxD->srb(bindings);
604 rhiCtxD->releaseCachedSrb(dcd.bindings);
605 dcd.bindings = bindings;
609 if (cubeFace == QSSGRenderTextureCubeFaceNone)
610 renderable.rhiRenderData.mainPass.srb = srb;
612 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
614 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
617 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
618 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
621 if (cubeFace == QSSGRenderTextureCubeFaceNone)
622 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
624 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
626 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
627 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
628 renderPassDescriptor,
630 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
632 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
633 renderPassDescriptor,
635 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
638 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
639 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;