Add runtime material buffer bindings

This commit is contained in:
2026-04-08 19:18:07 +08:00
parent bb0f4afe7d
commit 6bf9203eec
13 changed files with 781 additions and 22 deletions

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
};