refactor: drive builtin forward bindings from shader pass resources
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
@@ -71,9 +72,85 @@ private:
|
|||||||
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CachedMaterialBindings {
|
struct DescriptorBindingLocation {
|
||||||
OwnedDescriptorSet constantSet = {};
|
Core::uint32 set = UINT32_MAX;
|
||||||
OwnedDescriptorSet textureSet = {};
|
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;
|
Core::uint64 materialVersion = 0;
|
||||||
RHI::RHIResourceView* textureView = nullptr;
|
RHI::RHIResourceView* textureView = nullptr;
|
||||||
};
|
};
|
||||||
@@ -113,14 +190,29 @@ private:
|
|||||||
bool CreatePipelineResources(const RenderContext& context);
|
bool CreatePipelineResources(const RenderContext& context);
|
||||||
void DestroyPipelineResources();
|
void DestroyPipelineResources();
|
||||||
ResolvedShaderPass ResolveForwardShaderPass(const Resources::Material* material) const;
|
ResolvedShaderPass ResolveForwardShaderPass(const Resources::Material* material) const;
|
||||||
|
PassResourceLayout* GetOrCreatePassResourceLayout(
|
||||||
|
const RenderContext& context,
|
||||||
|
const ResolvedShaderPass& resolvedShaderPass);
|
||||||
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const Resources::Material* material);
|
const Resources::Material* material);
|
||||||
RHI::RHIDescriptorSet* GetOrCreatePerObjectSet(Core::uint64 objectId);
|
bool CreateOwnedDescriptorSet(
|
||||||
CachedMaterialBindings* GetOrCreateMaterialBindings(
|
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 Resources::Material* material,
|
||||||
|
const PerMaterialConstants& materialConstants,
|
||||||
RHI::RHIResourceView* textureView);
|
RHI::RHIResourceView* textureView);
|
||||||
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
||||||
|
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||||
|
|
||||||
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
||||||
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
||||||
@@ -137,12 +229,9 @@ private:
|
|||||||
|
|
||||||
RenderResourceCache m_resourceCache;
|
RenderResourceCache m_resourceCache;
|
||||||
|
|
||||||
RHI::RHIDescriptorPool* m_samplerPool = nullptr;
|
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
||||||
RHI::RHIDescriptorSet* m_samplerSet = nullptr;
|
|
||||||
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;
|
|
||||||
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
||||||
std::unordered_map<Core::uint64, OwnedDescriptorSet> m_perObjectSets;
|
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
||||||
std::unordered_map<const Resources::Material*, CachedMaterialBindings> m_materialBindings;
|
|
||||||
RHI::RHISampler* m_sampler = nullptr;
|
RHI::RHISampler* m_sampler = nullptr;
|
||||||
RHI::RHITexture* m_fallbackTexture = nullptr;
|
RHI::RHITexture* m_fallbackTexture = nullptr;
|
||||||
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "Resources/Shader/Shader.h"
|
#include "Resources/Shader/Shader.h"
|
||||||
#include "Resources/Texture/Texture.h"
|
#include "Resources/Texture/Texture.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
@@ -51,8 +52,137 @@ private:
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uint32_t kDescriptorFirstSet = 1;
|
enum class ForwardPassSemantic : uint8_t {
|
||||||
constexpr uint32_t kDescriptorSetCount = 5;
|
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::ShaderPass* FindForwardCompatiblePass(
|
||||||
const Resources::Shader& shader,
|
const Resources::Shader& shader,
|
||||||
@@ -325,80 +455,6 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
|||||||
return false;
|
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 = {};
|
RHI::SamplerDesc samplerDesc = {};
|
||||||
samplerDesc.filter = static_cast<uint32_t>(RHI::FilterMode::Linear);
|
samplerDesc.filter = static_cast<uint32_t>(RHI::FilterMode::Linear);
|
||||||
samplerDesc.addressU = static_cast<uint32_t>(RHI::TextureAddressMode::Clamp);
|
samplerDesc.addressU = static_cast<uint32_t>(RHI::TextureAddressMode::Clamp);
|
||||||
@@ -413,7 +469,6 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
|||||||
if (m_sampler == nullptr) {
|
if (m_sampler == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_samplerSet->UpdateSampler(0, m_sampler);
|
|
||||||
|
|
||||||
const unsigned char whitePixel[4] = { 255, 255, 255, 255 };
|
const unsigned char whitePixel[4] = { 255, 255, 255, 255 };
|
||||||
RHI::TextureDesc textureDesc = {};
|
RHI::TextureDesc textureDesc = {};
|
||||||
@@ -455,16 +510,15 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
|||||||
}
|
}
|
||||||
m_pipelineStates.clear();
|
m_pipelineStates.clear();
|
||||||
|
|
||||||
for (auto& perObjectSetPair : m_perObjectSets) {
|
for (auto& descriptorSetPair : m_dynamicDescriptorSets) {
|
||||||
DestroyOwnedDescriptorSet(perObjectSetPair.second);
|
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
|
||||||
}
|
}
|
||||||
m_perObjectSets.clear();
|
m_dynamicDescriptorSets.clear();
|
||||||
|
|
||||||
for (auto& materialBindingPair : m_materialBindings) {
|
for (auto& passLayoutPair : m_passResourceLayouts) {
|
||||||
DestroyOwnedDescriptorSet(materialBindingPair.second.constantSet);
|
DestroyPassResourceLayout(passLayoutPair.second);
|
||||||
DestroyOwnedDescriptorSet(materialBindingPair.second.textureSet);
|
|
||||||
}
|
}
|
||||||
m_materialBindings.clear();
|
m_passResourceLayouts.clear();
|
||||||
|
|
||||||
if (m_fallbackTextureView != nullptr) {
|
if (m_fallbackTextureView != nullptr) {
|
||||||
m_fallbackTextureView->Shutdown();
|
m_fallbackTextureView->Shutdown();
|
||||||
@@ -484,24 +538,6 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
|||||||
m_sampler = nullptr;
|
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_device = nullptr;
|
||||||
m_initialized = false;
|
m_initialized = false;
|
||||||
m_builtinForwardShader.Reset();
|
m_builtinForwardShader.Reset();
|
||||||
@@ -536,6 +572,214 @@ BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveForwar
|
|||||||
return resolved;
|
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(
|
RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const Resources::Material* material) {
|
const Resources::Material* material) {
|
||||||
@@ -547,6 +791,11 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||||
|
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
PipelineStateKey pipelineKey = {};
|
PipelineStateKey pipelineKey = {};
|
||||||
pipelineKey.renderState =
|
pipelineKey.renderState =
|
||||||
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
||||||
@@ -561,7 +810,7 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
|||||||
const RHI::GraphicsPipelineDesc pipelineDesc =
|
const RHI::GraphicsPipelineDesc pipelineDesc =
|
||||||
CreatePipelineDesc(
|
CreatePipelineDesc(
|
||||||
context.backendType,
|
context.backendType,
|
||||||
m_pipelineLayout,
|
passLayout->pipelineLayout,
|
||||||
*resolvedShaderPass.shader,
|
*resolvedShaderPass.shader,
|
||||||
resolvedShaderPass.passName,
|
resolvedShaderPass.passName,
|
||||||
material);
|
material);
|
||||||
@@ -581,117 +830,107 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
|||||||
return pipelineState;
|
return pipelineState;
|
||||||
}
|
}
|
||||||
|
|
||||||
RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreatePerObjectSet(Core::uint64 objectId) {
|
bool BuiltinForwardPipeline::CreateOwnedDescriptorSet(
|
||||||
const auto existing = m_perObjectSets.find(objectId);
|
const PassSetLayoutMetadata& setLayout,
|
||||||
if (existing != m_perObjectSets.end()) {
|
OwnedDescriptorSet& descriptorSet) {
|
||||||
return existing->second.set;
|
|
||||||
}
|
|
||||||
|
|
||||||
RHI::DescriptorPoolDesc poolDesc = {};
|
RHI::DescriptorPoolDesc poolDesc = {};
|
||||||
poolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
poolDesc.type = setLayout.heapType;
|
||||||
poolDesc.descriptorCount = 1;
|
poolDesc.descriptorCount = CountHeapDescriptors(setLayout.heapType, setLayout.bindings);
|
||||||
poolDesc.shaderVisible = false;
|
poolDesc.shaderVisible = setLayout.shaderVisible;
|
||||||
|
|
||||||
OwnedDescriptorSet descriptorSet = {};
|
|
||||||
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
||||||
if (descriptorSet.pool == nullptr) {
|
if (descriptorSet.pool == nullptr) {
|
||||||
return nullptr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RHI::DescriptorSetLayoutBinding binding = {};
|
descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout);
|
||||||
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);
|
|
||||||
if (descriptorSet.set == nullptr) {
|
if (descriptorSet.set == nullptr) {
|
||||||
DestroyOwnedDescriptorSet(descriptorSet);
|
DestroyOwnedDescriptorSet(descriptorSet);
|
||||||
return nullptr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto result = m_perObjectSets.emplace(objectId, descriptorSet);
|
return true;
|
||||||
return result.first->second.set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BuiltinForwardPipeline::CachedMaterialBindings* BuiltinForwardPipeline::GetOrCreateMaterialBindings(
|
RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreateStaticDescriptorSet(
|
||||||
const Resources::Material* material,
|
PassResourceLayout& passLayout,
|
||||||
RHI::RHIResourceView* textureView) {
|
Core::uint32 setIndex) {
|
||||||
if (textureView == nullptr) {
|
if (setIndex >= passLayout.setLayouts.size() ||
|
||||||
|
setIndex >= passLayout.staticDescriptorSets.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedMaterialBindings& cachedBindings = m_materialBindings[material];
|
OwnedDescriptorSet& descriptorSet = passLayout.staticDescriptorSets[setIndex];
|
||||||
|
if (descriptorSet.set == nullptr) {
|
||||||
if (cachedBindings.constantSet.set == nullptr) {
|
if (!CreateOwnedDescriptorSet(passLayout.setLayouts[setIndex], descriptorSet)) {
|
||||||
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) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RHI::DescriptorSetLayoutBinding constantBinding = {};
|
if (passLayout.setLayouts[setIndex].usesSampler) {
|
||||||
constantBinding.binding = 0;
|
if (m_sampler == nullptr ||
|
||||||
constantBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
!passLayout.linearClampSampler.IsValid() ||
|
||||||
constantBinding.count = 1;
|
passLayout.linearClampSampler.set != setIndex) {
|
||||||
|
DestroyOwnedDescriptorSet(descriptorSet);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
RHI::DescriptorSetLayoutDesc constantLayout = {};
|
descriptorSet.set->UpdateSampler(passLayout.linearClampSampler.binding, m_sampler);
|
||||||
constantLayout.bindings = &constantBinding;
|
|
||||||
constantLayout.bindingCount = 1;
|
|
||||||
cachedBindings.constantSet.set = cachedBindings.constantSet.pool->AllocateSet(constantLayout);
|
|
||||||
if (cachedBindings.constantSet.set == nullptr) {
|
|
||||||
DestroyOwnedDescriptorSet(cachedBindings.constantSet);
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cachedBindings.textureSet.set == nullptr) {
|
return descriptorSet.set;
|
||||||
RHI::DescriptorPoolDesc texturePoolDesc = {};
|
}
|
||||||
texturePoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
|
||||||
texturePoolDesc.descriptorCount = 1;
|
|
||||||
texturePoolDesc.shaderVisible = true;
|
|
||||||
|
|
||||||
cachedBindings.textureSet.pool = m_device->CreateDescriptorPool(texturePoolDesc);
|
BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreateDynamicDescriptorSet(
|
||||||
if (cachedBindings.textureSet.pool == nullptr) {
|
const PassLayoutKey& passLayoutKey,
|
||||||
DestroyOwnedDescriptorSet(cachedBindings.constantSet);
|
const PassResourceLayout& passLayout,
|
||||||
return nullptr;
|
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 = {};
|
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
|
||||||
textureBinding.binding = 0;
|
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
|
||||||
textureBinding.type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
|
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
|
||||||
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);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0;
|
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0;
|
||||||
if (cachedBindings.materialVersion != materialVersion || cachedBindings.textureView != textureView) {
|
if ((setLayout.usesMaterial || setLayout.usesTexture) &&
|
||||||
const BuiltinForwardMaterialData materialData = BuildBuiltinForwardMaterialData(material);
|
(cachedDescriptorSet.materialVersion != materialVersion ||
|
||||||
const PerMaterialConstants materialConstants = {
|
cachedDescriptorSet.textureView != textureView)) {
|
||||||
materialData.baseColorFactor
|
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));
|
if (setLayout.usesTexture) {
|
||||||
cachedBindings.textureSet.set->Update(0, textureView);
|
if (textureView == nullptr ||
|
||||||
cachedBindings.materialVersion = materialVersion;
|
!passLayout.baseColorTexture.IsValid() ||
|
||||||
cachedBindings.textureView = textureView;
|
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) {
|
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 {
|
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
|
||||||
return ResolveBuiltinBaseColorTexture(material);
|
return ResolveBuiltinBaseColorTexture(material);
|
||||||
}
|
}
|
||||||
@@ -766,31 +1026,91 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
|
|||||||
: Math::Vector4::Zero()
|
: Math::Vector4::Zero()
|
||||||
};
|
};
|
||||||
|
|
||||||
RHI::RHIResourceView* textureView = ResolveTextureView(visibleItem);
|
|
||||||
if (textureView == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||||
RHI::RHIDescriptorSet* constantSet = GetOrCreatePerObjectSet(
|
const ResolvedShaderPass resolvedShaderPass = ResolveForwardShaderPass(material);
|
||||||
visibleItem.gameObject != nullptr ? visibleItem.gameObject->GetID() : 0);
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
CachedMaterialBindings* materialBindings = GetOrCreateMaterialBindings(material, textureView);
|
|
||||||
if (constantSet == nullptr ||
|
|
||||||
materialBindings == nullptr ||
|
|
||||||
materialBindings->constantSet.set == nullptr ||
|
|
||||||
materialBindings->textureSet.set == nullptr ||
|
|
||||||
m_samplerSet == nullptr) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
constantSet->WriteConstant(0, &constants, sizeof(constants));
|
PassLayoutKey passLayoutKey = {};
|
||||||
RHI::RHIDescriptorSet* descriptorSets[] = {
|
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||||
constantSet,
|
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||||
materialBindings->constantSet.set,
|
|
||||||
materialBindings->textureSet.set,
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||||
m_samplerSet
|
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) {
|
if (visibleItem.hasSection) {
|
||||||
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
|
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
|
#include <XCEngine/Rendering/Passes/BuiltinObjectIdPass.h>
|
||||||
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
#include <XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h>
|
||||||
|
#include <XCEngine/Resources/BuiltinResources.h>
|
||||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||||
|
#include <XCEngine/Resources/Shader/Shader.h>
|
||||||
|
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||||
#include <XCEngine/RHI/RHIEnums.h>
|
#include <XCEngine/RHI/RHIEnums.h>
|
||||||
|
|
||||||
using namespace XCEngine::Rendering::Pipelines;
|
using namespace XCEngine::Rendering::Pipelines;
|
||||||
@@ -37,6 +40,42 @@ TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVert
|
|||||||
EXPECT_EQ(texcoord.alignedByteOffset, static_cast<uint32_t>(offsetof(StaticMeshVertex, uv0)));
|
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) {
|
TEST(BuiltinObjectIdPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
||||||
const InputLayoutDesc inputLayout = BuiltinObjectIdPass::BuildInputLayout();
|
const InputLayoutDesc inputLayout = BuiltinObjectIdPass::BuildInputLayout();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user