Add builtin GaussianSplat forward pass baseline

This commit is contained in:
2026-04-10 23:11:11 +08:00
parent 15b42c248f
commit 107b320aa7
12 changed files with 1642 additions and 19 deletions

View File

@@ -62,6 +62,18 @@ inline bool TryBuildBuiltinPassResourceBindingPlan(
case BuiltinPassResourceSemantic::VolumeField:
location = &outPlan.volumeField;
break;
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
location = &outPlan.gaussianSplatPositionBuffer;
break;
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
location = &outPlan.gaussianSplatOtherBuffer;
break;
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
location = &outPlan.gaussianSplatColorBuffer;
break;
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
location = &outPlan.gaussianSplatSHBuffer;
break;
case BuiltinPassResourceSemantic::BaseColorTexture:
location = &outPlan.baseColorTexture;
break;
@@ -334,6 +346,24 @@ inline bool TryBuildBuiltinPassDefaultResourceBindings(
return true;
}
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::GaussianSplat)) {
AppendBuiltinPassResourceBinding(
outBindings,
"PerObjectConstants",
Resources::ShaderResourceType::ConstantBuffer,
0u,
0u,
"PerObject");
AppendBuiltinPassResourceBinding(
outBindings,
"MaterialConstants",
Resources::ShaderResourceType::ConstantBuffer,
1u,
0u,
"Material");
return true;
}
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::PostProcess)) {
AppendBuiltinPassResourceBinding(
outBindings,
@@ -570,6 +600,18 @@ inline bool TryBuildBuiltinPassSetLayouts(
case BuiltinPassResourceSemantic::VolumeField:
setLayout.usesVolumeField = true;
break;
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
setLayout.usesGaussianSplatPositionBuffer = true;
break;
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
setLayout.usesGaussianSplatOtherBuffer = true;
break;
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
setLayout.usesGaussianSplatColorBuffer = true;
break;
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
setLayout.usesGaussianSplatSHBuffer = true;
break;
case BuiltinPassResourceSemantic::BaseColorTexture:
setLayout.usesTexture = true;
setLayout.usesBaseColorTexture = true;

View File

@@ -27,6 +27,8 @@ inline const char* GetBuiltinPassCanonicalName(BuiltinMaterialPass pass) {
return "selectionmask";
case BuiltinMaterialPass::Skybox:
return "skybox";
case BuiltinMaterialPass::GaussianSplat:
return "gaussiansplat";
case BuiltinMaterialPass::Volumetric:
return "volumetric";
case BuiltinMaterialPass::PostProcess:
@@ -145,6 +147,30 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic(
return BuiltinPassResourceSemantic::VolumeField;
}
if (semantic == Containers::String("gaussiansplatpositionbuffer") ||
semantic == Containers::String("gaussiansplatpositions") ||
semantic == Containers::String("splatpositions")) {
return BuiltinPassResourceSemantic::GaussianSplatPositionBuffer;
}
if (semantic == Containers::String("gaussiansplatotherbuffer") ||
semantic == Containers::String("gaussiansplatother") ||
semantic == Containers::String("splatother")) {
return BuiltinPassResourceSemantic::GaussianSplatOtherBuffer;
}
if (semantic == Containers::String("gaussiansplatcolorbuffer") ||
semantic == Containers::String("gaussiansplatcolor") ||
semantic == Containers::String("splatcolor")) {
return BuiltinPassResourceSemantic::GaussianSplatColorBuffer;
}
if (semantic == Containers::String("gaussiansplatshbuffer") ||
semantic == Containers::String("gaussiansplatsh") ||
semantic == Containers::String("splatsh")) {
return BuiltinPassResourceSemantic::GaussianSplatSHBuffer;
}
if (semantic == Containers::String("basecolortexture") ||
semantic == Containers::String("maintex")) {
return BuiltinPassResourceSemantic::BaseColorTexture;
@@ -207,6 +233,14 @@ inline const char* BuiltinPassResourceSemanticToString(BuiltinPassResourceSemant
return "PassConstants";
case BuiltinPassResourceSemantic::VolumeField:
return "VolumeField";
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
return "GaussianSplatPositionBuffer";
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
return "GaussianSplatOtherBuffer";
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
return "GaussianSplatColorBuffer";
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
return "GaussianSplatSHBuffer";
case BuiltinPassResourceSemantic::BaseColorTexture:
return "BaseColorTexture";
case BuiltinPassResourceSemantic::SourceColorTexture:
@@ -295,6 +329,12 @@ inline bool IsBuiltinPassResourceTypeCompatible(
case BuiltinPassResourceSemantic::VolumeField:
return type == Resources::ShaderResourceType::StructuredBuffer ||
type == Resources::ShaderResourceType::RawBuffer;
case BuiltinPassResourceSemantic::GaussianSplatPositionBuffer:
case BuiltinPassResourceSemantic::GaussianSplatOtherBuffer:
case BuiltinPassResourceSemantic::GaussianSplatColorBuffer:
case BuiltinPassResourceSemantic::GaussianSplatSHBuffer:
return type == Resources::ShaderResourceType::StructuredBuffer ||
type == Resources::ShaderResourceType::RawBuffer;
case BuiltinPassResourceSemantic::BaseColorTexture:
case BuiltinPassResourceSemantic::SourceColorTexture:
case BuiltinPassResourceSemantic::SkyboxPanoramicTexture:

View File

@@ -20,6 +20,7 @@ enum class BuiltinMaterialPass : Core::uint32 {
ObjectId,
SelectionMask,
Skybox,
GaussianSplat,
Volumetric,
PostProcess,
FinalColor,
@@ -45,6 +46,11 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
Environment,
PassConstants,
VolumeField,
GaussianSplatOrderBuffer,
GaussianSplatPositionBuffer,
GaussianSplatOtherBuffer,
GaussianSplatColorBuffer,
GaussianSplatSHBuffer,
BaseColorTexture,
SourceColorTexture,
SkyboxPanoramicTexture,
@@ -78,6 +84,11 @@ struct BuiltinPassResourceBindingPlan {
PassResourceBindingLocation shadowReceiver = {};
PassResourceBindingLocation environment = {};
PassResourceBindingLocation passConstants = {};
PassResourceBindingLocation gaussianSplatOrderBuffer = {};
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
PassResourceBindingLocation gaussianSplatColorBuffer = {};
PassResourceBindingLocation gaussianSplatSHBuffer = {};
PassResourceBindingLocation baseColorTexture = {};
PassResourceBindingLocation sourceColorTexture = {};
PassResourceBindingLocation skyboxPanoramicTexture = {};
@@ -111,6 +122,11 @@ struct BuiltinPassSetLayoutMetadata {
bool usesPassConstants = false;
bool usesMaterialBuffers = false;
bool usesVolumeField = false;
bool usesGaussianSplatOrderBuffer = false;
bool usesGaussianSplatPositionBuffer = false;
bool usesGaussianSplatOtherBuffer = false;
bool usesGaussianSplatColorBuffer = false;
bool usesGaussianSplatSHBuffer = false;
bool usesTexture = false;
bool usesBaseColorTexture = false;
bool usesSourceColorTexture = false;

View File

@@ -0,0 +1,225 @@
#pragma once
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Rendering/Builtin/BuiltinPassTypes.h>
#include <XCEngine/Rendering/Caches/RenderResourceCache.h>
#include <XCEngine/Rendering/Materials/RenderMaterialResolve.h>
#include <XCEngine/Rendering/Materials/RenderMaterialStateUtils.h>
#include <XCEngine/Rendering/RenderPass.h>
#include <XCEngine/RHI/RHIDescriptorPool.h>
#include <XCEngine/RHI/RHIDescriptorSet.h>
#include <XCEngine/RHI/RHIPipelineLayout.h>
#include <XCEngine/RHI/RHIPipelineState.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <memory>
#include <unordered_map>
#include <vector>
namespace XCEngine {
namespace Components {
class GameObject;
} // namespace Components
namespace Resources {
class GaussianSplat;
class Material;
class Shader;
} // namespace Resources
namespace Rendering {
struct VisibleGaussianSplatItem;
namespace Passes {
class BuiltinGaussianSplatPass final : public RenderPass {
public:
~BuiltinGaussianSplatPass() override;
const char* GetName() const override;
bool Initialize(const RenderContext& context) override;
bool PrepareGaussianSplatResources(
const RenderContext& context,
const RenderSceneData& sceneData);
bool Execute(const RenderPassContext& context) override;
void Shutdown() override;
private:
struct PerObjectConstants {
Math::Matrix4x4 projection = Math::Matrix4x4::Identity();
Math::Matrix4x4 view = Math::Matrix4x4::Identity();
Math::Matrix4x4 model = Math::Matrix4x4::Identity();
Math::Vector4 cameraRight = Math::Vector4::Zero();
Math::Vector4 cameraUp = Math::Vector4::Zero();
};
struct OwnedDescriptorSet {
RHI::RHIDescriptorPool* pool = nullptr;
RHI::RHIDescriptorSet* set = nullptr;
};
struct PassLayoutKey {
const Resources::Shader* shader = nullptr;
Containers::String passName;
bool operator==(const PassLayoutKey& other) const {
return shader == other.shader && passName == other.passName;
}
};
struct PassLayoutKeyHash {
size_t operator()(const PassLayoutKey& key) const noexcept {
size_t hash = reinterpret_cast<size_t>(key.shader);
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
return hash;
}
};
struct PassResourceLayout {
RHI::RHIPipelineLayout* pipelineLayout = nullptr;
Core::uint32 firstDescriptorSet = 0;
Core::uint32 descriptorSetCount = 0;
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
PassResourceBindingLocation perObject = {};
PassResourceBindingLocation material = {};
PassResourceBindingLocation gaussianSplatPositionBuffer = {};
PassResourceBindingLocation gaussianSplatOtherBuffer = {};
PassResourceBindingLocation gaussianSplatColorBuffer = {};
PassResourceBindingLocation gaussianSplatSHBuffer = {};
};
struct DynamicDescriptorSetKey {
PassLayoutKey passLayout = {};
Core::uint32 setIndex = 0;
Core::uint64 objectId = 0;
const Resources::Material* material = nullptr;
const Resources::GaussianSplat* gaussianSplat = nullptr;
bool operator==(const DynamicDescriptorSetKey& other) const {
return passLayout == other.passLayout &&
setIndex == other.setIndex &&
objectId == other.objectId &&
material == other.material &&
gaussianSplat == other.gaussianSplat;
}
};
struct DynamicDescriptorSetKeyHash {
size_t operator()(const DynamicDescriptorSetKey& key) const noexcept {
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.material) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= reinterpret_cast<size_t>(key.gaussianSplat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
return hash;
}
};
struct CachedDescriptorSet {
OwnedDescriptorSet descriptorSet = {};
Core::uint64 materialVersion = 0;
RHI::RHIResourceView* positionsView = nullptr;
RHI::RHIResourceView* otherView = nullptr;
RHI::RHIResourceView* colorView = nullptr;
RHI::RHIResourceView* shView = nullptr;
};
struct ResolvedShaderPass {
const Resources::Shader* shader = nullptr;
const Resources::ShaderPass* pass = nullptr;
Containers::String passName;
};
struct PipelineStateKey {
Resources::MaterialRenderState renderState;
const Resources::Shader* shader = nullptr;
Containers::String passName;
Containers::String keywordSignature;
Core::uint32 renderTargetCount = 0;
Core::uint32 renderTargetFormat = 0;
Core::uint32 depthStencilFormat = 0;
Core::uint32 sampleCount = 1;
Core::uint32 sampleQuality = 0;
bool operator==(const PipelineStateKey& other) const {
return renderState == other.renderState &&
shader == other.shader &&
passName == other.passName &&
keywordSignature == other.keywordSignature &&
renderTargetCount == other.renderTargetCount &&
renderTargetFormat == other.renderTargetFormat &&
depthStencilFormat == other.depthStencilFormat &&
sampleCount == other.sampleCount &&
sampleQuality == other.sampleQuality;
}
};
struct PipelineStateKeyHash {
size_t operator()(const PipelineStateKey& key) const noexcept {
size_t hash = MaterialRenderStateHash()(key.renderState);
hash ^= reinterpret_cast<size_t>(key.shader) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Containers::String>{}(key.keywordSignature) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Core::uint32>{}(key.renderTargetCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Core::uint32>{}(key.renderTargetFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Core::uint32>{}(key.depthStencilFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Core::uint32>{}(key.sampleCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Core::uint32>{}(key.sampleQuality) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
return hash;
}
};
bool EnsureInitialized(const RenderContext& context);
bool CreateResources(const RenderContext& context);
void DestroyResources();
const Resources::Material* ResolveGaussianSplatMaterial(
const VisibleGaussianSplatItem& visibleGaussianSplat) const;
ResolvedShaderPass ResolveGaussianSplatShaderPass(
const RenderSceneData& sceneData,
const Resources::Material* material) const;
PassResourceLayout* GetOrCreatePassResourceLayout(
const RenderContext& context,
const ResolvedShaderPass& resolvedShaderPass);
RHI::RHIPipelineState* GetOrCreatePipelineState(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const Resources::Material* material);
bool CreateOwnedDescriptorSet(
const BuiltinPassSetLayoutMetadata& setLayout,
OwnedDescriptorSet& descriptorSet);
CachedDescriptorSet* GetOrCreateDynamicDescriptorSet(
const PassLayoutKey& passLayoutKey,
const PassResourceLayout& passLayout,
const BuiltinPassSetLayoutMetadata& setLayout,
Core::uint32 setIndex,
Core::uint64 objectId,
const Resources::Material* material,
const Resources::GaussianSplat* gaussianSplat,
const MaterialConstantPayloadView& materialConstants,
const RenderResourceCache::CachedGaussianSplat& cachedGaussianSplat);
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
bool DrawVisibleGaussianSplat(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const VisibleGaussianSplatItem& visibleGaussianSplat);
RHI::RHIDevice* m_device = nullptr;
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
Resources::ResourceHandle<Resources::Shader> m_builtinGaussianSplatShader;
std::unique_ptr<Resources::Material> m_builtinGaussianSplatMaterial;
RenderResourceCache m_resourceCache;
std::unordered_map<PassLayoutKey, PassResourceLayout, PassLayoutKeyHash> m_passResourceLayouts;
std::unordered_map<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
};
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -39,6 +39,7 @@ namespace Pipelines {
} // namespace Pipelines
namespace Passes {
class BuiltinGaussianSplatPass;
class BuiltinVolumetricPass;
} // namespace Passes
@@ -355,6 +356,7 @@ private:
OwnedDescriptorSet m_skyboxSamplerSet = {};
RHI::RHIResourceView* m_skyboxBoundPanoramicTextureView = nullptr;
RHI::RHIResourceView* m_skyboxBoundCubemapTextureView = nullptr;
std::unique_ptr<Passes::BuiltinGaussianSplatPass> m_gaussianSplatPass;
std::unique_ptr<Passes::BuiltinVolumetricPass> m_volumetricPass;
};

View File

@@ -38,6 +38,7 @@ Containers::String GetBuiltinObjectIdOutlineShaderPath();
Containers::String GetBuiltinSelectionMaskShaderPath();
Containers::String GetBuiltinSelectionOutlineShaderPath();
Containers::String GetBuiltinSkyboxShaderPath();
Containers::String GetBuiltinGaussianSplatShaderPath();
Containers::String GetBuiltinVolumetricShaderPath();
Containers::String GetBuiltinColorScalePostProcessShaderPath();
Containers::String GetBuiltinFinalColorShaderPath();