Add runtime material buffer bindings
This commit is contained in:
@@ -45,6 +45,8 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
location = &outPlan.material;
|
||||
break;
|
||||
case BuiltinPassResourceSemantic::MaterialBuffer:
|
||||
break;
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
location = &outPlan.lighting;
|
||||
break;
|
||||
@@ -83,10 +85,10 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
||||
break;
|
||||
}
|
||||
|
||||
if (location == nullptr) {
|
||||
if (semantic != BuiltinPassResourceSemantic::MaterialBuffer && location == nullptr) {
|
||||
return fail("Builtin pass resource semantic could not be mapped");
|
||||
}
|
||||
if (location->IsValid()) {
|
||||
if (location != nullptr && location->IsValid()) {
|
||||
return fail("Builtin pass resource semantic appears more than once");
|
||||
}
|
||||
|
||||
@@ -97,13 +99,21 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
|
||||
}
|
||||
}
|
||||
|
||||
*location = { binding.set, binding.binding };
|
||||
const PassResourceBindingLocation resolvedLocation = { binding.set, binding.binding };
|
||||
if (location != nullptr) {
|
||||
*location = resolvedLocation;
|
||||
}
|
||||
|
||||
BuiltinPassResourceBindingDesc resolvedBinding = {};
|
||||
resolvedBinding.name = binding.name;
|
||||
resolvedBinding.semantic = semantic;
|
||||
resolvedBinding.resourceType = binding.type;
|
||||
resolvedBinding.location = *location;
|
||||
resolvedBinding.location = resolvedLocation;
|
||||
outPlan.bindings.PushBack(resolvedBinding);
|
||||
if (semantic == BuiltinPassResourceSemantic::MaterialBuffer) {
|
||||
outPlan.materialBufferBindings.PushBack(resolvedBinding);
|
||||
outPlan.usesMaterialBuffers = true;
|
||||
}
|
||||
|
||||
outPlan.maxSetIndex = std::max(outPlan.maxSetIndex, binding.set);
|
||||
minBoundSet = std::min(minBoundSet, binding.set);
|
||||
@@ -512,6 +522,10 @@ inline bool TryBuildBuiltinPassSetLayouts(
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
setLayout.usesMaterial = true;
|
||||
break;
|
||||
case BuiltinPassResourceSemantic::MaterialBuffer:
|
||||
setLayout.usesMaterialBuffers = true;
|
||||
setLayout.materialBufferBindings.push_back(binding);
|
||||
break;
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
setLayout.usesLighting = true;
|
||||
break;
|
||||
|
||||
@@ -111,6 +111,10 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
|
||||
return BuiltinPassResourceSemantic::Material;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("materialbuffer")) {
|
||||
return BuiltinPassResourceSemantic::MaterialBuffer;
|
||||
}
|
||||
|
||||
if (semantic == Containers::String("lighting") ||
|
||||
semantic == Containers::String("lightingconstants")) {
|
||||
return BuiltinPassResourceSemantic::Lighting;
|
||||
@@ -163,6 +167,16 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
|
||||
return BuiltinPassResourceSemantic::ShadowMapSampler;
|
||||
}
|
||||
|
||||
switch (binding.type) {
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return BuiltinPassResourceSemantic::MaterialBuffer;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return BuiltinPassResourceSemantic::Unknown;
|
||||
}
|
||||
|
||||
@@ -172,6 +186,8 @@ inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemant
|
||||
return "PerObject";
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
return "Material";
|
||||
case BuiltinPassResourceSemantic::MaterialBuffer:
|
||||
return "MaterialBuffer";
|
||||
case BuiltinPassResourceSemantic::Lighting:
|
||||
return "Lighting";
|
||||
case BuiltinPassResourceSemantic::ShadowReceiver:
|
||||
@@ -260,6 +276,11 @@ inline bool IsBuiltinPassResourceTypeCompatible(
|
||||
case BuiltinPassResourceSemantic::Environment:
|
||||
case BuiltinPassResourceSemantic::PassConstants:
|
||||
return type == Resources::ShaderResourceType::ConstantBuffer;
|
||||
case BuiltinPassResourceSemantic::MaterialBuffer:
|
||||
return type == Resources::ShaderResourceType::StructuredBuffer ||
|
||||
type == Resources::ShaderResourceType::RawBuffer ||
|
||||
type == Resources::ShaderResourceType::RWStructuredBuffer ||
|
||||
type == Resources::ShaderResourceType::RWRawBuffer;
|
||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||
case BuiltinPassResourceSemantic::SourceColorTexture:
|
||||
case BuiltinPassResourceSemantic::SkyboxPanoramicTexture:
|
||||
|
||||
@@ -37,6 +37,7 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
|
||||
Unknown = 0,
|
||||
PerObject,
|
||||
Material,
|
||||
MaterialBuffer,
|
||||
Lighting,
|
||||
ShadowReceiver,
|
||||
Environment,
|
||||
@@ -51,6 +52,7 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
|
||||
};
|
||||
|
||||
struct BuiltinPassResourceBindingDesc {
|
||||
Containers::String name;
|
||||
BuiltinPassResourceSemantic semantic = BuiltinPassResourceSemantic::Unknown;
|
||||
Resources::ShaderResourceType resourceType = Resources::ShaderResourceType::ConstantBuffer;
|
||||
PassResourceBindingLocation location = {};
|
||||
@@ -58,12 +60,14 @@ struct BuiltinPassResourceBindingDesc {
|
||||
|
||||
struct BuiltinPassResourceBindingPlan {
|
||||
Containers::Array<BuiltinPassResourceBindingDesc> bindings;
|
||||
Containers::Array<BuiltinPassResourceBindingDesc> materialBufferBindings;
|
||||
Core::uint32 maxSetIndex = 0;
|
||||
Core::uint32 firstDescriptorSet = 0;
|
||||
Core::uint32 descriptorSetCount = 0;
|
||||
bool usesConstantBuffers = false;
|
||||
bool usesTextures = false;
|
||||
bool usesSamplers = false;
|
||||
bool usesMaterialBuffers = false;
|
||||
PassResourceBindingLocation perObject = {};
|
||||
PassResourceBindingLocation material = {};
|
||||
PassResourceBindingLocation lighting = {};
|
||||
@@ -91,6 +95,7 @@ struct BuiltinPassResourceBindingPlan {
|
||||
|
||||
struct BuiltinPassSetLayoutMetadata {
|
||||
std::vector<RHI::DescriptorSetLayoutBinding> bindings;
|
||||
std::vector<BuiltinPassResourceBindingDesc> materialBufferBindings;
|
||||
RHI::DescriptorSetLayoutDesc layout = {};
|
||||
RHI::DescriptorHeapType heapType = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
bool shaderVisible = false;
|
||||
@@ -100,6 +105,7 @@ struct BuiltinPassSetLayoutMetadata {
|
||||
bool usesShadowReceiver = false;
|
||||
bool usesEnvironment = false;
|
||||
bool usesPassConstants = false;
|
||||
bool usesMaterialBuffers = false;
|
||||
bool usesTexture = false;
|
||||
bool usesBaseColorTexture = false;
|
||||
bool usesSourceColorTexture = false;
|
||||
|
||||
@@ -32,19 +32,61 @@ public:
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct CachedBufferView {
|
||||
RHI::RHIResourceView* resourceView = nullptr;
|
||||
};
|
||||
|
||||
~RenderResourceCache();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
const CachedMesh* GetOrCreateMesh(RHI::RHIDevice* device, const Resources::Mesh* mesh);
|
||||
const CachedTexture* GetOrCreateTexture(RHI::RHIDevice* device, const Resources::Texture* texture);
|
||||
const CachedBufferView* GetOrCreateBufferView(
|
||||
RHI::RHIDevice* device,
|
||||
RHI::RHIBuffer* buffer,
|
||||
RHI::ResourceViewType viewType,
|
||||
const RHI::ResourceViewDesc& viewDesc);
|
||||
|
||||
private:
|
||||
struct BufferViewCacheKey {
|
||||
const RHI::RHIBuffer* buffer = nullptr;
|
||||
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
|
||||
uint32_t format = 0;
|
||||
RHI::ResourceViewDimension dimension = RHI::ResourceViewDimension::Unknown;
|
||||
uint64_t bufferLocation = 0;
|
||||
uint32_t firstElement = 0;
|
||||
uint32_t elementCount = 0;
|
||||
uint32_t structureByteStride = 0;
|
||||
|
||||
bool operator==(const BufferViewCacheKey& other) const {
|
||||
return buffer == other.buffer &&
|
||||
viewType == other.viewType &&
|
||||
format == other.format &&
|
||||
dimension == other.dimension &&
|
||||
bufferLocation == other.bufferLocation &&
|
||||
firstElement == other.firstElement &&
|
||||
elementCount == other.elementCount &&
|
||||
structureByteStride == other.structureByteStride;
|
||||
}
|
||||
};
|
||||
|
||||
struct BufferViewCacheKeyHash {
|
||||
size_t operator()(const BufferViewCacheKey& key) const noexcept;
|
||||
};
|
||||
|
||||
bool UploadMesh(RHI::RHIDevice* device, const Resources::Mesh* mesh, CachedMesh& cachedMesh);
|
||||
bool UploadTexture(RHI::RHIDevice* device, const Resources::Texture* texture, CachedTexture& cachedTexture);
|
||||
bool CreateBufferView(
|
||||
RHI::RHIDevice* device,
|
||||
RHI::RHIBuffer* buffer,
|
||||
RHI::ResourceViewType viewType,
|
||||
const RHI::ResourceViewDesc& viewDesc,
|
||||
CachedBufferView& cachedBufferView);
|
||||
|
||||
std::unordered_map<const Resources::Mesh*, CachedMesh> m_meshCache;
|
||||
std::unordered_map<const Resources::Texture*, CachedTexture> m_textureCache;
|
||||
std::unordered_map<BufferViewCacheKey, CachedBufferView, BufferViewCacheKeyHash> m_bufferViewCache;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/RHI/RHIBuffer.h>
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
#include <XCEngine/RHI/RHITypes.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
|
||||
@@ -46,6 +49,31 @@ struct MaterialConstantPayloadView {
|
||||
}
|
||||
};
|
||||
|
||||
struct MaterialBufferResourceView {
|
||||
RHI::RHIBuffer* buffer = nullptr;
|
||||
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
|
||||
RHI::ResourceViewDesc viewDesc = {};
|
||||
|
||||
bool IsValid() const {
|
||||
return buffer != nullptr &&
|
||||
(viewType == RHI::ResourceViewType::ShaderResource ||
|
||||
viewType == RHI::ResourceViewType::UnorderedAccess) &&
|
||||
viewDesc.dimension != RHI::ResourceViewDimension::Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool IsMaterialBufferResourceType(Resources::ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline const Resources::ShaderPropertyDesc* FindShaderPropertyBySemantic(
|
||||
const Resources::Material* material,
|
||||
const Containers::String& semantic) {
|
||||
@@ -249,6 +277,62 @@ inline MaterialConstantPayloadView ResolveSchemaMaterialConstantPayload(const Re
|
||||
return { constantBufferData.Data(), constantBufferData.Size(), layoutView };
|
||||
}
|
||||
|
||||
inline bool TryResolveMaterialBufferResourceView(
|
||||
const Resources::Material* material,
|
||||
const BuiltinPassResourceBindingDesc& binding,
|
||||
MaterialBufferResourceView& outView) {
|
||||
outView = {};
|
||||
if (material == nullptr || !IsMaterialBufferResourceType(binding.resourceType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::MaterialBufferBinding* materialBinding = material->FindBufferBinding(binding.name);
|
||||
if (materialBinding == nullptr || materialBinding->buffer == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outView.buffer = materialBinding->buffer;
|
||||
outView.viewDesc.firstElement = materialBinding->viewDesc.firstElement;
|
||||
outView.viewDesc.elementCount = materialBinding->viewDesc.elementCount;
|
||||
|
||||
switch (binding.resourceType) {
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::ShaderResource;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
|
||||
outView.viewDesc.structureByteStride =
|
||||
materialBinding->viewDesc.structureByteStride > 0
|
||||
? materialBinding->viewDesc.structureByteStride
|
||||
: materialBinding->buffer->GetStride();
|
||||
break;
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::ShaderResource;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
|
||||
break;
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
|
||||
outView.viewDesc.structureByteStride =
|
||||
materialBinding->viewDesc.structureByteStride > 0
|
||||
? materialBinding->viewDesc.structureByteStride
|
||||
: materialBinding->buffer->GetStride();
|
||||
break;
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outView.viewDesc.dimension == RHI::ResourceViewDimension::StructuredBuffer &&
|
||||
outView.viewDesc.structureByteStride == 0) {
|
||||
outView = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
return outView.IsValid();
|
||||
}
|
||||
|
||||
inline const Resources::Material* ResolveMaterial(
|
||||
const Components::MeshRendererComponent* meshRenderer,
|
||||
const Resources::Mesh* mesh,
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
class RHIBuffer;
|
||||
}
|
||||
namespace Resources {
|
||||
|
||||
enum class MaterialRenderQueue : Core::int32 {
|
||||
@@ -77,6 +80,18 @@ struct MaterialTextureBinding {
|
||||
std::shared_ptr<PendingTextureLoadState> pendingLoad;
|
||||
};
|
||||
|
||||
struct MaterialBufferBindingViewDesc {
|
||||
Core::uint32 firstElement = 0;
|
||||
Core::uint32 elementCount = 0;
|
||||
Core::uint32 structureByteStride = 0;
|
||||
};
|
||||
|
||||
struct MaterialBufferBinding {
|
||||
Containers::String name;
|
||||
RHI::RHIBuffer* buffer = nullptr;
|
||||
MaterialBufferBindingViewDesc viewDesc = {};
|
||||
};
|
||||
|
||||
class Material : public IResource {
|
||||
public:
|
||||
Material();
|
||||
@@ -128,6 +143,11 @@ public:
|
||||
void SetInt(const Containers::String& name, Core::int32 value);
|
||||
void SetBool(const Containers::String& name, bool value);
|
||||
void SetTexture(const Containers::String& name, const ResourceHandle<Texture>& texture);
|
||||
void SetBuffer(const Containers::String& name, RHI::RHIBuffer* buffer);
|
||||
void SetBuffer(
|
||||
const Containers::String& name,
|
||||
RHI::RHIBuffer* buffer,
|
||||
const MaterialBufferBindingViewDesc& viewDesc);
|
||||
void SetTextureAssetRef(const Containers::String& name,
|
||||
const AssetRef& textureRef,
|
||||
const Containers::String& texturePath = Containers::String());
|
||||
@@ -140,13 +160,17 @@ public:
|
||||
Core::int32 GetInt(const Containers::String& name) const;
|
||||
bool GetBool(const Containers::String& name) const;
|
||||
ResourceHandle<Texture> GetTexture(const Containers::String& name) const;
|
||||
RHI::RHIBuffer* GetBuffer(const Containers::String& name) const;
|
||||
const MaterialBufferBinding* FindBufferBinding(const Containers::String& name) const;
|
||||
Core::uint32 GetTextureBindingCount() const { return static_cast<Core::uint32>(m_textureBindings.Size()); }
|
||||
Core::uint32 GetBufferBindingCount() const { return static_cast<Core::uint32>(m_bufferBindings.Size()); }
|
||||
Containers::String GetTextureBindingName(Core::uint32 index) const;
|
||||
AssetRef GetTextureBindingAssetRef(Core::uint32 index) const;
|
||||
Containers::String GetTextureBindingPath(Core::uint32 index) const;
|
||||
ResourceHandle<Texture> GetTextureBindingLoadedTexture(Core::uint32 index) const;
|
||||
ResourceHandle<Texture> GetTextureBindingTexture(Core::uint32 index) const;
|
||||
const Containers::Array<MaterialTextureBinding>& GetTextureBindings() const { return m_textureBindings; }
|
||||
const Containers::Array<MaterialBufferBinding>& GetBufferBindings() const { return m_bufferBindings; }
|
||||
std::vector<MaterialProperty> GetProperties() const;
|
||||
|
||||
const Containers::Array<Core::uint8>& GetConstantBufferData() const { return m_constantBufferData; }
|
||||
@@ -159,12 +183,15 @@ public:
|
||||
bool HasProperty(const Containers::String& name) const;
|
||||
void RemoveProperty(const Containers::String& name);
|
||||
void ClearAllProperties();
|
||||
void RemoveBufferBinding(const Containers::String& name);
|
||||
void ClearBufferBindings();
|
||||
|
||||
private:
|
||||
const ShaderPropertyDesc* FindShaderPropertyDesc(const Containers::String& name) const;
|
||||
bool CanAssignPropertyType(const Containers::String& name, MaterialPropertyType type) const;
|
||||
bool ResetPropertyToShaderDefault(const Containers::String& name);
|
||||
void SyncShaderSchemaProperties(bool removeUnknownProperties);
|
||||
void SyncShaderRuntimeBufferBindings(bool removeUnknownBindings);
|
||||
void BeginAsyncTextureLoad(Core::uint32 index);
|
||||
void ResolvePendingTextureBinding(Core::uint32 index);
|
||||
void ResolvePendingTextureBindings();
|
||||
@@ -182,6 +209,7 @@ private:
|
||||
Containers::Array<MaterialConstantFieldDesc> m_constantLayout;
|
||||
Containers::Array<Core::uint8> m_constantBufferData;
|
||||
Containers::Array<MaterialTextureBinding> m_textureBindings;
|
||||
Containers::Array<MaterialBufferBinding> m_bufferBindings;
|
||||
Core::uint64 m_changeVersion = 1;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
namespace {
|
||||
|
||||
inline void HashCombine(size_t& seed, size_t value) {
|
||||
seed ^= value + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
|
||||
}
|
||||
|
||||
RHI::Format ToRHITextureFormat(Resources::TextureFormat format) {
|
||||
switch (format) {
|
||||
case Resources::TextureFormat::R8_UNORM:
|
||||
@@ -125,6 +130,14 @@ void ShutdownTexture(RenderResourceCache::CachedTexture& cachedTexture) {
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownBufferView(RenderResourceCache::CachedBufferView& cachedBufferView) {
|
||||
if (cachedBufferView.resourceView != nullptr) {
|
||||
cachedBufferView.resourceView->Shutdown();
|
||||
delete cachedBufferView.resourceView;
|
||||
cachedBufferView.resourceView = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderResourceCache::~RenderResourceCache() {
|
||||
@@ -141,6 +154,11 @@ void RenderResourceCache::Shutdown() {
|
||||
ShutdownTexture(entry.second);
|
||||
}
|
||||
m_textureCache.clear();
|
||||
|
||||
for (auto& entry : m_bufferViewCache) {
|
||||
ShutdownBufferView(entry.second);
|
||||
}
|
||||
m_bufferViewCache.clear();
|
||||
}
|
||||
|
||||
const RenderResourceCache::CachedMesh* RenderResourceCache::GetOrCreateMesh(
|
||||
@@ -187,6 +205,52 @@ const RenderResourceCache::CachedTexture* RenderResourceCache::GetOrCreateTextur
|
||||
return &result.first->second;
|
||||
}
|
||||
|
||||
const RenderResourceCache::CachedBufferView* RenderResourceCache::GetOrCreateBufferView(
|
||||
RHI::RHIDevice* device,
|
||||
RHI::RHIBuffer* buffer,
|
||||
RHI::ResourceViewType viewType,
|
||||
const RHI::ResourceViewDesc& viewDesc) {
|
||||
if (device == nullptr || buffer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BufferViewCacheKey key = {};
|
||||
key.buffer = buffer;
|
||||
key.viewType = viewType;
|
||||
key.format = viewDesc.format;
|
||||
key.dimension = viewDesc.dimension;
|
||||
key.bufferLocation = viewDesc.bufferLocation;
|
||||
key.firstElement = viewDesc.firstElement;
|
||||
key.elementCount = viewDesc.elementCount;
|
||||
key.structureByteStride = viewDesc.structureByteStride;
|
||||
|
||||
const auto existing = m_bufferViewCache.find(key);
|
||||
if (existing != m_bufferViewCache.end()) {
|
||||
return &existing->second;
|
||||
}
|
||||
|
||||
CachedBufferView cachedBufferView = {};
|
||||
if (!CreateBufferView(device, buffer, viewType, viewDesc, cachedBufferView)) {
|
||||
ShutdownBufferView(cachedBufferView);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto result = m_bufferViewCache.emplace(key, cachedBufferView);
|
||||
return &result.first->second;
|
||||
}
|
||||
|
||||
size_t RenderResourceCache::BufferViewCacheKeyHash::operator()(const BufferViewCacheKey& key) const noexcept {
|
||||
size_t hash = std::hash<const void*>{}(static_cast<const void*>(key.buffer));
|
||||
HashCombine(hash, std::hash<uint32_t>{}(static_cast<uint32_t>(key.viewType)));
|
||||
HashCombine(hash, std::hash<uint32_t>{}(key.format));
|
||||
HashCombine(hash, std::hash<uint32_t>{}(static_cast<uint32_t>(key.dimension)));
|
||||
HashCombine(hash, std::hash<uint64_t>{}(key.bufferLocation));
|
||||
HashCombine(hash, std::hash<uint32_t>{}(key.firstElement));
|
||||
HashCombine(hash, std::hash<uint32_t>{}(key.elementCount));
|
||||
HashCombine(hash, std::hash<uint32_t>{}(key.structureByteStride));
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool RenderResourceCache::UploadMesh(
|
||||
RHI::RHIDevice* device,
|
||||
const Resources::Mesh* mesh,
|
||||
@@ -303,5 +367,25 @@ bool RenderResourceCache::UploadTexture(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderResourceCache::CreateBufferView(
|
||||
RHI::RHIDevice* device,
|
||||
RHI::RHIBuffer* buffer,
|
||||
RHI::ResourceViewType viewType,
|
||||
const RHI::ResourceViewDesc& viewDesc,
|
||||
CachedBufferView& cachedBufferView) {
|
||||
switch (viewType) {
|
||||
case RHI::ResourceViewType::ShaderResource:
|
||||
cachedBufferView.resourceView = device->CreateShaderResourceView(buffer, viewDesc);
|
||||
break;
|
||||
case RHI::ResourceViewType::UnorderedAccess:
|
||||
cachedBufferView.resourceView = device->CreateUnorderedAccessView(buffer, viewDesc);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return cachedBufferView.resourceView != nullptr;
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -31,12 +31,22 @@ Resources::ShaderKeywordSet ResolvePassKeywordSet(
|
||||
}
|
||||
|
||||
bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) {
|
||||
return bindingPlan.perObject.IsValid() &&
|
||||
bindingPlan.bindings.Size() == 1u &&
|
||||
bindingPlan.descriptorSetCount == 1u &&
|
||||
bindingPlan.usesConstantBuffers &&
|
||||
!bindingPlan.usesTextures &&
|
||||
!bindingPlan.usesSamplers;
|
||||
if (!(bindingPlan.perObject.IsValid() &&
|
||||
bindingPlan.descriptorSetCount >= 1u &&
|
||||
bindingPlan.usesConstantBuffers &&
|
||||
!bindingPlan.usesTextures &&
|
||||
!bindingPlan.usesSamplers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const BuiltinPassResourceBindingDesc& binding : bindingPlan.bindings) {
|
||||
if (binding.semantic != BuiltinPassResourceSemantic::PerObject &&
|
||||
binding.semantic != BuiltinPassResourceSemantic::MaterialBuffer) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UsesContiguousDescriptorSets(const BuiltinPassResourceBindingPlan& bindingPlan) {
|
||||
@@ -63,14 +73,31 @@ bool UsesContiguousDescriptorSets(const BuiltinPassResourceBindingPlan& bindingP
|
||||
}
|
||||
|
||||
bool IsSupportedAlphaTestBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) {
|
||||
return bindingPlan.perObject.IsValid() &&
|
||||
bindingPlan.material.IsValid() &&
|
||||
bindingPlan.baseColorTexture.IsValid() &&
|
||||
bindingPlan.linearClampSampler.IsValid() &&
|
||||
!bindingPlan.lighting.IsValid() &&
|
||||
!bindingPlan.shadowReceiver.IsValid() &&
|
||||
!bindingPlan.shadowMapTexture.IsValid() &&
|
||||
!bindingPlan.shadowMapSampler.IsValid();
|
||||
if (!(bindingPlan.perObject.IsValid() &&
|
||||
bindingPlan.material.IsValid() &&
|
||||
bindingPlan.baseColorTexture.IsValid() &&
|
||||
bindingPlan.linearClampSampler.IsValid() &&
|
||||
!bindingPlan.lighting.IsValid() &&
|
||||
!bindingPlan.shadowReceiver.IsValid() &&
|
||||
!bindingPlan.shadowMapTexture.IsValid() &&
|
||||
!bindingPlan.shadowMapSampler.IsValid())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const BuiltinPassResourceBindingDesc& binding : bindingPlan.bindings) {
|
||||
switch (binding.semantic) {
|
||||
case BuiltinPassResourceSemantic::PerObject:
|
||||
case BuiltinPassResourceSemantic::Material:
|
||||
case BuiltinPassResourceSemantic::MaterialBuffer:
|
||||
case BuiltinPassResourceSemantic::BaseColorTexture:
|
||||
case BuiltinPassResourceSemantic::LinearClampSampler:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t ResolveSurfaceColorAttachmentCount(const RenderSurface& surface) {
|
||||
@@ -592,6 +619,35 @@ BuiltinDepthStylePassBase::CachedDescriptorSet* BuiltinDepthStylePassBase::GetOr
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesMaterialBuffers) {
|
||||
if (material == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cachedDescriptorSet.materialVersion != materialVersion) {
|
||||
for (const BuiltinPassResourceBindingDesc& bufferBinding : setLayout.materialBufferBindings) {
|
||||
MaterialBufferResourceView resolvedView = {};
|
||||
if (!TryResolveMaterialBufferResourceView(material, bufferBinding, resolvedView)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RenderResourceCache::CachedBufferView* cachedBufferView =
|
||||
m_resourceCache.GetOrCreateBufferView(
|
||||
m_device,
|
||||
resolvedView.buffer,
|
||||
resolvedView.viewType,
|
||||
resolvedView.viewDesc);
|
||||
if (cachedBufferView == nullptr || cachedBufferView->resourceView == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cachedDescriptorSet.descriptorSet.set->Update(
|
||||
bufferBinding.location.binding,
|
||||
cachedBufferView->resourceView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesBaseColorTexture) {
|
||||
if (baseColorTextureView == nullptr ||
|
||||
!passLayout.baseColorTexture.IsValid() ||
|
||||
@@ -785,11 +841,16 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
|
||||
RHI::RHIDescriptorSet* descriptorSet = nullptr;
|
||||
if (setLayout.usesPerObject ||
|
||||
setLayout.usesMaterial ||
|
||||
setLayout.usesBaseColorTexture) {
|
||||
setLayout.usesBaseColorTexture ||
|
||||
setLayout.usesMaterialBuffers) {
|
||||
const Core::uint64 objectId =
|
||||
setLayout.usesPerObject ? visibleItem.gameObject->GetID() : 0;
|
||||
const Resources::Material* materialKey =
|
||||
(setLayout.usesMaterial || setLayout.usesBaseColorTexture) ? material : nullptr;
|
||||
(setLayout.usesMaterial ||
|
||||
setLayout.usesBaseColorTexture ||
|
||||
setLayout.usesMaterialBuffers)
|
||||
? material
|
||||
: nullptr;
|
||||
|
||||
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||
passLayoutKey,
|
||||
|
||||
@@ -398,6 +398,35 @@ BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreate
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesMaterialBuffers) {
|
||||
if (material == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cachedDescriptorSet.materialVersion != materialVersion) {
|
||||
for (const BuiltinPassResourceBindingDesc& bufferBinding : setLayout.materialBufferBindings) {
|
||||
MaterialBufferResourceView resolvedView = {};
|
||||
if (!TryResolveMaterialBufferResourceView(material, bufferBinding, resolvedView)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RenderResourceCache::CachedBufferView* cachedBufferView =
|
||||
m_resourceCache.GetOrCreateBufferView(
|
||||
m_device,
|
||||
resolvedView.buffer,
|
||||
resolvedView.viewType,
|
||||
resolvedView.viewDesc);
|
||||
if (cachedBufferView == nullptr || cachedBufferView->resourceView == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cachedDescriptorSet.descriptorSet.set->Update(
|
||||
bufferBinding.location.binding,
|
||||
cachedBufferView->resourceView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesLighting) {
|
||||
if (!passLayout.lighting.IsValid() || passLayout.lighting.set != setIndex) {
|
||||
return nullptr;
|
||||
@@ -679,13 +708,18 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
|
||||
setLayout.usesLighting ||
|
||||
setLayout.usesMaterial ||
|
||||
setLayout.usesShadowReceiver ||
|
||||
setLayout.usesTexture) {
|
||||
setLayout.usesTexture ||
|
||||
setLayout.usesMaterialBuffers) {
|
||||
const Core::uint64 objectId =
|
||||
(setLayout.usesPerObject && visibleItem.gameObject != nullptr)
|
||||
? visibleItem.gameObject->GetID()
|
||||
: 0;
|
||||
const Resources::Material* materialKey =
|
||||
(setLayout.usesMaterial || setLayout.usesBaseColorTexture) ? material : nullptr;
|
||||
(setLayout.usesMaterial ||
|
||||
setLayout.usesBaseColorTexture ||
|
||||
setLayout.usesMaterialBuffers)
|
||||
? material
|
||||
: nullptr;
|
||||
|
||||
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||
passLayoutKey,
|
||||
|
||||
@@ -81,6 +81,20 @@ void RemoveTextureBindingByName(
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveBufferBindingByName(
|
||||
Containers::Array<MaterialBufferBinding>& bufferBindings,
|
||||
const Containers::String& name) {
|
||||
for (size_t bindingIndex = 0; bindingIndex < bufferBindings.Size(); ++bindingIndex) {
|
||||
if (bufferBindings[bindingIndex].name == name) {
|
||||
if (bindingIndex != bufferBindings.Size() - 1) {
|
||||
bufferBindings[bindingIndex] = std::move(bufferBindings.Back());
|
||||
}
|
||||
bufferBindings.PopBack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureTextureProperty(Containers::HashMap<Containers::String, MaterialProperty>& properties,
|
||||
const Containers::String& name,
|
||||
MaterialPropertyType type = MaterialPropertyType::Texture) {
|
||||
@@ -329,6 +343,7 @@ void Material::Release() {
|
||||
m_properties.Clear();
|
||||
m_constantLayout.Clear();
|
||||
m_textureBindings.Clear();
|
||||
m_bufferBindings.Clear();
|
||||
m_constantBufferData.Clear();
|
||||
m_changeVersion = 1;
|
||||
m_isValid = false;
|
||||
@@ -338,6 +353,7 @@ void Material::Release() {
|
||||
void Material::SetShader(const ResourceHandle<Shader>& shader) {
|
||||
m_shader = shader;
|
||||
SyncShaderSchemaProperties(true);
|
||||
SyncShaderRuntimeBufferBindings(true);
|
||||
SyncShaderSchemaKeywords(true);
|
||||
MarkChanged(true);
|
||||
}
|
||||
@@ -643,6 +659,36 @@ void Material::SetTexture(const Containers::String& name, const ResourceHandle<T
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetBuffer(const Containers::String& name, RHI::RHIBuffer* buffer) {
|
||||
SetBuffer(name, buffer, MaterialBufferBindingViewDesc());
|
||||
}
|
||||
|
||||
void Material::SetBuffer(
|
||||
const Containers::String& name,
|
||||
RHI::RHIBuffer* buffer,
|
||||
const MaterialBufferBindingViewDesc& viewDesc) {
|
||||
if (buffer == nullptr) {
|
||||
RemoveBufferBinding(name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (MaterialBufferBinding& binding : m_bufferBindings) {
|
||||
if (binding.name == name) {
|
||||
binding.buffer = buffer;
|
||||
binding.viewDesc = viewDesc;
|
||||
MarkChanged(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialBufferBinding binding = {};
|
||||
binding.name = name;
|
||||
binding.buffer = buffer;
|
||||
binding.viewDesc = viewDesc;
|
||||
m_bufferBindings.PushBack(binding);
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetTextureAssetRef(const Containers::String& name,
|
||||
const AssetRef& textureRef,
|
||||
const Containers::String& texturePath) {
|
||||
@@ -785,6 +831,21 @@ ResourceHandle<Texture> Material::GetTexture(const Containers::String& name) con
|
||||
return ResourceHandle<Texture>();
|
||||
}
|
||||
|
||||
RHI::RHIBuffer* Material::GetBuffer(const Containers::String& name) const {
|
||||
const MaterialBufferBinding* binding = FindBufferBinding(name);
|
||||
return binding != nullptr ? binding->buffer : nullptr;
|
||||
}
|
||||
|
||||
const MaterialBufferBinding* Material::FindBufferBinding(const Containers::String& name) const {
|
||||
for (const MaterialBufferBinding& binding : m_bufferBindings) {
|
||||
if (binding.name == name) {
|
||||
return &binding;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Containers::String Material::GetTextureBindingName(Core::uint32 index) const {
|
||||
return index < m_textureBindings.Size() ? m_textureBindings[index].name : Containers::String();
|
||||
}
|
||||
@@ -996,6 +1057,23 @@ void Material::ClearAllProperties() {
|
||||
MarkChanged(true);
|
||||
}
|
||||
|
||||
void Material::RemoveBufferBinding(const Containers::String& name) {
|
||||
const size_t bindingCount = m_bufferBindings.Size();
|
||||
RemoveBufferBindingByName(m_bufferBindings, name);
|
||||
if (m_bufferBindings.Size() != bindingCount) {
|
||||
MarkChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::ClearBufferBindings() {
|
||||
if (m_bufferBindings.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_bufferBindings.Clear();
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
const ShaderPropertyDesc* Material::FindShaderPropertyDesc(const Containers::String& name) const {
|
||||
if (m_shader.Get() == nullptr) {
|
||||
return nullptr;
|
||||
@@ -1064,6 +1142,52 @@ void Material::SyncShaderSchemaProperties(bool removeUnknownProperties) {
|
||||
}
|
||||
}
|
||||
|
||||
void Material::SyncShaderRuntimeBufferBindings(bool removeUnknownBindings) {
|
||||
if (m_shader.Get() == nullptr || !removeUnknownBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Containers::String> unknownBindings;
|
||||
unknownBindings.reserve(m_bufferBindings.Size());
|
||||
for (const MaterialBufferBinding& binding : m_bufferBindings) {
|
||||
bool found = false;
|
||||
for (const ShaderPass& shaderPass : m_shader->GetPasses()) {
|
||||
for (const ShaderResourceBindingDesc& shaderBinding : shaderPass.resources) {
|
||||
if (shaderBinding.name != binding.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (shaderBinding.type) {
|
||||
case ShaderResourceType::StructuredBuffer:
|
||||
case ShaderResourceType::RawBuffer:
|
||||
case ShaderResourceType::RWStructuredBuffer:
|
||||
case ShaderResourceType::RWRawBuffer:
|
||||
found = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
unknownBindings.push_back(binding.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (const Containers::String& bindingName : unknownBindings) {
|
||||
RemoveBufferBindingByName(m_bufferBindings, bindingName);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::SyncShaderSchemaKeywords(bool removeUnknownKeywords) {
|
||||
if (m_shader.Get() == nullptr || !removeUnknownKeywords) {
|
||||
return;
|
||||
@@ -1104,6 +1228,7 @@ void Material::UpdateMemorySize() {
|
||||
m_tags.Size() * sizeof(MaterialTagEntry) +
|
||||
m_keywordSet.enabledKeywords.Size() * sizeof(Containers::String) +
|
||||
m_textureBindings.Size() * sizeof(MaterialTextureBinding) +
|
||||
m_bufferBindings.Size() * sizeof(MaterialBufferBinding) +
|
||||
m_properties.Size() * sizeof(MaterialProperty) +
|
||||
m_name.Length() +
|
||||
m_path.Length();
|
||||
@@ -1125,6 +1250,10 @@ void Material::UpdateMemorySize() {
|
||||
m_memorySize += binding.name.Length();
|
||||
m_memorySize += binding.texturePath.Length();
|
||||
}
|
||||
|
||||
for (const MaterialBufferBinding& binding : m_bufferBindings) {
|
||||
m_memorySize += binding.name.Length();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
|
||||
Reference in New Issue
Block a user