Formalize gaussian splat prepare-order pass
This commit is contained in:
181
docs/used/Shader统一预编译缓存并入Library正式方案_完成归档_2026-04-11.md
Normal file
181
docs/used/Shader统一预编译缓存并入Library正式方案_完成归档_2026-04-11.md
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# Shader 统一预编译缓存并入 Library 正式方案
|
||||||
|
|
||||||
|
文档日期:2026-04-11
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
把 shader 的预编译结果正式并入现有 `Library/Artifacts` 体系,不再允许出现:
|
||||||
|
|
||||||
|
1. project shader 走 `Library`,builtin shader 走裸加载。
|
||||||
|
2. 运行时每次开编辑器都重新现场编译重 shader。
|
||||||
|
3. 只修 D3D12,一到 Vulkan / OpenGL 又退回运行时临时编译。
|
||||||
|
|
||||||
|
本轮正式目标:
|
||||||
|
|
||||||
|
1. builtin shader 与 project shader 统一走同一个 artifact 产物格式。
|
||||||
|
2. `ShaderStageVariant` 保持现有 authoring / runtime 变体模型不变,不扩成“每后端一个 variant”。
|
||||||
|
3. 每个 variant 可以携带“按后端区分的已编译 payload”。
|
||||||
|
4. D3D12 / Vulkan / OpenGL 运行时优先消费 artifact 中的 payload,只有 miss 时才 fallback 到现场编译。
|
||||||
|
|
||||||
|
## 2. 架构原则
|
||||||
|
|
||||||
|
### 2.1 唯一正式缓存位置
|
||||||
|
|
||||||
|
唯一正式 shader 预编译缓存位置是:
|
||||||
|
|
||||||
|
`project/Library/Artifacts/.../main.xcshader`
|
||||||
|
|
||||||
|
不新增独立 runtime cache 目录,不做旁路缓存。
|
||||||
|
|
||||||
|
### 2.2 builtin shader 也必须纳入 Library
|
||||||
|
|
||||||
|
外部标识仍然保持:
|
||||||
|
|
||||||
|
`builtin://shaders/...`
|
||||||
|
|
||||||
|
但在有 project root 时,真实加载路径应优先变成:
|
||||||
|
|
||||||
|
`Library/Artifacts/.../main.xcshader`
|
||||||
|
|
||||||
|
这样 builtin shader 与普通 `Assets/*.shader` 在缓存语义上完全一致。
|
||||||
|
|
||||||
|
### 2.3 不改变现有 variant 选择逻辑
|
||||||
|
|
||||||
|
现有很多代码和测试都依赖:
|
||||||
|
|
||||||
|
1. authoring variant 可以是 `Generic`
|
||||||
|
2. 运行时按 backend 查找时先找精确 backend,再回退 `Generic`
|
||||||
|
|
||||||
|
因此不把一个 `Generic` variant 展开成三份后端 variant,而是让一个 variant 内部携带:
|
||||||
|
|
||||||
|
1. 旧字段 `compiledBinary`
|
||||||
|
2. 新字段 `backendCompiledBinaries`
|
||||||
|
|
||||||
|
## 3. 数据模型
|
||||||
|
|
||||||
|
### 3.1 运行时 compile desc
|
||||||
|
|
||||||
|
`RHI::ShaderCompileDesc` 新增:
|
||||||
|
|
||||||
|
1. `compiledBinaryBackend`
|
||||||
|
2. `compiledBinary`
|
||||||
|
|
||||||
|
含义:
|
||||||
|
|
||||||
|
1. source / fileName 仍然描述“可回退到现场编译的输入”
|
||||||
|
2. compiledBinary 描述“当前后端可直接消费的已编译 payload”
|
||||||
|
|
||||||
|
### 3.2 shader variant
|
||||||
|
|
||||||
|
`Resources::ShaderStageVariant` 支持:
|
||||||
|
|
||||||
|
1. `GetCompiledBinaryForBackend(...)`
|
||||||
|
2. `SetCompiledBinaryForBackend(...)`
|
||||||
|
|
||||||
|
语义:
|
||||||
|
|
||||||
|
1. backend-specific variant 可以继续把本后端 payload 放在旧 `compiledBinary`
|
||||||
|
2. `Generic` variant 的 D3D12 / Vulkan / OpenGL payload 放在 `backendCompiledBinaries`
|
||||||
|
|
||||||
|
### 3.3 artifact schema
|
||||||
|
|
||||||
|
shader artifact schema 升级为 `6`,`ShaderVariantArtifactHeader` 新增:
|
||||||
|
|
||||||
|
1. `backendCompiledBinaryCount`
|
||||||
|
|
||||||
|
并追加 `ShaderBackendCompiledBinaryArtifactHeader + payload` 序列。
|
||||||
|
|
||||||
|
## 4. 导入阶段
|
||||||
|
|
||||||
|
导入 `.shader` 时:
|
||||||
|
|
||||||
|
1. 先用 `ShaderLoader` 生成现有 authoring/runtime variant。
|
||||||
|
2. 对每个 pass / variant 生成运行时 compile desc。
|
||||||
|
3. 按目标后端尝试预编译:
|
||||||
|
- D3D12:生成 DXBC
|
||||||
|
- Vulkan:生成 Vulkan SPIR-V
|
||||||
|
- OpenGL:生成 OpenGL-target SPIR-V
|
||||||
|
4. 把结果写回 variant。
|
||||||
|
5. 最终统一写入 `main.xcshader` artifact。
|
||||||
|
|
||||||
|
后端目标规则:
|
||||||
|
|
||||||
|
1. `Generic` variant 预编译到 D3D12 / Vulkan / OpenGL
|
||||||
|
2. backend-specific variant 只预编译自己的 backend
|
||||||
|
|
||||||
|
失败策略:
|
||||||
|
|
||||||
|
1. 预编译失败不阻断 import
|
||||||
|
2. artifact 仍然照常生成
|
||||||
|
3. 运行时保持 fallback 能力
|
||||||
|
4. 日志明确记录 pass / stage / backend / reason
|
||||||
|
|
||||||
|
## 5. builtin shader 接入方式
|
||||||
|
|
||||||
|
### 5.1 ResourceManager
|
||||||
|
|
||||||
|
`ResourceManager::LoadResource(...)` 在 shader + builtin path 情况下:
|
||||||
|
|
||||||
|
1. 解析到真实 builtin shader 资产路径
|
||||||
|
2. 在当前 project 的 `Library` 中确保对应 artifact
|
||||||
|
3. 真正加载 artifact
|
||||||
|
4. 资源对外 path 仍然恢复成 `builtin://...`
|
||||||
|
|
||||||
|
### 5.2 AssetDatabase
|
||||||
|
|
||||||
|
由于 builtin shader 资产位于 project root 外部:
|
||||||
|
|
||||||
|
1. 允许绝对路径解析成相对 project root 的 `../engine/...`
|
||||||
|
2. 这些外部 source record 不写 `.meta`
|
||||||
|
3. 使用 synthetic GUID / synthetic meta hash
|
||||||
|
4. `Refresh()` 时显式扫描 builtin shader 资产,避免下次启动把 builtin artifact 当 orphan 清掉
|
||||||
|
|
||||||
|
## 6. 运行时消费
|
||||||
|
|
||||||
|
### 6.1 D3D12
|
||||||
|
|
||||||
|
1. `D3D12Shader` 增加 `InitializeFromBytecode(...)`
|
||||||
|
2. `CompileD3D12Shader(...)` 优先命中 `compiledBinaryBackend == D3D12`
|
||||||
|
3. miss 时才走 `D3DCompile(...)`
|
||||||
|
4. `CreateShader(...)` 统一复用这条逻辑
|
||||||
|
|
||||||
|
### 6.2 Vulkan
|
||||||
|
|
||||||
|
1. `CompileSpirvShader(...)` 支持直接消费 `compiledBinaryBackend == Vulkan`
|
||||||
|
2. `VulkanDevice / VulkanPipelineState` 通过既有 `CompileVulkanShader(...)` 自动命中缓存 payload
|
||||||
|
|
||||||
|
### 6.3 OpenGL
|
||||||
|
|
||||||
|
1. 若命中 `compiledBinaryBackend == OpenGL`
|
||||||
|
2. 则把该 payload 作为 SPIR-V 输入
|
||||||
|
3. 继续走现有 `SPIR-V -> GLSL` 转译路径
|
||||||
|
4. 原始 HLSL source 仍保留给 sampler/texture 绑定推导逻辑使用
|
||||||
|
|
||||||
|
## 7. 验收标准
|
||||||
|
|
||||||
|
### 7.1 工程标准
|
||||||
|
|
||||||
|
1. `shader_tests` 全绿
|
||||||
|
2. `XCEditor` 能完整编译通过
|
||||||
|
|
||||||
|
### 7.2 行为标准
|
||||||
|
|
||||||
|
1. shader artifact roundtrip 后不丢失 backend payload
|
||||||
|
2. builtin shader 与 project shader 都能命中 `Library/Artifacts`
|
||||||
|
3. D3D12 / Vulkan / OpenGL 运行时在 payload 存在时不再重复现场编译
|
||||||
|
|
||||||
|
### 7.3 性能标准
|
||||||
|
|
||||||
|
1. 影响 NanoVDB 首帧的 volumetric shader 不再在每次启动时重编
|
||||||
|
2. editor 打开主场景时,shader stall 不再成为 `SceneReady` 的主瓶颈
|
||||||
|
|
||||||
|
## 8. 后续扩展
|
||||||
|
|
||||||
|
后续若继续做:
|
||||||
|
|
||||||
|
1. import-time completeness 标记
|
||||||
|
2. 运行时回写 artifact
|
||||||
|
3. PSO cache blob
|
||||||
|
4. 更完整的 shader compile telemetry
|
||||||
|
|
||||||
|
都必须建立在本方案之上,不允许再引入平行缓存体系。
|
||||||
@@ -26,6 +26,7 @@ Shader "Builtin Gaussian Splat"
|
|||||||
float4 scaleReserved;
|
float4 scaleReserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
StructuredBuffer<uint> GaussianSplatOrderBuffer;
|
||||||
StructuredBuffer<float3> GaussianSplatPositions;
|
StructuredBuffer<float3> GaussianSplatPositions;
|
||||||
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
|
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
|
||||||
StructuredBuffer<float4> GaussianSplatColor;
|
StructuredBuffer<float4> GaussianSplatColor;
|
||||||
@@ -54,9 +55,10 @@ Shader "Builtin Gaussian Splat"
|
|||||||
{
|
{
|
||||||
VSOutput output;
|
VSOutput output;
|
||||||
const float2 corner = ResolveQuadCorner(vertexId);
|
const float2 corner = ResolveQuadCorner(vertexId);
|
||||||
const float3 localCenter = GaussianSplatPositions[instanceId];
|
const uint splatIndex = GaussianSplatOrderBuffer[instanceId];
|
||||||
const GaussianSplatOtherData otherData = GaussianSplatOther[instanceId];
|
const float3 localCenter = GaussianSplatPositions[splatIndex];
|
||||||
const float4 colorOpacity = GaussianSplatColor[instanceId];
|
const GaussianSplatOtherData otherData = GaussianSplatOther[splatIndex];
|
||||||
|
const float4 colorOpacity = GaussianSplatColor[splatIndex];
|
||||||
|
|
||||||
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
|
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
|
||||||
const float maxAxisScale =
|
const float maxAxisScale =
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Resources {
|
|||||||
constexpr Core::uint32 kTextureArtifactSchemaVersion = 1;
|
constexpr Core::uint32 kTextureArtifactSchemaVersion = 1;
|
||||||
constexpr Core::uint32 kMaterialArtifactSchemaVersion = 6;
|
constexpr Core::uint32 kMaterialArtifactSchemaVersion = 6;
|
||||||
constexpr Core::uint32 kMeshArtifactSchemaVersion = 2;
|
constexpr Core::uint32 kMeshArtifactSchemaVersion = 2;
|
||||||
constexpr Core::uint32 kShaderArtifactSchemaVersion = 5;
|
constexpr Core::uint32 kShaderArtifactSchemaVersion = 6;
|
||||||
constexpr Core::uint32 kUIDocumentArtifactSchemaVersion = 2;
|
constexpr Core::uint32 kUIDocumentArtifactSchemaVersion = 2;
|
||||||
constexpr Core::uint32 kVolumeFieldArtifactSchemaVersion = 2;
|
constexpr Core::uint32 kVolumeFieldArtifactSchemaVersion = 2;
|
||||||
constexpr Core::uint32 kModelArtifactSchemaVersion = 1;
|
constexpr Core::uint32 kModelArtifactSchemaVersion = 1;
|
||||||
@@ -104,7 +104,7 @@ struct ModelMaterialBindingArtifact {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderArtifactFileHeader {
|
struct ShaderArtifactFileHeader {
|
||||||
char magic[8] = { 'X', 'C', 'S', 'H', 'D', '0', '5', '\0' };
|
char magic[8] = { 'X', 'C', 'S', 'H', 'D', '0', '6', '\0' };
|
||||||
Core::uint32 schemaVersion = kShaderArtifactSchemaVersion;
|
Core::uint32 schemaVersion = kShaderArtifactSchemaVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,6 +138,14 @@ struct ShaderVariantArtifactHeader {
|
|||||||
Core::uint32 backend = 0;
|
Core::uint32 backend = 0;
|
||||||
Core::uint32 keywordCount = 0;
|
Core::uint32 keywordCount = 0;
|
||||||
Core::uint64 compiledBinarySize = 0;
|
Core::uint64 compiledBinarySize = 0;
|
||||||
|
Core::uint32 backendCompiledBinaryCount = 0;
|
||||||
|
Core::uint32 reserved = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderBackendCompiledBinaryArtifactHeader {
|
||||||
|
Core::uint32 backend = 0;
|
||||||
|
Core::uint32 reserved = 0;
|
||||||
|
Core::uint64 compiledBinarySize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderKeywordDeclarationArtifactHeader {
|
struct ShaderKeywordDeclarationArtifactHeader {
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ public:
|
|||||||
const D3D_SHADER_MACRO* macros,
|
const D3D_SHADER_MACRO* macros,
|
||||||
const char* entryPoint,
|
const char* entryPoint,
|
||||||
const char* target);
|
const char* target);
|
||||||
|
bool InitializeFromBytecode(
|
||||||
|
const void* bytecodeData,
|
||||||
|
size_t bytecodeSize,
|
||||||
|
const char* target = nullptr);
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
|
|
||||||
const D3D12_SHADER_BYTECODE GetD3D12Bytecode() const;
|
const D3D12_SHADER_BYTECODE GetD3D12Bytecode() const;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
class GameObject;
|
class GameObject;
|
||||||
|
class GaussianSplatRendererComponent;
|
||||||
} // namespace Components
|
} // namespace Components
|
||||||
|
|
||||||
namespace Resources {
|
namespace Resources {
|
||||||
@@ -31,6 +32,11 @@ class Shader;
|
|||||||
|
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
struct VisibleGaussianSplatItem;
|
struct VisibleGaussianSplatItem;
|
||||||
|
namespace Passes {
|
||||||
|
namespace Internal {
|
||||||
|
class BuiltinGaussianSplatPassResources;
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Passes
|
||||||
|
|
||||||
namespace Passes {
|
namespace Passes {
|
||||||
|
|
||||||
@@ -84,16 +90,20 @@ private:
|
|||||||
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||||
PassResourceBindingLocation perObject = {};
|
PassResourceBindingLocation perObject = {};
|
||||||
PassResourceBindingLocation material = {};
|
PassResourceBindingLocation material = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatSortDistanceBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatOrderBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||||
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||||
|
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DynamicDescriptorSetKey {
|
struct DynamicDescriptorSetKey {
|
||||||
PassLayoutKey passLayout = {};
|
PassLayoutKey passLayout = {};
|
||||||
Core::uint32 setIndex = 0;
|
Core::uint32 setIndex = 0;
|
||||||
Core::uint64 objectId = 0;
|
Core::uint64 objectId = 0;
|
||||||
|
const Components::GaussianSplatRendererComponent* gaussianSplatRenderer = nullptr;
|
||||||
const Resources::Material* material = nullptr;
|
const Resources::Material* material = nullptr;
|
||||||
const Resources::GaussianSplat* gaussianSplat = nullptr;
|
const Resources::GaussianSplat* gaussianSplat = nullptr;
|
||||||
|
|
||||||
@@ -101,6 +111,7 @@ private:
|
|||||||
return passLayout == other.passLayout &&
|
return passLayout == other.passLayout &&
|
||||||
setIndex == other.setIndex &&
|
setIndex == other.setIndex &&
|
||||||
objectId == other.objectId &&
|
objectId == other.objectId &&
|
||||||
|
gaussianSplatRenderer == other.gaussianSplatRenderer &&
|
||||||
material == other.material &&
|
material == other.material &&
|
||||||
gaussianSplat == other.gaussianSplat;
|
gaussianSplat == other.gaussianSplat;
|
||||||
}
|
}
|
||||||
@@ -111,6 +122,7 @@ private:
|
|||||||
size_t hash = PassLayoutKeyHash()(key.passLayout);
|
size_t hash = PassLayoutKeyHash()(key.passLayout);
|
||||||
hash ^= std::hash<Core::uint32>{}(key.setIndex) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
hash ^= std::hash<Core::uint32>{}(key.setIndex) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
hash ^= std::hash<Core::uint64>{}(key.objectId) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
hash ^= std::hash<Core::uint64>{}(key.objectId) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= reinterpret_cast<size_t>(key.gaussianSplatRenderer) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
hash ^= reinterpret_cast<size_t>(key.material) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
hash ^= reinterpret_cast<size_t>(key.material) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
hash ^= reinterpret_cast<size_t>(key.gaussianSplat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
hash ^= reinterpret_cast<size_t>(key.gaussianSplat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
return hash;
|
return hash;
|
||||||
@@ -120,10 +132,13 @@ private:
|
|||||||
struct CachedDescriptorSet {
|
struct CachedDescriptorSet {
|
||||||
OwnedDescriptorSet descriptorSet = {};
|
OwnedDescriptorSet descriptorSet = {};
|
||||||
Core::uint64 materialVersion = 0;
|
Core::uint64 materialVersion = 0;
|
||||||
|
RHI::RHIResourceView* sortDistanceView = nullptr;
|
||||||
|
RHI::RHIResourceView* orderView = nullptr;
|
||||||
RHI::RHIResourceView* positionsView = nullptr;
|
RHI::RHIResourceView* positionsView = nullptr;
|
||||||
RHI::RHIResourceView* otherView = nullptr;
|
RHI::RHIResourceView* otherView = nullptr;
|
||||||
RHI::RHIResourceView* colorView = nullptr;
|
RHI::RHIResourceView* colorView = nullptr;
|
||||||
RHI::RHIResourceView* shView = nullptr;
|
RHI::RHIResourceView* shView = nullptr;
|
||||||
|
RHI::RHIResourceView* viewDataView = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResolvedShaderPass {
|
struct ResolvedShaderPass {
|
||||||
@@ -171,6 +186,32 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ComputePipelineKey {
|
||||||
|
const Resources::Shader* shader = nullptr;
|
||||||
|
Containers::String passName;
|
||||||
|
Containers::String keywordSignature;
|
||||||
|
|
||||||
|
bool operator==(const ComputePipelineKey& other) const {
|
||||||
|
return shader == other.shader &&
|
||||||
|
passName == other.passName &&
|
||||||
|
keywordSignature == other.keywordSignature;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ComputePipelineKeyHash {
|
||||||
|
size_t operator()(const ComputePipelineKey& key) const noexcept {
|
||||||
|
size_t hash = reinterpret_cast<size_t>(key.shader);
|
||||||
|
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
hash ^= std::hash<Containers::String>{}(key.keywordSignature) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PassLayoutUsage : Core::uint8 {
|
||||||
|
Draw,
|
||||||
|
PrepareOrder
|
||||||
|
};
|
||||||
|
|
||||||
bool EnsureInitialized(const RenderContext& context);
|
bool EnsureInitialized(const RenderContext& context);
|
||||||
bool CreateResources(const RenderContext& context);
|
bool CreateResources(const RenderContext& context);
|
||||||
void DestroyResources();
|
void DestroyResources();
|
||||||
@@ -180,14 +221,19 @@ private:
|
|||||||
ResolvedShaderPass ResolveGaussianSplatShaderPass(
|
ResolvedShaderPass ResolveGaussianSplatShaderPass(
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
const Resources::Material* material) const;
|
const Resources::Material* material) const;
|
||||||
|
ResolvedShaderPass ResolvePrepareOrderShaderPass(const RenderSceneData& sceneData) const;
|
||||||
PassResourceLayout* GetOrCreatePassResourceLayout(
|
PassResourceLayout* GetOrCreatePassResourceLayout(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const ResolvedShaderPass& resolvedShaderPass);
|
const ResolvedShaderPass& resolvedShaderPass,
|
||||||
|
PassLayoutUsage usage);
|
||||||
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const RenderSurface& surface,
|
const RenderSurface& surface,
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
const Resources::Material* material);
|
const Resources::Material* material);
|
||||||
|
RHI::RHIPipelineState* GetOrCreateComputePipelineState(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData);
|
||||||
bool CreateOwnedDescriptorSet(
|
bool CreateOwnedDescriptorSet(
|
||||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
OwnedDescriptorSet& descriptorSet);
|
OwnedDescriptorSet& descriptorSet);
|
||||||
@@ -197,12 +243,20 @@ private:
|
|||||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
Core::uint32 setIndex,
|
Core::uint32 setIndex,
|
||||||
Core::uint64 objectId,
|
Core::uint64 objectId,
|
||||||
|
const Components::GaussianSplatRendererComponent* gaussianSplatRenderer,
|
||||||
const Resources::Material* material,
|
const Resources::Material* material,
|
||||||
const Resources::GaussianSplat* gaussianSplat,
|
const Resources::GaussianSplat* gaussianSplat,
|
||||||
const MaterialConstantPayloadView& materialConstants,
|
const MaterialConstantPayloadView& materialConstants,
|
||||||
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat);
|
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
||||||
|
RHI::RHIResourceView* sortDistanceView,
|
||||||
|
RHI::RHIResourceView* orderView,
|
||||||
|
RHI::RHIResourceView* viewDataView);
|
||||||
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
||||||
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||||
|
bool PrepareVisibleGaussianSplat(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat);
|
||||||
bool DrawVisibleGaussianSplat(
|
bool DrawVisibleGaussianSplat(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const RenderSurface& surface,
|
const RenderSurface& surface,
|
||||||
@@ -212,11 +266,14 @@ private:
|
|||||||
RHI::RHIDevice* m_device = nullptr;
|
RHI::RHIDevice* m_device = nullptr;
|
||||||
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
|
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
|
||||||
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatShader;
|
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatShader;
|
||||||
|
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatUtilitiesShader;
|
||||||
std::unique_ptr<Resources::Material> m_builtinGaussianSplatMaterial;
|
std::unique_ptr<Resources::Material> m_builtinGaussianSplatMaterial;
|
||||||
RenderResourceCache m_resourceCache;
|
RenderResourceCache m_resourceCache;
|
||||||
|
Internal::BuiltinGaussianSplatPassResources* m_passResources = nullptr;
|
||||||
|
|
||||||
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
||||||
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
|
||||||
|
std::unordered_map<ComputePipelineKey, RHI::RHIPipelineState*, ComputePipelineKeyHash> m_computePipelineStates;
|
||||||
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ struct ShaderResourceBindingDesc {
|
|||||||
Containers::String semantic;
|
Containers::String semantic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ShaderBackendCompiledBinary {
|
||||||
|
ShaderBackend backend = ShaderBackend::Generic;
|
||||||
|
Containers::Array<Core::uint8> payload;
|
||||||
|
};
|
||||||
|
|
||||||
struct ShaderStageVariant {
|
struct ShaderStageVariant {
|
||||||
ShaderType stage = ShaderType::Fragment;
|
ShaderType stage = ShaderType::Fragment;
|
||||||
ShaderLanguage language = ShaderLanguage::GLSL;
|
ShaderLanguage language = ShaderLanguage::GLSL;
|
||||||
@@ -96,6 +101,16 @@ struct ShaderStageVariant {
|
|||||||
Containers::String profile;
|
Containers::String profile;
|
||||||
Containers::String sourceCode;
|
Containers::String sourceCode;
|
||||||
Containers::Array<Core::uint8> compiledBinary;
|
Containers::Array<Core::uint8> compiledBinary;
|
||||||
|
Containers::Array<ShaderBackendCompiledBinary> backendCompiledBinaries;
|
||||||
|
|
||||||
|
const Containers::Array<Core::uint8>* GetCompiledBinaryForBackend(
|
||||||
|
ShaderBackend targetBackend) const;
|
||||||
|
void SetCompiledBinaryForBackend(
|
||||||
|
ShaderBackend targetBackend,
|
||||||
|
const Containers::Array<Core::uint8>& binary);
|
||||||
|
void SetCompiledBinaryForBackend(
|
||||||
|
ShaderBackend targetBackend,
|
||||||
|
Containers::Array<Core::uint8>&& binary);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderPass {
|
struct ShaderPass {
|
||||||
|
|||||||
@@ -1,23 +1,33 @@
|
|||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
||||||
|
|
||||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||||
#include <XCEngine/Debug/Logger.h>
|
#include <XCEngine/Debug/Logger.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12Shader.h>
|
||||||
|
#include <XCEngine/RHI/ShaderCompiler/SpirvShaderCompiler.h>
|
||||||
|
#include <XCEngine/RHI/Vulkan/VulkanShaderCompiler.h>
|
||||||
#include <XCEngine/Resources/GaussianSplat/GaussianSplatArtifactIO.h>
|
#include <XCEngine/Resources/GaussianSplat/GaussianSplatArtifactIO.h>
|
||||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||||
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
|
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
|
||||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||||
#include <XCEngine/Resources/Model/AssimpModelImporter.h>
|
#include <XCEngine/Resources/Model/AssimpModelImporter.h>
|
||||||
#include <XCEngine/Resources/Model/ModelArtifactIO.h>
|
#include <XCEngine/Resources/Model/ModelArtifactIO.h>
|
||||||
|
#include <XCEngine/Resources/BuiltinResources.h>
|
||||||
#include "Resources/GaussianSplat/Internal/GaussianSplatPlyImporter.h"
|
#include "Resources/GaussianSplat/Internal/GaussianSplatPlyImporter.h"
|
||||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||||
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
||||||
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
|
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
|
||||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||||
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
||||||
|
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <d3dcompiler.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -134,10 +144,294 @@ bool IsCurrentShaderArtifactFile(const fs::path& artifactPath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::memcmp(header.magic, "XCSHD05", 7) == 0 &&
|
return std::memcmp(header.magic, "XCSHD06", 7) == 0 &&
|
||||||
header.schemaVersion == kShaderArtifactSchemaVersion;
|
header.schemaVersion == kShaderArtifactSchemaVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsManagedProjectAssetPath(const Containers::String& relativePath) {
|
||||||
|
return relativePath == "Assets" || relativePath.StartsWith("Assets/");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsesExternalSyntheticSourceRecord(const Containers::String& relativePath) {
|
||||||
|
return !relativePath.Empty() && !IsManagedProjectAssetPath(relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::String BuildSyntheticMetaHash(
|
||||||
|
const Containers::String& relativePath,
|
||||||
|
const Containers::String& importerName,
|
||||||
|
Core::uint32 importerVersion) {
|
||||||
|
Containers::String signature = relativePath;
|
||||||
|
signature += ":";
|
||||||
|
signature += importerName;
|
||||||
|
signature += ":";
|
||||||
|
signature += Containers::String(std::to_string(importerVersion).c_str());
|
||||||
|
return HashStringToAssetGUID(signature).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetShaderStageLabel(ShaderType stage) {
|
||||||
|
switch (stage) {
|
||||||
|
case ShaderType::Vertex:
|
||||||
|
return "Vertex";
|
||||||
|
case ShaderType::Fragment:
|
||||||
|
return "Fragment";
|
||||||
|
case ShaderType::Geometry:
|
||||||
|
return "Geometry";
|
||||||
|
case ShaderType::Compute:
|
||||||
|
return "Compute";
|
||||||
|
case ShaderType::Hull:
|
||||||
|
return "Hull";
|
||||||
|
case ShaderType::Domain:
|
||||||
|
return "Domain";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetShaderBackendLabel(ShaderBackend backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case ShaderBackend::D3D12:
|
||||||
|
return "D3D12";
|
||||||
|
case ShaderBackend::OpenGL:
|
||||||
|
return "OpenGL";
|
||||||
|
case ShaderBackend::Vulkan:
|
||||||
|
return "Vulkan";
|
||||||
|
case ShaderBackend::Generic:
|
||||||
|
default:
|
||||||
|
return "Generic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendCompiledPayload(
|
||||||
|
const std::vector<uint8_t>& source,
|
||||||
|
Containers::Array<Core::uint8>& destination) {
|
||||||
|
destination.Resize(source.size());
|
||||||
|
if (!source.empty()) {
|
||||||
|
std::memcpy(destination.Data(), source.data(), source.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendCompiledPayload(
|
||||||
|
const std::vector<uint32_t>& source,
|
||||||
|
Containers::Array<Core::uint8>& destination) {
|
||||||
|
destination.Resize(source.size() * sizeof(uint32_t));
|
||||||
|
if (!source.empty()) {
|
||||||
|
std::memcpy(destination.Data(), source.data(), destination.Size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NarrowAscii(const std::wstring& value) {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(value.size());
|
||||||
|
for (wchar_t ch : value) {
|
||||||
|
result.push_back(static_cast<char>(ch));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildD3D12MacroTable(const RHI::ShaderCompileDesc& desc,
|
||||||
|
std::vector<std::string>& outNames,
|
||||||
|
std::vector<std::string>& outDefinitions,
|
||||||
|
std::vector<D3D_SHADER_MACRO>& outTable) {
|
||||||
|
outNames.clear();
|
||||||
|
outDefinitions.clear();
|
||||||
|
outTable.clear();
|
||||||
|
|
||||||
|
if (desc.macros.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
outNames.reserve(desc.macros.size());
|
||||||
|
outDefinitions.reserve(desc.macros.size());
|
||||||
|
outTable.reserve(desc.macros.size() + 1u);
|
||||||
|
for (const RHI::ShaderCompileMacro& macro : desc.macros) {
|
||||||
|
std::string name;
|
||||||
|
name.reserve(macro.name.size());
|
||||||
|
for (wchar_t ch : macro.name) {
|
||||||
|
name.push_back(static_cast<char>(ch));
|
||||||
|
}
|
||||||
|
if (name.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string definition;
|
||||||
|
definition.reserve(macro.definition.size());
|
||||||
|
for (wchar_t ch : macro.definition) {
|
||||||
|
definition.push_back(static_cast<char>(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
outNames.push_back(std::move(name));
|
||||||
|
outDefinitions.push_back(std::move(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t macroIndex = 0; macroIndex < outNames.size(); ++macroIndex) {
|
||||||
|
D3D_SHADER_MACRO macro = {};
|
||||||
|
macro.Name = outNames[macroIndex].c_str();
|
||||||
|
macro.Definition =
|
||||||
|
outDefinitions[macroIndex].empty() ? "1" : outDefinitions[macroIndex].c_str();
|
||||||
|
outTable.push_back(macro);
|
||||||
|
}
|
||||||
|
outTable.push_back({ nullptr, nullptr });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryPrecompileShaderVariantForBackend(const Containers::String& shaderPath,
|
||||||
|
const ShaderPass& pass,
|
||||||
|
ShaderStageVariant& variant,
|
||||||
|
ShaderBackend targetBackend,
|
||||||
|
Containers::String& outError) {
|
||||||
|
outError.Clear();
|
||||||
|
|
||||||
|
RHI::ShaderCompileDesc compileDesc = {};
|
||||||
|
Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
shaderPath,
|
||||||
|
pass,
|
||||||
|
targetBackend,
|
||||||
|
variant,
|
||||||
|
compileDesc);
|
||||||
|
|
||||||
|
if (targetBackend == ShaderBackend::D3D12) {
|
||||||
|
if (compileDesc.sourceLanguage != RHI::ShaderLanguage::HLSL) {
|
||||||
|
outError = "D3D12 precompile requires HLSL input.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string entryPoint = NarrowAscii(compileDesc.entryPoint);
|
||||||
|
const std::string profile = NarrowAscii(compileDesc.profile);
|
||||||
|
|
||||||
|
std::vector<std::string> macroNames;
|
||||||
|
std::vector<std::string> macroDefinitions;
|
||||||
|
std::vector<D3D_SHADER_MACRO> macroTable;
|
||||||
|
BuildD3D12MacroTable(compileDesc, macroNames, macroDefinitions, macroTable);
|
||||||
|
const D3D_SHADER_MACRO* macroPtr = macroTable.empty() ? nullptr : macroTable.data();
|
||||||
|
|
||||||
|
RHI::D3D12Shader compiledShader;
|
||||||
|
if (!compiledShader.Compile(
|
||||||
|
compileDesc.source.data(),
|
||||||
|
compileDesc.source.size(),
|
||||||
|
compileDesc.fileName.empty() ? nullptr : compileDesc.fileName.c_str(),
|
||||||
|
macroPtr,
|
||||||
|
entryPoint.empty() ? nullptr : entryPoint.c_str(),
|
||||||
|
profile.empty() ? nullptr : profile.c_str())) {
|
||||||
|
outError = "FXC compile failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::Array<Core::uint8> payload;
|
||||||
|
payload.Resize(compiledShader.GetBytecodeSize());
|
||||||
|
if (compiledShader.GetBytecodeSize() > 0) {
|
||||||
|
std::memcpy(
|
||||||
|
payload.Data(),
|
||||||
|
compiledShader.GetBytecode(),
|
||||||
|
compiledShader.GetBytecodeSize());
|
||||||
|
}
|
||||||
|
variant.SetCompiledBinaryForBackend(targetBackend, std::move(payload));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::CompiledSpirvShader compiledShader = {};
|
||||||
|
std::string errorMessage;
|
||||||
|
const bool compiled =
|
||||||
|
targetBackend == ShaderBackend::Vulkan
|
||||||
|
? RHI::CompileVulkanShader(compileDesc, compiledShader, &errorMessage)
|
||||||
|
: RHI::CompileSpirvShader(
|
||||||
|
compileDesc,
|
||||||
|
RHI::SpirvTargetEnvironment::OpenGL,
|
||||||
|
compiledShader,
|
||||||
|
&errorMessage);
|
||||||
|
if (!compiled) {
|
||||||
|
outError = Containers::String(
|
||||||
|
errorMessage.empty() ? "SPIR-V compile failed." : errorMessage.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::Array<Core::uint8> payload;
|
||||||
|
AppendCompiledPayload(compiledShader.spirvWords, payload);
|
||||||
|
variant.SetCompiledBinaryForBackend(targetBackend, std::move(payload));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrecompileShaderVariants(const Containers::String& shaderPath, Shader& shader) {
|
||||||
|
Containers::Array<Containers::String> passNames;
|
||||||
|
passNames.Reserve(shader.GetPasses().Size());
|
||||||
|
for (const ShaderPass& pass : shader.GetPasses()) {
|
||||||
|
passNames.PushBack(pass.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Containers::String& passName : passNames) {
|
||||||
|
ShaderPass* pass = shader.FindPass(passName);
|
||||||
|
if (pass == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderStageVariant& variant : pass->variants) {
|
||||||
|
Containers::Array<ShaderBackend> targetBackends;
|
||||||
|
if (variant.backend == ShaderBackend::Generic) {
|
||||||
|
targetBackends.PushBack(ShaderBackend::D3D12);
|
||||||
|
targetBackends.PushBack(ShaderBackend::OpenGL);
|
||||||
|
targetBackends.PushBack(ShaderBackend::Vulkan);
|
||||||
|
} else {
|
||||||
|
targetBackends.PushBack(variant.backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderBackend targetBackend : targetBackends) {
|
||||||
|
Containers::String errorMessage;
|
||||||
|
if (TryPrecompileShaderVariantForBackend(
|
||||||
|
shaderPath,
|
||||||
|
*pass,
|
||||||
|
variant,
|
||||||
|
targetBackend,
|
||||||
|
errorMessage)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::Logger::Get().Warning(
|
||||||
|
Debug::LogCategory::Rendering,
|
||||||
|
Containers::String("[AssetDatabase] Shader backend precompile skipped path=") +
|
||||||
|
shaderPath +
|
||||||
|
" pass=" +
|
||||||
|
passName +
|
||||||
|
" stage=" +
|
||||||
|
GetShaderStageLabel(variant.stage) +
|
||||||
|
" backend=" +
|
||||||
|
GetShaderBackendLabel(targetBackend) +
|
||||||
|
" reason=" +
|
||||||
|
errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<fs::path> CollectBuiltinShaderAssetPaths() {
|
||||||
|
std::vector<fs::path> paths;
|
||||||
|
paths.reserve(14u);
|
||||||
|
|
||||||
|
Containers::String resolvedPath;
|
||||||
|
const Containers::String builtinShaderPaths[] = {
|
||||||
|
GetBuiltinForwardLitShaderPath(),
|
||||||
|
GetBuiltinUnlitShaderPath(),
|
||||||
|
GetBuiltinDepthOnlyShaderPath(),
|
||||||
|
GetBuiltinShadowCasterShaderPath(),
|
||||||
|
GetBuiltinObjectIdShaderPath(),
|
||||||
|
GetBuiltinObjectIdOutlineShaderPath(),
|
||||||
|
GetBuiltinSelectionMaskShaderPath(),
|
||||||
|
GetBuiltinSelectionOutlineShaderPath(),
|
||||||
|
GetBuiltinSkyboxShaderPath(),
|
||||||
|
GetBuiltinGaussianSplatShaderPath(),
|
||||||
|
GetBuiltinGaussianSplatUtilitiesShaderPath(),
|
||||||
|
GetBuiltinVolumetricShaderPath(),
|
||||||
|
GetBuiltinColorScalePostProcessShaderPath(),
|
||||||
|
GetBuiltinFinalColorShaderPath()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const Containers::String& builtinPath : builtinShaderPaths) {
|
||||||
|
if (TryResolveBuiltinShaderAssetPath(builtinPath, resolvedPath) && !resolvedPath.Empty()) {
|
||||||
|
paths.push_back(fs::path(resolvedPath.CStr()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
std::string TrimCopy(const std::string& text) {
|
std::string TrimCopy(const std::string& text) {
|
||||||
const auto begin = std::find_if_not(text.begin(), text.end(), [](unsigned char ch) {
|
const auto begin = std::find_if_not(text.begin(), text.end(), [](unsigned char ch) {
|
||||||
return std::isspace(ch) != 0;
|
return std::isspace(ch) != 0;
|
||||||
@@ -714,6 +1008,8 @@ bool WriteShaderArtifactFile(const fs::path& artifactPath, const Shader& shader)
|
|||||||
variantHeader.keywordCount =
|
variantHeader.keywordCount =
|
||||||
static_cast<Core::uint32>(variant.requiredKeywords.enabledKeywords.Size());
|
static_cast<Core::uint32>(variant.requiredKeywords.enabledKeywords.Size());
|
||||||
variantHeader.compiledBinarySize = static_cast<Core::uint64>(variant.compiledBinary.Size());
|
variantHeader.compiledBinarySize = static_cast<Core::uint64>(variant.compiledBinary.Size());
|
||||||
|
variantHeader.backendCompiledBinaryCount =
|
||||||
|
static_cast<Core::uint32>(variant.backendCompiledBinaries.Size());
|
||||||
output.write(reinterpret_cast<const char*>(&variantHeader), sizeof(variantHeader));
|
output.write(reinterpret_cast<const char*>(&variantHeader), sizeof(variantHeader));
|
||||||
|
|
||||||
WriteString(output, variant.entryPoint);
|
WriteString(output, variant.entryPoint);
|
||||||
@@ -727,6 +1023,17 @@ bool WriteShaderArtifactFile(const fs::path& artifactPath, const Shader& shader)
|
|||||||
reinterpret_cast<const char*>(variant.compiledBinary.Data()),
|
reinterpret_cast<const char*>(variant.compiledBinary.Data()),
|
||||||
static_cast<std::streamsize>(variant.compiledBinary.Size()));
|
static_cast<std::streamsize>(variant.compiledBinary.Size()));
|
||||||
}
|
}
|
||||||
|
for (const ShaderBackendCompiledBinary& record : variant.backendCompiledBinaries) {
|
||||||
|
ShaderBackendCompiledBinaryArtifactHeader binaryHeader;
|
||||||
|
binaryHeader.backend = static_cast<Core::uint32>(record.backend);
|
||||||
|
binaryHeader.compiledBinarySize = static_cast<Core::uint64>(record.payload.Size());
|
||||||
|
output.write(reinterpret_cast<const char*>(&binaryHeader), sizeof(binaryHeader));
|
||||||
|
if (!record.payload.Empty()) {
|
||||||
|
output.write(
|
||||||
|
reinterpret_cast<const char*>(record.payload.Data()),
|
||||||
|
static_cast<std::streamsize>(record.payload.Size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -843,12 +1150,7 @@ bool AssetDatabase::ResolvePath(const Containers::String& requestPath,
|
|||||||
const fs::path projectRootPath(m_projectRoot.CStr());
|
const fs::path projectRootPath(m_projectRoot.CStr());
|
||||||
const fs::path relativePath = fs::relative(inputPath, projectRootPath, ec);
|
const fs::path relativePath = fs::relative(inputPath, projectRootPath, ec);
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
const Containers::String normalizedRelative = NormalizePathString(relativePath);
|
outRelativePath = NormalizePathString(relativePath);
|
||||||
if (normalizedRelative.StartsWith("Assets/") || normalizedRelative == "Assets") {
|
|
||||||
outRelativePath = normalizedRelative;
|
|
||||||
} else {
|
|
||||||
outRelativePath.Clear();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
outRelativePath.Clear();
|
outRelativePath.Clear();
|
||||||
}
|
}
|
||||||
@@ -1300,6 +1602,9 @@ AssetDatabase::MaintenanceStats AssetDatabase::ScanAssets() {
|
|||||||
if (fs::exists(assetsRootPath)) {
|
if (fs::exists(assetsRootPath)) {
|
||||||
ScanAssetPath(assetsRootPath, seenPaths);
|
ScanAssetPath(assetsRootPath, seenPaths);
|
||||||
}
|
}
|
||||||
|
for (const fs::path& builtinShaderPath : CollectBuiltinShaderAssetPaths()) {
|
||||||
|
ScanAssetPath(builtinShaderPath, seenPaths);
|
||||||
|
}
|
||||||
RemoveMissingRecords(seenPaths);
|
RemoveMissingRecords(seenPaths);
|
||||||
stats.removedArtifactCount = CleanupOrphanedArtifacts();
|
stats.removedArtifactCount = CleanupOrphanedArtifacts();
|
||||||
SaveSourceAssetDB();
|
SaveSourceAssetDB();
|
||||||
@@ -1446,6 +1751,47 @@ bool AssetDatabase::EnsureMetaForPath(const fs::path& sourcePath,
|
|||||||
outRecord.importerName = GetImporterNameForPath(relativePath, isFolder);
|
outRecord.importerName = GetImporterNameForPath(relativePath, isFolder);
|
||||||
outRecord.importerVersion = kCurrentImporterVersion;
|
outRecord.importerVersion = kCurrentImporterVersion;
|
||||||
|
|
||||||
|
if (UsesExternalSyntheticSourceRecord(relativePath)) {
|
||||||
|
if (!outRecord.guid.IsValid()) {
|
||||||
|
outRecord.guid = HashStringToAssetGUID(NormalizePathString(sourcePath));
|
||||||
|
}
|
||||||
|
outRecord.metaPath.Clear();
|
||||||
|
outRecord.metaHash =
|
||||||
|
BuildSyntheticMetaHash(relativePath, outRecord.importerName, outRecord.importerVersion);
|
||||||
|
|
||||||
|
const auto duplicateGuidIt = m_sourcesByGuid.find(outRecord.guid);
|
||||||
|
if (duplicateGuidIt != m_sourcesByGuid.end() &&
|
||||||
|
duplicateGuidIt->second.relativePath != relativePath) {
|
||||||
|
Containers::String duplicateSignature = NormalizePathString(sourcePath);
|
||||||
|
duplicateSignature += ":";
|
||||||
|
duplicateSignature += relativePath;
|
||||||
|
outRecord.guid = HashStringToAssetGUID(duplicateSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFolder) {
|
||||||
|
outRecord.sourceHash.Clear();
|
||||||
|
outRecord.sourceFileSize = 0;
|
||||||
|
outRecord.sourceWriteTime = 0;
|
||||||
|
} else {
|
||||||
|
const Core::uint64 fileSize = GetFileSizeValue(sourcePath);
|
||||||
|
const Core::uint64 writeTime = GetFileWriteTimeValue(sourcePath);
|
||||||
|
if (existingIt != m_sourcesByPathKey.end() &&
|
||||||
|
existingIt->second.sourceFileSize == fileSize &&
|
||||||
|
existingIt->second.sourceWriteTime == writeTime &&
|
||||||
|
!existingIt->second.sourceHash.Empty()) {
|
||||||
|
outRecord.sourceHash = existingIt->second.sourceHash;
|
||||||
|
} else {
|
||||||
|
outRecord.sourceHash = ComputeFileHash(sourcePath);
|
||||||
|
}
|
||||||
|
outRecord.sourceFileSize = fileSize;
|
||||||
|
outRecord.sourceWriteTime = writeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sourcesByPathKey[pathKey] = outRecord;
|
||||||
|
m_sourcesByGuid[outRecord.guid] = outRecord;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const fs::path metaPath(sourcePath.string() + ".meta");
|
const fs::path metaPath(sourcePath.string() + ".meta");
|
||||||
outRecord.metaPath = NormalizeRelativePath(metaPath);
|
outRecord.metaPath = NormalizeRelativePath(metaPath);
|
||||||
const Containers::String expectedImporterName = GetImporterNameForPath(relativePath, isFolder);
|
const Containers::String expectedImporterName = GetImporterNameForPath(relativePath, isFolder);
|
||||||
@@ -2100,10 +2446,14 @@ bool AssetDatabase::ImportShaderAsset(const SourceAssetRecord& sourceRecord,
|
|||||||
NormalizePathString(fs::path(m_projectRoot.CStr()) / sourceRecord.relativePath.CStr());
|
NormalizePathString(fs::path(m_projectRoot.CStr()) / sourceRecord.relativePath.CStr());
|
||||||
LoadResult result = loader.Load(absolutePath);
|
LoadResult result = loader.Load(absolutePath);
|
||||||
if (!result || result.resource == nullptr) {
|
if (!result || result.resource == nullptr) {
|
||||||
|
if (!result.errorMessage.Empty()) {
|
||||||
|
SetLastErrorMessage(result.errorMessage);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader* shader = static_cast<Shader*>(result.resource);
|
Shader* shader = static_cast<Shader*>(result.resource);
|
||||||
|
PrecompileShaderVariants(absolutePath, *shader);
|
||||||
std::vector<ArtifactDependencyRecord> dependencies;
|
std::vector<ArtifactDependencyRecord> dependencies;
|
||||||
if (!CollectShaderDependencies(sourceRecord, dependencies)) {
|
if (!CollectShaderDependencies(sourceRecord, dependencies)) {
|
||||||
delete shader;
|
delete shader;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||||
#include <XCEngine/Core/IO/ResourceFileSystem.h>
|
#include <XCEngine/Core/IO/ResourceFileSystem.h>
|
||||||
#include <XCEngine/Resources/GaussianSplat/GaussianSplatLoader.h>
|
#include <XCEngine/Resources/GaussianSplat/GaussianSplatLoader.h>
|
||||||
|
#include <XCEngine/Resources/BuiltinResources.h>
|
||||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||||
#include <XCEngine/Resources/Model/ModelLoader.h>
|
#include <XCEngine/Resources/Model/ModelLoader.h>
|
||||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||||
@@ -548,6 +549,7 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Containers::String loadPath = path;
|
Containers::String loadPath = path;
|
||||||
|
Containers::String cachePath = path;
|
||||||
AssetImportService::ImportedAsset resolvedAsset;
|
AssetImportService::ImportedAsset resolvedAsset;
|
||||||
ResourceType importableType = ResourceType::Unknown;
|
ResourceType importableType = ResourceType::Unknown;
|
||||||
const bool shouldUseProjectArtifact =
|
const bool shouldUseProjectArtifact =
|
||||||
@@ -560,6 +562,7 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
|||||||
resolvedAsset.artifactReady) {
|
resolvedAsset.artifactReady) {
|
||||||
m_projectAssetIndex.RememberResolvedPath(resolvedAsset.assetGuid, resolvedAsset.relativePath);
|
m_projectAssetIndex.RememberResolvedPath(resolvedAsset.assetGuid, resolvedAsset.relativePath);
|
||||||
loadPath = resolvedAsset.runtimeLoadPath;
|
loadPath = resolvedAsset.runtimeLoadPath;
|
||||||
|
cachePath = loadPath;
|
||||||
if (ShouldTraceResourcePath(path)) {
|
if (ShouldTraceResourcePath(path)) {
|
||||||
Debug::Logger::Get().Info(
|
Debug::Logger::Get().Info(
|
||||||
Debug::LogCategory::FileSystem,
|
Debug::LogCategory::FileSystem,
|
||||||
@@ -568,6 +571,35 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
|||||||
" artifact=" +
|
" artifact=" +
|
||||||
loadPath);
|
loadPath);
|
||||||
}
|
}
|
||||||
|
} else if (type == ResourceType::Shader && IsBuiltinShaderPath(path)) {
|
||||||
|
Containers::String builtinShaderAssetPath;
|
||||||
|
if (TryResolveBuiltinShaderAssetPath(path, builtinShaderAssetPath)) {
|
||||||
|
ResourceType builtinImportableType = ResourceType::Unknown;
|
||||||
|
AssetImportService::ImportedAsset builtinResolvedAsset;
|
||||||
|
if (!m_resourceRoot.Empty() &&
|
||||||
|
m_assetImportService.TryGetImportableResourceType(
|
||||||
|
builtinShaderAssetPath,
|
||||||
|
builtinImportableType) &&
|
||||||
|
builtinImportableType == type &&
|
||||||
|
m_assetImportService.EnsureArtifact(
|
||||||
|
builtinShaderAssetPath,
|
||||||
|
type,
|
||||||
|
builtinResolvedAsset) &&
|
||||||
|
builtinResolvedAsset.artifactReady) {
|
||||||
|
loadPath = builtinResolvedAsset.runtimeLoadPath;
|
||||||
|
} else {
|
||||||
|
loadPath = builtinShaderAssetPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldTraceResourcePath(path)) {
|
||||||
|
Debug::Logger::Get().Info(
|
||||||
|
Debug::LogCategory::FileSystem,
|
||||||
|
Containers::String("[ResourceManager] LoadResource builtin shader path=") +
|
||||||
|
path +
|
||||||
|
" loadPath=" +
|
||||||
|
loadPath);
|
||||||
|
}
|
||||||
} else if (ShouldTraceResourcePath(path)) {
|
} else if (ShouldTraceResourcePath(path)) {
|
||||||
Debug::Logger::Get().Info(
|
Debug::Logger::Get().Info(
|
||||||
Debug::LogCategory::FileSystem,
|
Debug::LogCategory::FileSystem,
|
||||||
@@ -577,7 +609,7 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
|||||||
loadPath);
|
loadPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResourceGUID guid = ResourceGUID::Generate(loadPath);
|
const ResourceGUID guid = ResourceGUID::Generate(cachePath);
|
||||||
|
|
||||||
if (IResource* cached = FindInCache(guid)) {
|
if (IResource* cached = FindInCache(guid)) {
|
||||||
if (ShouldTraceResourcePath(path)) {
|
if (ShouldTraceResourcePath(path)) {
|
||||||
|
|||||||
@@ -24,6 +24,33 @@ std::string NarrowAscii(const std::wstring& value) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShaderType ResolveShaderTypeFromTarget(const char* target) {
|
||||||
|
if (target == nullptr) {
|
||||||
|
return ShaderType::Vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(target, "vs_")) {
|
||||||
|
return ShaderType::Vertex;
|
||||||
|
}
|
||||||
|
if (strstr(target, "ps_")) {
|
||||||
|
return ShaderType::Fragment;
|
||||||
|
}
|
||||||
|
if (strstr(target, "gs_")) {
|
||||||
|
return ShaderType::Geometry;
|
||||||
|
}
|
||||||
|
if (strstr(target, "cs_")) {
|
||||||
|
return ShaderType::Compute;
|
||||||
|
}
|
||||||
|
if (strstr(target, "hs_")) {
|
||||||
|
return ShaderType::Hull;
|
||||||
|
}
|
||||||
|
if (strstr(target, "ds_")) {
|
||||||
|
return ShaderType::Domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShaderType::Vertex;
|
||||||
|
}
|
||||||
|
|
||||||
class D3D12ShaderIncludeHandler final : public ID3DInclude {
|
class D3D12ShaderIncludeHandler final : public ID3DInclude {
|
||||||
public:
|
public:
|
||||||
explicit D3D12ShaderIncludeHandler(const std::filesystem::path& sourcePath)
|
explicit D3D12ShaderIncludeHandler(const std::filesystem::path& sourcePath)
|
||||||
@@ -127,15 +154,7 @@ bool D3D12Shader::CompileFromFile(const wchar_t* filePath, const char* entryPoin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(target, "vs_")) {
|
m_type = ResolveShaderTypeFromTarget(target);
|
||||||
m_type = ShaderType::Vertex;
|
|
||||||
} else if (strstr(target, "ps_")) {
|
|
||||||
m_type = ShaderType::Fragment;
|
|
||||||
} else if (strstr(target, "gs_")) {
|
|
||||||
m_type = ShaderType::Geometry;
|
|
||||||
} else if (strstr(target, "cs_")) {
|
|
||||||
m_type = ShaderType::Compute;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_uniformsCached = false;
|
m_uniformsCached = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -182,16 +201,29 @@ bool D3D12Shader::Compile(const void* sourceData,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(target, "vs_")) {
|
m_type = ResolveShaderTypeFromTarget(target);
|
||||||
m_type = ShaderType::Vertex;
|
|
||||||
} else if (strstr(target, "ps_")) {
|
m_uniformsCached = false;
|
||||||
m_type = ShaderType::Fragment;
|
return true;
|
||||||
} else if (strstr(target, "gs_")) {
|
}
|
||||||
m_type = ShaderType::Geometry;
|
|
||||||
} else if (strstr(target, "cs_")) {
|
bool D3D12Shader::InitializeFromBytecode(const void* bytecodeData,
|
||||||
m_type = ShaderType::Compute;
|
size_t bytecodeSize,
|
||||||
|
const char* target) {
|
||||||
|
if (bytecodeData == nullptr || bytecodeSize == 0) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
ComPtr<ID3DBlob> blob;
|
||||||
|
if (FAILED(D3DCreateBlob(bytecodeSize, &blob)) || blob == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(blob->GetBufferPointer(), bytecodeData, bytecodeSize);
|
||||||
|
m_bytecode = std::move(blob);
|
||||||
|
m_type = ResolveShaderTypeFromTarget(target);
|
||||||
m_uniformsCached = false;
|
m_uniformsCached = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,6 +272,21 @@ bool IsHlslInput(const ShaderCompileDesc& desc) {
|
|||||||
return extension == ".hlsl";
|
return extension == ".hlsl";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasMatchingCompiledSpirvBinary(const ShaderCompileDesc& desc,
|
||||||
|
SpirvTargetEnvironment targetEnvironment) {
|
||||||
|
if (desc.compiledBinary.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (targetEnvironment) {
|
||||||
|
case SpirvTargetEnvironment::OpenGL:
|
||||||
|
return desc.compiledBinaryBackend == ShaderBinaryBackend::OpenGL;
|
||||||
|
case SpirvTargetEnvironment::Vulkan:
|
||||||
|
default:
|
||||||
|
return desc.compiledBinaryBackend == ShaderBinaryBackend::Vulkan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring GetEnvironmentVariableValue(const wchar_t* name) {
|
std::wstring GetEnvironmentVariableValue(const wchar_t* name) {
|
||||||
const DWORD length = GetEnvironmentVariableW(name, nullptr, 0);
|
const DWORD length = GetEnvironmentVariableW(name, nullptr, 0);
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
@@ -1021,16 +1036,19 @@ bool CompileSpirvShader(const ShaderCompileDesc& desc,
|
|||||||
outShader.entryPoint = "main";
|
outShader.entryPoint = "main";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.source.empty() && desc.fileName.empty()) {
|
if (desc.source.empty() && desc.fileName.empty() &&
|
||||||
|
!HasMatchingCompiledSpirvBinary(desc, targetEnvironment)) {
|
||||||
if (errorMessage != nullptr) {
|
if (errorMessage != nullptr) {
|
||||||
*errorMessage = "No shader source or file name was provided.";
|
*errorMessage = "No shader source or file name was provided.";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsSpirvInput(desc)) {
|
if (IsSpirvInput(desc) || HasMatchingCompiledSpirvBinary(desc, targetEnvironment)) {
|
||||||
std::vector<uint8_t> bytes;
|
std::vector<uint8_t> bytes;
|
||||||
if (!desc.source.empty()) {
|
if (HasMatchingCompiledSpirvBinary(desc, targetEnvironment)) {
|
||||||
|
bytes = desc.compiledBinary;
|
||||||
|
} else if (!desc.source.empty()) {
|
||||||
bytes = desc.source;
|
bytes = desc.source;
|
||||||
} else if (!LoadBinaryFile(std::filesystem::path(desc.fileName), bytes)) {
|
} else if (!LoadBinaryFile(std::filesystem::path(desc.fileName), bytes)) {
|
||||||
if (errorMessage != nullptr) {
|
if (errorMessage != nullptr) {
|
||||||
|
|||||||
@@ -39,6 +39,20 @@ inline RHI::ShaderLanguage ToRHIShaderLanguage(Resources::ShaderLanguage languag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline RHI::ShaderBinaryBackend ToRHIShaderBinaryBackend(Resources::ShaderBackend backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case Resources::ShaderBackend::D3D12:
|
||||||
|
return RHI::ShaderBinaryBackend::D3D12;
|
||||||
|
case Resources::ShaderBackend::OpenGL:
|
||||||
|
return RHI::ShaderBinaryBackend::OpenGL;
|
||||||
|
case Resources::ShaderBackend::Vulkan:
|
||||||
|
return RHI::ShaderBinaryBackend::Vulkan;
|
||||||
|
case Resources::ShaderBackend::Generic:
|
||||||
|
default:
|
||||||
|
return RHI::ShaderBinaryBackend::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline std::wstring ToWideAscii(const Containers::String& value) {
|
inline std::wstring ToWideAscii(const Containers::String& value) {
|
||||||
std::wstring wide;
|
std::wstring wide;
|
||||||
wide.reserve(value.Length());
|
wide.reserve(value.Length());
|
||||||
@@ -360,6 +374,13 @@ inline void ApplyShaderStageVariant(
|
|||||||
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
||||||
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
||||||
compileDesc.profile = ToWideAscii(variant.profile);
|
compileDesc.profile = ToWideAscii(variant.profile);
|
||||||
|
compileDesc.compiledBinaryBackend = ToRHIShaderBinaryBackend(variant.backend);
|
||||||
|
compileDesc.compiledBinary.clear();
|
||||||
|
if (!variant.compiledBinary.Empty()) {
|
||||||
|
compileDesc.compiledBinary.assign(
|
||||||
|
variant.compiledBinary.Data(),
|
||||||
|
variant.compiledBinary.Data() + variant.compiledBinary.Size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::wstring ResolveRuntimeShaderSourcePath(const Containers::String& shaderPath) {
|
inline std::wstring ResolveRuntimeShaderSourcePath(const Containers::String& shaderPath) {
|
||||||
@@ -398,6 +419,12 @@ inline void ApplyShaderStageVariant(
|
|||||||
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
||||||
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
||||||
compileDesc.profile = ToWideAscii(variant.profile);
|
compileDesc.profile = ToWideAscii(variant.profile);
|
||||||
|
compileDesc.compiledBinaryBackend = RHI::ShaderBinaryBackend::Unknown;
|
||||||
|
compileDesc.compiledBinary.clear();
|
||||||
|
if (const Containers::Array<Core::uint8>* binary = variant.GetCompiledBinaryForBackend(backend)) {
|
||||||
|
compileDesc.compiledBinaryBackend = ToRHIShaderBinaryBackend(backend);
|
||||||
|
compileDesc.compiledBinary.assign(binary->Data(), binary->Data() + binary->Size());
|
||||||
|
}
|
||||||
InjectShaderBackendMacros(backend, compileDesc);
|
InjectShaderBackendMacros(backend, compileDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "Rendering/FrameData/VisibleGaussianSplatItem.h"
|
#include "Rendering/FrameData/VisibleGaussianSplatItem.h"
|
||||||
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
||||||
#include "Rendering/Internal/ShaderVariantUtils.h"
|
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||||
|
#include "Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.h"
|
||||||
#include "Rendering/RenderSurface.h"
|
#include "Rendering/RenderSurface.h"
|
||||||
#include "Resources/BuiltinResources.h"
|
#include "Resources/BuiltinResources.h"
|
||||||
#include "Resources/GaussianSplat/GaussianSplat.h"
|
#include "Resources/GaussianSplat/GaussianSplat.h"
|
||||||
@@ -49,6 +50,21 @@ const Resources::ShaderPass* FindCompatibleGaussianSplatPass(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Resources::ShaderPass* FindCompatibleComputePass(
|
||||||
|
const Resources::Shader& shader,
|
||||||
|
const Containers::String& passName,
|
||||||
|
const Resources::ShaderKeywordSet& keywordSet,
|
||||||
|
Resources::ShaderBackend backend) {
|
||||||
|
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
|
||||||
|
if (shaderPass == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader.FindVariant(passName, Resources::ShaderType::Compute, backend, keywordSet) != nullptr
|
||||||
|
? shaderPass
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||||
RHI::RHIType backendType,
|
RHI::RHIType backendType,
|
||||||
RHI::RHIPipelineLayout* pipelineLayout,
|
RHI::RHIPipelineLayout* pipelineLayout,
|
||||||
@@ -89,6 +105,88 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|||||||
return pipelineDesc;
|
return pipelineDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RHI::ComputePipelineDesc CreateComputePipelineDesc(
|
||||||
|
RHI::RHIType backendType,
|
||||||
|
RHI::RHIPipelineLayout* pipelineLayout,
|
||||||
|
const Resources::Shader& shader,
|
||||||
|
const Resources::ShaderPass& shaderPass,
|
||||||
|
const Containers::String& passName,
|
||||||
|
const Resources::ShaderKeywordSet& keywordSet) {
|
||||||
|
RHI::ComputePipelineDesc pipelineDesc = {};
|
||||||
|
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||||
|
|
||||||
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType);
|
||||||
|
if (const Resources::ShaderStageVariant* computeVariant =
|
||||||
|
shader.FindVariant(passName, Resources::ShaderType::Compute, backend, keywordSet)) {
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
shader.GetPath(),
|
||||||
|
shaderPass,
|
||||||
|
backend,
|
||||||
|
*computeVariant,
|
||||||
|
pipelineDesc.computeShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipelineDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RHI::DescriptorSetLayoutBinding* FindSetLayoutBinding(
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
|
Core::uint32 binding) {
|
||||||
|
for (const RHI::DescriptorSetLayoutBinding& layoutBinding : setLayout.bindings) {
|
||||||
|
if (layoutBinding.binding == binding) {
|
||||||
|
return &layoutBinding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHIResourceView* ResolveWorkingSetView(
|
||||||
|
const Internal::BuiltinGaussianSplatPassResources::CachedBufferView& bufferView,
|
||||||
|
const RHI::DescriptorSetLayoutBinding* layoutBinding) {
|
||||||
|
if (layoutBinding == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (static_cast<RHI::DescriptorType>(layoutBinding->type)) {
|
||||||
|
case RHI::DescriptorType::UAV:
|
||||||
|
return bufferView.unorderedAccessView;
|
||||||
|
case RHI::DescriptorType::SRV:
|
||||||
|
return bufferView.shaderResourceView;
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename BindFn>
|
||||||
|
void BindDescriptorSetRanges(
|
||||||
|
Core::uint32 firstDescriptorSet,
|
||||||
|
std::vector<RHI::RHIDescriptorSet*>& descriptorSets,
|
||||||
|
BindFn&& bindFn) {
|
||||||
|
const Core::uint32 descriptorSetCount = static_cast<Core::uint32>(descriptorSets.size());
|
||||||
|
Core::uint32 rangeStart = 0u;
|
||||||
|
while (rangeStart < descriptorSetCount) {
|
||||||
|
while (rangeStart < descriptorSetCount && descriptorSets[rangeStart] == nullptr) {
|
||||||
|
++rangeStart;
|
||||||
|
}
|
||||||
|
if (rangeStart >= descriptorSetCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::uint32 rangeCount = 0u;
|
||||||
|
while ((rangeStart + rangeCount) < descriptorSetCount &&
|
||||||
|
descriptorSets[rangeStart + rangeCount] != nullptr) {
|
||||||
|
++rangeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bindFn(
|
||||||
|
firstDescriptorSet + rangeStart,
|
||||||
|
rangeCount,
|
||||||
|
descriptorSets.data() + rangeStart);
|
||||||
|
rangeStart += rangeCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() {
|
BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() {
|
||||||
@@ -124,6 +222,19 @@ bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
|
|||||||
cachedGaussianSplat->color.shaderResourceView == nullptr) {
|
cachedGaussianSplat->color.shaderResourceView == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_passResources == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
||||||
|
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
||||||
|
workingSet == nullptr ||
|
||||||
|
workingSet->sortDistances.unorderedAccessView == nullptr ||
|
||||||
|
workingSet->orderIndices.shaderResourceView == nullptr ||
|
||||||
|
workingSet->orderIndices.unorderedAccessView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -177,6 +288,13 @@ bool BuiltinGaussianSplatPass::Execute(const RenderPassContext& context) {
|
|||||||
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||||
|
|
||||||
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
|
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
|
||||||
|
if (!PrepareVisibleGaussianSplat(
|
||||||
|
context.renderContext,
|
||||||
|
context.sceneData,
|
||||||
|
visibleGaussianSplat)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!DrawVisibleGaussianSplat(
|
if (!DrawVisibleGaussianSplat(
|
||||||
context.renderContext,
|
context.renderContext,
|
||||||
context.surface,
|
context.surface,
|
||||||
@@ -222,6 +340,16 @@ bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_builtinGaussianSplatUtilitiesShader = Resources::ResourceManager::Get().Load<Resources::Shader>(
|
||||||
|
Resources::GetBuiltinGaussianSplatUtilitiesShaderPath());
|
||||||
|
if (!m_builtinGaussianSplatUtilitiesShader.IsValid()) {
|
||||||
|
Debug::Logger::Get().Error(
|
||||||
|
Debug::LogCategory::Rendering,
|
||||||
|
"BuiltinGaussianSplatPass failed to load builtin gaussian splat utilities shader resource");
|
||||||
|
DestroyResources();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_builtinGaussianSplatMaterial = std::make_unique<Resources::Material>();
|
m_builtinGaussianSplatMaterial = std::make_unique<Resources::Material>();
|
||||||
Resources::IResource::ConstructParams params = {};
|
Resources::IResource::ConstructParams params = {};
|
||||||
params.name = "BuiltinGaussianSplatMaterial";
|
params.name = "BuiltinGaussianSplatMaterial";
|
||||||
@@ -230,10 +358,17 @@ bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) {
|
|||||||
m_builtinGaussianSplatMaterial->Initialize(params);
|
m_builtinGaussianSplatMaterial->Initialize(params);
|
||||||
m_builtinGaussianSplatMaterial->SetShader(m_builtinGaussianSplatShader);
|
m_builtinGaussianSplatMaterial->SetShader(m_builtinGaussianSplatShader);
|
||||||
m_builtinGaussianSplatMaterial->SetRenderQueue(Resources::MaterialRenderQueue::Transparent);
|
m_builtinGaussianSplatMaterial->SetRenderQueue(Resources::MaterialRenderQueue::Transparent);
|
||||||
|
m_passResources = new Internal::BuiltinGaussianSplatPassResources();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuiltinGaussianSplatPass::DestroyResources() {
|
void BuiltinGaussianSplatPass::DestroyResources() {
|
||||||
|
if (m_passResources != nullptr) {
|
||||||
|
m_passResources->Shutdown();
|
||||||
|
delete m_passResources;
|
||||||
|
m_passResources = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
m_resourceCache.Shutdown();
|
m_resourceCache.Shutdown();
|
||||||
|
|
||||||
for (auto& pipelinePair : m_pipelineStates) {
|
for (auto& pipelinePair : m_pipelineStates) {
|
||||||
@@ -244,6 +379,14 @@ void BuiltinGaussianSplatPass::DestroyResources() {
|
|||||||
}
|
}
|
||||||
m_pipelineStates.clear();
|
m_pipelineStates.clear();
|
||||||
|
|
||||||
|
for (auto& pipelinePair : m_computePipelineStates) {
|
||||||
|
if (pipelinePair.second != nullptr) {
|
||||||
|
pipelinePair.second->Shutdown();
|
||||||
|
delete pipelinePair.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_computePipelineStates.clear();
|
||||||
|
|
||||||
for (auto& descriptorSetPair : m_dynamicDescriptorSets) {
|
for (auto& descriptorSetPair : m_dynamicDescriptorSets) {
|
||||||
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
|
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
|
||||||
}
|
}
|
||||||
@@ -255,6 +398,7 @@ void BuiltinGaussianSplatPass::DestroyResources() {
|
|||||||
m_passResourceLayouts.clear();
|
m_passResourceLayouts.clear();
|
||||||
|
|
||||||
m_builtinGaussianSplatMaterial.reset();
|
m_builtinGaussianSplatMaterial.reset();
|
||||||
|
m_builtinGaussianSplatUtilitiesShader.Reset();
|
||||||
m_builtinGaussianSplatShader.Reset();
|
m_builtinGaussianSplatShader.Reset();
|
||||||
m_device = nullptr;
|
m_device = nullptr;
|
||||||
m_backendType = RHI::RHIType::D3D12;
|
m_backendType = RHI::RHIType::D3D12;
|
||||||
@@ -301,9 +445,30 @@ BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveGa
|
|||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolvePrepareOrderShaderPass(
|
||||||
|
const RenderSceneData& sceneData) const {
|
||||||
|
ResolvedShaderPass resolved = {};
|
||||||
|
if (!m_builtinGaussianSplatUtilitiesShader.IsValid()) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Resources::Shader* shader = m_builtinGaussianSplatUtilitiesShader.Get();
|
||||||
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType);
|
||||||
|
const Containers::String passName("GaussianSplatPrepareOrder");
|
||||||
|
if (const Resources::ShaderPass* shaderPass =
|
||||||
|
FindCompatibleComputePass(*shader, passName, sceneData.globalShaderKeywords, backend)) {
|
||||||
|
resolved.shader = shader;
|
||||||
|
resolved.pass = shaderPass;
|
||||||
|
resolved.passName = passName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCreatePassResourceLayout(
|
BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCreatePassResourceLayout(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
const ResolvedShaderPass& resolvedShaderPass) {
|
const ResolvedShaderPass& resolvedShaderPass,
|
||||||
|
PassLayoutUsage usage) {
|
||||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -345,21 +510,36 @@ BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCre
|
|||||||
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
||||||
passLayout.perObject = bindingPlan.perObject;
|
passLayout.perObject = bindingPlan.perObject;
|
||||||
passLayout.material = bindingPlan.material;
|
passLayout.material = bindingPlan.material;
|
||||||
|
passLayout.gaussianSplatSortDistanceBuffer = bindingPlan.gaussianSplatSortDistanceBuffer;
|
||||||
|
passLayout.gaussianSplatOrderBuffer = bindingPlan.gaussianSplatOrderBuffer;
|
||||||
passLayout.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer;
|
passLayout.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer;
|
||||||
passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer;
|
passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer;
|
||||||
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
|
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
|
||||||
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
|
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
|
||||||
|
passLayout.gaussianSplatViewDataBuffer = bindingPlan.gaussianSplatViewDataBuffer;
|
||||||
|
|
||||||
if (!passLayout.perObject.IsValid()) {
|
if (!passLayout.perObject.IsValid()) {
|
||||||
return failLayout("BuiltinGaussianSplatPass requires a PerObject resource binding");
|
return failLayout("BuiltinGaussianSplatPass requires a PerObject resource binding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (usage == PassLayoutUsage::Draw) {
|
||||||
if (!passLayout.material.IsValid()) {
|
if (!passLayout.material.IsValid()) {
|
||||||
return failLayout("BuiltinGaussianSplatPass requires a Material resource binding");
|
return failLayout("BuiltinGaussianSplatPass requires a Material resource binding");
|
||||||
}
|
}
|
||||||
if (!passLayout.gaussianSplatPositionBuffer.IsValid() ||
|
if (!passLayout.gaussianSplatOrderBuffer.IsValid() ||
|
||||||
|
!passLayout.gaussianSplatPositionBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||||
!passLayout.gaussianSplatColorBuffer.IsValid()) {
|
!passLayout.gaussianSplatColorBuffer.IsValid()) {
|
||||||
return failLayout("BuiltinGaussianSplatPass requires position, other, and color gaussian splat buffer bindings");
|
return failLayout(
|
||||||
|
"BuiltinGaussianSplatPass draw pass requires order, position, other, and color gaussian splat buffer bindings");
|
||||||
|
}
|
||||||
|
} else if (usage == PassLayoutUsage::PrepareOrder) {
|
||||||
|
if (!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
|
||||||
|
!passLayout.gaussianSplatOrderBuffer.IsValid() ||
|
||||||
|
!passLayout.gaussianSplatPositionBuffer.IsValid()) {
|
||||||
|
return failLayout(
|
||||||
|
"BuiltinGaussianSplatPass prepare-order pass requires sort distance, order, and position gaussian splat buffer bindings");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
|
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
|
||||||
@@ -392,7 +572,10 @@ RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||||
|
context,
|
||||||
|
resolvedShaderPass,
|
||||||
|
PassLayoutUsage::Draw);
|
||||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -440,6 +623,54 @@ RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState(
|
|||||||
return pipelineState;
|
return pipelineState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreateComputePipelineState(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData) {
|
||||||
|
const ResolvedShaderPass resolvedShaderPass = ResolvePrepareOrderShaderPass(sceneData);
|
||||||
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||||
|
context,
|
||||||
|
resolvedShaderPass,
|
||||||
|
PassLayoutUsage::PrepareOrder);
|
||||||
|
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputePipelineKey pipelineKey = {};
|
||||||
|
pipelineKey.shader = resolvedShaderPass.shader;
|
||||||
|
pipelineKey.passName = resolvedShaderPass.passName;
|
||||||
|
pipelineKey.keywordSignature =
|
||||||
|
::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(sceneData.globalShaderKeywords);
|
||||||
|
|
||||||
|
const auto existing = m_computePipelineStates.find(pipelineKey);
|
||||||
|
if (existing != m_computePipelineStates.end()) {
|
||||||
|
return existing->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RHI::ComputePipelineDesc pipelineDesc =
|
||||||
|
CreateComputePipelineDesc(
|
||||||
|
context.backendType,
|
||||||
|
passLayout->pipelineLayout,
|
||||||
|
*resolvedShaderPass.shader,
|
||||||
|
*resolvedShaderPass.pass,
|
||||||
|
resolvedShaderPass.passName,
|
||||||
|
sceneData.globalShaderKeywords);
|
||||||
|
RHI::RHIPipelineState* pipelineState = context.device->CreateComputePipelineState(pipelineDesc);
|
||||||
|
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
||||||
|
if (pipelineState != nullptr) {
|
||||||
|
pipelineState->Shutdown();
|
||||||
|
delete pipelineState;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_computePipelineStates.emplace(pipelineKey, pipelineState);
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
bool BuiltinGaussianSplatPass::CreateOwnedDescriptorSet(
|
bool BuiltinGaussianSplatPass::CreateOwnedDescriptorSet(
|
||||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
OwnedDescriptorSet& descriptorSet) {
|
OwnedDescriptorSet& descriptorSet) {
|
||||||
@@ -468,14 +699,19 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
Core::uint32 setIndex,
|
Core::uint32 setIndex,
|
||||||
Core::uint64 objectId,
|
Core::uint64 objectId,
|
||||||
|
const Components::GaussianSplatRendererComponent* gaussianSplatRenderer,
|
||||||
const Resources::Material* material,
|
const Resources::Material* material,
|
||||||
const Resources::GaussianSplat* gaussianSplat,
|
const Resources::GaussianSplat* gaussianSplat,
|
||||||
const MaterialConstantPayloadView& materialConstants,
|
const MaterialConstantPayloadView& materialConstants,
|
||||||
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat) {
|
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
||||||
|
RHI::RHIResourceView* sortDistanceView,
|
||||||
|
RHI::RHIResourceView* orderView,
|
||||||
|
RHI::RHIResourceView* viewDataView) {
|
||||||
DynamicDescriptorSetKey key = {};
|
DynamicDescriptorSetKey key = {};
|
||||||
key.passLayout = passLayoutKey;
|
key.passLayout = passLayoutKey;
|
||||||
key.setIndex = setIndex;
|
key.setIndex = setIndex;
|
||||||
key.objectId = objectId;
|
key.objectId = objectId;
|
||||||
|
key.gaussianSplatRenderer = gaussianSplatRenderer;
|
||||||
key.material = material;
|
key.material = material;
|
||||||
key.gaussianSplat = gaussianSplat;
|
key.gaussianSplat = gaussianSplat;
|
||||||
|
|
||||||
@@ -486,6 +722,41 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet =
|
||||||
|
(gaussianSplatRenderer != nullptr && m_passResources != nullptr)
|
||||||
|
? m_passResources->FindWorkingSet(gaussianSplatRenderer)
|
||||||
|
: nullptr;
|
||||||
|
RHI::RHIResourceView* resolvedSortDistanceView = sortDistanceView;
|
||||||
|
RHI::RHIResourceView* resolvedOrderView = orderView;
|
||||||
|
RHI::RHIResourceView* resolvedViewDataView = viewDataView;
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatSortDistanceBuffer && workingSet != nullptr) {
|
||||||
|
if (const RHI::DescriptorSetLayoutBinding* layoutBinding =
|
||||||
|
FindSetLayoutBinding(setLayout, passLayout.gaussianSplatSortDistanceBuffer.binding)) {
|
||||||
|
if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->sortDistances, layoutBinding)) {
|
||||||
|
resolvedSortDistanceView = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatOrderBuffer && workingSet != nullptr) {
|
||||||
|
if (const RHI::DescriptorSetLayoutBinding* layoutBinding =
|
||||||
|
FindSetLayoutBinding(setLayout, passLayout.gaussianSplatOrderBuffer.binding)) {
|
||||||
|
if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->orderIndices, layoutBinding)) {
|
||||||
|
resolvedOrderView = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatViewDataBuffer && workingSet != nullptr) {
|
||||||
|
if (const RHI::DescriptorSetLayoutBinding* layoutBinding =
|
||||||
|
FindSetLayoutBinding(setLayout, passLayout.gaussianSplatViewDataBuffer.binding)) {
|
||||||
|
if (RHI::RHIResourceView* view = ResolveWorkingSetView(workingSet->viewData, layoutBinding)) {
|
||||||
|
resolvedViewDataView = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0u;
|
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0u;
|
||||||
if (setLayout.usesMaterial) {
|
if (setLayout.usesMaterial) {
|
||||||
if (!passLayout.material.IsValid() ||
|
if (!passLayout.material.IsValid() ||
|
||||||
@@ -516,6 +787,34 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatSortDistanceBuffer) {
|
||||||
|
if (resolvedSortDistanceView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatSortDistanceBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatSortDistanceBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.sortDistanceView != resolvedSortDistanceView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatSortDistanceBuffer.binding,
|
||||||
|
resolvedSortDistanceView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatOrderBuffer) {
|
||||||
|
if (resolvedOrderView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatOrderBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatOrderBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.orderView != resolvedOrderView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatOrderBuffer.binding,
|
||||||
|
resolvedOrderView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (setLayout.usesGaussianSplatOtherBuffer) {
|
if (setLayout.usesGaussianSplatOtherBuffer) {
|
||||||
if (cachedGaussianSplat.other.shaderResourceView == nullptr ||
|
if (cachedGaussianSplat.other.shaderResourceView == nullptr ||
|
||||||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||||
@@ -558,11 +857,28 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setLayout.usesGaussianSplatViewDataBuffer) {
|
||||||
|
if (resolvedViewDataView == nullptr ||
|
||||||
|
!passLayout.gaussianSplatViewDataBuffer.IsValid() ||
|
||||||
|
passLayout.gaussianSplatViewDataBuffer.set != setIndex) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedDescriptorSet.viewDataView != resolvedViewDataView) {
|
||||||
|
cachedDescriptorSet.descriptorSet.set->Update(
|
||||||
|
passLayout.gaussianSplatViewDataBuffer.binding,
|
||||||
|
resolvedViewDataView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cachedDescriptorSet.materialVersion = materialVersion;
|
cachedDescriptorSet.materialVersion = materialVersion;
|
||||||
|
cachedDescriptorSet.sortDistanceView = resolvedSortDistanceView;
|
||||||
|
cachedDescriptorSet.orderView = resolvedOrderView;
|
||||||
cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView;
|
cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView;
|
||||||
cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView;
|
cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView;
|
||||||
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
|
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
|
||||||
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
|
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
|
||||||
|
cachedDescriptorSet.viewDataView = resolvedViewDataView;
|
||||||
return &cachedDescriptorSet;
|
return &cachedDescriptorSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,10 +908,169 @@ void BuiltinGaussianSplatPass::DestroyPassResourceLayout(PassResourceLayout& pas
|
|||||||
passLayout.descriptorSetCount = 0;
|
passLayout.descriptorSetCount = 0;
|
||||||
passLayout.perObject = {};
|
passLayout.perObject = {};
|
||||||
passLayout.material = {};
|
passLayout.material = {};
|
||||||
|
passLayout.gaussianSplatSortDistanceBuffer = {};
|
||||||
|
passLayout.gaussianSplatOrderBuffer = {};
|
||||||
passLayout.gaussianSplatPositionBuffer = {};
|
passLayout.gaussianSplatPositionBuffer = {};
|
||||||
passLayout.gaussianSplatOtherBuffer = {};
|
passLayout.gaussianSplatOtherBuffer = {};
|
||||||
passLayout.gaussianSplatColorBuffer = {};
|
passLayout.gaussianSplatColorBuffer = {};
|
||||||
passLayout.gaussianSplatSHBuffer = {};
|
passLayout.gaussianSplatSHBuffer = {};
|
||||||
|
passLayout.gaussianSplatViewDataBuffer = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuiltinGaussianSplatPass::PrepareVisibleGaussianSplat(
|
||||||
|
const RenderContext& context,
|
||||||
|
const RenderSceneData& sceneData,
|
||||||
|
const VisibleGaussianSplatItem& visibleGaussianSplat) {
|
||||||
|
if (visibleGaussianSplat.gameObject == nullptr ||
|
||||||
|
visibleGaussianSplat.gaussianSplat == nullptr ||
|
||||||
|
!visibleGaussianSplat.gaussianSplat->IsValid() ||
|
||||||
|
m_passResources == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
|
||||||
|
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
|
||||||
|
if (cachedGaussianSplat == nullptr || cachedGaussianSplat->positions.shaderResourceView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
||||||
|
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
||||||
|
workingSet == nullptr ||
|
||||||
|
workingSet->sortDistances.unorderedAccessView == nullptr ||
|
||||||
|
workingSet->orderIndices.unorderedAccessView == nullptr ||
|
||||||
|
workingSet->orderIndices.shaderResourceView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResolvedShaderPass resolvedShaderPass = ResolvePrepareOrderShaderPass(sceneData);
|
||||||
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassLayoutKey passLayoutKey = {};
|
||||||
|
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||||
|
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||||
|
|
||||||
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||||
|
context,
|
||||||
|
resolvedShaderPass,
|
||||||
|
PassLayoutUsage::PrepareOrder);
|
||||||
|
RHI::RHIPipelineState* pipelineState = GetOrCreateComputePipelineState(context, sceneData);
|
||||||
|
if (passLayout == nullptr || pipelineState == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHICommandList* commandList = context.commandList;
|
||||||
|
|
||||||
|
if (workingSet->sortDistances.currentState != RHI::ResourceStates::UnorderedAccess) {
|
||||||
|
commandList->TransitionBarrier(
|
||||||
|
workingSet->sortDistances.unorderedAccessView,
|
||||||
|
workingSet->sortDistances.currentState,
|
||||||
|
RHI::ResourceStates::UnorderedAccess);
|
||||||
|
workingSet->sortDistances.currentState = RHI::ResourceStates::UnorderedAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workingSet->orderIndices.currentState != RHI::ResourceStates::UnorderedAccess) {
|
||||||
|
commandList->TransitionBarrier(
|
||||||
|
workingSet->orderIndices.unorderedAccessView,
|
||||||
|
workingSet->orderIndices.currentState,
|
||||||
|
RHI::ResourceStates::UnorderedAccess);
|
||||||
|
workingSet->orderIndices.currentState = RHI::ResourceStates::UnorderedAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
commandList->SetPipelineState(pipelineState);
|
||||||
|
|
||||||
|
const PerObjectConstants perObjectConstants = {
|
||||||
|
sceneData.cameraData.projection,
|
||||||
|
sceneData.cameraData.view,
|
||||||
|
visibleGaussianSplat.localToWorld.Transpose(),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldRight, 0.0f),
|
||||||
|
Math::Vector4(sceneData.cameraData.worldUp, 0.0f)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (passLayout->descriptorSetCount > 0u) {
|
||||||
|
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
|
||||||
|
for (Core::uint32 descriptorOffset = 0u; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
|
||||||
|
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
|
||||||
|
if (setIndex >= passLayout->setLayouts.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||||
|
if (setLayout.layout.bindingCount == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(setLayout.usesPerObject ||
|
||||||
|
setLayout.usesGaussianSplatSortDistanceBuffer ||
|
||||||
|
setLayout.usesGaussianSplatOrderBuffer ||
|
||||||
|
setLayout.usesGaussianSplatPositionBuffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::uint64 objectId =
|
||||||
|
setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u;
|
||||||
|
const Resources::GaussianSplat* gaussianSplatKey =
|
||||||
|
(setLayout.usesGaussianSplatSortDistanceBuffer ||
|
||||||
|
setLayout.usesGaussianSplatOrderBuffer ||
|
||||||
|
setLayout.usesGaussianSplatPositionBuffer)
|
||||||
|
? visibleGaussianSplat.gaussianSplat
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||||
|
passLayoutKey,
|
||||||
|
*passLayout,
|
||||||
|
setLayout,
|
||||||
|
setIndex,
|
||||||
|
objectId,
|
||||||
|
visibleGaussianSplat.gaussianSplatRenderer,
|
||||||
|
nullptr,
|
||||||
|
gaussianSplatKey,
|
||||||
|
MaterialConstantPayloadView(),
|
||||||
|
*cachedGaussianSplat,
|
||||||
|
workingSet->sortDistances.unorderedAccessView,
|
||||||
|
workingSet->orderIndices.unorderedAccessView,
|
||||||
|
workingSet->viewData.unorderedAccessView);
|
||||||
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHI::RHIDescriptorSet* descriptorSet = cachedDescriptorSet->descriptorSet.set;
|
||||||
|
if (setLayout.usesPerObject) {
|
||||||
|
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSet->WriteConstant(
|
||||||
|
passLayout->perObject.binding,
|
||||||
|
&perObjectConstants,
|
||||||
|
sizeof(perObjectConstants));
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSets[descriptorOffset] = descriptorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindDescriptorSetRanges(
|
||||||
|
passLayout->firstDescriptorSet,
|
||||||
|
descriptorSets,
|
||||||
|
[commandList, passLayout](Core::uint32 firstSet, Core::uint32 count, RHI::RHIDescriptorSet** sets) {
|
||||||
|
commandList->SetComputeDescriptorSets(
|
||||||
|
firstSet,
|
||||||
|
count,
|
||||||
|
sets,
|
||||||
|
passLayout->pipelineLayout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
commandList->Dispatch((cachedGaussianSplat->splatCount + 63u) / 64u, 1u, 1u);
|
||||||
|
|
||||||
|
commandList->TransitionBarrier(
|
||||||
|
workingSet->orderIndices.shaderResourceView,
|
||||||
|
RHI::ResourceStates::UnorderedAccess,
|
||||||
|
RHI::ResourceStates::NonPixelShaderResource);
|
||||||
|
workingSet->orderIndices.currentState = RHI::ResourceStates::NonPixelShaderResource;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||||
@@ -618,6 +1093,17 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_passResources == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::BuiltinGaussianSplatPassResources::WorkingSet* workingSet = nullptr;
|
||||||
|
if (!m_passResources->EnsureWorkingSet(m_device, visibleGaussianSplat, workingSet) ||
|
||||||
|
workingSet == nullptr ||
|
||||||
|
workingSet->orderIndices.shaderResourceView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat);
|
const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat);
|
||||||
if (material == nullptr) {
|
if (material == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -632,7 +1118,10 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||||
|
|
||||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||||
|
context,
|
||||||
|
resolvedShaderPass,
|
||||||
|
PassLayoutUsage::Draw);
|
||||||
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
|
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
|
||||||
if (passLayout == nullptr || pipelineState == nullptr) {
|
if (passLayout == nullptr || pipelineState == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -663,8 +1152,13 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||||
|
if (setLayout.layout.bindingCount == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(setLayout.usesPerObject ||
|
if (!(setLayout.usesPerObject ||
|
||||||
setLayout.usesMaterial ||
|
setLayout.usesMaterial ||
|
||||||
|
setLayout.usesGaussianSplatOrderBuffer ||
|
||||||
setLayout.usesGaussianSplatPositionBuffer ||
|
setLayout.usesGaussianSplatPositionBuffer ||
|
||||||
setLayout.usesGaussianSplatOtherBuffer ||
|
setLayout.usesGaussianSplatOtherBuffer ||
|
||||||
setLayout.usesGaussianSplatColorBuffer ||
|
setLayout.usesGaussianSplatColorBuffer ||
|
||||||
@@ -677,7 +1171,8 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
const Resources::Material* materialKey =
|
const Resources::Material* materialKey =
|
||||||
setLayout.usesMaterial ? material : nullptr;
|
setLayout.usesMaterial ? material : nullptr;
|
||||||
const Resources::GaussianSplat* gaussianSplatKey =
|
const Resources::GaussianSplat* gaussianSplatKey =
|
||||||
(setLayout.usesGaussianSplatPositionBuffer ||
|
(setLayout.usesGaussianSplatOrderBuffer ||
|
||||||
|
setLayout.usesGaussianSplatPositionBuffer ||
|
||||||
setLayout.usesGaussianSplatOtherBuffer ||
|
setLayout.usesGaussianSplatOtherBuffer ||
|
||||||
setLayout.usesGaussianSplatColorBuffer ||
|
setLayout.usesGaussianSplatColorBuffer ||
|
||||||
setLayout.usesGaussianSplatSHBuffer)
|
setLayout.usesGaussianSplatSHBuffer)
|
||||||
@@ -690,10 +1185,14 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
setLayout,
|
setLayout,
|
||||||
setIndex,
|
setIndex,
|
||||||
objectId,
|
objectId,
|
||||||
|
visibleGaussianSplat.gaussianSplatRenderer,
|
||||||
materialKey,
|
materialKey,
|
||||||
gaussianSplatKey,
|
gaussianSplatKey,
|
||||||
materialConstants,
|
materialConstants,
|
||||||
*cachedGaussianSplat);
|
*cachedGaussianSplat,
|
||||||
|
nullptr,
|
||||||
|
workingSet->orderIndices.shaderResourceView,
|
||||||
|
workingSet->viewData.shaderResourceView);
|
||||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -713,11 +1212,16 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
|||||||
descriptorSets[descriptorOffset] = descriptorSet;
|
descriptorSets[descriptorOffset] = descriptorSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList->SetGraphicsDescriptorSets(
|
BindDescriptorSetRanges(
|
||||||
passLayout->firstDescriptorSet,
|
passLayout->firstDescriptorSet,
|
||||||
passLayout->descriptorSetCount,
|
descriptorSets,
|
||||||
descriptorSets.data(),
|
[commandList, passLayout](Core::uint32 firstSet, Core::uint32 count, RHI::RHIDescriptorSet** sets) {
|
||||||
|
commandList->SetGraphicsDescriptorSets(
|
||||||
|
firstSet,
|
||||||
|
count,
|
||||||
|
sets,
|
||||||
passLayout->pipelineLayout);
|
passLayout->pipelineLayout);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList);
|
ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Resources {
|
namespace Resources {
|
||||||
@@ -64,7 +65,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
|||||||
|
|
||||||
const std::string magic(fileHeader.magic, fileHeader.magic + 7);
|
const std::string magic(fileHeader.magic, fileHeader.magic + 7);
|
||||||
const bool isCurrentSchema =
|
const bool isCurrentSchema =
|
||||||
magic == "XCSHD05" && fileHeader.schemaVersion == kShaderArtifactSchemaVersion;
|
magic == "XCSHD06" && fileHeader.schemaVersion == kShaderArtifactSchemaVersion;
|
||||||
if (!isCurrentSchema) {
|
if (!isCurrentSchema) {
|
||||||
return LoadResult("Invalid shader artifact header: " + path);
|
return LoadResult("Invalid shader artifact header: " + path);
|
||||||
}
|
}
|
||||||
@@ -187,6 +188,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
|||||||
ShaderStageVariant variant = {};
|
ShaderStageVariant variant = {};
|
||||||
Core::uint64 compiledBinarySize = 0;
|
Core::uint64 compiledBinarySize = 0;
|
||||||
Core::uint32 keywordCount = 0;
|
Core::uint32 keywordCount = 0;
|
||||||
|
Core::uint32 backendCompiledBinaryCount = 0;
|
||||||
ShaderVariantArtifactHeader variantHeader = {};
|
ShaderVariantArtifactHeader variantHeader = {};
|
||||||
if (!ReadShaderArtifactValue(data, offset, variantHeader)) {
|
if (!ReadShaderArtifactValue(data, offset, variantHeader)) {
|
||||||
return LoadResult("Failed to read shader artifact variants: " + path);
|
return LoadResult("Failed to read shader artifact variants: " + path);
|
||||||
@@ -197,6 +199,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
|||||||
variant.backend = static_cast<ShaderBackend>(variantHeader.backend);
|
variant.backend = static_cast<ShaderBackend>(variantHeader.backend);
|
||||||
keywordCount = variantHeader.keywordCount;
|
keywordCount = variantHeader.keywordCount;
|
||||||
compiledBinarySize = variantHeader.compiledBinarySize;
|
compiledBinarySize = variantHeader.compiledBinarySize;
|
||||||
|
backendCompiledBinaryCount = variantHeader.backendCompiledBinaryCount;
|
||||||
|
|
||||||
if (!ReadShaderArtifactString(data, offset, variant.entryPoint) ||
|
if (!ReadShaderArtifactString(data, offset, variant.entryPoint) ||
|
||||||
!ReadShaderArtifactString(data, offset, variant.profile) ||
|
!ReadShaderArtifactString(data, offset, variant.profile) ||
|
||||||
@@ -227,6 +230,32 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
|||||||
offset += static_cast<size_t>(compiledBinarySize);
|
offset += static_cast<size_t>(compiledBinarySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (Core::uint32 binaryIndex = 0;
|
||||||
|
binaryIndex < backendCompiledBinaryCount;
|
||||||
|
++binaryIndex) {
|
||||||
|
ShaderBackendCompiledBinaryArtifactHeader binaryHeader = {};
|
||||||
|
if (!ReadShaderArtifactValue(data, offset, binaryHeader)) {
|
||||||
|
return LoadResult("Failed to read shader artifact backend binaries: " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + binaryHeader.compiledBinarySize > data.Size()) {
|
||||||
|
return LoadResult(
|
||||||
|
"Shader artifact backend binary payload is truncated: " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderBackendCompiledBinary record = {};
|
||||||
|
record.backend = static_cast<ShaderBackend>(binaryHeader.backend);
|
||||||
|
if (binaryHeader.compiledBinarySize > 0) {
|
||||||
|
record.payload.Resize(static_cast<size_t>(binaryHeader.compiledBinarySize));
|
||||||
|
std::memcpy(
|
||||||
|
record.payload.Data(),
|
||||||
|
data.Data() + offset,
|
||||||
|
static_cast<size_t>(binaryHeader.compiledBinarySize));
|
||||||
|
offset += static_cast<size_t>(binaryHeader.compiledBinarySize);
|
||||||
|
}
|
||||||
|
variant.backendCompiledBinaries.PushBack(std::move(record));
|
||||||
|
}
|
||||||
|
|
||||||
shader->AddPassVariant(passName, variant);
|
shader->AddPassVariant(passName, variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include <XCEngine/Resources/Shader/Shader.h>
|
#include <XCEngine/Resources/Shader/Shader.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Resources {
|
namespace Resources {
|
||||||
|
|
||||||
@@ -40,6 +42,62 @@ Shader::Shader() = default;
|
|||||||
|
|
||||||
Shader::~Shader() = default;
|
Shader::~Shader() = default;
|
||||||
|
|
||||||
|
const Containers::Array<Core::uint8>* ShaderStageVariant::GetCompiledBinaryForBackend(
|
||||||
|
ShaderBackend targetBackend) const {
|
||||||
|
if ((targetBackend == backend || targetBackend == ShaderBackend::Generic) &&
|
||||||
|
!compiledBinary.Empty()) {
|
||||||
|
return &compiledBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ShaderBackendCompiledBinary& record : backendCompiledBinaries) {
|
||||||
|
if (record.backend == targetBackend && !record.payload.Empty()) {
|
||||||
|
return &record.payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderStageVariant::SetCompiledBinaryForBackend(
|
||||||
|
ShaderBackend targetBackend,
|
||||||
|
const Containers::Array<Core::uint8>& binary) {
|
||||||
|
if (targetBackend == backend || targetBackend == ShaderBackend::Generic) {
|
||||||
|
compiledBinary = binary;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderBackendCompiledBinary& record : backendCompiledBinaries) {
|
||||||
|
if (record.backend == targetBackend) {
|
||||||
|
record.payload = binary;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderBackendCompiledBinary& record = backendCompiledBinaries.EmplaceBack();
|
||||||
|
record.backend = targetBackend;
|
||||||
|
record.payload = binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderStageVariant::SetCompiledBinaryForBackend(
|
||||||
|
ShaderBackend targetBackend,
|
||||||
|
Containers::Array<Core::uint8>&& binary) {
|
||||||
|
if (targetBackend == backend || targetBackend == ShaderBackend::Generic) {
|
||||||
|
compiledBinary = std::move(binary);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderBackendCompiledBinary& record : backendCompiledBinaries) {
|
||||||
|
if (record.backend == targetBackend) {
|
||||||
|
record.payload = std::move(binary);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderBackendCompiledBinary& record = backendCompiledBinaries.EmplaceBack();
|
||||||
|
record.backend = targetBackend;
|
||||||
|
record.payload = std::move(binary);
|
||||||
|
}
|
||||||
|
|
||||||
void Shader::Release() {
|
void Shader::Release() {
|
||||||
m_uniforms.Clear();
|
m_uniforms.Clear();
|
||||||
m_attributes.Clear();
|
m_attributes.Clear();
|
||||||
|
|||||||
@@ -885,7 +885,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
|
|||||||
|
|
||||||
const ShaderPass* pass = shader->FindPass("GaussianSplat");
|
const ShaderPass* pass = shader->FindPass("GaussianSplat");
|
||||||
ASSERT_NE(pass, nullptr);
|
ASSERT_NE(pass, nullptr);
|
||||||
EXPECT_EQ(pass->resources.Size(), 5u);
|
EXPECT_EQ(pass->resources.Size(), 6u);
|
||||||
EXPECT_TRUE(pass->hasFixedFunctionState);
|
EXPECT_TRUE(pass->hasFixedFunctionState);
|
||||||
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::None);
|
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::None);
|
||||||
EXPECT_FALSE(pass->fixedFunctionState.depthWriteEnable);
|
EXPECT_FALSE(pass->fixedFunctionState.depthWriteEnable);
|
||||||
@@ -900,12 +900,22 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
|
|||||||
ASSERT_NE(opacityScale, nullptr);
|
ASSERT_NE(opacityScale, nullptr);
|
||||||
EXPECT_EQ(opacityScale->type, ShaderPropertyType::Float);
|
EXPECT_EQ(opacityScale->type, ShaderPropertyType::Float);
|
||||||
|
|
||||||
|
const ShaderResourceBindingDesc* order =
|
||||||
|
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatOrderBuffer");
|
||||||
|
ASSERT_NE(order, nullptr);
|
||||||
|
EXPECT_EQ(order->type, ShaderResourceType::StructuredBuffer);
|
||||||
|
EXPECT_EQ(order->set, 2u);
|
||||||
|
EXPECT_EQ(order->binding, 0u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
ResolveBuiltinPassResourceSemantic(*order),
|
||||||
|
BuiltinPassResourceSemantic::GaussianSplatOrderBuffer);
|
||||||
|
|
||||||
const ShaderResourceBindingDesc* positions =
|
const ShaderResourceBindingDesc* positions =
|
||||||
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatPositions");
|
shader->FindPassResourceBinding("GaussianSplat", "GaussianSplatPositions");
|
||||||
ASSERT_NE(positions, nullptr);
|
ASSERT_NE(positions, nullptr);
|
||||||
EXPECT_EQ(positions->type, ShaderResourceType::StructuredBuffer);
|
EXPECT_EQ(positions->type, ShaderResourceType::StructuredBuffer);
|
||||||
EXPECT_EQ(positions->set, 2u);
|
EXPECT_EQ(positions->set, 2u);
|
||||||
EXPECT_EQ(positions->binding, 0u);
|
EXPECT_EQ(positions->binding, 1u);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
ResolveBuiltinPassResourceSemantic(*positions),
|
ResolveBuiltinPassResourceSemantic(*positions),
|
||||||
BuiltinPassResourceSemantic::GaussianSplatPositionBuffer);
|
BuiltinPassResourceSemantic::GaussianSplatPositionBuffer);
|
||||||
@@ -915,7 +925,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
|
|||||||
ASSERT_NE(other, nullptr);
|
ASSERT_NE(other, nullptr);
|
||||||
EXPECT_EQ(other->type, ShaderResourceType::StructuredBuffer);
|
EXPECT_EQ(other->type, ShaderResourceType::StructuredBuffer);
|
||||||
EXPECT_EQ(other->set, 2u);
|
EXPECT_EQ(other->set, 2u);
|
||||||
EXPECT_EQ(other->binding, 1u);
|
EXPECT_EQ(other->binding, 2u);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
ResolveBuiltinPassResourceSemantic(*other),
|
ResolveBuiltinPassResourceSemantic(*other),
|
||||||
BuiltinPassResourceSemantic::GaussianSplatOtherBuffer);
|
BuiltinPassResourceSemantic::GaussianSplatOtherBuffer);
|
||||||
@@ -925,7 +935,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinGaussianSplatShaderUsesAuthoringContrac
|
|||||||
ASSERT_NE(color, nullptr);
|
ASSERT_NE(color, nullptr);
|
||||||
EXPECT_EQ(color->type, ShaderResourceType::StructuredBuffer);
|
EXPECT_EQ(color->type, ShaderResourceType::StructuredBuffer);
|
||||||
EXPECT_EQ(color->set, 2u);
|
EXPECT_EQ(color->set, 2u);
|
||||||
EXPECT_EQ(color->binding, 2u);
|
EXPECT_EQ(color->binding, 3u);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
ResolveBuiltinPassResourceSemantic(*color),
|
ResolveBuiltinPassResourceSemantic(*color),
|
||||||
BuiltinPassResourceSemantic::GaussianSplatColorBuffer);
|
BuiltinPassResourceSemantic::GaussianSplatColorBuffer);
|
||||||
@@ -948,21 +958,24 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
BuiltinPassResourceBindingPlan plan = {};
|
BuiltinPassResourceBindingPlan plan = {};
|
||||||
String error;
|
String error;
|
||||||
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
|
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
|
||||||
ASSERT_EQ(plan.bindings.Size(), 5u);
|
ASSERT_EQ(plan.bindings.Size(), 6u);
|
||||||
EXPECT_TRUE(plan.perObject.IsValid());
|
EXPECT_TRUE(plan.perObject.IsValid());
|
||||||
EXPECT_TRUE(plan.material.IsValid());
|
EXPECT_TRUE(plan.material.IsValid());
|
||||||
|
EXPECT_TRUE(plan.gaussianSplatOrderBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatPositionBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatPositionBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatOtherBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatOtherBuffer.IsValid());
|
||||||
EXPECT_TRUE(plan.gaussianSplatColorBuffer.IsValid());
|
EXPECT_TRUE(plan.gaussianSplatColorBuffer.IsValid());
|
||||||
EXPECT_FALSE(plan.gaussianSplatSHBuffer.IsValid());
|
EXPECT_FALSE(plan.gaussianSplatSHBuffer.IsValid());
|
||||||
EXPECT_EQ(plan.perObject.set, 0u);
|
EXPECT_EQ(plan.perObject.set, 0u);
|
||||||
EXPECT_EQ(plan.material.set, 1u);
|
EXPECT_EQ(plan.material.set, 1u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatOrderBuffer.set, 2u);
|
||||||
|
EXPECT_EQ(plan.gaussianSplatOrderBuffer.binding, 0u);
|
||||||
EXPECT_EQ(plan.gaussianSplatPositionBuffer.set, 2u);
|
EXPECT_EQ(plan.gaussianSplatPositionBuffer.set, 2u);
|
||||||
EXPECT_EQ(plan.gaussianSplatPositionBuffer.binding, 0u);
|
EXPECT_EQ(plan.gaussianSplatPositionBuffer.binding, 1u);
|
||||||
EXPECT_EQ(plan.gaussianSplatOtherBuffer.set, 2u);
|
EXPECT_EQ(plan.gaussianSplatOtherBuffer.set, 2u);
|
||||||
EXPECT_EQ(plan.gaussianSplatOtherBuffer.binding, 1u);
|
EXPECT_EQ(plan.gaussianSplatOtherBuffer.binding, 2u);
|
||||||
EXPECT_EQ(plan.gaussianSplatColorBuffer.set, 2u);
|
EXPECT_EQ(plan.gaussianSplatColorBuffer.set, 2u);
|
||||||
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 2u);
|
EXPECT_EQ(plan.gaussianSplatColorBuffer.binding, 3u);
|
||||||
EXPECT_EQ(plan.firstDescriptorSet, 0u);
|
EXPECT_EQ(plan.firstDescriptorSet, 0u);
|
||||||
EXPECT_EQ(plan.descriptorSetCount, 3u);
|
EXPECT_EQ(plan.descriptorSetCount, 3u);
|
||||||
|
|
||||||
@@ -971,10 +984,11 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
ASSERT_EQ(setLayouts.size(), 3u);
|
ASSERT_EQ(setLayouts.size(), 3u);
|
||||||
EXPECT_TRUE(setLayouts[0].usesPerObject);
|
EXPECT_TRUE(setLayouts[0].usesPerObject);
|
||||||
EXPECT_TRUE(setLayouts[1].usesMaterial);
|
EXPECT_TRUE(setLayouts[1].usesMaterial);
|
||||||
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOrderBuffer);
|
||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatPositionBuffer);
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatPositionBuffer);
|
||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOtherBuffer);
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatOtherBuffer);
|
||||||
EXPECT_TRUE(setLayouts[2].usesGaussianSplatColorBuffer);
|
EXPECT_TRUE(setLayouts[2].usesGaussianSplatColorBuffer);
|
||||||
ASSERT_EQ(setLayouts[2].bindings.size(), 3u);
|
ASSERT_EQ(setLayouts[2].bindings.size(), 4u);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
||||||
DescriptorType::SRV);
|
DescriptorType::SRV);
|
||||||
@@ -984,6 +998,9 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
static_cast<DescriptorType>(setLayouts[2].bindings[2].type),
|
static_cast<DescriptorType>(setLayouts[2].bindings[2].type),
|
||||||
DescriptorType::SRV);
|
DescriptorType::SRV);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[2].bindings[3].type),
|
||||||
|
DescriptorType::SRV);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
setLayouts[2].bindings[0].resourceDimension,
|
setLayouts[2].bindings[0].resourceDimension,
|
||||||
ResourceViewDimension::StructuredBuffer);
|
ResourceViewDimension::StructuredBuffer);
|
||||||
@@ -993,6 +1010,9 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
|||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
setLayouts[2].bindings[2].resourceDimension,
|
setLayouts[2].bindings[2].resourceDimension,
|
||||||
ResourceViewDimension::StructuredBuffer);
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
EXPECT_EQ(
|
||||||
|
setLayouts[2].bindings[3].resourceDimension,
|
||||||
|
ResourceViewDimension::StructuredBuffer);
|
||||||
|
|
||||||
delete shader;
|
delete shader;
|
||||||
}
|
}
|
||||||
@@ -1034,16 +1054,20 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGauss
|
|||||||
"register(b1)"));
|
"register(b1)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
d3d12Source,
|
d3d12Source,
|
||||||
"StructuredBuffer<float3> GaussianSplatPositions",
|
"StructuredBuffer<uint> GaussianSplatOrderBuffer",
|
||||||
"register(t0)"));
|
"register(t0)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
d3d12Source,
|
d3d12Source,
|
||||||
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
|
"StructuredBuffer<float3> GaussianSplatPositions",
|
||||||
"register(t1)"));
|
"register(t1)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
d3d12Source,
|
d3d12Source,
|
||||||
"StructuredBuffer<float4> GaussianSplatColor",
|
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
|
||||||
"register(t2)"));
|
"register(t2)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
d3d12Source,
|
||||||
|
"StructuredBuffer<float4> GaussianSplatColor",
|
||||||
|
"register(t3)"));
|
||||||
EXPECT_EQ(d3d12Source.find("space0"), std::string::npos);
|
EXPECT_EQ(d3d12Source.find("space0"), std::string::npos);
|
||||||
EXPECT_EQ(d3d12Source.find("space1"), std::string::npos);
|
EXPECT_EQ(d3d12Source.find("space1"), std::string::npos);
|
||||||
EXPECT_EQ(d3d12Source.find("space2"), std::string::npos);
|
EXPECT_EQ(d3d12Source.find("space2"), std::string::npos);
|
||||||
@@ -1073,16 +1097,20 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringGauss
|
|||||||
"register(b0, space1)"));
|
"register(b0, space1)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
vulkanSource,
|
vulkanSource,
|
||||||
"StructuredBuffer<float3> GaussianSplatPositions",
|
"StructuredBuffer<uint> GaussianSplatOrderBuffer",
|
||||||
"register(t0, space2)"));
|
"register(t0, space2)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
vulkanSource,
|
vulkanSource,
|
||||||
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
|
"StructuredBuffer<float3> GaussianSplatPositions",
|
||||||
"register(t1, space2)"));
|
"register(t1, space2)"));
|
||||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
vulkanSource,
|
vulkanSource,
|
||||||
"StructuredBuffer<float4> GaussianSplatColor",
|
"StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther",
|
||||||
"register(t2, space2)"));
|
"register(t2, space2)"));
|
||||||
|
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||||
|
vulkanSource,
|
||||||
|
"StructuredBuffer<float4> GaussianSplatColor",
|
||||||
|
"register(t3, space2)"));
|
||||||
|
|
||||||
delete shader;
|
delete shader;
|
||||||
}
|
}
|
||||||
@@ -2410,6 +2438,57 @@ TEST(BuiltinPassLayout_Test, AcceptsRuntimeMaterialRawUavBufferBindings) {
|
|||||||
EXPECT_TRUE(setLayouts[4].usesMaterialBuffers);
|
EXPECT_TRUE(setLayouts[4].usesMaterialBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BuiltinPassLayout_Test, AcceptsRuntimeMaterialTextureBindingsWithoutBuiltinSemanticMetadata) {
|
||||||
|
Array<ShaderResourceBindingDesc> bindings;
|
||||||
|
|
||||||
|
ShaderResourceBindingDesc perObjectBinding = {};
|
||||||
|
perObjectBinding.name = "PerObjectConstants";
|
||||||
|
perObjectBinding.type = ShaderResourceType::ConstantBuffer;
|
||||||
|
perObjectBinding.set = 0u;
|
||||||
|
perObjectBinding.binding = 0u;
|
||||||
|
perObjectBinding.semantic = "PerObject";
|
||||||
|
bindings.PushBack(perObjectBinding);
|
||||||
|
|
||||||
|
ShaderResourceBindingDesc materialBinding = {};
|
||||||
|
materialBinding.name = "MaterialConstants";
|
||||||
|
materialBinding.type = ShaderResourceType::ConstantBuffer;
|
||||||
|
materialBinding.set = 1u;
|
||||||
|
materialBinding.binding = 0u;
|
||||||
|
materialBinding.semantic = "Material";
|
||||||
|
bindings.PushBack(materialBinding);
|
||||||
|
|
||||||
|
ShaderResourceBindingDesc textureBinding = {};
|
||||||
|
textureBinding.name = "_LightMap";
|
||||||
|
textureBinding.type = ShaderResourceType::Texture2D;
|
||||||
|
textureBinding.set = 4u;
|
||||||
|
textureBinding.binding = 1u;
|
||||||
|
bindings.PushBack(textureBinding);
|
||||||
|
|
||||||
|
BuiltinPassResourceBindingPlan plan = {};
|
||||||
|
String error;
|
||||||
|
ASSERT_TRUE(TryBuildBuiltinPassResourceBindingPlan(bindings, plan, &error)) << error.CStr();
|
||||||
|
EXPECT_TRUE(plan.usesMaterialTextures);
|
||||||
|
ASSERT_EQ(plan.materialTextureBindings.Size(), 1u);
|
||||||
|
EXPECT_EQ(plan.materialTextureBindings[0].name, "_LightMap");
|
||||||
|
EXPECT_EQ(plan.materialTextureBindings[0].semantic, BuiltinPassResourceSemantic::MaterialTexture);
|
||||||
|
EXPECT_EQ(plan.materialTextureBindings[0].resourceType, ShaderResourceType::Texture2D);
|
||||||
|
EXPECT_EQ(plan.materialTextureBindings[0].location.set, 4u);
|
||||||
|
EXPECT_EQ(plan.materialTextureBindings[0].location.binding, 1u);
|
||||||
|
|
||||||
|
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||||
|
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
|
||||||
|
ASSERT_EQ(setLayouts.size(), 5u);
|
||||||
|
EXPECT_TRUE(setLayouts[4].usesTexture);
|
||||||
|
EXPECT_TRUE(setLayouts[4].usesMaterialTextures);
|
||||||
|
ASSERT_EQ(setLayouts[4].materialTextureBindings.size(), 1u);
|
||||||
|
EXPECT_EQ(setLayouts[4].materialTextureBindings[0].name, "_LightMap");
|
||||||
|
ASSERT_EQ(setLayouts[4].bindings.size(), 1u);
|
||||||
|
EXPECT_EQ(
|
||||||
|
static_cast<DescriptorType>(setLayouts[4].bindings[0].type),
|
||||||
|
DescriptorType::SRV);
|
||||||
|
EXPECT_EQ(setLayouts[4].bindings[0].resourceDimension, ResourceViewDimension::Texture2D);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(BuiltinDepthOnlyPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
TEST(BuiltinDepthOnlyPass_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) {
|
||||||
const InputLayoutDesc inputLayout = BuiltinDepthOnlyPass::BuildInputLayout();
|
const InputLayoutDesc inputLayout = BuiltinDepthOnlyPass::BuildInputLayout();
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include <XCEngine/Resources/Shader/Shader.h>
|
#include <XCEngine/Resources/Shader/Shader.h>
|
||||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||||
#include <XCEngine/Core/Containers/Array.h>
|
#include <XCEngine/Core/Containers/Array.h>
|
||||||
|
#include <XCEngine/RHI/RHITypes.h>
|
||||||
|
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||||
|
|
||||||
using namespace XCEngine::Resources;
|
using namespace XCEngine::Resources;
|
||||||
using namespace XCEngine::Containers;
|
using namespace XCEngine::Containers;
|
||||||
@@ -77,6 +79,69 @@ TEST(Shader, AddGetAttributes) {
|
|||||||
EXPECT_EQ(attributes[0].name, "aPosition");
|
EXPECT_EQ(attributes[0].name, "aPosition");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Shader, StoresBackendCompiledBinariesPerBackend) {
|
||||||
|
ShaderStageVariant variant = {};
|
||||||
|
variant.stage = ShaderType::Fragment;
|
||||||
|
variant.language = ShaderLanguage::HLSL;
|
||||||
|
variant.backend = ShaderBackend::Generic;
|
||||||
|
|
||||||
|
Array<XCEngine::Core::uint8> d3d12Payload;
|
||||||
|
d3d12Payload.PushBack(0x01);
|
||||||
|
d3d12Payload.PushBack(0x02);
|
||||||
|
variant.SetCompiledBinaryForBackend(ShaderBackend::D3D12, d3d12Payload);
|
||||||
|
|
||||||
|
Array<XCEngine::Core::uint8> vulkanPayload;
|
||||||
|
vulkanPayload.PushBack(0x03);
|
||||||
|
vulkanPayload.PushBack(0x04);
|
||||||
|
vulkanPayload.PushBack(0x05);
|
||||||
|
variant.SetCompiledBinaryForBackend(ShaderBackend::Vulkan, vulkanPayload);
|
||||||
|
|
||||||
|
const Array<XCEngine::Core::uint8>* d3d12Binary =
|
||||||
|
variant.GetCompiledBinaryForBackend(ShaderBackend::D3D12);
|
||||||
|
ASSERT_NE(d3d12Binary, nullptr);
|
||||||
|
ASSERT_EQ(d3d12Binary->Size(), 2u);
|
||||||
|
EXPECT_EQ((*d3d12Binary)[1], 0x02);
|
||||||
|
|
||||||
|
const Array<XCEngine::Core::uint8>* vulkanBinary =
|
||||||
|
variant.GetCompiledBinaryForBackend(ShaderBackend::Vulkan);
|
||||||
|
ASSERT_NE(vulkanBinary, nullptr);
|
||||||
|
ASSERT_EQ(vulkanBinary->Size(), 3u);
|
||||||
|
EXPECT_EQ((*vulkanBinary)[2], 0x05);
|
||||||
|
|
||||||
|
EXPECT_EQ(variant.GetCompiledBinaryForBackend(ShaderBackend::OpenGL), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Shader, ApplyShaderStageVariantCarriesMatchedBackendCompiledBinary) {
|
||||||
|
ShaderPass pass = {};
|
||||||
|
pass.name = "ForwardLit";
|
||||||
|
|
||||||
|
ShaderStageVariant variant = {};
|
||||||
|
variant.stage = ShaderType::Fragment;
|
||||||
|
variant.language = ShaderLanguage::HLSL;
|
||||||
|
variant.backend = ShaderBackend::Generic;
|
||||||
|
variant.entryPoint = "MainPS";
|
||||||
|
variant.profile = "ps_5_1";
|
||||||
|
variant.sourceCode = "float4 MainPS() : SV_TARGET { return 1; }";
|
||||||
|
|
||||||
|
Array<XCEngine::Core::uint8> vulkanPayload;
|
||||||
|
vulkanPayload.PushBack(0x07);
|
||||||
|
vulkanPayload.PushBack(0x08);
|
||||||
|
vulkanPayload.PushBack(0x09);
|
||||||
|
variant.SetCompiledBinaryForBackend(ShaderBackend::Vulkan, vulkanPayload);
|
||||||
|
|
||||||
|
XCEngine::RHI::ShaderCompileDesc compileDesc = {};
|
||||||
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
||||||
|
pass,
|
||||||
|
ShaderBackend::Vulkan,
|
||||||
|
variant,
|
||||||
|
compileDesc);
|
||||||
|
|
||||||
|
EXPECT_EQ(compileDesc.compiledBinaryBackend, XCEngine::RHI::ShaderBinaryBackend::Vulkan);
|
||||||
|
ASSERT_EQ(compileDesc.compiledBinary.size(), 3u);
|
||||||
|
EXPECT_EQ(compileDesc.compiledBinary[0], 0x07);
|
||||||
|
EXPECT_EQ(compileDesc.compiledBinary[2], 0x09);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Shader, FindsBackendSpecificVariantAndFallsBackToGeneric) {
|
TEST(Shader, FindsBackendSpecificVariantAndFallsBackToGeneric) {
|
||||||
Shader shader;
|
Shader shader;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user