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

@@ -0,0 +1,730 @@
#include "Rendering/Passes/BuiltinGaussianSplatPass.h"
#include "Components/GameObject.h"
#include "Debug/Logger.h"
#include "RHI/RHICommandList.h"
#include "RHI/RHIDevice.h"
#include "Rendering/Builtin/BuiltinPassLayoutUtils.h"
#include "Rendering/FrameData/RenderSceneData.h"
#include "Rendering/FrameData/VisibleGaussianSplatItem.h"
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
#include "Rendering/Internal/ShaderVariantUtils.h"
#include "Rendering/RenderSurface.h"
#include "Resources/BuiltinResources.h"
#include "Resources/GaussianSplat/GaussianSplat.h"
#include "Resources/Material/Material.h"
#include "Resources/Shader/Shader.h"
namespace XCEngine {
namespace Rendering {
namespace Passes {
namespace {
Resources::ShaderKeywordSet ResolvePassKeywordSet(
const RenderSceneData& sceneData,
const Resources::Material* material) {
return Resources::CombineShaderKeywordSets(
sceneData.globalShaderKeywords,
material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet());
}
const Resources::ShaderPass* FindCompatibleGaussianSplatPass(
const Resources::Shader& shader,
const RenderSceneData& sceneData,
const Resources::Material* material,
Resources::ShaderBackend backend) {
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) {
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::GaussianSplat) &&
::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(
shader,
shaderPass.name,
backend,
keywordSet)) {
return &shaderPass;
}
}
return nullptr;
}
RHI::GraphicsPipelineDesc CreatePipelineDesc(
RHI::RHIType backendType,
RHI::RHIPipelineLayout* pipelineLayout,
const Resources::Shader& shader,
const Resources::ShaderPass& shaderPass,
const Containers::String& passName,
const Resources::ShaderKeywordSet& keywordSet,
const Resources::Material* material,
const RenderSurface& surface) {
RHI::GraphicsPipelineDesc pipelineDesc = {};
pipelineDesc.pipelineLayout = pipelineLayout;
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc(surface, pipelineDesc);
pipelineDesc.depthStencilFormat =
static_cast<uint32_t>(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface));
ApplyResolvedRenderState(&shaderPass, material, pipelineDesc);
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType);
if (const Resources::ShaderStageVariant* vertexVariant =
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet)) {
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
shader.GetPath(),
shaderPass,
backend,
*vertexVariant,
pipelineDesc.vertexShader);
}
if (const Resources::ShaderStageVariant* fragmentVariant =
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend, keywordSet)) {
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
shader.GetPath(),
shaderPass,
backend,
*fragmentVariant,
pipelineDesc.fragmentShader);
}
return pipelineDesc;
}
} // namespace
BuiltinGaussianSplatPass::~BuiltinGaussianSplatPass() {
Shutdown();
}
const char* BuiltinGaussianSplatPass::GetName() const {
return "BuiltinGaussianSplatPass";
}
bool BuiltinGaussianSplatPass::Initialize(const RenderContext& context) {
return EnsureInitialized(context);
}
bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources(
const RenderContext& context,
const RenderSceneData& sceneData) {
if (!EnsureInitialized(context)) {
return false;
}
for (const VisibleGaussianSplatItem& visibleGaussianSplat : sceneData.visibleGaussianSplats) {
if (visibleGaussianSplat.gaussianSplat == nullptr ||
!visibleGaussianSplat.gaussianSplat->IsValid()) {
continue;
}
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
if (cachedGaussianSplat == nullptr ||
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
cachedGaussianSplat->other.shaderResourceView == nullptr ||
cachedGaussianSplat->color.shaderResourceView == nullptr) {
return false;
}
}
return true;
}
bool BuiltinGaussianSplatPass::Execute(const RenderPassContext& context) {
if (!context.renderContext.IsValid()) {
return false;
}
if (context.sceneData.visibleGaussianSplats.empty()) {
return true;
}
const std::vector<RHI::RHIResourceView*>& colorAttachments = context.surface.GetColorAttachments();
if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(context.surface) ||
colorAttachments.empty() ||
colorAttachments[0] == nullptr) {
return false;
}
const Math::RectInt renderArea = context.surface.GetRenderArea();
if (renderArea.width <= 0 || renderArea.height <= 0) {
return false;
}
if (!PrepareGaussianSplatResources(context.renderContext, context.sceneData)) {
return false;
}
RHI::RHICommandList* commandList = context.renderContext.commandList;
RHI::RHIResourceView* renderTarget = colorAttachments[0];
commandList->SetRenderTargets(1, &renderTarget, context.surface.GetDepthAttachment());
const RHI::Viewport viewport = {
static_cast<float>(renderArea.x),
static_cast<float>(renderArea.y),
static_cast<float>(renderArea.width),
static_cast<float>(renderArea.height),
0.0f,
1.0f
};
const RHI::Rect scissorRect = {
renderArea.x,
renderArea.y,
renderArea.x + renderArea.width,
renderArea.y + renderArea.height
};
commandList->SetViewport(viewport);
commandList->SetScissorRect(scissorRect);
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
for (const VisibleGaussianSplatItem& visibleGaussianSplat : context.sceneData.visibleGaussianSplats) {
if (!DrawVisibleGaussianSplat(
context.renderContext,
context.surface,
context.sceneData,
visibleGaussianSplat)) {
return false;
}
}
return true;
}
void BuiltinGaussianSplatPass::Shutdown() {
DestroyResources();
}
bool BuiltinGaussianSplatPass::EnsureInitialized(const RenderContext& context) {
if (!context.IsValid()) {
return false;
}
if (m_device == context.device &&
m_backendType == context.backendType &&
m_builtinGaussianSplatShader.IsValid() &&
m_builtinGaussianSplatMaterial != nullptr) {
return true;
}
DestroyResources();
return CreateResources(context);
}
bool BuiltinGaussianSplatPass::CreateResources(const RenderContext& context) {
m_device = context.device;
m_backendType = context.backendType;
m_builtinGaussianSplatShader = Resources::ResourceManager::Get().Load<Resources::Shader>(
Resources::GetBuiltinGaussianSplatShaderPath());
if (!m_builtinGaussianSplatShader.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinGaussianSplatPass failed to load builtin gaussian splat shader resource");
DestroyResources();
return false;
}
m_builtinGaussianSplatMaterial = std::make_unique<Resources::Material>();
Resources::IResource::ConstructParams params = {};
params.name = "BuiltinGaussianSplatMaterial";
params.path = "builtin://materials/gaussian-splat-default";
params.guid = Resources::ResourceGUID::Generate(params.path);
m_builtinGaussianSplatMaterial->Initialize(params);
m_builtinGaussianSplatMaterial->SetShader(m_builtinGaussianSplatShader);
m_builtinGaussianSplatMaterial->SetRenderQueue(Resources::MaterialRenderQueue::Transparent);
return true;
}
void BuiltinGaussianSplatPass::DestroyResources() {
m_resourceCache.Shutdown();
for (auto& pipelinePair : m_pipelineStates) {
if (pipelinePair.second != nullptr) {
pipelinePair.second->Shutdown();
delete pipelinePair.second;
}
}
m_pipelineStates.clear();
for (auto& descriptorSetPair : m_dynamicDescriptorSets) {
DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet);
}
m_dynamicDescriptorSets.clear();
for (auto& passLayoutPair : m_passResourceLayouts) {
DestroyPassResourceLayout(passLayoutPair.second);
}
m_passResourceLayouts.clear();
m_builtinGaussianSplatMaterial.reset();
m_builtinGaussianSplatShader.Reset();
m_device = nullptr;
m_backendType = RHI::RHIType::D3D12;
}
const Resources::Material* BuiltinGaussianSplatPass::ResolveGaussianSplatMaterial(
const VisibleGaussianSplatItem& visibleGaussianSplat) const {
if (visibleGaussianSplat.material != nullptr &&
visibleGaussianSplat.material->GetShader() != nullptr &&
MatchesBuiltinPass(visibleGaussianSplat.material, BuiltinMaterialPass::GaussianSplat)) {
return visibleGaussianSplat.material;
}
return m_builtinGaussianSplatMaterial.get();
}
BuiltinGaussianSplatPass::ResolvedShaderPass BuiltinGaussianSplatPass::ResolveGaussianSplatShaderPass(
const RenderSceneData& sceneData,
const Resources::Material* material) const {
ResolvedShaderPass resolved = {};
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType);
if (material != nullptr && material->GetShader() != nullptr) {
const Resources::Shader* shader = material->GetShader();
if (const Resources::ShaderPass* shaderPass =
FindCompatibleGaussianSplatPass(*shader, sceneData, material, backend)) {
resolved.shader = shader;
resolved.pass = shaderPass;
resolved.passName = shaderPass->name;
return resolved;
}
}
if (m_builtinGaussianSplatShader.IsValid()) {
const Resources::Shader* shader = m_builtinGaussianSplatShader.Get();
if (const Resources::ShaderPass* shaderPass =
FindCompatibleGaussianSplatPass(*shader, sceneData, material, backend)) {
resolved.shader = shader;
resolved.pass = shaderPass;
resolved.passName = shaderPass->name;
}
}
return resolved;
}
BuiltinGaussianSplatPass::PassResourceLayout* BuiltinGaussianSplatPass::GetOrCreatePassResourceLayout(
const RenderContext& context,
const ResolvedShaderPass& resolvedShaderPass) {
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return nullptr;
}
PassLayoutKey passLayoutKey = {};
passLayoutKey.shader = resolvedShaderPass.shader;
passLayoutKey.passName = resolvedShaderPass.passName;
const auto existing = m_passResourceLayouts.find(passLayoutKey);
if (existing != m_passResourceLayouts.end()) {
return &existing->second;
}
PassResourceLayout passLayout = {};
auto failLayout = [this, &passLayout](const char* message) -> PassResourceLayout* {
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
DestroyPassResourceLayout(passLayout);
return nullptr;
};
BuiltinPassResourceBindingPlan bindingPlan = {};
Containers::String bindingPlanError;
if (!TryBuildBuiltinPassResourceBindingPlan(*resolvedShaderPass.pass, bindingPlan, &bindingPlanError)) {
const Containers::String contextualError =
Containers::String("BuiltinGaussianSplatPass failed to resolve pass resource bindings for shader='") +
resolvedShaderPass.shader->GetPath() +
"', pass='" + resolvedShaderPass.passName +
"': " + bindingPlanError +
". Bindings: " + DescribeShaderResourceBindings(resolvedShaderPass.pass->resources);
return failLayout(contextualError.CStr());
}
Containers::String setLayoutError;
if (!TryBuildBuiltinPassSetLayouts(bindingPlan, passLayout.setLayouts, &setLayoutError)) {
return failLayout(setLayoutError.CStr());
}
passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet;
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
passLayout.perObject = bindingPlan.perObject;
passLayout.material = bindingPlan.material;
passLayout.gaussianSplatPositionBuffer = bindingPlan.gaussianSplatPositionBuffer;
passLayout.gaussianSplatOtherBuffer = bindingPlan.gaussianSplatOtherBuffer;
passLayout.gaussianSplatColorBuffer = bindingPlan.gaussianSplatColorBuffer;
passLayout.gaussianSplatSHBuffer = bindingPlan.gaussianSplatSHBuffer;
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");
}
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
for (size_t setIndex = 0; setIndex < passLayout.setLayouts.size(); ++setIndex) {
nativeSetLayouts[setIndex] = passLayout.setLayouts[setIndex].layout;
}
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
pipelineLayoutDesc.setLayouts = nativeSetLayouts.empty() ? nullptr : nativeSetLayouts.data();
pipelineLayoutDesc.setLayoutCount = static_cast<Core::uint32>(nativeSetLayouts.size());
passLayout.pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc);
if (passLayout.pipelineLayout == nullptr) {
return failLayout("BuiltinGaussianSplatPass failed to create pipeline layout from shader pass resources");
}
const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout);
PassResourceLayout& storedPassLayout = result.first->second;
RefreshBuiltinPassSetLayouts(storedPassLayout.setLayouts);
return &storedPassLayout;
}
RHI::RHIPipelineState* BuiltinGaussianSplatPass::GetOrCreatePipelineState(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const Resources::Material* material) {
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return nullptr;
}
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
return nullptr;
}
PipelineStateKey pipelineKey = {};
pipelineKey.renderState =
BuildStaticPipelineRenderStateKey(ResolveEffectiveRenderState(resolvedShaderPass.pass, material));
pipelineKey.shader = resolvedShaderPass.shader;
pipelineKey.passName = resolvedShaderPass.passName;
pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet);
pipelineKey.renderTargetCount =
::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) ? 1u : 0u;
pipelineKey.renderTargetFormat =
static_cast<Core::uint32>(::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u));
pipelineKey.depthStencilFormat =
static_cast<Core::uint32>(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface));
pipelineKey.sampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface);
pipelineKey.sampleQuality = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleQuality(surface);
const auto existing = m_pipelineStates.find(pipelineKey);
if (existing != m_pipelineStates.end()) {
return existing->second;
}
const RHI::GraphicsPipelineDesc pipelineDesc =
CreatePipelineDesc(
context.backendType,
passLayout->pipelineLayout,
*resolvedShaderPass.shader,
*resolvedShaderPass.pass,
resolvedShaderPass.passName,
keywordSet,
material,
surface);
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
if (pipelineState == nullptr || !pipelineState->IsValid()) {
if (pipelineState != nullptr) {
pipelineState->Shutdown();
delete pipelineState;
}
return nullptr;
}
m_pipelineStates.emplace(pipelineKey, pipelineState);
return pipelineState;
}
bool BuiltinGaussianSplatPass::CreateOwnedDescriptorSet(
const BuiltinPassSetLayoutMetadata& setLayout,
OwnedDescriptorSet& descriptorSet) {
RHI::DescriptorPoolDesc poolDesc = {};
poolDesc.type = setLayout.heapType;
poolDesc.descriptorCount = CountBuiltinPassHeapDescriptors(setLayout.heapType, setLayout.bindings);
poolDesc.shaderVisible = setLayout.shaderVisible;
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
if (descriptorSet.pool == nullptr) {
return false;
}
descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout);
if (descriptorSet.set == nullptr) {
DestroyOwnedDescriptorSet(descriptorSet);
return false;
}
return true;
}
BuiltinGaussianSplatPass::CachedDescriptorSet* BuiltinGaussianSplatPass::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) {
DynamicDescriptorSetKey key = {};
key.passLayout = passLayoutKey;
key.setIndex = setIndex;
key.objectId = objectId;
key.material = material;
key.gaussianSplat = gaussianSplat;
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
return nullptr;
}
}
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0u;
if (setLayout.usesMaterial) {
if (!passLayout.material.IsValid() ||
passLayout.material.set != setIndex ||
!materialConstants.IsValid()) {
return nullptr;
}
if (cachedDescriptorSet.materialVersion != materialVersion) {
cachedDescriptorSet.descriptorSet.set->WriteConstant(
passLayout.material.binding,
materialConstants.data,
materialConstants.size);
}
}
if (setLayout.usesGaussianSplatPositionBuffer) {
if (cachedGaussianSplat.positions.shaderResourceView == nullptr ||
!passLayout.gaussianSplatPositionBuffer.IsValid() ||
passLayout.gaussianSplatPositionBuffer.set != setIndex) {
return nullptr;
}
if (cachedDescriptorSet.positionsView != cachedGaussianSplat.positions.shaderResourceView) {
cachedDescriptorSet.descriptorSet.set->Update(
passLayout.gaussianSplatPositionBuffer.binding,
cachedGaussianSplat.positions.shaderResourceView);
}
}
if (setLayout.usesGaussianSplatOtherBuffer) {
if (cachedGaussianSplat.other.shaderResourceView == nullptr ||
!passLayout.gaussianSplatOtherBuffer.IsValid() ||
passLayout.gaussianSplatOtherBuffer.set != setIndex) {
return nullptr;
}
if (cachedDescriptorSet.otherView != cachedGaussianSplat.other.shaderResourceView) {
cachedDescriptorSet.descriptorSet.set->Update(
passLayout.gaussianSplatOtherBuffer.binding,
cachedGaussianSplat.other.shaderResourceView);
}
}
if (setLayout.usesGaussianSplatColorBuffer) {
if (cachedGaussianSplat.color.shaderResourceView == nullptr ||
!passLayout.gaussianSplatColorBuffer.IsValid() ||
passLayout.gaussianSplatColorBuffer.set != setIndex) {
return nullptr;
}
if (cachedDescriptorSet.colorView != cachedGaussianSplat.color.shaderResourceView) {
cachedDescriptorSet.descriptorSet.set->Update(
passLayout.gaussianSplatColorBuffer.binding,
cachedGaussianSplat.color.shaderResourceView);
}
}
if (setLayout.usesGaussianSplatSHBuffer) {
if (cachedGaussianSplat.sh.shaderResourceView == nullptr ||
!passLayout.gaussianSplatSHBuffer.IsValid() ||
passLayout.gaussianSplatSHBuffer.set != setIndex) {
return nullptr;
}
if (cachedDescriptorSet.shView != cachedGaussianSplat.sh.shaderResourceView) {
cachedDescriptorSet.descriptorSet.set->Update(
passLayout.gaussianSplatSHBuffer.binding,
cachedGaussianSplat.sh.shaderResourceView);
}
}
cachedDescriptorSet.materialVersion = materialVersion;
cachedDescriptorSet.positionsView = cachedGaussianSplat.positions.shaderResourceView;
cachedDescriptorSet.otherView = cachedGaussianSplat.other.shaderResourceView;
cachedDescriptorSet.colorView = cachedGaussianSplat.color.shaderResourceView;
cachedDescriptorSet.shView = cachedGaussianSplat.sh.shaderResourceView;
return &cachedDescriptorSet;
}
void BuiltinGaussianSplatPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) {
if (descriptorSet.set != nullptr) {
descriptorSet.set->Shutdown();
delete descriptorSet.set;
descriptorSet.set = nullptr;
}
if (descriptorSet.pool != nullptr) {
descriptorSet.pool->Shutdown();
delete descriptorSet.pool;
descriptorSet.pool = nullptr;
}
}
void BuiltinGaussianSplatPass::DestroyPassResourceLayout(PassResourceLayout& passLayout) {
if (passLayout.pipelineLayout != nullptr) {
passLayout.pipelineLayout->Shutdown();
delete passLayout.pipelineLayout;
passLayout.pipelineLayout = nullptr;
}
passLayout.setLayouts.clear();
passLayout.firstDescriptorSet = 0;
passLayout.descriptorSetCount = 0;
passLayout.perObject = {};
passLayout.material = {};
passLayout.gaussianSplatPositionBuffer = {};
passLayout.gaussianSplatOtherBuffer = {};
passLayout.gaussianSplatColorBuffer = {};
passLayout.gaussianSplatSHBuffer = {};
}
bool BuiltinGaussianSplatPass::DrawVisibleGaussianSplat(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const VisibleGaussianSplatItem& visibleGaussianSplat) {
if (visibleGaussianSplat.gameObject == nullptr ||
visibleGaussianSplat.gaussianSplat == nullptr ||
!visibleGaussianSplat.gaussianSplat->IsValid()) {
return false;
}
const RenderResourceCache::CachedGaussianSplat* cachedGaussianSplat =
m_resourceCache.GetOrCreateGaussianSplat(m_device, visibleGaussianSplat.gaussianSplat);
if (cachedGaussianSplat == nullptr ||
cachedGaussianSplat->positions.shaderResourceView == nullptr ||
cachedGaussianSplat->other.shaderResourceView == nullptr ||
cachedGaussianSplat->color.shaderResourceView == nullptr) {
return false;
}
const Resources::Material* material = ResolveGaussianSplatMaterial(visibleGaussianSplat);
if (material == nullptr) {
return false;
}
const ResolvedShaderPass resolvedShaderPass = ResolveGaussianSplatShaderPass(sceneData, material);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return false;
}
PassLayoutKey passLayoutKey = {};
passLayoutKey.shader = resolvedShaderPass.shader;
passLayoutKey.passName = resolvedShaderPass.passName;
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
if (passLayout == nullptr || pipelineState == nullptr) {
return false;
}
const MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
if (!materialConstants.IsValid()) {
return false;
}
RHI::RHICommandList* commandList = context.commandList;
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.usesPerObject ||
setLayout.usesMaterial ||
setLayout.usesGaussianSplatPositionBuffer ||
setLayout.usesGaussianSplatOtherBuffer ||
setLayout.usesGaussianSplatColorBuffer ||
setLayout.usesGaussianSplatSHBuffer)) {
return false;
}
const Core::uint64 objectId =
setLayout.usesPerObject ? visibleGaussianSplat.gameObject->GetID() : 0u;
const Resources::Material* materialKey =
setLayout.usesMaterial ? material : nullptr;
const Resources::GaussianSplat* gaussianSplatKey =
(setLayout.usesGaussianSplatPositionBuffer ||
setLayout.usesGaussianSplatOtherBuffer ||
setLayout.usesGaussianSplatColorBuffer ||
setLayout.usesGaussianSplatSHBuffer)
? visibleGaussianSplat.gaussianSplat
: nullptr;
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
passLayoutKey,
*passLayout,
setLayout,
setIndex,
objectId,
materialKey,
gaussianSplatKey,
materialConstants,
*cachedGaussianSplat);
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;
}
commandList->SetGraphicsDescriptorSets(
passLayout->firstDescriptorSet,
passLayout->descriptorSetCount,
descriptorSets.data(),
passLayout->pipelineLayout);
}
ApplyDynamicRenderState(ResolveEffectiveRenderState(resolvedShaderPass.pass, material), *commandList);
commandList->Draw(6u, cachedGaussianSplat->splatCount, 0u, 0u);
return true;
}
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine