Remove legacy object-id and depth-style binding fallbacks

This commit is contained in:
2026-04-05 13:38:50 +08:00
parent 10fda68694
commit f407e2d15c
4 changed files with 119 additions and 79 deletions

View File

@@ -267,45 +267,6 @@ inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuilti
return bindings; return bindings;
} }
inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuiltinObjectIdPassResourceBindings() {
Containers::Array<Resources::ShaderResourceBindingDesc> bindings;
bindings.Resize(1);
bindings[0].name = "PerObjectConstants";
bindings[0].type = Resources::ShaderResourceType::ConstantBuffer;
bindings[0].set = 0;
bindings[0].binding = 0;
bindings[0].semantic = "PerObject";
return bindings;
}
inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuiltinDepthOnlyPassResourceBindings() {
Containers::Array<Resources::ShaderResourceBindingDesc> bindings;
bindings.Resize(1);
bindings[0].name = "PerObjectConstants";
bindings[0].type = Resources::ShaderResourceType::ConstantBuffer;
bindings[0].set = 0;
bindings[0].binding = 0;
bindings[0].semantic = "PerObject";
return bindings;
}
inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuiltinShadowCasterPassResourceBindings() {
Containers::Array<Resources::ShaderResourceBindingDesc> bindings;
bindings.Resize(1);
bindings[0].name = "PerObjectConstants";
bindings[0].type = Resources::ShaderResourceType::ConstantBuffer;
bindings[0].set = 0;
bindings[0].binding = 0;
bindings[0].semantic = "PerObject";
return bindings;
}
inline bool IsBuiltinPassResourceTypeCompatible( inline bool IsBuiltinPassResourceTypeCompatible(
BuiltinPassResourceSemantic semantic, BuiltinPassResourceSemantic semantic,
Resources::ShaderResourceType type) { Resources::ShaderResourceType type) {

View File

@@ -24,18 +24,6 @@ namespace Passes {
namespace { namespace {
Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuiltinDepthStylePassResourceBindings(
BuiltinMaterialPass passType) {
switch (passType) {
case BuiltinMaterialPass::DepthOnly:
return BuildLegacyBuiltinDepthOnlyPassResourceBindings();
case BuiltinMaterialPass::ShadowCaster:
return BuildLegacyBuiltinShadowCasterPassResourceBindings();
default:
return {};
}
}
bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) { bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) {
return bindingPlan.perObject.IsValid() && return bindingPlan.perObject.IsValid() &&
bindingPlan.bindings.Size() == 1u && bindingPlan.bindings.Size() == 1u &&
@@ -91,7 +79,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
ApplyMaterialRenderState(material, pipelineDesc); ApplyMaterialRenderState(material, pipelineDesc);
pipelineDesc.blendState.blendEnable = false; pipelineDesc.blendState.blendEnable = false;
pipelineDesc.blendState.colorWriteMask = 0; pipelineDesc.blendState.colorWriteMask = pipelineDesc.renderTargetCount > 0 ? 0xF : 0;
pipelineDesc.depthStencilState.depthTestEnable = true; pipelineDesc.depthStencilState.depthTestEnable = true;
pipelineDesc.depthStencilState.depthWriteEnable = true; pipelineDesc.depthStencilState.depthWriteEnable = true;
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual); pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual);
@@ -101,11 +89,9 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
} }
if (pipelineDesc.renderTargetCount > 0) { if (const Resources::ShaderStageVariant* fragmentVariant =
if (const Resources::ShaderStageVariant* fragmentVariant = shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
}
} }
return pipelineDesc; return pipelineDesc;
@@ -387,12 +373,16 @@ bool BuiltinDepthStylePassBase::TryBuildSupportedBindingPlan(
const Resources::ShaderPass& shaderPass, const Resources::ShaderPass& shaderPass,
BuiltinPassResourceBindingPlan& outPlan, BuiltinPassResourceBindingPlan& outPlan,
Containers::String* outError) const { Containers::String* outError) const {
Containers::Array<Resources::ShaderResourceBindingDesc> resourceBindings = shaderPass.resources; if (shaderPass.resources.Empty()) {
if (resourceBindings.Empty()) { if (outError != nullptr) {
resourceBindings = BuildLegacyBuiltinDepthStylePassResourceBindings(m_passType); *outError =
Containers::String("Builtin depth-style pass requires explicit resource bindings on shader pass: ") +
shaderPass.name;
}
return false;
} }
if (!TryBuildBuiltinPassResourceBindingPlan(resourceBindings, outPlan, outError)) { if (!TryBuildBuiltinPassResourceBindingPlan(shaderPass.resources, outPlan, outError)) {
return false; return false;
} }
@@ -463,7 +453,8 @@ BuiltinDepthStylePassBase::PassResourceLayout* BuiltinDepthStylePassBase::GetOrC
return failLayout("Builtin depth-style pass failed to create pipeline layout"); return failLayout("Builtin depth-style pass failed to create pipeline layout");
} }
const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout); const auto result = m_passResourceLayouts.emplace(passLayoutKey, std::move(passLayout));
RefreshBuiltinPassSetLayoutMetadata(result.first->second.perObjectSetLayout);
return &result.first->second; return &result.first->second;
} }
@@ -597,18 +588,29 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
const RenderSceneData& sceneData, const RenderSceneData& sceneData,
const VisibleRenderItem& visibleItem) { const VisibleRenderItem& visibleItem) {
if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) { if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinDepthStylePassBase skipped visible item because mesh or game object was null");
return false; return false;
} }
const RenderResourceCache::CachedMesh* cachedMesh = const RenderResourceCache::CachedMesh* cachedMesh =
m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh); m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh);
if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) { if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinDepthStylePassBase failed to cache mesh for ") +
visibleItem.gameObject->GetName().c_str()).CStr());
return false; return false;
} }
const Resources::Material* material = ResolveMaterial(visibleItem); const Resources::Material* material = ResolveMaterial(visibleItem);
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinDepthStylePassBase could not resolve shader pass for ") +
visibleItem.gameObject->GetName().c_str()).CStr());
return false; return false;
} }
@@ -618,11 +620,20 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass); PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) { if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinDepthStylePassBase failed to create pass layout for ") +
visibleItem.gameObject->GetName().c_str()).CStr());
return false; return false;
} }
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material); RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material);
if (pipelineState == nullptr) { if (pipelineState == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinDepthStylePassBase failed to create pipeline state for ") +
visibleItem.gameObject->GetName().c_str() +
" using pass " + resolvedShaderPass.passName).CStr());
return false; return false;
} }
@@ -642,12 +653,24 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
*passLayout, *passLayout,
visibleItem.gameObject->GetID()); visibleItem.gameObject->GetID());
if (constantSet == nullptr) { if (constantSet == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinDepthStylePassBase failed to allocate descriptor set for ") +
visibleItem.gameObject->GetName().c_str()).CStr());
return false; return false;
} }
const Math::Matrix4x4 projectionMatrix =
m_passType == BuiltinMaterialPass::ShadowCaster
? sceneData.cameraData.viewProjection
: sceneData.cameraData.projection;
const Math::Matrix4x4 viewMatrix =
m_passType == BuiltinMaterialPass::ShadowCaster
? Math::Matrix4x4::Identity()
: sceneData.cameraData.view;
const PerObjectConstants constants = { const PerObjectConstants constants = {
sceneData.cameraData.projection, projectionMatrix,
sceneData.cameraData.view, viewMatrix,
visibleItem.localToWorld.Transpose() visibleItem.localToWorld.Transpose()
}; };
constantSet->WriteConstant(passLayout->perObject.binding, &constants, sizeof(constants)); constantSet->WriteConstant(passLayout->perObject.binding, &constants, sizeof(constants));
@@ -662,6 +685,10 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
if (visibleItem.hasSection) { if (visibleItem.hasSection) {
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections(); const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
if (visibleItem.sectionIndex >= sections.Size()) { if (visibleItem.sectionIndex >= sections.Size()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinDepthStylePassBase received invalid mesh section for ") +
visibleItem.gameObject->GetName().c_str()).CStr());
return false; return false;
} }

View File

@@ -243,9 +243,14 @@ bool BuiltinObjectIdPass::CreateResources(const RenderContext& context) {
return false; return false;
} }
Containers::Array<Resources::ShaderResourceBindingDesc> resourceBindings = objectIdPass->resources; const Containers::Array<Resources::ShaderResourceBindingDesc>& resourceBindings = objectIdPass->resources;
if (resourceBindings.Empty()) { if (resourceBindings.Empty()) {
resourceBindings = BuildLegacyBuiltinObjectIdPassResourceBindings(); Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinObjectIdPass requires explicit resource bindings on shader pass: ") +
objectIdPass->name).CStr());
DestroyResources();
return false;
} }
BuiltinPassResourceBindingPlan bindingPlan = {}; BuiltinPassResourceBindingPlan bindingPlan = {};

View File

@@ -300,13 +300,22 @@ TEST(BuiltinObjectIdPass_Test, BuiltinObjectIdShaderDeclaresExplicitPerObjectRes
delete shader; delete shader;
} }
TEST(BuiltinObjectIdPass_Test, BuildsBuiltinPassResourceBindingPlanFromLegacyFallbackResources) { TEST(BuiltinObjectIdPass_Test, BuildsBuiltinPassResourceBindingPlanFromExplicitObjectIdResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinObjectIdPassResourceBindings(); ShaderLoader loader;
ASSERT_EQ(bindings.Size(), 1u); LoadResult result = loader.Load(GetBuiltinObjectIdShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ObjectId");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
BuiltinPassResourceBindingPlan plan = {}; BuiltinPassResourceBindingPlan plan = {};
String error; String error;
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr(); EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
ASSERT_EQ(plan.bindings.Size(), 1u); ASSERT_EQ(plan.bindings.Size(), 1u);
EXPECT_TRUE(plan.perObject.IsValid()); EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_FALSE(plan.material.IsValid()); EXPECT_FALSE(plan.material.IsValid());
@@ -317,6 +326,8 @@ TEST(BuiltinObjectIdPass_Test, BuildsBuiltinPassResourceBindingPlanFromLegacyFal
EXPECT_TRUE(plan.usesConstantBuffers); EXPECT_TRUE(plan.usesConstantBuffers);
EXPECT_FALSE(plan.usesTextures); EXPECT_FALSE(plan.usesTextures);
EXPECT_FALSE(plan.usesSamplers); EXPECT_FALSE(plan.usesSamplers);
delete shader;
} }
TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
@@ -346,12 +357,22 @@ TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertice
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0))); EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
} }
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyObjectIdResources) { TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitObjectIdResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinObjectIdPassResourceBindings(); ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinObjectIdShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ObjectId");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
BuiltinPassResourceBindingPlan plan = {}; BuiltinPassResourceBindingPlan plan = {};
String error; String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr(); ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts; std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
@@ -363,14 +384,26 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyObjectIdResources)
EXPECT_FALSE(setLayouts[0].usesSampler); EXPECT_FALSE(setLayouts[0].usesSampler);
EXPECT_FALSE(setLayouts[0].shaderVisible); EXPECT_FALSE(setLayouts[0].shaderVisible);
EXPECT_EQ(setLayouts[0].heapType, DescriptorHeapType::CBV_SRV_UAV); EXPECT_EQ(setLayouts[0].heapType, DescriptorHeapType::CBV_SRV_UAV);
delete shader;
} }
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyDepthOnlyResources) { TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitDepthOnlyResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinDepthOnlyPassResourceBindings(); ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("DepthOnly");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
BuiltinPassResourceBindingPlan plan = {}; BuiltinPassResourceBindingPlan plan = {};
String error; String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr(); ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts; std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
@@ -380,14 +413,26 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyDepthOnlyResources)
EXPECT_FALSE(setLayouts[0].usesMaterial); EXPECT_FALSE(setLayouts[0].usesMaterial);
EXPECT_FALSE(setLayouts[0].usesTexture); EXPECT_FALSE(setLayouts[0].usesTexture);
EXPECT_FALSE(setLayouts[0].usesSampler); EXPECT_FALSE(setLayouts[0].usesSampler);
delete shader;
} }
TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyShadowCasterResources) { TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromExplicitShadowCasterResources) {
const Array<ShaderResourceBindingDesc> bindings = BuildLegacyBuiltinShadowCasterPassResourceBindings(); ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinShadowCasterShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ShadowCaster");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 1u);
BuiltinPassResourceBindingPlan plan = {}; BuiltinPassResourceBindingPlan plan = {};
String error; String error;
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr(); ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
std::vector<BuiltinPassSetLayoutMetadata> setLayouts; std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr(); ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
@@ -397,6 +442,8 @@ TEST(BuiltinPassLayout_Test, BuildsSharedSetLayoutsFromLegacyShadowCasterResourc
EXPECT_FALSE(setLayouts[0].usesMaterial); EXPECT_FALSE(setLayouts[0].usesMaterial);
EXPECT_FALSE(setLayouts[0].usesTexture); EXPECT_FALSE(setLayouts[0].usesTexture);
EXPECT_FALSE(setLayouts[0].usesSampler); EXPECT_FALSE(setLayouts[0].usesSampler);
delete shader;
} }
TEST(BuiltinPassLayout_Test, RejectsMixedSamplerAndNonSamplerBindingsInOneSet) { TEST(BuiltinPassLayout_Test, RejectsMixedSamplerAndNonSamplerBindingsInOneSet) {