Files
XCEngine/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp
ssdfasd cd6f9aa4cf feat(srp): add shader pass selection for scene draws
- let DrawingSettings carry an optional shaderPassName across managed and native scene draw APIs
- allow RenderObjectsRendererFeature to author explicit ForwardLit or Unlit scene draws
- exercise the new scene draw pass selection seam in the project render pipeline probe
2026-04-20 22:58:08 +08:00

706 lines
26 KiB
C++

#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
#include "Components/GameObject.h"
#include "Debug/Logger.h"
#include "RHI/RHICommandList.h"
#include "RHI/RHIDevice.h"
#include "Rendering/FrameData/RendererListUtils.h"
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
#include "Rendering/Internal/ShaderVariantUtils.h"
#include "Rendering/Materials/RenderMaterialResolve.h"
#include "Rendering/Materials/RenderMaterialStateUtils.h"
#include "Resources/Material/Material.h"
#include "Resources/Shader/Shader.h"
#include "Resources/Texture/Texture.h"
#include <algorithm>
#include <cstddef>
#include <cmath>
namespace XCEngine {
namespace Rendering {
namespace Pipelines {
namespace {
constexpr float kForwardAmbientIntensity = 0.28f;
constexpr float kSpotInnerAngleRatio = 0.8f;
Resources::ShaderKeywordSet ResolvePassKeywordSet(
const RenderSceneData& sceneData,
const Resources::Material* material) {
return Resources::CombineShaderKeywordSets(
sceneData.globalShaderKeywords,
material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet());
}
const Resources::ShaderPass* FindCompatibleSurfacePass(
const Resources::Shader& shader,
const RenderSceneData& sceneData,
const Resources::Material* material,
BuiltinMaterialPass pass,
Resources::ShaderBackend backend) {
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) {
if (ShaderPassMatchesBuiltinPass(shaderPass, pass) &&
::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(
shader,
shaderPass.name,
backend,
keywordSet)) {
return &shaderPass;
}
}
return nullptr;
}
RHI::GraphicsPipelineDesc CreatePipelineDesc(
RHI::RHIType backendType,
RHI::RHIPipelineLayout* pipelineLayout,
const Resources::Shader& shader,
const Resources::ShaderPass& shaderPass,
const Containers::String& passName,
const Resources::ShaderKeywordSet& keywordSet,
const Resources::Material* material,
const RenderSurface& surface) {
RHI::GraphicsPipelineDesc pipelineDesc = {};
pipelineDesc.pipelineLayout = pipelineLayout;
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
::XCEngine::Rendering::Internal::ApplySurfacePropertiesToGraphicsPipelineDesc(surface, pipelineDesc);
ApplyResolvedRenderState(&shaderPass, material, pipelineDesc);
pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout();
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType);
const Resources::ShaderStageVariant* vertexVariant =
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet);
const Resources::ShaderStageVariant* fragmentVariant =
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend, keywordSet);
if (vertexVariant != nullptr) {
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
shader.GetPath(),
shaderPass,
backend,
*vertexVariant,
pipelineDesc.vertexShader);
}
if (fragmentVariant != nullptr) {
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
shader.GetPath(),
shaderPass,
backend,
*fragmentVariant,
pipelineDesc.fragmentShader);
}
return pipelineDesc;
}
bool UsesDynamicSurfaceDescriptorSet(const BuiltinPassSetLayoutMetadata& setLayout) {
return setLayout.usesPerObject ||
setLayout.usesLighting ||
setLayout.usesMaterial ||
setLayout.usesShadowReceiver ||
setLayout.usesTexture ||
setLayout.usesMaterialBuffers;
}
bool TryResolveRequestedSurfacePassType(
const DrawSettings& drawSettings,
BuiltinMaterialPass& outPass,
bool& outHasRequestedPass) {
outHasRequestedPass = false;
if (!drawSettings.HasShaderPassName()) {
return true;
}
outHasRequestedPass = true;
const Containers::String normalizedPassName =
NormalizeBuiltinPassMetadataValue(drawSettings.shaderPassName);
if (normalizedPassName == Containers::String("forward") ||
MatchesBuiltinPassName(normalizedPassName, BuiltinMaterialPass::ForwardLit)) {
outPass = BuiltinMaterialPass::ForwardLit;
return true;
}
if (MatchesBuiltinPassName(normalizedPassName, BuiltinMaterialPass::Unlit)) {
outPass = BuiltinMaterialPass::Unlit;
return true;
}
return false;
}
} // namespace
RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() {
RHI::InputLayoutDesc inputLayout = {};
RHI::InputElementDesc position = {};
position.semanticName = "POSITION";
position.semanticIndex = 0;
position.format = static_cast<uint32_t>(RHI::Format::R32G32B32_Float);
position.inputSlot = 0;
position.alignedByteOffset = 0;
inputLayout.elements.push_back(position);
RHI::InputElementDesc normal = {};
normal.semanticName = "NORMAL";
normal.semanticIndex = 0;
normal.format = static_cast<uint32_t>(RHI::Format::R32G32B32_Float);
normal.inputSlot = 0;
normal.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, normal));
inputLayout.elements.push_back(normal);
RHI::InputElementDesc texcoord = {};
texcoord.semanticName = "TEXCOORD";
texcoord.semanticIndex = 0;
texcoord.format = static_cast<uint32_t>(RHI::Format::R32G32_Float);
texcoord.inputSlot = 0;
texcoord.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, uv0));
inputLayout.elements.push_back(texcoord);
RHI::InputElementDesc backTexcoord = {};
backTexcoord.semanticName = "TEXCOORD";
backTexcoord.semanticIndex = 1;
backTexcoord.format = static_cast<uint32_t>(RHI::Format::R32G32_Float);
backTexcoord.inputSlot = 0;
backTexcoord.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, uv1));
inputLayout.elements.push_back(backTexcoord);
RHI::InputElementDesc tangent = {};
tangent.semanticName = "TEXCOORD";
tangent.semanticIndex = 2;
tangent.format = static_cast<uint32_t>(RHI::Format::R32G32B32_Float);
tangent.inputSlot = 0;
tangent.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, tangent));
inputLayout.elements.push_back(tangent);
RHI::InputElementDesc bitangent = {};
bitangent.semanticName = "TEXCOORD";
bitangent.semanticIndex = 3;
bitangent.format = static_cast<uint32_t>(RHI::Format::R32G32B32_Float);
bitangent.inputSlot = 0;
bitangent.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, bitangent));
inputLayout.elements.push_back(bitangent);
RHI::InputElementDesc color = {};
color.semanticName = "COLOR";
color.semanticIndex = 0;
color.format = static_cast<uint32_t>(RHI::Format::R32G32B32A32_Float);
color.inputSlot = 0;
color.alignedByteOffset = static_cast<uint32_t>(offsetof(Resources::StaticMeshVertex, color));
inputLayout.elements.push_back(color);
return inputLayout;
}
bool BuiltinForwardPipeline::TryResolveSurfacePassType(
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass,
BuiltinMaterialPass& outPass) {
if (preferredPass != nullptr) {
if (MatchesBuiltinPass(material, *preferredPass)) {
outPass = *preferredPass;
return true;
}
return false;
}
if (MatchesBuiltinPass(material, BuiltinMaterialPass::Unlit)) {
outPass = BuiltinMaterialPass::Unlit;
return true;
}
if (MatchesBuiltinPass(material, BuiltinMaterialPass::ForwardLit)) {
outPass = BuiltinMaterialPass::ForwardLit;
return true;
}
return false;
}
BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfaceShaderPass(
const RenderSceneData& sceneData,
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) const {
ResolvedShaderPass resolved = {};
BuiltinMaterialPass pass =
preferredPass != nullptr
? *preferredPass
: BuiltinMaterialPass::ForwardLit;
if (!TryResolveSurfacePassType(material, preferredPass, pass)) {
return resolved;
}
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType);
if (material != nullptr && material->GetShader() != nullptr) {
const Resources::Shader* materialShader = material->GetShader();
if (const Resources::ShaderPass* shaderPass =
FindCompatibleSurfacePass(*materialShader, sceneData, material, pass, backend)) {
resolved.shader = materialShader;
resolved.pass = shaderPass;
resolved.passName = shaderPass->name;
return resolved;
}
}
const Resources::ResourceHandle<Resources::Shader>* builtinShaderHandle =
pass == BuiltinMaterialPass::Unlit ? &m_builtinUnlitShader : &m_builtinForwardShader;
if (builtinShaderHandle->IsValid()) {
const Resources::Shader* builtinShader = builtinShaderHandle->Get();
if (const Resources::ShaderPass* shaderPass =
FindCompatibleSurfacePass(*builtinShader, sceneData, material, pass, backend)) {
resolved.shader = builtinShader;
resolved.pass = shaderPass;
resolved.passName = shaderPass->name;
}
}
return resolved;
}
RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) {
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
const ResolvedShaderPass resolvedShaderPass =
ResolveSurfaceShaderPass(
sceneData,
material,
preferredPass);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline could not resolve a valid surface shader pass");
return nullptr;
}
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
return nullptr;
}
PipelineStateKey pipelineKey = {};
pipelineKey.renderState =
BuildStaticPipelineRenderStateKey(ResolveEffectiveRenderState(resolvedShaderPass.pass, material));
pipelineKey.shader = resolvedShaderPass.shader;
pipelineKey.passName = resolvedShaderPass.passName;
pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet);
pipelineKey.renderTargetCount =
::XCEngine::Rendering::Internal::ResolveSurfaceColorAttachmentCount(surface);
pipelineKey.renderTargetFormats =
::XCEngine::Rendering::Internal::ResolveSurfaceColorFormats(surface);
pipelineKey.depthStencilFormat =
static_cast<Core::uint32>(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface));
pipelineKey.sampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface);
pipelineKey.sampleQuality = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality(surface);
const auto existing = m_pipelineStates.find(pipelineKey);
if (existing != m_pipelineStates.end()) {
return existing->second;
}
const RHI::GraphicsPipelineDesc pipelineDesc =
CreatePipelineDesc(
context.backendType,
passLayout->pipelineLayout,
*resolvedShaderPass.shader,
*resolvedShaderPass.pass,
resolvedShaderPass.passName,
keywordSet,
material,
surface);
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
if (pipelineState == nullptr || !pipelineState->IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline failed to create pipeline state");
if (pipelineState != nullptr) {
pipelineState->Shutdown();
delete pipelineState;
}
return nullptr;
}
m_pipelineStates.emplace(pipelineKey, pipelineState);
return pipelineState;
}
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
return ResolveBuiltinBaseColorTexture(material);
}
RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
const Resources::Texture* texture) {
if (texture == nullptr) {
return nullptr;
}
const RenderResourceCache::CachedTexture* cachedTexture = m_resourceCache.GetOrCreateTexture(m_device, texture);
if (cachedTexture != nullptr && cachedTexture->shaderResourceView != nullptr) {
return cachedTexture->shaderResourceView;
}
return nullptr;
}
RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
const Resources::Material* material) {
const Resources::Texture* texture = ResolveTexture(material);
RHI::RHIResourceView* textureView = ResolveTextureView(texture);
return textureView != nullptr ? textureView : m_fallbackTexture2DView;
}
BuiltinForwardPipeline::LightingConstants BuiltinForwardPipeline::BuildLightingConstants(
const RenderLightingData& lightingData) {
LightingConstants lightingConstants = {};
if (lightingData.HasMainDirectionalLight()) {
lightingConstants.mainLightDirectionAndIntensity = Math::Vector4(
lightingData.mainDirectionalLight.direction.x,
lightingData.mainDirectionalLight.direction.y,
lightingData.mainDirectionalLight.direction.z,
lightingData.mainDirectionalLight.intensity);
lightingConstants.mainLightColorAndFlags = Math::Vector4(
lightingData.mainDirectionalLight.color.r,
lightingData.mainDirectionalLight.color.g,
lightingData.mainDirectionalLight.color.b,
1.0f);
}
const Core::uint32 additionalLightCount = std::min<Core::uint32>(
lightingData.additionalLightCount,
kMaxLightingAdditionalLightCount);
lightingConstants.lightingParams = Math::Vector4(
static_cast<float>(additionalLightCount),
kForwardAmbientIntensity,
0.0f,
0.0f);
for (Core::uint32 index = 0; index < additionalLightCount; ++index) {
lightingConstants.additionalLights[index] =
BuildAdditionalLightConstants(lightingData.additionalLights[index]);
}
return lightingConstants;
}
BuiltinForwardPipeline::AdditionalLightConstants BuiltinForwardPipeline::BuildAdditionalLightConstants(
const RenderAdditionalLightData& lightData) {
AdditionalLightConstants constants = {};
constants.colorAndIntensity = Math::Vector4(
lightData.color.r,
lightData.color.g,
lightData.color.b,
lightData.intensity);
constants.positionAndRange = Math::Vector4(
lightData.position.x,
lightData.position.y,
lightData.position.z,
lightData.range);
constants.directionAndType = Math::Vector4(
lightData.direction.x,
lightData.direction.y,
lightData.direction.z,
static_cast<float>(lightData.type));
if (lightData.type == RenderLightType::Spot) {
const float outerHalfAngleRadians = Math::Radians(lightData.spotAngle * 0.5f);
const float innerHalfAngleRadians = outerHalfAngleRadians * kSpotInnerAngleRatio;
constants.spotAnglesAndFlags = Math::Vector4(
std::cos(outerHalfAngleRadians),
std::cos(innerHalfAngleRadians),
lightData.castsShadows ? 1.0f : 0.0f,
0.0f);
} else {
constants.spotAnglesAndFlags = Math::Vector4(
0.0f,
0.0f,
lightData.castsShadows ? 1.0f : 0.0f,
0.0f);
}
return constants;
}
bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(
const ScenePhaseExecutionContext& executionContext) {
return DrawVisibleItems(
executionContext.frameContext,
BuildDrawSettings(executionContext.scenePhase));
}
bool BuiltinForwardPipeline::ExecuteForwardTransparentPass(
const ScenePhaseExecutionContext& executionContext) {
return DrawVisibleItems(
executionContext.frameContext,
BuildDrawSettings(executionContext.scenePhase));
}
bool BuiltinForwardPipeline::DrawVisibleItem(
const FrameExecutionContext& executionContext,
const VisibleRenderItem& visibleItem,
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) {
const RenderContext& context = executionContext.renderContext;
const RenderSceneData& sceneData = executionContext.sceneData;
const RenderResourceCache::CachedMesh* cachedMesh = m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh);
if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) {
return false;
}
RHI::RHICommandList* commandList = context.commandList;
RHI::RHIResourceView* vertexBuffers[] = { cachedMesh->vertexBufferView };
const uint64_t offsets[] = { 0 };
const uint32_t strides[] = { cachedMesh->vertexStride };
commandList->SetVertexBuffers(0, 1, vertexBuffers, offsets, strides);
if (cachedMesh->indexBufferView != nullptr) {
commandList->SetIndexBuffer(cachedMesh->indexBufferView, 0);
}
const PerObjectConstants constants = {
sceneData.cameraData.projection,
sceneData.cameraData.view,
visibleItem.localToWorld.Transpose(),
visibleItem.localToWorld.Inverse()
};
const LightingConstants lightingConstants = BuildLightingConstants(sceneData.lighting);
ShadowReceiverConstants shadowReceiverConstants = {};
if (sceneData.lighting.HasMainDirectionalShadow()) {
shadowReceiverConstants.worldToShadow = sceneData.lighting.mainDirectionalShadow.viewProjection;
shadowReceiverConstants.shadowMapMetrics = sceneData.lighting.mainDirectionalShadow.mapMetrics;
shadowReceiverConstants.shadowSampling = sceneData.lighting.mainDirectionalShadow.sampling;
}
const ResolvedShaderPass resolvedShaderPass =
ResolveSurfaceShaderPass(
sceneData,
material,
preferredPass);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return false;
}
const Resources::MaterialRenderState effectiveRenderState =
ResolveEffectiveRenderState(resolvedShaderPass.pass, material);
PassLayoutKey passLayoutKey = {};
passLayoutKey.shader = resolvedShaderPass.shader;
passLayoutKey.passName = resolvedShaderPass.passName;
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
return false;
}
RHI::RHIResourceView* baseColorTextureView = ResolveTextureView(material);
if (passLayout->baseColorTexture.IsValid() && baseColorTextureView == nullptr) {
return false;
}
RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow()
? sceneData.lighting.mainDirectionalShadow.shadowMap
: m_fallbackTexture2DView;
if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) {
return false;
}
MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
if (passLayout->material.IsValid() && !materialConstants.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline requires a schema-backed material constant payload");
return false;
}
if (passLayout->descriptorSetCount > 0) {
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
for (Core::uint32 descriptorOffset = 0; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
if (setIndex >= passLayout->setLayouts.size()) {
return false;
}
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
RHI::RHIDescriptorSet* descriptorSet = nullptr;
if (UsesDynamicSurfaceDescriptorSet(setLayout)) {
const Core::uint64 objectId =
(setLayout.usesPerObject && visibleItem.gameObject != nullptr)
? visibleItem.gameObject->GetID()
: 0;
const Resources::Material* materialKey =
(setLayout.usesMaterial ||
setLayout.usesBaseColorTexture ||
setLayout.usesMaterialBuffers ||
setLayout.usesMaterialTextures)
? material
: nullptr;
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
passLayoutKey,
*passLayout,
setLayout,
setIndex,
objectId,
materialKey,
materialConstants,
lightingConstants,
shadowReceiverConstants,
baseColorTextureView,
shadowMapTextureView);
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
return false;
}
descriptorSet = cachedDescriptorSet->descriptorSet.set;
if (setLayout.usesPerObject) {
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
return false;
}
descriptorSet->WriteConstant(
passLayout->perObject.binding,
&constants,
sizeof(constants));
}
} else {
descriptorSet = GetOrCreateStaticDescriptorSet(*passLayout, setIndex);
if (descriptorSet == nullptr) {
return false;
}
}
descriptorSets[descriptorOffset] = descriptorSet;
}
commandList->SetGraphicsDescriptorSets(
passLayout->firstDescriptorSet,
passLayout->descriptorSetCount,
descriptorSets.data(),
passLayout->pipelineLayout);
}
ApplyDynamicRenderState(effectiveRenderState, *commandList);
if (visibleItem.hasSection) {
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
if (visibleItem.sectionIndex >= sections.Size()) {
return false;
}
const Resources::MeshSection& section = sections[visibleItem.sectionIndex];
if (cachedMesh->indexBufferView != nullptr && section.indexCount > 0) {
// MeshLoader flattens section indices into a single global index buffer.
commandList->DrawIndexed(section.indexCount, 1, section.startIndex, 0, 0);
} else if (section.vertexCount > 0) {
commandList->Draw(section.vertexCount, 1, section.baseVertex, 0);
}
return true;
}
if (cachedMesh->indexBufferView != nullptr && cachedMesh->indexCount > 0) {
commandList->DrawIndexed(cachedMesh->indexCount, 1, 0, 0, 0);
} else if (cachedMesh->vertexCount > 0) {
commandList->Draw(cachedMesh->vertexCount, 1, 0, 0);
}
return true;
}
bool BuiltinForwardPipeline::DrawVisibleItems(
const FrameExecutionContext& executionContext,
const DrawSettings& drawSettings) {
const RenderContext& context = executionContext.renderContext;
const RenderSurface& surface = executionContext.surface;
const RenderSceneData& sceneData = executionContext.sceneData;
RHI::RHICommandList* commandList = context.commandList;
RHI::RHIPipelineState* currentPipelineState = nullptr;
bool drawFailed = false;
BuiltinMaterialPass requestedPass = BuiltinMaterialPass::ForwardLit;
bool hasRequestedPass = false;
if (!TryResolveRequestedSurfacePassType(
drawSettings,
requestedPass,
hasRequestedPass)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String(
"BuiltinForwardPipeline only supports explicit shaderPassName values of ForwardLit or Unlit for scene draws, received: ") +
drawSettings.shaderPassName)
.CStr());
return false;
}
const BuiltinMaterialPass* preferredPass =
hasRequestedPass
? &requestedPass
: nullptr;
auto drawVisibleItem = [&](const VisibleRenderItem& visibleItem) {
if (drawFailed) {
return;
}
const Resources::Material* material =
drawSettings.HasOverrideMaterial()
? drawSettings.overrideMaterial.Get()
: ResolveMaterial(visibleItem);
BuiltinMaterialPass pass =
preferredPass != nullptr
? *preferredPass
: BuiltinMaterialPass::ForwardLit;
if (!TryResolveSurfacePassType(
material,
preferredPass,
pass)) {
return;
}
RHI::RHIPipelineState* pipelineState =
GetOrCreatePipelineState(
context,
surface,
sceneData,
material,
preferredPass);
if (pipelineState == nullptr) {
drawFailed = true;
return;
}
if (pipelineState != currentPipelineState) {
commandList->SetPipelineState(pipelineState);
currentPipelineState = pipelineState;
}
if (!DrawVisibleItem(
executionContext,
visibleItem,
material,
preferredPass)) {
drawFailed = true;
}
};
VisitRendererListVisibleItems(
sceneData,
drawSettings.rendererListDesc,
drawVisibleItem);
return !drawFailed;
}
} // namespace Pipelines
} // namespace Rendering
} // namespace XCEngine