refactor: drive builtin forward bindings from shader pass resources
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
@@ -71,9 +72,85 @@ private:
|
||||
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
||||
};
|
||||
|
||||
struct CachedMaterialBindings {
|
||||
OwnedDescriptorSet constantSet = {};
|
||||
OwnedDescriptorSet textureSet = {};
|
||||
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;
|
||||
|
||||
bool operator==(const PassLayoutKey& other) const {
|
||||
return shader == other.shader && passName == other.passName;
|
||||
}
|
||||
};
|
||||
|
||||
struct PassLayoutKeyHash {
|
||||
size_t operator()(const PassLayoutKey& key) const noexcept {
|
||||
size_t hash = reinterpret_cast<size_t>(key.shader);
|
||||
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct PassSetLayoutMetadata {
|
||||
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 usesTexture = false;
|
||||
bool usesSampler = false;
|
||||
};
|
||||
|
||||
struct PassResourceLayout {
|
||||
RHI::RHIPipelineLayout* pipelineLayout = nullptr;
|
||||
Core::uint32 firstDescriptorSet = 0;
|
||||
Core::uint32 descriptorSetCount = 0;
|
||||
std::vector<PassSetLayoutMetadata> setLayouts;
|
||||
std::vector<OwnedDescriptorSet> staticDescriptorSets;
|
||||
DescriptorBindingLocation perObject = {};
|
||||
DescriptorBindingLocation material = {};
|
||||
DescriptorBindingLocation baseColorTexture = {};
|
||||
DescriptorBindingLocation linearClampSampler = {};
|
||||
};
|
||||
|
||||
struct DynamicDescriptorSetKey {
|
||||
PassLayoutKey passLayout = {};
|
||||
Core::uint32 setIndex = 0;
|
||||
Core::uint64 objectId = 0;
|
||||
const Resources::Material* material = nullptr;
|
||||
|
||||
bool operator==(const DynamicDescriptorSetKey& other) const {
|
||||
return passLayout == other.passLayout &&
|
||||
setIndex == other.setIndex &&
|
||||
objectId == other.objectId &&
|
||||
material == other.material;
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicDescriptorSetKeyHash {
|
||||
size_t operator()(const DynamicDescriptorSetKey& key) const noexcept {
|
||||
size_t hash = PassLayoutKeyHash()(key.passLayout);
|
||||
auto combine = [&hash](size_t value) {
|
||||
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
};
|
||||
|
||||
combine(std::hash<Core::uint32>{}(key.setIndex));
|
||||
combine(std::hash<Core::uint64>{}(key.objectId));
|
||||
combine(reinterpret_cast<size_t>(key.material));
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct CachedDescriptorSet {
|
||||
OwnedDescriptorSet descriptorSet = {};
|
||||
Core::uint64 materialVersion = 0;
|
||||
RHI::RHIResourceView* textureView = nullptr;
|
||||
};
|
||||
@@ -113,14 +190,29 @@ private:
|
||||
bool CreatePipelineResources(const RenderContext& context);
|
||||
void DestroyPipelineResources();
|
||||
ResolvedShaderPass ResolveForwardShaderPass(const Resources::Material* material) const;
|
||||
PassResourceLayout* GetOrCreatePassResourceLayout(
|
||||
const RenderContext& context,
|
||||
const ResolvedShaderPass& resolvedShaderPass);
|
||||
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||
const RenderContext& context,
|
||||
const Resources::Material* material);
|
||||
RHI::RHIDescriptorSet* GetOrCreatePerObjectSet(Core::uint64 objectId);
|
||||
CachedMaterialBindings* GetOrCreateMaterialBindings(
|
||||
bool CreateOwnedDescriptorSet(
|
||||
const PassSetLayoutMetadata& setLayout,
|
||||
OwnedDescriptorSet& descriptorSet);
|
||||
RHI::RHIDescriptorSet* GetOrCreateStaticDescriptorSet(
|
||||
PassResourceLayout& passLayout,
|
||||
Core::uint32 setIndex);
|
||||
CachedDescriptorSet* GetOrCreateDynamicDescriptorSet(
|
||||
const PassLayoutKey& passLayoutKey,
|
||||
const PassResourceLayout& passLayout,
|
||||
const PassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Resources::Material* material,
|
||||
const PerMaterialConstants& materialConstants,
|
||||
RHI::RHIResourceView* textureView);
|
||||
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
||||
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||
|
||||
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
||||
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
||||
@@ -137,12 +229,9 @@ private:
|
||||
|
||||
RenderResourceCache m_resourceCache;
|
||||
|
||||
RHI::RHIDescriptorPool* m_samplerPool = nullptr;
|
||||
RHI::RHIDescriptorSet* m_samplerSet = nullptr;
|
||||
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;
|
||||
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
||||
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
||||
std::unordered_map<Core::uint64, OwnedDescriptorSet> m_perObjectSets;
|
||||
std::unordered_map<const Resources::Material*, CachedMaterialBindings> m_materialBindings;
|
||||
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
||||
RHI::RHISampler* m_sampler = nullptr;
|
||||
RHI::RHITexture* m_fallbackTexture = nullptr;
|
||||
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Resources/Shader/Shader.h"
|
||||
#include "Resources/Texture/Texture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -51,8 +52,137 @@ private:
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kDescriptorFirstSet = 1;
|
||||
constexpr uint32_t kDescriptorSetCount = 5;
|
||||
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:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::DescriptorHeapType ResolveDescriptorHeapType(Resources::ShaderResourceType type) {
|
||||
return type == Resources::ShaderResourceType::Sampler
|
||||
? RHI::DescriptorHeapType::Sampler
|
||||
: RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
}
|
||||
|
||||
bool IsShaderVisibleSet(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;
|
||||
}
|
||||
|
||||
uint32_t CountHeapDescriptors(
|
||||
RHI::DescriptorHeapType heapType,
|
||||
const std::vector<RHI::DescriptorSetLayoutBinding>& bindings) {
|
||||
uint32_t 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;
|
||||
}
|
||||
|
||||
bool BindingNumberExists(
|
||||
const std::vector<RHI::DescriptorSetLayoutBinding>& bindings,
|
||||
uint32_t bindingNumber) {
|
||||
for (const RHI::DescriptorSetLayoutBinding& binding : bindings) {
|
||||
if (binding.binding == bindingNumber) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -325,80 +455,6 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding constantBinding = {};
|
||||
constantBinding.binding = 0;
|
||||
constantBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
||||
constantBinding.count = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc constantLayout = {};
|
||||
constantLayout.bindings = &constantBinding;
|
||||
constantLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc materialConstantLayout = {};
|
||||
materialConstantLayout.bindings = &constantBinding;
|
||||
materialConstantLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutBinding textureBinding = {};
|
||||
textureBinding.binding = 0;
|
||||
textureBinding.type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
|
||||
textureBinding.count = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc textureLayout = {};
|
||||
textureLayout.bindings = &textureBinding;
|
||||
textureLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorPoolDesc samplerPoolDesc = {};
|
||||
samplerPoolDesc.type = RHI::DescriptorHeapType::Sampler;
|
||||
samplerPoolDesc.descriptorCount = 1;
|
||||
samplerPoolDesc.shaderVisible = true;
|
||||
m_samplerPool = context.device->CreateDescriptorPool(samplerPoolDesc);
|
||||
if (m_samplerPool == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding samplerBinding = {};
|
||||
samplerBinding.binding = 0;
|
||||
samplerBinding.type = static_cast<uint32_t>(RHI::DescriptorType::Sampler);
|
||||
samplerBinding.count = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc samplerLayout = {};
|
||||
samplerLayout.bindings = &samplerBinding;
|
||||
samplerLayout.bindingCount = 1;
|
||||
m_samplerSet = m_samplerPool->AllocateSet(samplerLayout);
|
||||
if (m_samplerSet == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding reservedBindings[3] = {};
|
||||
reservedBindings[0].binding = 0;
|
||||
reservedBindings[0].type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
||||
reservedBindings[0].count = 1;
|
||||
reservedBindings[1].binding = 0;
|
||||
reservedBindings[1].type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
|
||||
reservedBindings[1].count = 1;
|
||||
reservedBindings[2].binding = 0;
|
||||
reservedBindings[2].type = static_cast<uint32_t>(RHI::DescriptorType::Sampler);
|
||||
reservedBindings[2].count = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc reservedLayout = {};
|
||||
reservedLayout.bindings = reservedBindings;
|
||||
reservedLayout.bindingCount = 3;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc setLayouts[kDescriptorSetCount] = {};
|
||||
setLayouts[0] = reservedLayout;
|
||||
setLayouts[1] = constantLayout;
|
||||
setLayouts[2] = materialConstantLayout;
|
||||
setLayouts[3] = textureLayout;
|
||||
setLayouts[4] = samplerLayout;
|
||||
|
||||
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||
pipelineLayoutDesc.setLayouts = setLayouts;
|
||||
pipelineLayoutDesc.setLayoutCount = kDescriptorSetCount;
|
||||
m_pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||
if (m_pipelineLayout == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::SamplerDesc samplerDesc = {};
|
||||
samplerDesc.filter = static_cast<uint32_t>(RHI::FilterMode::Linear);
|
||||
samplerDesc.addressU = static_cast<uint32_t>(RHI::TextureAddressMode::Clamp);
|
||||
@@ -413,7 +469,6 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
||||
if (m_sampler == nullptr) {
|
||||
return false;
|
||||
}
|
||||
m_samplerSet->UpdateSampler(0, m_sampler);
|
||||
|
||||
const unsigned char whitePixel[4] = { 255, 255, 255, 255 };
|
||||
RHI::TextureDesc textureDesc = {};
|
||||
@@ -455,16 +510,15 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
||||
}
|
||||
m_pipelineStates.clear();
|
||||
|
||||
for (auto& perObjectSetPair : m_perObjectSets) {
|
||||
DestroyOwnedDescriptorSet(perObjectSetPair.second);
|
||||
for (auto& descriptorSetPair : m_dynamicDescriptorSets) {
|
||||
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
|
||||
}
|
||||
m_perObjectSets.clear();
|
||||
m_dynamicDescriptorSets.clear();
|
||||
|
||||
for (auto& materialBindingPair : m_materialBindings) {
|
||||
DestroyOwnedDescriptorSet(materialBindingPair.second.constantSet);
|
||||
DestroyOwnedDescriptorSet(materialBindingPair.second.textureSet);
|
||||
for (auto& passLayoutPair : m_passResourceLayouts) {
|
||||
DestroyPassResourceLayout(passLayoutPair.second);
|
||||
}
|
||||
m_materialBindings.clear();
|
||||
m_passResourceLayouts.clear();
|
||||
|
||||
if (m_fallbackTextureView != nullptr) {
|
||||
m_fallbackTextureView->Shutdown();
|
||||
@@ -484,24 +538,6 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
||||
m_sampler = nullptr;
|
||||
}
|
||||
|
||||
if (m_pipelineLayout != nullptr) {
|
||||
m_pipelineLayout->Shutdown();
|
||||
delete m_pipelineLayout;
|
||||
m_pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
if (m_samplerSet != nullptr) {
|
||||
m_samplerSet->Shutdown();
|
||||
delete m_samplerSet;
|
||||
m_samplerSet = nullptr;
|
||||
}
|
||||
|
||||
if (m_samplerPool != nullptr) {
|
||||
m_samplerPool->Shutdown();
|
||||
delete m_samplerPool;
|
||||
m_samplerPool = nullptr;
|
||||
}
|
||||
|
||||
m_device = nullptr;
|
||||
m_initialized = false;
|
||||
m_builtinForwardShader.Reset();
|
||||
@@ -536,6 +572,214 @@ BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveForwar
|
||||
return resolved;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreatePassResourceLayout(
|
||||
const RenderContext& context,
|
||||
const ResolvedShaderPass& resolvedShaderPass) {
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
const auto existing = m_passResourceLayouts.find(passLayoutKey);
|
||||
if (existing != m_passResourceLayouts.end()) {
|
||||
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);
|
||||
DestroyPassResourceLayout(passLayout);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAnyResource) {
|
||||
passLayout.setLayouts.resize(static_cast<size_t>(maxSetIndex) + 1u);
|
||||
passLayout.staticDescriptorSets.resize(passLayout.setLayouts.size());
|
||||
passLayout.firstDescriptorSet = minBoundSet;
|
||||
passLayout.descriptorSetCount = maxBoundSet - minBoundSet + 1u;
|
||||
}
|
||||
|
||||
for (const Resources::ShaderResourceBindingDesc& binding : resourceBindings) {
|
||||
ForwardPassSemantic semantic = ResolveForwardPassSemantic(binding);
|
||||
if (semantic == ForwardPassSemantic::Unknown) {
|
||||
return failLayout("BuiltinForwardPipeline encountered an unsupported forward shader resource semantic");
|
||||
}
|
||||
|
||||
if (binding.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];
|
||||
|
||||
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)) {
|
||||
return failLayout("BuiltinForwardPipeline encountered duplicate bindings inside one descriptor set");
|
||||
}
|
||||
|
||||
if (setLayout.bindings.empty()) {
|
||||
setLayout.heapType = heapType;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding layoutBinding = {};
|
||||
layoutBinding.binding = binding.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 };
|
||||
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 };
|
||||
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 };
|
||||
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 };
|
||||
setLayout.usesSampler = true;
|
||||
break;
|
||||
default:
|
||||
return failLayout("BuiltinForwardPipeline encountered an unsupported forward shader resource semantic");
|
||||
}
|
||||
}
|
||||
|
||||
if (!passLayout.perObject.IsValid()) {
|
||||
return failLayout("BuiltinForwardPipeline requires a PerObject resource binding");
|
||||
}
|
||||
|
||||
if (hasAnyResource &&
|
||||
passLayout.firstDescriptorSet > 0 &&
|
||||
!passLayout.setLayouts.empty() &&
|
||||
passLayout.setLayouts[0].bindings.empty()) {
|
||||
PassSetLayoutMetadata& compatibilitySet = passLayout.setLayouts[0];
|
||||
if (usesConstantBuffers) {
|
||||
compatibilitySet.bindings.push_back({
|
||||
0,
|
||||
static_cast<uint32_t>(RHI::DescriptorType::CBV),
|
||||
1,
|
||||
0
|
||||
});
|
||||
}
|
||||
if (usesTextures) {
|
||||
compatibilitySet.bindings.push_back({
|
||||
0,
|
||||
static_cast<uint32_t>(RHI::DescriptorType::SRV),
|
||||
1,
|
||||
0
|
||||
});
|
||||
}
|
||||
if (usesSamplers) {
|
||||
compatibilitySet.bindings.push_back({
|
||||
0,
|
||||
static_cast<uint32_t>(RHI::DescriptorType::Sampler),
|
||||
1,
|
||||
0
|
||||
});
|
||||
}
|
||||
compatibilitySet.shaderVisible = true;
|
||||
}
|
||||
|
||||
for (Core::uint32 setIndex = 0; setIndex < passLayout.setLayouts.size(); ++setIndex) {
|
||||
PassSetLayoutMetadata& setLayout = passLayout.setLayouts[setIndex];
|
||||
std::sort(
|
||||
setLayout.bindings.begin(),
|
||||
setLayout.bindings.end(),
|
||||
[](const RHI::DescriptorSetLayoutBinding& left, const RHI::DescriptorSetLayoutBinding& right) {
|
||||
return left.binding < right.binding;
|
||||
});
|
||||
setLayout.shaderVisible = IsShaderVisibleSet(setLayout.bindings);
|
||||
setLayout.layout.bindings = setLayout.bindings.empty() ? nullptr : setLayout.bindings.data();
|
||||
setLayout.layout.bindingCount = static_cast<uint32_t>(setLayout.bindings.size());
|
||||
}
|
||||
|
||||
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
|
||||
for (size_t i = 0; i < passLayout.setLayouts.size(); ++i) {
|
||||
nativeSetLayouts[i] = passLayout.setLayouts[i].layout;
|
||||
}
|
||||
|
||||
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||
pipelineLayoutDesc.setLayouts = nativeSetLayouts.empty() ? nullptr : nativeSetLayouts.data();
|
||||
pipelineLayoutDesc.setLayoutCount = static_cast<uint32_t>(nativeSetLayouts.size());
|
||||
passLayout.pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||
if (passLayout.pipelineLayout == nullptr) {
|
||||
return failLayout("BuiltinForwardPipeline failed to create a pipeline layout from shader pass resources");
|
||||
}
|
||||
|
||||
const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout);
|
||||
PassResourceLayout& storedPassLayout = result.first->second;
|
||||
for (PassSetLayoutMetadata& setLayout : storedPassLayout.setLayouts) {
|
||||
setLayout.layout.bindings = setLayout.bindings.empty() ? nullptr : setLayout.bindings.data();
|
||||
setLayout.layout.bindingCount = static_cast<uint32_t>(setLayout.bindings.size());
|
||||
}
|
||||
return &storedPassLayout;
|
||||
}
|
||||
|
||||
RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||
const RenderContext& context,
|
||||
const Resources::Material* material) {
|
||||
@@ -547,6 +791,11 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PipelineStateKey pipelineKey = {};
|
||||
pipelineKey.renderState =
|
||||
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
||||
@@ -561,7 +810,7 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||
const RHI::GraphicsPipelineDesc pipelineDesc =
|
||||
CreatePipelineDesc(
|
||||
context.backendType,
|
||||
m_pipelineLayout,
|
||||
passLayout->pipelineLayout,
|
||||
*resolvedShaderPass.shader,
|
||||
resolvedShaderPass.passName,
|
||||
material);
|
||||
@@ -581,117 +830,107 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreatePerObjectSet(Core::uint64 objectId) {
|
||||
const auto existing = m_perObjectSets.find(objectId);
|
||||
if (existing != m_perObjectSets.end()) {
|
||||
return existing->second.set;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::CreateOwnedDescriptorSet(
|
||||
const PassSetLayoutMetadata& setLayout,
|
||||
OwnedDescriptorSet& descriptorSet) {
|
||||
RHI::DescriptorPoolDesc poolDesc = {};
|
||||
poolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
poolDesc.descriptorCount = 1;
|
||||
poolDesc.shaderVisible = false;
|
||||
poolDesc.type = setLayout.heapType;
|
||||
poolDesc.descriptorCount = CountHeapDescriptors(setLayout.heapType, setLayout.bindings);
|
||||
poolDesc.shaderVisible = setLayout.shaderVisible;
|
||||
|
||||
OwnedDescriptorSet descriptorSet = {};
|
||||
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
||||
if (descriptorSet.pool == nullptr) {
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding binding = {};
|
||||
binding.binding = 0;
|
||||
binding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
||||
binding.count = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc layout = {};
|
||||
layout.bindings = &binding;
|
||||
layout.bindingCount = 1;
|
||||
descriptorSet.set = descriptorSet.pool->AllocateSet(layout);
|
||||
descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout);
|
||||
if (descriptorSet.set == nullptr) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto result = m_perObjectSets.emplace(objectId, descriptorSet);
|
||||
return result.first->second.set;
|
||||
return true;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::CachedMaterialBindings* BuiltinForwardPipeline::GetOrCreateMaterialBindings(
|
||||
const Resources::Material* material,
|
||||
RHI::RHIResourceView* textureView) {
|
||||
if (textureView == nullptr) {
|
||||
RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreateStaticDescriptorSet(
|
||||
PassResourceLayout& passLayout,
|
||||
Core::uint32 setIndex) {
|
||||
if (setIndex >= passLayout.setLayouts.size() ||
|
||||
setIndex >= passLayout.staticDescriptorSets.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CachedMaterialBindings& cachedBindings = m_materialBindings[material];
|
||||
|
||||
if (cachedBindings.constantSet.set == nullptr) {
|
||||
RHI::DescriptorPoolDesc constantPoolDesc = {};
|
||||
constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
constantPoolDesc.descriptorCount = 1;
|
||||
constantPoolDesc.shaderVisible = false;
|
||||
|
||||
cachedBindings.constantSet.pool = m_device->CreateDescriptorPool(constantPoolDesc);
|
||||
if (cachedBindings.constantSet.pool == nullptr) {
|
||||
OwnedDescriptorSet& descriptorSet = passLayout.staticDescriptorSets[setIndex];
|
||||
if (descriptorSet.set == nullptr) {
|
||||
if (!CreateOwnedDescriptorSet(passLayout.setLayouts[setIndex], descriptorSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding constantBinding = {};
|
||||
constantBinding.binding = 0;
|
||||
constantBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
||||
constantBinding.count = 1;
|
||||
if (passLayout.setLayouts[setIndex].usesSampler) {
|
||||
if (m_sampler == nullptr ||
|
||||
!passLayout.linearClampSampler.IsValid() ||
|
||||
passLayout.linearClampSampler.set != setIndex) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutDesc constantLayout = {};
|
||||
constantLayout.bindings = &constantBinding;
|
||||
constantLayout.bindingCount = 1;
|
||||
cachedBindings.constantSet.set = cachedBindings.constantSet.pool->AllocateSet(constantLayout);
|
||||
if (cachedBindings.constantSet.set == nullptr) {
|
||||
DestroyOwnedDescriptorSet(cachedBindings.constantSet);
|
||||
return nullptr;
|
||||
descriptorSet.set->UpdateSampler(passLayout.linearClampSampler.binding, m_sampler);
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedBindings.textureSet.set == nullptr) {
|
||||
RHI::DescriptorPoolDesc texturePoolDesc = {};
|
||||
texturePoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
texturePoolDesc.descriptorCount = 1;
|
||||
texturePoolDesc.shaderVisible = true;
|
||||
return descriptorSet.set;
|
||||
}
|
||||
|
||||
cachedBindings.textureSet.pool = m_device->CreateDescriptorPool(texturePoolDesc);
|
||||
if (cachedBindings.textureSet.pool == nullptr) {
|
||||
DestroyOwnedDescriptorSet(cachedBindings.constantSet);
|
||||
return nullptr;
|
||||
}
|
||||
BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreateDynamicDescriptorSet(
|
||||
const PassLayoutKey& passLayoutKey,
|
||||
const PassResourceLayout& passLayout,
|
||||
const PassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Resources::Material* material,
|
||||
const PerMaterialConstants& materialConstants,
|
||||
RHI::RHIResourceView* textureView) {
|
||||
DynamicDescriptorSetKey key = {};
|
||||
key.passLayout = passLayoutKey;
|
||||
key.setIndex = setIndex;
|
||||
key.objectId = objectId;
|
||||
key.material = material;
|
||||
|
||||
RHI::DescriptorSetLayoutBinding textureBinding = {};
|
||||
textureBinding.binding = 0;
|
||||
textureBinding.type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
|
||||
textureBinding.count = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc textureLayout = {};
|
||||
textureLayout.bindings = &textureBinding;
|
||||
textureLayout.bindingCount = 1;
|
||||
cachedBindings.textureSet.set = cachedBindings.textureSet.pool->AllocateSet(textureLayout);
|
||||
if (cachedBindings.textureSet.set == nullptr) {
|
||||
DestroyOwnedDescriptorSet(cachedBindings.textureSet);
|
||||
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
|
||||
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
|
||||
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0;
|
||||
if (cachedBindings.materialVersion != materialVersion || cachedBindings.textureView != textureView) {
|
||||
const BuiltinForwardMaterialData materialData = BuildBuiltinForwardMaterialData(material);
|
||||
const PerMaterialConstants materialConstants = {
|
||||
materialData.baseColorFactor
|
||||
};
|
||||
if ((setLayout.usesMaterial || setLayout.usesTexture) &&
|
||||
(cachedDescriptorSet.materialVersion != materialVersion ||
|
||||
cachedDescriptorSet.textureView != textureView)) {
|
||||
if (setLayout.usesMaterial) {
|
||||
if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.material.binding,
|
||||
&materialConstants,
|
||||
sizeof(materialConstants));
|
||||
}
|
||||
|
||||
cachedBindings.constantSet.set->WriteConstant(0, &materialConstants, sizeof(materialConstants));
|
||||
cachedBindings.textureSet.set->Update(0, textureView);
|
||||
cachedBindings.materialVersion = materialVersion;
|
||||
cachedBindings.textureView = textureView;
|
||||
if (setLayout.usesTexture) {
|
||||
if (textureView == nullptr ||
|
||||
!passLayout.baseColorTexture.IsValid() ||
|
||||
passLayout.baseColorTexture.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
cachedDescriptorSet.descriptorSet.set->Update(passLayout.baseColorTexture.binding, textureView);
|
||||
}
|
||||
|
||||
cachedDescriptorSet.materialVersion = materialVersion;
|
||||
cachedDescriptorSet.textureView = textureView;
|
||||
}
|
||||
|
||||
return &cachedBindings;
|
||||
return &cachedDescriptorSet;
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) {
|
||||
@@ -708,6 +947,27 @@ void BuiltinForwardPipeline::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descr
|
||||
}
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::DestroyPassResourceLayout(PassResourceLayout& passLayout) {
|
||||
for (OwnedDescriptorSet& descriptorSet : passLayout.staticDescriptorSets) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
}
|
||||
passLayout.staticDescriptorSets.clear();
|
||||
|
||||
if (passLayout.pipelineLayout != nullptr) {
|
||||
passLayout.pipelineLayout->Shutdown();
|
||||
delete passLayout.pipelineLayout;
|
||||
passLayout.pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
passLayout.setLayouts.clear();
|
||||
passLayout.firstDescriptorSet = 0;
|
||||
passLayout.descriptorSetCount = 0;
|
||||
passLayout.perObject = {};
|
||||
passLayout.material = {};
|
||||
passLayout.baseColorTexture = {};
|
||||
passLayout.linearClampSampler = {};
|
||||
}
|
||||
|
||||
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
|
||||
return ResolveBuiltinBaseColorTexture(material);
|
||||
}
|
||||
@@ -766,31 +1026,91 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
|
||||
: Math::Vector4::Zero()
|
||||
};
|
||||
|
||||
RHI::RHIResourceView* textureView = ResolveTextureView(visibleItem);
|
||||
if (textureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
RHI::RHIDescriptorSet* constantSet = GetOrCreatePerObjectSet(
|
||||
visibleItem.gameObject != nullptr ? visibleItem.gameObject->GetID() : 0);
|
||||
CachedMaterialBindings* materialBindings = GetOrCreateMaterialBindings(material, textureView);
|
||||
if (constantSet == nullptr ||
|
||||
materialBindings == nullptr ||
|
||||
materialBindings->constantSet.set == nullptr ||
|
||||
materialBindings->textureSet.set == nullptr ||
|
||||
m_samplerSet == nullptr) {
|
||||
const ResolvedShaderPass resolvedShaderPass = ResolveForwardShaderPass(material);
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constantSet->WriteConstant(0, &constants, sizeof(constants));
|
||||
RHI::RHIDescriptorSet* descriptorSets[] = {
|
||||
constantSet,
|
||||
materialBindings->constantSet.set,
|
||||
materialBindings->textureSet.set,
|
||||
m_samplerSet
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* textureView = ResolveTextureView(visibleItem);
|
||||
if (passLayout->baseColorTexture.IsValid() && textureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const BuiltinForwardMaterialData materialData = BuildBuiltinForwardMaterialData(material);
|
||||
const PerMaterialConstants materialConstants = {
|
||||
materialData.baseColorFactor
|
||||
};
|
||||
commandList->SetGraphicsDescriptorSets(kDescriptorFirstSet, 4, descriptorSets, m_pipelineLayout);
|
||||
|
||||
if (passLayout->descriptorSetCount > 0) {
|
||||
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
|
||||
|
||||
for (Core::uint32 descriptorOffset = 0; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
|
||||
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
|
||||
if (setIndex >= passLayout->setLayouts.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||
RHI::RHIDescriptorSet* descriptorSet = nullptr;
|
||||
|
||||
if (setLayout.usesPerObject || setLayout.usesMaterial || setLayout.usesTexture) {
|
||||
const Core::uint64 objectId =
|
||||
(setLayout.usesPerObject && visibleItem.gameObject != nullptr)
|
||||
? visibleItem.gameObject->GetID()
|
||||
: 0;
|
||||
const Resources::Material* materialKey =
|
||||
(setLayout.usesMaterial || setLayout.usesTexture) ? material : nullptr;
|
||||
|
||||
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||
passLayoutKey,
|
||||
*passLayout,
|
||||
setLayout,
|
||||
setIndex,
|
||||
objectId,
|
||||
materialKey,
|
||||
materialConstants,
|
||||
textureView);
|
||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet = cachedDescriptorSet->descriptorSet.set;
|
||||
if (setLayout.usesPerObject) {
|
||||
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet->WriteConstant(
|
||||
passLayout->perObject.binding,
|
||||
&constants,
|
||||
sizeof(constants));
|
||||
}
|
||||
} else {
|
||||
descriptorSet = GetOrCreateStaticDescriptorSet(*passLayout, setIndex);
|
||||
if (descriptorSet == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
descriptorSets[descriptorOffset] = descriptorSet;
|
||||
}
|
||||
|
||||
commandList->SetGraphicsDescriptorSets(
|
||||
passLayout->firstDescriptorSet,
|
||||
passLayout->descriptorSetCount,
|
||||
descriptorSets.data(),
|
||||
passLayout->pipelineLayout);
|
||||
}
|
||||
|
||||
if (visibleItem.hasSection) {
|
||||
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
|
||||
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
|
||||
using namespace XCEngine::Rendering::Pipelines;
|
||||
@@ -37,6 +40,42 @@ TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVert
|
||||
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
|
||||
}
|
||||
|
||||
TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardResourceContract) {
|
||||
ShaderLoader loader;
|
||||
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_NE(result.resource, nullptr);
|
||||
|
||||
Shader* shader = static_cast<Shader*>(result.resource);
|
||||
ASSERT_NE(shader, nullptr);
|
||||
|
||||
const ShaderPass* pass = shader->FindPass("ForwardLit");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
ASSERT_EQ(pass->resources.Size(), 4u);
|
||||
|
||||
EXPECT_EQ(pass->resources[0].semantic, "PerObject");
|
||||
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
|
||||
EXPECT_EQ(pass->resources[0].set, 1u);
|
||||
EXPECT_EQ(pass->resources[0].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[1].semantic, "Material");
|
||||
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
|
||||
EXPECT_EQ(pass->resources[1].set, 2u);
|
||||
EXPECT_EQ(pass->resources[1].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[2].semantic, "BaseColorTexture");
|
||||
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
|
||||
EXPECT_EQ(pass->resources[2].set, 3u);
|
||||
EXPECT_EQ(pass->resources[2].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
|
||||
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
|
||||
EXPECT_EQ(pass->resources[3].set, 4u);
|
||||
EXPECT_EQ(pass->resources[3].binding, 0u);
|
||||
|
||||
delete shader;
|
||||
}
|
||||
|
||||
TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
||||
const InputLayoutDesc inputLayout = BuiltinObjectIdPass::BuildInputLayout();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user