Formalize gaussian splat prepare-order pass
This commit is contained in:
@@ -26,6 +26,7 @@ Shader "Builtin Gaussian Splat"
|
||||
float4 scaleReserved;
|
||||
};
|
||||
|
||||
StructuredBuffer<uint> GaussianSplatOrderBuffer;
|
||||
StructuredBuffer<float3> GaussianSplatPositions;
|
||||
StructuredBuffer<GaussianSplatOtherData> GaussianSplatOther;
|
||||
StructuredBuffer<float4> GaussianSplatColor;
|
||||
@@ -54,9 +55,10 @@ Shader "Builtin Gaussian Splat"
|
||||
{
|
||||
VSOutput output;
|
||||
const float2 corner = ResolveQuadCorner(vertexId);
|
||||
const float3 localCenter = GaussianSplatPositions[instanceId];
|
||||
const GaussianSplatOtherData otherData = GaussianSplatOther[instanceId];
|
||||
const float4 colorOpacity = GaussianSplatColor[instanceId];
|
||||
const uint splatIndex = GaussianSplatOrderBuffer[instanceId];
|
||||
const float3 localCenter = GaussianSplatPositions[splatIndex];
|
||||
const GaussianSplatOtherData otherData = GaussianSplatOther[splatIndex];
|
||||
const float4 colorOpacity = GaussianSplatColor[splatIndex];
|
||||
|
||||
const float3 worldCenter = mul(gModelMatrix, float4(localCenter, 1.0)).xyz;
|
||||
const float maxAxisScale =
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Resources {
|
||||
constexpr Core::uint32 kTextureArtifactSchemaVersion = 1;
|
||||
constexpr Core::uint32 kMaterialArtifactSchemaVersion = 6;
|
||||
constexpr Core::uint32 kMeshArtifactSchemaVersion = 2;
|
||||
constexpr Core::uint32 kShaderArtifactSchemaVersion = 5;
|
||||
constexpr Core::uint32 kShaderArtifactSchemaVersion = 6;
|
||||
constexpr Core::uint32 kUIDocumentArtifactSchemaVersion = 2;
|
||||
constexpr Core::uint32 kVolumeFieldArtifactSchemaVersion = 2;
|
||||
constexpr Core::uint32 kModelArtifactSchemaVersion = 1;
|
||||
@@ -104,7 +104,7 @@ struct ModelMaterialBindingArtifact {
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -138,6 +138,14 @@ struct ShaderVariantArtifactHeader {
|
||||
Core::uint32 backend = 0;
|
||||
Core::uint32 keywordCount = 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 {
|
||||
|
||||
@@ -30,6 +30,10 @@ public:
|
||||
const D3D_SHADER_MACRO* macros,
|
||||
const char* entryPoint,
|
||||
const char* target);
|
||||
bool InitializeFromBytecode(
|
||||
const void* bytecodeData,
|
||||
size_t bytecodeSize,
|
||||
const char* target = nullptr);
|
||||
void Shutdown() override;
|
||||
|
||||
const D3D12_SHADER_BYTECODE GetD3D12Bytecode() const;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class GameObject;
|
||||
class GaussianSplatRendererComponent;
|
||||
} // namespace Components
|
||||
|
||||
namespace Resources {
|
||||
@@ -31,6 +32,11 @@ class Shader;
|
||||
|
||||
namespace Rendering {
|
||||
struct VisibleGaussianSplatItem;
|
||||
namespace Passes {
|
||||
namespace Internal {
|
||||
class BuiltinGaussianSplatPassResources;
|
||||
} // namespace Internal
|
||||
} // namespace Passes
|
||||
|
||||
namespace Passes {
|
||||
|
||||
@@ -84,16 +90,20 @@ private:
|
||||
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||
PassResourceBindingLocation perObject = {};
|
||||
PassResourceBindingLocation material = {};
|
||||
PassResourceBindingLocation gaussianSplatSortDistanceBuffer = {};
|
||||
PassResourceBindingLocation gaussianSplatOrderBuffer = {};
|
||||
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
|
||||
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
|
||||
PassResourceBindingLocation gaussianSplatColorBuffer = {};
|
||||
PassResourceBindingLocation gaussianSplatSHBuffer = {};
|
||||
PassResourceBindingLocation gaussianSplatViewDataBuffer = {};
|
||||
};
|
||||
|
||||
struct DynamicDescriptorSetKey {
|
||||
PassLayoutKey passLayout = {};
|
||||
Core::uint32 setIndex = 0;
|
||||
Core::uint64 objectId = 0;
|
||||
const Components::GaussianSplatRendererComponent* gaussianSplatRenderer = nullptr;
|
||||
const Resources::Material* material = nullptr;
|
||||
const Resources::GaussianSplat* gaussianSplat = nullptr;
|
||||
|
||||
@@ -101,6 +111,7 @@ private:
|
||||
return passLayout == other.passLayout &&
|
||||
setIndex == other.setIndex &&
|
||||
objectId == other.objectId &&
|
||||
gaussianSplatRenderer == other.gaussianSplatRenderer &&
|
||||
material == other.material &&
|
||||
gaussianSplat == other.gaussianSplat;
|
||||
}
|
||||
@@ -111,6 +122,7 @@ private:
|
||||
size_t hash = PassLayoutKeyHash()(key.passLayout);
|
||||
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 ^= 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.gaussianSplat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
return hash;
|
||||
@@ -120,10 +132,13 @@ private:
|
||||
struct CachedDescriptorSet {
|
||||
OwnedDescriptorSet descriptorSet = {};
|
||||
Core::uint64 materialVersion = 0;
|
||||
RHI::RHIResourceView* sortDistanceView = nullptr;
|
||||
RHI::RHIResourceView* orderView = nullptr;
|
||||
RHI::RHIResourceView* positionsView = nullptr;
|
||||
RHI::RHIResourceView* otherView = nullptr;
|
||||
RHI::RHIResourceView* colorView = nullptr;
|
||||
RHI::RHIResourceView* shView = nullptr;
|
||||
RHI::RHIResourceView* viewDataView = nullptr;
|
||||
};
|
||||
|
||||
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 CreateResources(const RenderContext& context);
|
||||
void DestroyResources();
|
||||
@@ -180,14 +221,19 @@ private:
|
||||
ResolvedShaderPass ResolveGaussianSplatShaderPass(
|
||||
const RenderSceneData& sceneData,
|
||||
const Resources::Material* material) const;
|
||||
ResolvedShaderPass ResolvePrepareOrderShaderPass(const RenderSceneData& sceneData) const;
|
||||
PassResourceLayout* GetOrCreatePassResourceLayout(
|
||||
const RenderContext& context,
|
||||
const ResolvedShaderPass& resolvedShaderPass);
|
||||
const ResolvedShaderPass& resolvedShaderPass,
|
||||
PassLayoutUsage usage);
|
||||
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface,
|
||||
const RenderSceneData& sceneData,
|
||||
const Resources::Material* material);
|
||||
RHI::RHIPipelineState* GetOrCreateComputePipelineState(
|
||||
const RenderContext& context,
|
||||
const RenderSceneData& sceneData);
|
||||
bool CreateOwnedDescriptorSet(
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
OwnedDescriptorSet& descriptorSet);
|
||||
@@ -197,12 +243,20 @@ private:
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Components::GaussianSplatRendererComponent* gaussianSplatRenderer,
|
||||
const Resources::Material* material,
|
||||
const Resources::GaussianSplat* gaussianSplat,
|
||||
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 DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||
bool PrepareVisibleGaussianSplat(
|
||||
const RenderContext& context,
|
||||
const RenderSceneData& sceneData,
|
||||
const VisibleGaussianSplatItem& visibleGaussianSplat);
|
||||
bool DrawVisibleGaussianSplat(
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface,
|
||||
@@ -212,11 +266,14 @@ private:
|
||||
RHI::RHIDevice* m_device = nullptr;
|
||||
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
|
||||
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatShader;
|
||||
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatUtilitiesShader;
|
||||
std::unique_ptr<Resources::Material> m_builtinGaussianSplatMaterial;
|
||||
RenderResourceCache m_resourceCache;
|
||||
Internal::BuiltinGaussianSplatPassResources* m_passResources = nullptr;
|
||||
|
||||
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -87,6 +87,11 @@ struct ShaderResourceBindingDesc {
|
||||
Containers::String semantic;
|
||||
};
|
||||
|
||||
struct ShaderBackendCompiledBinary {
|
||||
ShaderBackend backend = ShaderBackend::Generic;
|
||||
Containers::Array<Core::uint8> payload;
|
||||
};
|
||||
|
||||
struct ShaderStageVariant {
|
||||
ShaderType stage = ShaderType::Fragment;
|
||||
ShaderLanguage language = ShaderLanguage::GLSL;
|
||||
@@ -96,6 +101,16 @@ struct ShaderStageVariant {
|
||||
Containers::String profile;
|
||||
Containers::String sourceCode;
|
||||
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 {
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
||||
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.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/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||
#include <XCEngine/Resources/Model/AssimpModelImporter.h>
|
||||
#include <XCEngine/Resources/Model/ModelArtifactIO.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include "Resources/GaussianSplat/Internal/GaussianSplatPlyImporter.h"
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
||||
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
||||
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <d3dcompiler.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -134,10 +144,294 @@ bool IsCurrentShaderArtifactFile(const fs::path& artifactPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::memcmp(header.magic, "XCSHD05", 7) == 0 &&
|
||||
return std::memcmp(header.magic, "XCSHD06", 7) == 0 &&
|
||||
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) {
|
||||
const auto begin = std::find_if_not(text.begin(), text.end(), [](unsigned char ch) {
|
||||
return std::isspace(ch) != 0;
|
||||
@@ -714,6 +1008,8 @@ bool WriteShaderArtifactFile(const fs::path& artifactPath, const Shader& shader)
|
||||
variantHeader.keywordCount =
|
||||
static_cast<Core::uint32>(variant.requiredKeywords.enabledKeywords.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));
|
||||
|
||||
WriteString(output, variant.entryPoint);
|
||||
@@ -727,6 +1023,17 @@ bool WriteShaderArtifactFile(const fs::path& artifactPath, const Shader& shader)
|
||||
reinterpret_cast<const char*>(variant.compiledBinary.Data()),
|
||||
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 relativePath = fs::relative(inputPath, projectRootPath, ec);
|
||||
if (!ec) {
|
||||
const Containers::String normalizedRelative = NormalizePathString(relativePath);
|
||||
if (normalizedRelative.StartsWith("Assets/") || normalizedRelative == "Assets") {
|
||||
outRelativePath = normalizedRelative;
|
||||
} else {
|
||||
outRelativePath.Clear();
|
||||
}
|
||||
outRelativePath = NormalizePathString(relativePath);
|
||||
} else {
|
||||
outRelativePath.Clear();
|
||||
}
|
||||
@@ -1300,6 +1602,9 @@ AssetDatabase::MaintenanceStats AssetDatabase::ScanAssets() {
|
||||
if (fs::exists(assetsRootPath)) {
|
||||
ScanAssetPath(assetsRootPath, seenPaths);
|
||||
}
|
||||
for (const fs::path& builtinShaderPath : CollectBuiltinShaderAssetPaths()) {
|
||||
ScanAssetPath(builtinShaderPath, seenPaths);
|
||||
}
|
||||
RemoveMissingRecords(seenPaths);
|
||||
stats.removedArtifactCount = CleanupOrphanedArtifacts();
|
||||
SaveSourceAssetDB();
|
||||
@@ -1446,6 +1751,47 @@ bool AssetDatabase::EnsureMetaForPath(const fs::path& sourcePath,
|
||||
outRecord.importerName = GetImporterNameForPath(relativePath, isFolder);
|
||||
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");
|
||||
outRecord.metaPath = NormalizeRelativePath(metaPath);
|
||||
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());
|
||||
LoadResult result = loader.Load(absolutePath);
|
||||
if (!result || result.resource == nullptr) {
|
||||
if (!result.errorMessage.Empty()) {
|
||||
SetLastErrorMessage(result.errorMessage);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Shader* shader = static_cast<Shader*>(result.resource);
|
||||
PrecompileShaderVariants(absolutePath, *shader);
|
||||
std::vector<ArtifactDependencyRecord> dependencies;
|
||||
if (!CollectShaderDependencies(sourceRecord, dependencies)) {
|
||||
delete shader;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Core/IO/ResourceFileSystem.h>
|
||||
#include <XCEngine/Resources/GaussianSplat/GaussianSplatLoader.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Resources/Model/ModelLoader.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||
@@ -548,6 +549,7 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
}
|
||||
|
||||
Containers::String loadPath = path;
|
||||
Containers::String cachePath = path;
|
||||
AssetImportService::ImportedAsset resolvedAsset;
|
||||
ResourceType importableType = ResourceType::Unknown;
|
||||
const bool shouldUseProjectArtifact =
|
||||
@@ -560,6 +562,7 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
resolvedAsset.artifactReady) {
|
||||
m_projectAssetIndex.RememberResolvedPath(resolvedAsset.assetGuid, resolvedAsset.relativePath);
|
||||
loadPath = resolvedAsset.runtimeLoadPath;
|
||||
cachePath = loadPath;
|
||||
if (ShouldTraceResourcePath(path)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
@@ -568,6 +571,35 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
" artifact=" +
|
||||
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)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
@@ -577,7 +609,7 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
loadPath);
|
||||
}
|
||||
|
||||
const ResourceGUID guid = ResourceGUID::Generate(loadPath);
|
||||
const ResourceGUID guid = ResourceGUID::Generate(cachePath);
|
||||
|
||||
if (IResource* cached = FindInCache(guid)) {
|
||||
if (ShouldTraceResourcePath(path)) {
|
||||
|
||||
@@ -24,6 +24,33 @@ std::string NarrowAscii(const std::wstring& value) {
|
||||
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 {
|
||||
public:
|
||||
explicit D3D12ShaderIncludeHandler(const std::filesystem::path& sourcePath)
|
||||
@@ -127,15 +154,7 @@ bool D3D12Shader::CompileFromFile(const wchar_t* filePath, const char* entryPoin
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstr(target, "vs_")) {
|
||||
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_type = ResolveShaderTypeFromTarget(target);
|
||||
|
||||
m_uniformsCached = false;
|
||||
return true;
|
||||
@@ -182,16 +201,29 @@ bool D3D12Shader::Compile(const void* sourceData,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstr(target, "vs_")) {
|
||||
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_type = ResolveShaderTypeFromTarget(target);
|
||||
|
||||
m_uniformsCached = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Shader::InitializeFromBytecode(const void* bytecodeData,
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -272,6 +272,21 @@ bool IsHlslInput(const ShaderCompileDesc& desc) {
|
||||
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) {
|
||||
const DWORD length = GetEnvironmentVariableW(name, nullptr, 0);
|
||||
if (length == 0) {
|
||||
@@ -1021,16 +1036,19 @@ bool CompileSpirvShader(const ShaderCompileDesc& desc,
|
||||
outShader.entryPoint = "main";
|
||||
}
|
||||
|
||||
if (desc.source.empty() && desc.fileName.empty()) {
|
||||
if (desc.source.empty() && desc.fileName.empty() &&
|
||||
!HasMatchingCompiledSpirvBinary(desc, targetEnvironment)) {
|
||||
if (errorMessage != nullptr) {
|
||||
*errorMessage = "No shader source or file name was provided.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsSpirvInput(desc)) {
|
||||
if (IsSpirvInput(desc) || HasMatchingCompiledSpirvBinary(desc, targetEnvironment)) {
|
||||
std::vector<uint8_t> bytes;
|
||||
if (!desc.source.empty()) {
|
||||
if (HasMatchingCompiledSpirvBinary(desc, targetEnvironment)) {
|
||||
bytes = desc.compiledBinary;
|
||||
} else if (!desc.source.empty()) {
|
||||
bytes = desc.source;
|
||||
} else if (!LoadBinaryFile(std::filesystem::path(desc.fileName), bytes)) {
|
||||
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) {
|
||||
std::wstring wide;
|
||||
wide.reserve(value.Length());
|
||||
@@ -360,6 +374,13 @@ inline void ApplyShaderStageVariant(
|
||||
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
||||
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
||||
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) {
|
||||
@@ -398,6 +419,12 @@ inline void ApplyShaderStageVariant(
|
||||
compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language);
|
||||
compileDesc.entryPoint = ToWideAscii(variant.entryPoint);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Rendering/FrameData/VisibleGaussianSplatItem.h"
|
||||
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
||||
#include "Rendering/Internal/ShaderVariantUtils.h"
|
||||
#include "Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.h"
|
||||
#include "Rendering/RenderSurface.h"
|
||||
#include "Resources/BuiltinResources.h"
|
||||
#include "Resources/GaussianSplat/GaussianSplat.h"
|
||||
@@ -49,6 +50,21 @@ const Resources::ShaderPass* FindCompatibleGaussianSplatPass(
|
||||
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::RHIType backendType,
|
||||
RHI::RHIPipelineLayout* pipelineLayout,
|
||||
@@ -89,6 +105,88 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||
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
|
||||
|
||||
BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() {
|
||||
@@ -124,6 +222,19 @@ bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
|
||||
cachedGaussianSplat->color.shaderResourceView == nullptr) {
|
||||
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;
|
||||
@@ -177,6 +288,13 @@ bool BuiltinGaussianSplatPass::Execute(const RenderPassContext& context) {
|
||||
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||
|
||||
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
|
||||
if (!PrepareVisibleGaussianSplat(
|
||||
context.renderContext,
|
||||
context.sceneData,
|
||||
visibleGaussianSplat)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DrawVisibleGaussianSplat(
|
||||
context.renderContext,
|
||||
context.surface,
|
||||
@@ -222,6 +340,16 @@ bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) {
|
||||
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>();
|
||||
Resources::IResource::ConstructParams params = {};
|
||||
params.name = "BuiltinGaussianSplatMaterial";
|
||||
@@ -230,10 +358,17 @@ bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) {
|
||||
m_builtinGaussianSplatMaterial->Initialize(params);
|
||||
m_builtinGaussianSplatMaterial->SetShader(m_builtinGaussianSplatShader);
|
||||
m_builtinGaussianSplatMaterial->SetRenderQueue(Resources::MaterialRenderQueue::Transparent);
|
||||
m_passResources = new Internal::BuiltinGaussianSplatPassResources();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BuiltinGaussianSplatPass::DestroyResources() {
|
||||
if (m_passResources != nullptr) {
|
||||
m_passResources->Shutdown();
|
||||
delete m_passResources;
|
||||
m_passResources = nullptr;
|
||||
}
|
||||
|
||||
m_resourceCache.Shutdown();
|
||||
|
||||
for (auto& pipelinePair : m_pipelineStates) {
|
||||
@@ -244,6 +379,14 @@ void BuiltinGaussianSplatPass::DestroyResources() {
|
||||
}
|
||||
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) {
|
||||
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
|
||||
}
|
||||
@@ -255,6 +398,7 @@ void BuiltinGaussianSplatPass::DestroyResources() {
|
||||
m_passResourceLayouts.clear();
|
||||
|
||||
m_builtinGaussianSplatMaterial.reset();
|
||||
m_builtinGaussianSplatUtilitiesShader.Reset();
|
||||
m_builtinGaussianSplatShader.Reset();
|
||||
m_device = nullptr;
|
||||
m_backendType = RHI::RHIType::D3D12;
|
||||
@@ -301,9 +445,30 @@ BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveGa
|
||||
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(
|
||||
const RenderContext& context,
|
||||
const ResolvedShaderPass& resolvedShaderPass) {
|
||||
const ResolvedShaderPass& resolvedShaderPass,
|
||||
PassLayoutUsage usage) {
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -345,21 +510,36 @@ BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCre
|
||||
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
||||
passLayout.perObject = bindingPlan.perObject;
|
||||
passLayout.material = bindingPlan.material;
|
||||
passLayout.gaussianSplatSortDistanceBuffer = bindingPlan.gaussianSplatSortDistanceBuffer;
|
||||
passLayout.gaussianSplatOrderBuffer = bindingPlan.gaussianSplatOrderBuffer;
|
||||
passLayout.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer;
|
||||
passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer;
|
||||
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
|
||||
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
|
||||
passLayout.gaussianSplatViewDataBuffer = bindingPlan.gaussianSplatViewDataBuffer;
|
||||
|
||||
if (!passLayout.perObject.IsValid()) {
|
||||
return failLayout("BuiltinGaussianSplatPass requires a PerObject resource binding");
|
||||
}
|
||||
if (!passLayout.material.IsValid()) {
|
||||
return failLayout("BuiltinGaussianSplatPass requires a Material resource binding");
|
||||
}
|
||||
if (!passLayout.gaussianSplatPositionBuffer.IsValid() ||
|
||||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||
!passLayout.gaussianSplatColorBuffer.IsValid()) {
|
||||
return failLayout("BuiltinGaussianSplatPass requires position, other, and color gaussian splat buffer bindings");
|
||||
|
||||
if (usage == PassLayoutUsage::Draw) {
|
||||
if (!passLayout.material.IsValid()) {
|
||||
return failLayout("BuiltinGaussianSplatPass requires a Material resource binding");
|
||||
}
|
||||
if (!passLayout.gaussianSplatOrderBuffer.IsValid() ||
|
||||
!passLayout.gaussianSplatPositionBuffer.IsValid() ||
|
||||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
|
||||
!passLayout.gaussianSplatColorBuffer.IsValid()) {
|
||||
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());
|
||||
@@ -392,7 +572,10 @@ RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||
context,
|
||||
resolvedShaderPass,
|
||||
PassLayoutUsage::Draw);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -440,6 +623,54 @@ RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState(
|
||||
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(
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
OwnedDescriptorSet& descriptorSet) {
|
||||
@@ -468,14 +699,19 @@ BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::GetOrCr
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Components::GaussianSplatRendererComponent* gaussianSplatRenderer,
|
||||
const Resources::Material* material,
|
||||
const Resources::GaussianSplat* gaussianSplat,
|
||||
const MaterialConstantPayloadView& materialConstants,
|
||||
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat) {
|
||||
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat,
|
||||
RHI::RHIResourceView* sortDistanceView,
|
||||
RHI::RHIResourceView* orderView,
|
||||
RHI::RHIResourceView* viewDataView) {
|
||||
DynamicDescriptorSetKey key = {};
|
||||
key.passLayout = passLayoutKey;
|
||||
key.setIndex = setIndex;
|
||||
key.objectId = objectId;
|
||||
key.gaussianSplatRenderer = gaussianSplatRenderer;
|
||||
key.material = material;
|
||||
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;
|
||||
if (setLayout.usesMaterial) {
|
||||
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 (cachedGaussianSplat.other.shaderResourceView == nullptr ||
|
||||
!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.sortDistanceView = resolvedSortDistanceView;
|
||||
cachedDescriptorSet.orderView = resolvedOrderView;
|
||||
cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView;
|
||||
cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView;
|
||||
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
|
||||
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
|
||||
cachedDescriptorSet.viewDataView = resolvedViewDataView;
|
||||
return &cachedDescriptorSet;
|
||||
}
|
||||
|
||||
@@ -592,10 +908,169 @@ void BuiltinGaussianSplatPass::DestroyPassResourceLayout(PassResourceLayout& pas
|
||||
passLayout.descriptorSetCount = 0;
|
||||
passLayout.perObject = {};
|
||||
passLayout.material = {};
|
||||
passLayout.gaussianSplatSortDistanceBuffer = {};
|
||||
passLayout.gaussianSplatOrderBuffer = {};
|
||||
passLayout.gaussianSplatPositionBuffer = {};
|
||||
passLayout.gaussianSplatOtherBuffer = {};
|
||||
passLayout.gaussianSplatColorBuffer = {};
|
||||
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(
|
||||
@@ -618,6 +1093,17 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||
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);
|
||||
if (material == nullptr) {
|
||||
return false;
|
||||
@@ -632,7 +1118,10 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(
|
||||
context,
|
||||
resolvedShaderPass,
|
||||
PassLayoutUsage::Draw);
|
||||
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
|
||||
if (passLayout == nullptr || pipelineState == nullptr) {
|
||||
return false;
|
||||
@@ -663,8 +1152,13 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||
}
|
||||
|
||||
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||
if (setLayout.layout.bindingCount == 0u) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(setLayout.usesPerObject ||
|
||||
setLayout.usesMaterial ||
|
||||
setLayout.usesGaussianSplatOrderBuffer ||
|
||||
setLayout.usesGaussianSplatPositionBuffer ||
|
||||
setLayout.usesGaussianSplatOtherBuffer ||
|
||||
setLayout.usesGaussianSplatColorBuffer ||
|
||||
@@ -677,7 +1171,8 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||
const Resources::Material* materialKey =
|
||||
setLayout.usesMaterial ? material : nullptr;
|
||||
const Resources::GaussianSplat* gaussianSplatKey =
|
||||
(setLayout.usesGaussianSplatPositionBuffer ||
|
||||
(setLayout.usesGaussianSplatOrderBuffer ||
|
||||
setLayout.usesGaussianSplatPositionBuffer ||
|
||||
setLayout.usesGaussianSplatOtherBuffer ||
|
||||
setLayout.usesGaussianSplatColorBuffer ||
|
||||
setLayout.usesGaussianSplatSHBuffer)
|
||||
@@ -690,10 +1185,14 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||
setLayout,
|
||||
setIndex,
|
||||
objectId,
|
||||
visibleGaussianSplat.gaussianSplatRenderer,
|
||||
materialKey,
|
||||
gaussianSplatKey,
|
||||
materialConstants,
|
||||
*cachedGaussianSplat);
|
||||
*cachedGaussianSplat,
|
||||
nullptr,
|
||||
workingSet->orderIndices.shaderResourceView,
|
||||
workingSet->viewData.shaderResourceView);
|
||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -713,11 +1212,16 @@ bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
|
||||
descriptorSets[descriptorOffset] = descriptorSet;
|
||||
}
|
||||
|
||||
commandList->SetGraphicsDescriptorSets(
|
||||
BindDescriptorSetRanges(
|
||||
passLayout->firstDescriptorSet,
|
||||
passLayout->descriptorSetCount,
|
||||
descriptorSets.data(),
|
||||
passLayout->pipelineLayout);
|
||||
descriptorSets,
|
||||
[commandList, passLayout](Core::uint32 firstSet, Core::uint32 count, RHI::RHIDescriptorSet** sets) {
|
||||
commandList->SetGraphicsDescriptorSets(
|
||||
firstSet,
|
||||
count,
|
||||
sets,
|
||||
passLayout->pipelineLayout);
|
||||
});
|
||||
}
|
||||
|
||||
ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
@@ -64,7 +65,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
||||
|
||||
const std::string magic(fileHeader.magic, fileHeader.magic + 7);
|
||||
const bool isCurrentSchema =
|
||||
magic == "XCSHD05" && fileHeader.schemaVersion == kShaderArtifactSchemaVersion;
|
||||
magic == "XCSHD06" && fileHeader.schemaVersion == kShaderArtifactSchemaVersion;
|
||||
if (!isCurrentSchema) {
|
||||
return LoadResult("Invalid shader artifact header: " + path);
|
||||
}
|
||||
@@ -187,6 +188,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
||||
ShaderStageVariant variant = {};
|
||||
Core::uint64 compiledBinarySize = 0;
|
||||
Core::uint32 keywordCount = 0;
|
||||
Core::uint32 backendCompiledBinaryCount = 0;
|
||||
ShaderVariantArtifactHeader variantHeader = {};
|
||||
if (!ReadShaderArtifactValue(data, offset, variantHeader)) {
|
||||
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);
|
||||
keywordCount = variantHeader.keywordCount;
|
||||
compiledBinarySize = variantHeader.compiledBinarySize;
|
||||
backendCompiledBinaryCount = variantHeader.backendCompiledBinaryCount;
|
||||
|
||||
if (!ReadShaderArtifactString(data, offset, variant.entryPoint) ||
|
||||
!ReadShaderArtifactString(data, offset, variant.profile) ||
|
||||
@@ -227,6 +230,32 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
@@ -40,6 +42,62 @@ 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() {
|
||||
m_uniforms.Clear();
|
||||
m_attributes.Clear();
|
||||
|
||||
Reference in New Issue
Block a user