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

@@ -68,19 +68,10 @@ private:
Math::Vector4 mainLightColorAndFlags = Math::Vector4::Zero();
};
struct PerMaterialConstants {
struct FallbackPerMaterialConstants {
Math::Vector4 baseColorFactor = Math::Vector4::One();
};
struct DescriptorBindingLocation {
Core::uint32 set = UINT32_MAX;
Core::uint32 binding = UINT32_MAX;
bool IsValid() const {
return set != UINT32_MAX && binding != UINT32_MAX;
}
};
struct PassLayoutKey {
const Resources::Shader* shader = nullptr;
Containers::String passName;
@@ -115,10 +106,10 @@ private:
Core::uint32 descriptorSetCount = 0;
std::vector<PassSetLayoutMetadata> setLayouts;
std::vector<OwnedDescriptorSet> staticDescriptorSets;
DescriptorBindingLocation perObject = {};
DescriptorBindingLocation material = {};
DescriptorBindingLocation baseColorTexture = {};
DescriptorBindingLocation linearClampSampler = {};
PassResourceBindingLocation perObject = {};
PassResourceBindingLocation material = {};
PassResourceBindingLocation baseColorTexture = {};
PassResourceBindingLocation linearClampSampler = {};
};
struct DynamicDescriptorSetKey {
@@ -209,7 +200,7 @@ private:
Core::uint32 setIndex,
Core::uint64 objectId,
const Resources::Material* material,
const PerMaterialConstants& materialConstants,
const MaterialConstantPayloadView& materialConstants,
RHI::RHIResourceView* textureView);
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
void DestroyPassResourceLayout(PassResourceLayout& passLayout);

View File

@@ -7,7 +7,9 @@
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Rendering/VisibleRenderObject.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
namespace XCEngine {
namespace Rendering {
@@ -21,6 +23,53 @@ enum class BuiltinMaterialPass : Core::uint32 {
Forward = ForwardLit
};
struct PassResourceBindingLocation {
Core::uint32 set = UINT32_MAX;
Core::uint32 binding = UINT32_MAX;
bool IsValid() const {
return set != UINT32_MAX && binding != UINT32_MAX;
}
};
enum class BuiltinPassResourceSemantic : Core::uint8 {
Unknown = 0,
PerObject,
Material,
BaseColorTexture,
LinearClampSampler
};
struct BuiltinPassResourceBindingDesc {
BuiltinPassResourceSemantic semantic = BuiltinPassResourceSemantic::Unknown;
Resources::ShaderResourceType resourceType = Resources::ShaderResourceType::ConstantBuffer;
PassResourceBindingLocation location = {};
};
struct BuiltinPassResourceBindingPlan {
Containers::Array<BuiltinPassResourceBindingDesc> bindings;
Core::uint32 maxSetIndex = 0;
Core::uint32 firstDescriptorSet = 0;
Core::uint32 descriptorSetCount = 0;
bool usesConstantBuffers = false;
bool usesTextures = false;
bool usesSamplers = false;
PassResourceBindingLocation perObject = {};
PassResourceBindingLocation material = {};
PassResourceBindingLocation baseColorTexture = {};
PassResourceBindingLocation linearClampSampler = {};
const BuiltinPassResourceBindingDesc* FindBinding(BuiltinPassResourceSemantic semantic) const {
for (const BuiltinPassResourceBindingDesc& binding : bindings) {
if (binding.semantic == semantic) {
return &binding;
}
}
return nullptr;
}
};
inline Containers::String NormalizeBuiltinPassMetadataValue(const Containers::String& value) {
return value.Trim().ToLower();
}
@@ -120,6 +169,180 @@ inline bool ShaderPassMatchesBuiltinPass(
return hasMetadata;
}
inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
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 BuiltinPassResourceSemantic::PerObject;
}
if (semantic == Containers::String("material") ||
semantic == Containers::String("materialconstants")) {
return BuiltinPassResourceSemantic::Material;
}
if (semantic == Containers::String("basecolortexture") ||
semantic == Containers::String("maintex")) {
return BuiltinPassResourceSemantic::BaseColorTexture;
}
if (semantic == Containers::String("linearclampsampler")) {
return BuiltinPassResourceSemantic::LinearClampSampler;
}
return BuiltinPassResourceSemantic::Unknown;
}
inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuiltinForwardPassResourceBindings() {
Containers::Array<Resources::ShaderResourceBindingDesc> bindings;
bindings.Resize(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;
}
inline bool IsBuiltinPassResourceTypeCompatible(
BuiltinPassResourceSemantic semantic,
Resources::ShaderResourceType type) {
switch (semantic) {
case BuiltinPassResourceSemantic::PerObject:
case BuiltinPassResourceSemantic::Material:
return type == Resources::ShaderResourceType::ConstantBuffer;
case BuiltinPassResourceSemantic::BaseColorTexture:
return type == Resources::ShaderResourceType::Texture2D ||
type == Resources::ShaderResourceType::TextureCube;
case BuiltinPassResourceSemantic::LinearClampSampler:
return type == Resources::ShaderResourceType::Sampler;
case BuiltinPassResourceSemantic::Unknown:
default:
return false;
}
}
inline bool TryBuildBuiltinPassResourceBindingPlan(
const Containers::Array<Resources::ShaderResourceBindingDesc>& bindings,
BuiltinPassResourceBindingPlan& outPlan,
Containers::String* outError = nullptr) {
outPlan = {};
auto fail = [&outError](const char* message) {
if (outError != nullptr) {
*outError = message;
}
return false;
};
if (bindings.Empty()) {
return true;
}
outPlan.bindings.Reserve(bindings.Size());
Core::uint32 minBoundSet = UINT32_MAX;
Core::uint32 maxBoundSet = 0;
for (const Resources::ShaderResourceBindingDesc& binding : bindings) {
const BuiltinPassResourceSemantic semantic = ResolveBuiltinPassResourceSemantic(binding);
if (semantic == BuiltinPassResourceSemantic::Unknown) {
return fail("Unsupported builtin pass resource semantic");
}
if (!IsBuiltinPassResourceTypeCompatible(semantic, binding.type)) {
return fail("Builtin pass resource semantic/type combination is invalid");
}
PassResourceBindingLocation* location = nullptr;
switch (semantic) {
case BuiltinPassResourceSemantic::PerObject:
location = &outPlan.perObject;
break;
case BuiltinPassResourceSemantic::Material:
location = &outPlan.material;
break;
case BuiltinPassResourceSemantic::BaseColorTexture:
location = &outPlan.baseColorTexture;
break;
case BuiltinPassResourceSemantic::LinearClampSampler:
location = &outPlan.linearClampSampler;
break;
case BuiltinPassResourceSemantic::Unknown:
default:
break;
}
if (location == nullptr) {
return fail("Builtin pass resource semantic could not be mapped");
}
if (location->IsValid()) {
return fail("Builtin pass resource semantic appears more than once");
}
for (const BuiltinPassResourceBindingDesc& existingBinding : outPlan.bindings) {
if (existingBinding.location.set == binding.set &&
existingBinding.location.binding == binding.binding) {
return fail("Builtin pass resource set/binding pair appears more than once");
}
}
*location = { binding.set, binding.binding };
BuiltinPassResourceBindingDesc resolvedBinding = {};
resolvedBinding.semantic = semantic;
resolvedBinding.resourceType = binding.type;
resolvedBinding.location = *location;
outPlan.bindings.PushBack(resolvedBinding);
outPlan.maxSetIndex = std::max(outPlan.maxSetIndex, binding.set);
minBoundSet = std::min(minBoundSet, binding.set);
maxBoundSet = std::max(maxBoundSet, binding.set);
switch (binding.type) {
case Resources::ShaderResourceType::ConstantBuffer:
outPlan.usesConstantBuffers = true;
break;
case Resources::ShaderResourceType::Texture2D:
case Resources::ShaderResourceType::TextureCube:
outPlan.usesTextures = true;
break;
case Resources::ShaderResourceType::Sampler:
outPlan.usesSamplers = true;
break;
default:
break;
}
}
outPlan.firstDescriptorSet = minBoundSet;
outPlan.descriptorSetCount = maxBoundSet - minBoundSet + 1u;
return true;
}
struct BuiltinForwardMaterialData {
Math::Vector4 baseColorFactor = Math::Vector4::One();
};