Files
XCEngine/engine/include/XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h

427 lines
15 KiB
C++

#pragma once
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
#include <algorithm>
namespace XCEngine {
namespace Rendering {
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::Lighting:
location = &outPlan.lighting;
break;
case BuiltinPassResourceSemantic::ShadowReceiver:
location = &outPlan.shadowReceiver;
break;
case BuiltinPassResourceSemantic::BaseColorTexture:
location = &outPlan.baseColorTexture;
break;
case BuiltinPassResourceSemantic::LinearClampSampler:
location = &outPlan.linearClampSampler;
break;
case BuiltinPassResourceSemantic::ShadowMapTexture:
location = &outPlan.shadowMapTexture;
break;
case BuiltinPassResourceSemantic::ShadowMapSampler:
location = &outPlan.shadowMapSampler;
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;
}
inline void AppendBuiltinPassResourceBinding(
Containers::Array<Resources::ShaderResourceBindingDesc>& bindings,
const char* name,
Resources::ShaderResourceType type,
Core::uint32 set,
Core::uint32 binding,
const char* semantic) {
Resources::ShaderResourceBindingDesc desc = {};
desc.name = name;
desc.type = type;
desc.set = set;
desc.binding = binding;
desc.semantic = semantic;
bindings.PushBack(desc);
}
inline bool TryBuildImplicitBuiltinPassResourceBindings(
const Resources::ShaderPass& shaderPass,
Containers::Array<Resources::ShaderResourceBindingDesc>& outBindings) {
outBindings.Clear();
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ForwardLit)) {
AppendBuiltinPassResourceBinding(
outBindings,
"PerObjectConstants",
Resources::ShaderResourceType::ConstantBuffer,
0u,
0u,
"PerObject");
AppendBuiltinPassResourceBinding(
outBindings,
"LightingConstants",
Resources::ShaderResourceType::ConstantBuffer,
1u,
0u,
"Lighting");
AppendBuiltinPassResourceBinding(
outBindings,
"MaterialConstants",
Resources::ShaderResourceType::ConstantBuffer,
2u,
0u,
"Material");
AppendBuiltinPassResourceBinding(
outBindings,
"ShadowReceiverConstants",
Resources::ShaderResourceType::ConstantBuffer,
3u,
0u,
"ShadowReceiver");
AppendBuiltinPassResourceBinding(
outBindings,
"BaseColorTexture",
Resources::ShaderResourceType::Texture2D,
4u,
0u,
"BaseColorTexture");
AppendBuiltinPassResourceBinding(
outBindings,
"LinearClampSampler",
Resources::ShaderResourceType::Sampler,
5u,
0u,
"LinearClampSampler");
AppendBuiltinPassResourceBinding(
outBindings,
"ShadowMapTexture",
Resources::ShaderResourceType::Texture2D,
6u,
0u,
"ShadowMapTexture");
AppendBuiltinPassResourceBinding(
outBindings,
"ShadowMapSampler",
Resources::ShaderResourceType::Sampler,
7u,
0u,
"ShadowMapSampler");
return true;
}
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::Unlit) ||
ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::DepthOnly) ||
ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ShadowCaster)) {
AppendBuiltinPassResourceBinding(
outBindings,
"PerObjectConstants",
Resources::ShaderResourceType::ConstantBuffer,
0u,
0u,
"PerObject");
AppendBuiltinPassResourceBinding(
outBindings,
"MaterialConstants",
Resources::ShaderResourceType::ConstantBuffer,
1u,
0u,
"Material");
AppendBuiltinPassResourceBinding(
outBindings,
"BaseColorTexture",
Resources::ShaderResourceType::Texture2D,
2u,
0u,
"BaseColorTexture");
AppendBuiltinPassResourceBinding(
outBindings,
"LinearClampSampler",
Resources::ShaderResourceType::Sampler,
3u,
0u,
"LinearClampSampler");
return true;
}
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ObjectId)) {
AppendBuiltinPassResourceBinding(
outBindings,
"PerObjectConstants",
Resources::ShaderResourceType::ConstantBuffer,
0u,
0u,
"PerObject");
return true;
}
return false;
}
inline bool TryBuildBuiltinPassResourceBindingPlan(
const Resources::ShaderPass& shaderPass,
BuiltinPassResourceBindingPlan& outPlan,
Containers::String* outError = nullptr) {
if (!shaderPass.resources.Empty()) {
return TryBuildBuiltinPassResourceBindingPlan(shaderPass.resources, outPlan, outError);
}
Containers::Array<Resources::ShaderResourceBindingDesc> implicitBindings;
if (!TryBuildImplicitBuiltinPassResourceBindings(shaderPass, implicitBindings)) {
if (outError != nullptr) {
*outError = "Builtin pass does not declare explicit bindings and no implicit contract is available";
}
outPlan = {};
return false;
}
return TryBuildBuiltinPassResourceBindingPlan(implicitBindings, outPlan, outError);
}
inline RHI::DescriptorType ToBuiltinPassDescriptorType(Resources::ShaderResourceType type) {
switch (type) {
case Resources::ShaderResourceType::ConstantBuffer:
return RHI::DescriptorType::CBV;
case Resources::ShaderResourceType::Texture2D:
case Resources::ShaderResourceType::TextureCube:
return RHI::DescriptorType::SRV;
case Resources::ShaderResourceType::Sampler:
return RHI::DescriptorType::Sampler;
default:
return RHI::DescriptorType::CBV;
}
}
inline RHI::DescriptorHeapType ResolveBuiltinPassDescriptorHeapType(Resources::ShaderResourceType type) {
return type == Resources::ShaderResourceType::Sampler
? RHI::DescriptorHeapType::Sampler
: RHI::DescriptorHeapType::CBV_SRV_UAV;
}
inline bool IsBuiltinPassShaderVisibleSet(const std::vector<RHI::DescriptorSetLayoutBinding>& bindings) {
for (const RHI::DescriptorSetLayoutBinding& binding : bindings) {
const RHI::DescriptorType descriptorType = static_cast<RHI::DescriptorType>(binding.type);
if (descriptorType == RHI::DescriptorType::SRV ||
descriptorType == RHI::DescriptorType::UAV ||
descriptorType == RHI::DescriptorType::Sampler) {
return true;
}
}
return false;
}
inline Core::uint32 CountBuiltinPassHeapDescriptors(
RHI::DescriptorHeapType heapType,
const std::vector<RHI::DescriptorSetLayoutBinding>& bindings) {
Core::uint32 descriptorCount = 0;
for (const RHI::DescriptorSetLayoutBinding& binding : bindings) {
const RHI::DescriptorType descriptorType = static_cast<RHI::DescriptorType>(binding.type);
if (heapType == RHI::DescriptorHeapType::Sampler) {
if (descriptorType == RHI::DescriptorType::Sampler) {
descriptorCount += binding.count > 0 ? binding.count : 1u;
}
continue;
}
if (descriptorType != RHI::DescriptorType::Sampler) {
descriptorCount += binding.count > 0 ? binding.count : 1u;
}
}
return descriptorCount > 0 ? descriptorCount : 1u;
}
inline void RefreshBuiltinPassSetLayoutMetadata(BuiltinPassSetLayoutMetadata& setLayout) {
std::sort(
setLayout.bindings.begin(),
setLayout.bindings.end(),
[](const RHI::DescriptorSetLayoutBinding& left, const RHI::DescriptorSetLayoutBinding& right) {
return left.binding < right.binding;
});
setLayout.shaderVisible = IsBuiltinPassShaderVisibleSet(setLayout.bindings);
setLayout.layout.bindings = setLayout.bindings.empty() ? nullptr : setLayout.bindings.data();
setLayout.layout.bindingCount = static_cast<Core::uint32>(setLayout.bindings.size());
}
inline void RefreshBuiltinPassSetLayouts(std::vector<BuiltinPassSetLayoutMetadata>& setLayouts) {
for (BuiltinPassSetLayoutMetadata& setLayout : setLayouts) {
RefreshBuiltinPassSetLayoutMetadata(setLayout);
}
}
inline bool TryBuildBuiltinPassSetLayouts(
const BuiltinPassResourceBindingPlan& bindingPlan,
std::vector<BuiltinPassSetLayoutMetadata>& outSetLayouts,
Containers::String* outError = nullptr) {
outSetLayouts.clear();
auto fail = [&outError](const char* message) {
if (outError != nullptr) {
*outError = message;
}
return false;
};
if (bindingPlan.bindings.Empty()) {
return true;
}
outSetLayouts.resize(static_cast<size_t>(bindingPlan.maxSetIndex) + 1u);
for (const BuiltinPassResourceBindingDesc& binding : bindingPlan.bindings) {
if (binding.location.set >= outSetLayouts.size()) {
return fail("Builtin pass encountered an invalid descriptor set index");
}
const RHI::DescriptorType descriptorType = ToBuiltinPassDescriptorType(binding.resourceType);
const RHI::DescriptorHeapType heapType = ResolveBuiltinPassDescriptorHeapType(binding.resourceType);
BuiltinPassSetLayoutMetadata& setLayout = outSetLayouts[binding.location.set];
if (!setLayout.bindings.empty() && setLayout.heapType != heapType) {
return fail("Builtin pass does not support mixing sampler and non-sampler bindings in one set");
}
for (const RHI::DescriptorSetLayoutBinding& existingBinding : setLayout.bindings) {
if (existingBinding.binding == binding.location.binding) {
return fail("Builtin pass encountered duplicate bindings inside one descriptor set");
}
}
if (setLayout.bindings.empty()) {
setLayout.heapType = heapType;
}
RHI::DescriptorSetLayoutBinding layoutBinding = {};
layoutBinding.binding = binding.location.binding;
layoutBinding.type = static_cast<Core::uint32>(descriptorType);
layoutBinding.count = 1;
setLayout.bindings.push_back(layoutBinding);
switch (binding.semantic) {
case BuiltinPassResourceSemantic::PerObject:
setLayout.usesPerObject = true;
break;
case BuiltinPassResourceSemantic::Material:
setLayout.usesMaterial = true;
break;
case BuiltinPassResourceSemantic::Lighting:
setLayout.usesLighting = true;
break;
case BuiltinPassResourceSemantic::ShadowReceiver:
setLayout.usesShadowReceiver = true;
break;
case BuiltinPassResourceSemantic::BaseColorTexture:
setLayout.usesTexture = true;
setLayout.usesBaseColorTexture = true;
break;
case BuiltinPassResourceSemantic::ShadowMapTexture:
setLayout.usesTexture = true;
setLayout.usesShadowMapTexture = true;
break;
case BuiltinPassResourceSemantic::LinearClampSampler:
setLayout.usesSampler = true;
setLayout.usesLinearClampSampler = true;
break;
case BuiltinPassResourceSemantic::ShadowMapSampler:
setLayout.usesSampler = true;
setLayout.usesShadowMapSampler = true;
break;
case BuiltinPassResourceSemantic::Unknown:
default:
return fail("Builtin pass encountered an unsupported resource semantic");
}
}
RefreshBuiltinPassSetLayouts(outSetLayouts);
return true;
}
} // namespace Rendering
} // namespace XCEngine