Split builtin pass contract from material utility
This commit is contained in:
@@ -431,6 +431,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderCameraData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/CameraRenderRequest.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/VisibleRenderObject.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/BuiltinPassContract.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderSceneUtility.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPass.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderSceneExtractor.h
|
||||
|
||||
600
engine/include/XCEngine/Rendering/BuiltinPassContract.h
Normal file
600
engine/include/XCEngine/Rendering/BuiltinPassContract.h
Normal file
@@ -0,0 +1,600 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/RHI/RHITypes.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class BuiltinMaterialPass : Core::uint32 {
|
||||
ForwardLit = 0,
|
||||
Unlit,
|
||||
DepthOnly,
|
||||
ShadowCaster,
|
||||
ObjectId,
|
||||
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,
|
||||
Lighting,
|
||||
ShadowReceiver,
|
||||
BaseColorTexture,
|
||||
ShadowMapTexture,
|
||||
LinearClampSampler,
|
||||
ShadowMapSampler
|
||||
};
|
||||
|
||||
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 lighting = {};
|
||||
PassResourceBindingLocation shadowReceiver = {};
|
||||
PassResourceBindingLocation baseColorTexture = {};
|
||||
PassResourceBindingLocation linearClampSampler = {};
|
||||
PassResourceBindingLocation shadowMapTexture = {};
|
||||
PassResourceBindingLocation shadowMapSampler = {};
|
||||
|
||||
const BuiltinPassResourceBindingDesc* FindBinding(BuiltinPassResourceSemantic semantic) const {
|
||||
for (const BuiltinPassResourceBindingDesc& binding : bindings) {
|
||||
if (binding.semantic == semantic) {
|
||||
return &binding;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct BuiltinPassSetLayoutMetadata {
|
||||
std::vector<RHI::DescriptorSetLayoutBinding> bindings;
|
||||
RHI::DescriptorSetLayoutDesc layout = {};
|
||||
RHI::DescriptorHeapType heapType = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
bool shaderVisible = false;
|
||||
bool usesPerObject = false;
|
||||
bool usesMaterial = false;
|
||||
bool usesLighting = false;
|
||||
bool usesShadowReceiver = false;
|
||||
bool usesTexture = false;
|
||||
bool usesBaseColorTexture = false;
|
||||
bool usesShadowMapTexture = false;
|
||||
bool usesSampler = false;
|
||||
bool usesLinearClampSampler = false;
|
||||
bool usesShadowMapSampler = false;
|
||||
};
|
||||
|
||||
inline Containers::String NormalizeBuiltinPassMetadataValue(const Containers::String& value) {
|
||||
return value.Trim().ToLower();
|
||||
}
|
||||
|
||||
inline bool IsForwardPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized.Empty() ||
|
||||
normalized == Containers::String("forward") ||
|
||||
normalized == Containers::String("forwardbase") ||
|
||||
normalized == Containers::String("forwardlit") ||
|
||||
normalized == Containers::String("forwardonly");
|
||||
}
|
||||
|
||||
inline bool IsUnlitPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized == Containers::String("unlit") ||
|
||||
normalized == Containers::String("forwardunlit") ||
|
||||
normalized == Containers::String("srpdefaultunlit");
|
||||
}
|
||||
|
||||
inline bool IsDepthOnlyPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized == Containers::String("depthonly") ||
|
||||
normalized == Containers::String("depth");
|
||||
}
|
||||
|
||||
inline bool IsShadowCasterPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized == Containers::String("shadowcaster") ||
|
||||
normalized == Containers::String("shadow");
|
||||
}
|
||||
|
||||
inline bool IsObjectIdPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = value.Trim().ToLower();
|
||||
return normalized == Containers::String("objectid") ||
|
||||
normalized == Containers::String("editorobjectid");
|
||||
}
|
||||
|
||||
inline bool MatchesBuiltinPassName(
|
||||
const Containers::String& value,
|
||||
BuiltinMaterialPass pass) {
|
||||
switch (pass) {
|
||||
case BuiltinMaterialPass::ForwardLit:
|
||||
return IsForwardPassName(value);
|
||||
case BuiltinMaterialPass::Unlit:
|
||||
return IsUnlitPassName(value);
|
||||
case BuiltinMaterialPass::DepthOnly:
|
||||
return IsDepthOnlyPassName(value);
|
||||
case BuiltinMaterialPass::ShadowCaster:
|
||||
return IsShadowCasterPassName(value);
|
||||
case BuiltinMaterialPass::ObjectId:
|
||||
return IsObjectIdPassName(value);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ShaderPassHasExplicitBuiltinMetadata(const Resources::ShaderPass& shaderPass) {
|
||||
if (!shaderPass.name.Empty() &&
|
||||
shaderPass.name != Containers::String("Default")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
|
||||
if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("lightmode")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ShaderPassMatchesBuiltinPass(
|
||||
const Resources::ShaderPass& shaderPass,
|
||||
BuiltinMaterialPass pass) {
|
||||
bool hasMetadata = false;
|
||||
|
||||
if (!shaderPass.name.Empty() &&
|
||||
shaderPass.name != Containers::String("Default")) {
|
||||
hasMetadata = true;
|
||||
if (!MatchesBuiltinPassName(shaderPass.name, pass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
|
||||
if (NormalizeBuiltinPassMetadataValue(tag.name) != Containers::String("lightmode")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hasMetadata = true;
|
||||
if (!MatchesBuiltinPassName(tag.value, pass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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("lighting") ||
|
||||
semantic == Containers::String("lightingconstants")) {
|
||||
return BuiltinPassResourceSemantic::Lighting;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("shadowreceiver") ||
|
||||
semantic == Containers::String("shadowreceiverconstants")) {
|
||||
return BuiltinPassResourceSemantic::ShadowReceiver;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("basecolortexture") ||
|
||||
semantic == Containers::String("maintex")) {
|
||||
return BuiltinPassResourceSemantic::BaseColorTexture;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("shadowmaptexture") ||
|
||||
semantic == Containers::String("shadowmap")) {
|
||||
return BuiltinPassResourceSemantic::ShadowMapTexture;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("linearclampsampler")) {
|
||||
return BuiltinPassResourceSemantic::LinearClampSampler;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("shadowmapsampler") ||
|
||||
semantic == Containers::String("shadowsampler")) {
|
||||
return BuiltinPassResourceSemantic::ShadowMapSampler;
|
||||
}
|
||||
|
||||
return BuiltinPassResourceSemantic::Unknown;
|
||||
}
|
||||
|
||||
inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemantic semantic) {
|
||||
switch (semantic) {
|
||||
case BuiltinPassResourceSemantic::PerObject:
|
||||
return "PerObject";
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
return "Material";
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
return "Lighting";
|
||||
case BuiltinPassResourceSemantic::ShadowReceiver:
|
||||
return "ShadowReceiver";
|
||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||
return "BaseColorTexture";
|
||||
case BuiltinPassResourceSemantic::ShadowMapTexture:
|
||||
return "ShadowMapTexture";
|
||||
case BuiltinPassResourceSemantic::LinearClampSampler:
|
||||
return "LinearClampSampler";
|
||||
case BuiltinPassResourceSemantic::ShadowMapSampler:
|
||||
return "ShadowMapSampler";
|
||||
case BuiltinPassResourceSemantic::Unknown:
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline const char* ShaderResourceTypeToString(Resources::ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case Resources::ShaderResourceType::ConstantBuffer:
|
||||
return "ConstantBuffer";
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
return "Texture2D";
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
return "TextureCube";
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
return "Sampler";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline Containers::String DescribeShaderResourceBinding(
|
||||
const Resources::ShaderResourceBindingDesc& binding) {
|
||||
const BuiltinPassResourceSemantic resolvedSemantic = ResolveBuiltinPassResourceSemantic(binding);
|
||||
return Containers::String("name=") + binding.name +
|
||||
", semantic=" + binding.semantic +
|
||||
", resolvedSemantic=" + Containers::String(BuiltinPassResourceSemanticToString(resolvedSemantic)) +
|
||||
", type=" + Containers::String(ShaderResourceTypeToString(binding.type)) +
|
||||
", set=" + Containers::String(std::to_string(binding.set).c_str()) +
|
||||
", binding=" + Containers::String(std::to_string(binding.binding).c_str());
|
||||
}
|
||||
|
||||
inline Containers::String DescribeShaderResourceBindings(
|
||||
const Containers::Array<Resources::ShaderResourceBindingDesc>& bindings) {
|
||||
Containers::String description;
|
||||
for (size_t bindingIndex = 0; bindingIndex < bindings.Size(); ++bindingIndex) {
|
||||
if (!description.Empty()) {
|
||||
description += " | ";
|
||||
}
|
||||
|
||||
description += "[" + Containers::String(std::to_string(bindingIndex).c_str()) + "] ";
|
||||
description += DescribeShaderResourceBinding(bindings[bindingIndex]);
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
inline bool IsBuiltinPassResourceTypeCompatible(
|
||||
BuiltinPassResourceSemantic semantic,
|
||||
Resources::ShaderResourceType type) {
|
||||
switch (semantic) {
|
||||
case BuiltinPassResourceSemantic::PerObject:
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
case BuiltinPassResourceSemantic::ShadowReceiver:
|
||||
return type == Resources::ShaderResourceType::ConstantBuffer;
|
||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||
case BuiltinPassResourceSemantic::ShadowMapTexture:
|
||||
return type == Resources::ShaderResourceType::Texture2D ||
|
||||
type == Resources::ShaderResourceType::TextureCube;
|
||||
case BuiltinPassResourceSemantic::LinearClampSampler:
|
||||
case BuiltinPassResourceSemantic::ShadowMapSampler:
|
||||
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::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 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
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <XCEngine/RHI/RHITypes.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Rendering/BuiltinPassContract.h>
|
||||
#include <XCEngine/Rendering/VisibleRenderObject.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -16,588 +17,6 @@
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class BuiltinMaterialPass : Core::uint32 {
|
||||
ForwardLit = 0,
|
||||
Unlit,
|
||||
DepthOnly,
|
||||
ShadowCaster,
|
||||
ObjectId,
|
||||
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,
|
||||
Lighting,
|
||||
ShadowReceiver,
|
||||
BaseColorTexture,
|
||||
ShadowMapTexture,
|
||||
LinearClampSampler,
|
||||
ShadowMapSampler
|
||||
};
|
||||
|
||||
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 lighting = {};
|
||||
PassResourceBindingLocation shadowReceiver = {};
|
||||
PassResourceBindingLocation baseColorTexture = {};
|
||||
PassResourceBindingLocation linearClampSampler = {};
|
||||
PassResourceBindingLocation shadowMapTexture = {};
|
||||
PassResourceBindingLocation shadowMapSampler = {};
|
||||
|
||||
const BuiltinPassResourceBindingDesc* FindBinding(BuiltinPassResourceSemantic semantic) const {
|
||||
for (const BuiltinPassResourceBindingDesc& binding : bindings) {
|
||||
if (binding.semantic == semantic) {
|
||||
return &binding;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct BuiltinPassSetLayoutMetadata {
|
||||
std::vector<RHI::DescriptorSetLayoutBinding> bindings;
|
||||
RHI::DescriptorSetLayoutDesc layout = {};
|
||||
RHI::DescriptorHeapType heapType = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
bool shaderVisible = false;
|
||||
bool usesPerObject = false;
|
||||
bool usesMaterial = false;
|
||||
bool usesLighting = false;
|
||||
bool usesShadowReceiver = false;
|
||||
bool usesTexture = false;
|
||||
bool usesBaseColorTexture = false;
|
||||
bool usesShadowMapTexture = false;
|
||||
bool usesSampler = false;
|
||||
bool usesLinearClampSampler = false;
|
||||
bool usesShadowMapSampler = false;
|
||||
};
|
||||
|
||||
inline Containers::String NormalizeBuiltinPassMetadataValue(const Containers::String& value) {
|
||||
return value.Trim().ToLower();
|
||||
}
|
||||
|
||||
inline bool IsForwardPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized.Empty() ||
|
||||
normalized == Containers::String("forward") ||
|
||||
normalized == Containers::String("forwardbase") ||
|
||||
normalized == Containers::String("forwardlit") ||
|
||||
normalized == Containers::String("forwardonly");
|
||||
}
|
||||
|
||||
inline bool IsUnlitPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized == Containers::String("unlit") ||
|
||||
normalized == Containers::String("forwardunlit") ||
|
||||
normalized == Containers::String("srpdefaultunlit");
|
||||
}
|
||||
|
||||
inline bool IsDepthOnlyPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized == Containers::String("depthonly") ||
|
||||
normalized == Containers::String("depth");
|
||||
}
|
||||
|
||||
inline bool IsShadowCasterPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
|
||||
return normalized == Containers::String("shadowcaster") ||
|
||||
normalized == Containers::String("shadow");
|
||||
}
|
||||
|
||||
inline bool IsObjectIdPassName(const Containers::String& value) {
|
||||
const Containers::String normalized = value.Trim().ToLower();
|
||||
return normalized == Containers::String("objectid") ||
|
||||
normalized == Containers::String("editorobjectid");
|
||||
}
|
||||
|
||||
inline bool MatchesBuiltinPassName(
|
||||
const Containers::String& value,
|
||||
BuiltinMaterialPass pass) {
|
||||
switch (pass) {
|
||||
case BuiltinMaterialPass::ForwardLit:
|
||||
return IsForwardPassName(value);
|
||||
case BuiltinMaterialPass::Unlit:
|
||||
return IsUnlitPassName(value);
|
||||
case BuiltinMaterialPass::DepthOnly:
|
||||
return IsDepthOnlyPassName(value);
|
||||
case BuiltinMaterialPass::ShadowCaster:
|
||||
return IsShadowCasterPassName(value);
|
||||
case BuiltinMaterialPass::ObjectId:
|
||||
return IsObjectIdPassName(value);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ShaderPassHasExplicitBuiltinMetadata(const Resources::ShaderPass& shaderPass) {
|
||||
if (!shaderPass.name.Empty() &&
|
||||
shaderPass.name != Containers::String("Default")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
|
||||
if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("lightmode")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ShaderPassMatchesBuiltinPass(
|
||||
const Resources::ShaderPass& shaderPass,
|
||||
BuiltinMaterialPass pass) {
|
||||
bool hasMetadata = false;
|
||||
|
||||
if (!shaderPass.name.Empty() &&
|
||||
shaderPass.name != Containers::String("Default")) {
|
||||
hasMetadata = true;
|
||||
if (!MatchesBuiltinPassName(shaderPass.name, pass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
|
||||
if (NormalizeBuiltinPassMetadataValue(tag.name) != Containers::String("lightmode")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hasMetadata = true;
|
||||
if (!MatchesBuiltinPassName(tag.value, pass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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("lighting") ||
|
||||
semantic == Containers::String("lightingconstants")) {
|
||||
return BuiltinPassResourceSemantic::Lighting;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("shadowreceiver") ||
|
||||
semantic == Containers::String("shadowreceiverconstants")) {
|
||||
return BuiltinPassResourceSemantic::ShadowReceiver;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("basecolortexture") ||
|
||||
semantic == Containers::String("maintex")) {
|
||||
return BuiltinPassResourceSemantic::BaseColorTexture;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("shadowmaptexture") ||
|
||||
semantic == Containers::String("shadowmap")) {
|
||||
return BuiltinPassResourceSemantic::ShadowMapTexture;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("linearclampsampler")) {
|
||||
return BuiltinPassResourceSemantic::LinearClampSampler;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("shadowmapsampler") ||
|
||||
semantic == Containers::String("shadowsampler")) {
|
||||
return BuiltinPassResourceSemantic::ShadowMapSampler;
|
||||
}
|
||||
|
||||
return BuiltinPassResourceSemantic::Unknown;
|
||||
}
|
||||
|
||||
inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemantic semantic) {
|
||||
switch (semantic) {
|
||||
case BuiltinPassResourceSemantic::PerObject:
|
||||
return "PerObject";
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
return "Material";
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
return "Lighting";
|
||||
case BuiltinPassResourceSemantic::ShadowReceiver:
|
||||
return "ShadowReceiver";
|
||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||
return "BaseColorTexture";
|
||||
case BuiltinPassResourceSemantic::ShadowMapTexture:
|
||||
return "ShadowMapTexture";
|
||||
case BuiltinPassResourceSemantic::LinearClampSampler:
|
||||
return "LinearClampSampler";
|
||||
case BuiltinPassResourceSemantic::ShadowMapSampler:
|
||||
return "ShadowMapSampler";
|
||||
case BuiltinPassResourceSemantic::Unknown:
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline const char* ShaderResourceTypeToString(Resources::ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case Resources::ShaderResourceType::ConstantBuffer:
|
||||
return "ConstantBuffer";
|
||||
case Resources::ShaderResourceType::Texture2D:
|
||||
return "Texture2D";
|
||||
case Resources::ShaderResourceType::TextureCube:
|
||||
return "TextureCube";
|
||||
case Resources::ShaderResourceType::Sampler:
|
||||
return "Sampler";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline Containers::String DescribeShaderResourceBinding(
|
||||
const Resources::ShaderResourceBindingDesc& binding) {
|
||||
const BuiltinPassResourceSemantic resolvedSemantic = ResolveBuiltinPassResourceSemantic(binding);
|
||||
return Containers::String("name=") + binding.name +
|
||||
", semantic=" + binding.semantic +
|
||||
", resolvedSemantic=" + Containers::String(BuiltinPassResourceSemanticToString(resolvedSemantic)) +
|
||||
", type=" + Containers::String(ShaderResourceTypeToString(binding.type)) +
|
||||
", set=" + Containers::String(std::to_string(binding.set).c_str()) +
|
||||
", binding=" + Containers::String(std::to_string(binding.binding).c_str());
|
||||
}
|
||||
|
||||
inline Containers::String DescribeShaderResourceBindings(
|
||||
const Containers::Array<Resources::ShaderResourceBindingDesc>& bindings) {
|
||||
Containers::String description;
|
||||
for (size_t bindingIndex = 0; bindingIndex < bindings.Size(); ++bindingIndex) {
|
||||
if (!description.Empty()) {
|
||||
description += " | ";
|
||||
}
|
||||
|
||||
description += "[" + Containers::String(std::to_string(bindingIndex).c_str()) + "] ";
|
||||
description += DescribeShaderResourceBinding(bindings[bindingIndex]);
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
inline bool IsBuiltinPassResourceTypeCompatible(
|
||||
BuiltinPassResourceSemantic semantic,
|
||||
Resources::ShaderResourceType type) {
|
||||
switch (semantic) {
|
||||
case BuiltinPassResourceSemantic::PerObject:
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
case BuiltinPassResourceSemantic::ShadowReceiver:
|
||||
return type == Resources::ShaderResourceType::ConstantBuffer;
|
||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||
case BuiltinPassResourceSemantic::ShadowMapTexture:
|
||||
return type == Resources::ShaderResourceType::Texture2D ||
|
||||
type == Resources::ShaderResourceType::TextureCube;
|
||||
case BuiltinPassResourceSemantic::LinearClampSampler:
|
||||
case BuiltinPassResourceSemantic::ShadowMapSampler:
|
||||
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::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 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;
|
||||
}
|
||||
|
||||
struct BuiltinForwardMaterialData {
|
||||
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user