Generalize builtin pass resource binding plan

This commit is contained in:
2026-04-03 16:59:18 +08:00
parent e0e5c1fcaa
commit 2de254a16f
5 changed files with 311 additions and 159 deletions

View File

@@ -52,42 +52,6 @@ private:
namespace {
enum class ForwardPassSemantic : uint8_t {
Unknown = 0,
PerObject,
Material,
BaseColorTexture,
LinearClampSampler
};
ForwardPassSemantic ResolveForwardPassSemantic(const Resources::ShaderResourceBindingDesc& binding) {
Containers::String semantic = NormalizeBuiltinPassMetadataValue(binding.semantic);
if (semantic.Empty()) {
semantic = NormalizeBuiltinPassMetadataValue(binding.name);
}
if (semantic == Containers::String("perobject") ||
semantic == Containers::String("perobjectconstants")) {
return ForwardPassSemantic::PerObject;
}
if (semantic == Containers::String("material") ||
semantic == Containers::String("materialconstants")) {
return ForwardPassSemantic::Material;
}
if (semantic == Containers::String("basecolortexture") ||
semantic == Containers::String("maintex")) {
return ForwardPassSemantic::BaseColorTexture;
}
if (semantic == Containers::String("linearclampsampler")) {
return ForwardPassSemantic::LinearClampSampler;
}
return ForwardPassSemantic::Unknown;
}
RHI::DescriptorType ToDescriptorType(Resources::ShaderResourceType type) {
switch (type) {
case Resources::ShaderResourceType::ConstantBuffer:
@@ -154,36 +118,6 @@ bool BindingNumberExists(
return false;
}
std::vector<Resources::ShaderResourceBindingDesc> BuildLegacyForwardResourceBindings() {
std::vector<Resources::ShaderResourceBindingDesc> bindings(4);
bindings[0].name = "PerObjectConstants";
bindings[0].type = Resources::ShaderResourceType::ConstantBuffer;
bindings[0].set = 1;
bindings[0].binding = 0;
bindings[0].semantic = "PerObject";
bindings[1].name = "MaterialConstants";
bindings[1].type = Resources::ShaderResourceType::ConstantBuffer;
bindings[1].set = 2;
bindings[1].binding = 0;
bindings[1].semantic = "Material";
bindings[2].name = "BaseColorTexture";
bindings[2].type = Resources::ShaderResourceType::Texture2D;
bindings[2].set = 3;
bindings[2].binding = 0;
bindings[2].semantic = "BaseColorTexture";
bindings[3].name = "LinearClampSampler";
bindings[3].type = Resources::ShaderResourceType::Sampler;
bindings[3].set = 4;
bindings[3].binding = 0;
bindings[3].semantic = "LinearClampSampler";
return bindings;
}
const Resources::ShaderPass* FindForwardCompatiblePass(
const Resources::Shader& shader,
const Resources::Material* material,
@@ -588,16 +522,6 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
return &existing->second;
}
std::vector<Resources::ShaderResourceBindingDesc> resourceBindings;
if (resolvedShaderPass.pass->resources.Empty()) {
resourceBindings = BuildLegacyForwardResourceBindings();
} else {
resourceBindings.reserve(resolvedShaderPass.pass->resources.Size());
for (const Resources::ShaderResourceBindingDesc& binding : resolvedShaderPass.pass->resources) {
resourceBindings.push_back(binding);
}
}
PassResourceLayout passLayout = {};
auto failLayout = [this, &passLayout](const char* message) -> PassResourceLayout* {
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
@@ -605,61 +529,44 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
return nullptr;
};
Core::uint32 minBoundSet = UINT32_MAX;
Core::uint32 maxBoundSet = 0;
Core::uint32 maxSetIndex = 0;
bool hasAnyResource = false;
bool usesConstantBuffers = false;
bool usesTextures = false;
bool usesSamplers = false;
for (const Resources::ShaderResourceBindingDesc& binding : resourceBindings) {
maxSetIndex = std::max(maxSetIndex, binding.set);
hasAnyResource = true;
minBoundSet = std::min(minBoundSet, binding.set);
maxBoundSet = std::max(maxBoundSet, binding.set);
switch (binding.type) {
case Resources::ShaderResourceType::ConstantBuffer:
usesConstantBuffers = true;
break;
case Resources::ShaderResourceType::Texture2D:
case Resources::ShaderResourceType::TextureCube:
usesTextures = true;
break;
case Resources::ShaderResourceType::Sampler:
usesSamplers = true;
break;
default:
break;
}
Containers::Array<Resources::ShaderResourceBindingDesc> resourceBindings = resolvedShaderPass.pass->resources;
if (resourceBindings.Empty()) {
resourceBindings = BuildLegacyBuiltinForwardPassResourceBindings();
}
BuiltinPassResourceBindingPlan bindingPlan = {};
Containers::String bindingPlanError;
if (!TryBuildBuiltinPassResourceBindingPlan(resourceBindings, bindingPlan, &bindingPlanError)) {
return failLayout(bindingPlanError.CStr());
}
const bool hasAnyResource = !bindingPlan.bindings.Empty();
if (hasAnyResource) {
passLayout.setLayouts.resize(static_cast<size_t>(maxSetIndex) + 1u);
passLayout.setLayouts.resize(static_cast<size_t>(bindingPlan.maxSetIndex) + 1u);
passLayout.staticDescriptorSets.resize(passLayout.setLayouts.size());
passLayout.firstDescriptorSet = minBoundSet;
passLayout.descriptorSetCount = maxBoundSet - minBoundSet + 1u;
passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet;
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
}
for (const Resources::ShaderResourceBindingDesc& binding : resourceBindings) {
ForwardPassSemantic semantic = ResolveForwardPassSemantic(binding);
if (semantic == ForwardPassSemantic::Unknown) {
return failLayout("BuiltinForwardPipeline encountered an unsupported forward shader resource semantic");
}
passLayout.perObject = bindingPlan.perObject;
passLayout.material = bindingPlan.material;
passLayout.baseColorTexture = bindingPlan.baseColorTexture;
passLayout.linearClampSampler = bindingPlan.linearClampSampler;
if (binding.set >= passLayout.setLayouts.size()) {
for (const BuiltinPassResourceBindingDesc& binding : bindingPlan.bindings) {
if (binding.location.set >= passLayout.setLayouts.size()) {
return failLayout("BuiltinForwardPipeline encountered an invalid forward shader resource set");
}
const RHI::DescriptorType descriptorType = ToDescriptorType(binding.type);
const RHI::DescriptorHeapType heapType = ResolveDescriptorHeapType(binding.type);
PassSetLayoutMetadata& setLayout = passLayout.setLayouts[binding.set];
const RHI::DescriptorType descriptorType = ToDescriptorType(binding.resourceType);
const RHI::DescriptorHeapType heapType = ResolveDescriptorHeapType(binding.resourceType);
PassSetLayoutMetadata& setLayout = passLayout.setLayouts[binding.location.set];
if (!setLayout.bindings.empty() && setLayout.heapType != heapType) {
return failLayout("BuiltinForwardPipeline does not support mixing sampler and non-sampler bindings in one set");
}
if (BindingNumberExists(setLayout.bindings, binding.binding)) {
if (BindingNumberExists(setLayout.bindings, binding.location.binding)) {
return failLayout("BuiltinForwardPipeline encountered duplicate bindings inside one descriptor set");
}
@@ -668,40 +575,22 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
}
RHI::DescriptorSetLayoutBinding layoutBinding = {};
layoutBinding.binding = binding.binding;
layoutBinding.binding = binding.location.binding;
layoutBinding.type = static_cast<uint32_t>(descriptorType);
layoutBinding.count = 1;
setLayout.bindings.push_back(layoutBinding);
switch (semantic) {
case ForwardPassSemantic::PerObject:
if (binding.type != Resources::ShaderResourceType::ConstantBuffer || passLayout.perObject.IsValid()) {
return failLayout("BuiltinForwardPipeline requires a single constant-buffer PerObject resource");
}
passLayout.perObject = { binding.set, binding.binding };
switch (binding.semantic) {
case BuiltinPassResourceSemantic::PerObject:
setLayout.usesPerObject = true;
break;
case ForwardPassSemantic::Material:
if (binding.type != Resources::ShaderResourceType::ConstantBuffer || passLayout.material.IsValid()) {
return failLayout("BuiltinForwardPipeline requires a single constant-buffer Material resource");
}
passLayout.material = { binding.set, binding.binding };
case BuiltinPassResourceSemantic::Material:
setLayout.usesMaterial = true;
break;
case ForwardPassSemantic::BaseColorTexture:
if ((binding.type != Resources::ShaderResourceType::Texture2D &&
binding.type != Resources::ShaderResourceType::TextureCube) ||
passLayout.baseColorTexture.IsValid()) {
return failLayout("BuiltinForwardPipeline requires a single texture BaseColorTexture resource");
}
passLayout.baseColorTexture = { binding.set, binding.binding };
case BuiltinPassResourceSemantic::BaseColorTexture:
setLayout.usesTexture = true;
break;
case ForwardPassSemantic::LinearClampSampler:
if (binding.type != Resources::ShaderResourceType::Sampler || passLayout.linearClampSampler.IsValid()) {
return failLayout("BuiltinForwardPipeline requires a single sampler LinearClampSampler resource");
}
passLayout.linearClampSampler = { binding.set, binding.binding };
case BuiltinPassResourceSemantic::LinearClampSampler:
setLayout.usesSampler = true;
break;
default:
@@ -718,7 +607,7 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
!passLayout.setLayouts.empty() &&
passLayout.setLayouts[0].bindings.empty()) {
PassSetLayoutMetadata& compatibilitySet = passLayout.setLayouts[0];
if (usesConstantBuffers) {
if (bindingPlan.usesConstantBuffers) {
compatibilitySet.bindings.push_back({
0,
static_cast<uint32_t>(RHI::DescriptorType::CBV),
@@ -726,7 +615,7 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
0
});
}
if (usesTextures) {
if (bindingPlan.usesTextures) {
compatibilitySet.bindings.push_back({
0,
static_cast<uint32_t>(RHI::DescriptorType::SRV),
@@ -734,7 +623,7 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
0
});
}
if (usesSamplers) {
if (bindingPlan.usesSamplers) {
compatibilitySet.bindings.push_back({
0,
static_cast<uint32_t>(RHI::DescriptorType::Sampler),