Formalize GaussianSplat transient pass resources

This commit is contained in:
2026-04-10 22:15:05 +08:00
parent 977a4cf2a4
commit 15b42c248f
5 changed files with 844 additions and 0 deletions

View File

@@ -526,6 +526,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVolumetricPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Extraction/RenderSceneExtractor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Extraction/RenderSceneUtility.cpp

View File

@@ -0,0 +1,297 @@
#include "Rendering/Passes/Internal/BuiltinGaussianSplatPassResources.h"
#include "Components/GaussianSplatRendererComponent.h"
#include "Resources/GaussianSplat/GaussianSplat.h"
namespace XCEngine {
namespace Rendering {
namespace Passes {
namespace Internal {
namespace {
bool CreateStructuredBufferViews(
RHI::RHIDevice* device,
Core::uint32 elementCount,
Core::uint32 elementStride,
BuiltinGaussianSplatPassResources::CachedBufferView& bufferView) {
if (device == nullptr || elementCount == 0u || elementStride == 0u) {
return false;
}
RHI::BufferDesc bufferDesc = {};
bufferDesc.size = static_cast<Core::uint64>(elementCount) * static_cast<Core::uint64>(elementStride);
bufferDesc.stride = elementStride;
bufferDesc.bufferType = static_cast<uint32_t>(RHI::BufferType::Storage);
bufferDesc.flags = 0u;
bufferView.buffer = device->CreateBuffer(bufferDesc);
if (bufferView.buffer == nullptr) {
return false;
}
bufferView.buffer->SetStride(elementStride);
bufferView.buffer->SetBufferType(RHI::BufferType::Storage);
bufferView.buffer->SetState(RHI::ResourceStates::Common);
RHI::ResourceViewDesc viewDesc = {};
viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
viewDesc.firstElement = 0u;
viewDesc.elementCount = elementCount;
viewDesc.structureByteStride = elementStride;
bufferView.shaderResourceView = device->CreateShaderResourceView(bufferView.buffer, viewDesc);
if (bufferView.shaderResourceView == nullptr) {
return false;
}
bufferView.unorderedAccessView = device->CreateUnorderedAccessView(bufferView.buffer, viewDesc);
if (bufferView.unorderedAccessView == nullptr) {
return false;
}
bufferView.elementCount = elementCount;
bufferView.elementStride = elementStride;
bufferView.currentState = RHI::ResourceStates::Common;
return true;
}
} // namespace
BuiltinGaussianSplatPassResources::~BuiltinGaussianSplatPassResources() {
Shutdown();
}
bool BuiltinGaussianSplatPassResources::EnsureWorkingSet(
RHI::RHIDevice* device,
const VisibleGaussianSplatItem& visibleGaussianSplat,
WorkingSet*& outWorkingSet) {
outWorkingSet = nullptr;
if (device == nullptr ||
visibleGaussianSplat.gaussianSplatRenderer == nullptr ||
visibleGaussianSplat.gaussianSplat == nullptr ||
!visibleGaussianSplat.gaussianSplat->IsValid()) {
return false;
}
const Core::uint32 splatCapacity = visibleGaussianSplat.gaussianSplat->GetSplatCount();
if (splatCapacity == 0u) {
return false;
}
if (!ResetForDevice(device)) {
return false;
}
WorkingSet& workingSet = m_workingSets[visibleGaussianSplat.gaussianSplatRenderer];
if (workingSet.splatCapacity < splatCapacity) {
DestroyBufferView(workingSet.sortDistances);
DestroyBufferView(workingSet.orderIndices);
DestroyBufferView(workingSet.viewData);
if (!RecreateWorkingSet(device, splatCapacity, workingSet)) {
m_workingSets.erase(visibleGaussianSplat.gaussianSplatRenderer);
return false;
}
}
workingSet.renderer = visibleGaussianSplat.gaussianSplatRenderer;
outWorkingSet = &workingSet;
return true;
}
bool BuiltinGaussianSplatPassResources::EnsureAccumulationSurface(
RHI::RHIDevice* device,
Core::uint32 width,
Core::uint32 height,
RHI::Format format,
AccumulationSurface*& outSurface) {
outSurface = nullptr;
if (device == nullptr ||
width == 0u ||
height == 0u ||
format == RHI::Format::Unknown) {
return false;
}
if (!ResetForDevice(device)) {
return false;
}
if (m_accumulationSurface.texture == nullptr ||
m_accumulationSurface.width != width ||
m_accumulationSurface.height != height ||
m_accumulationSurface.format != format) {
if (!RecreateAccumulationSurface(device, width, height, format)) {
return false;
}
}
outSurface = &m_accumulationSurface;
return true;
}
void BuiltinGaussianSplatPassResources::Shutdown() {
for (auto& workingSetPair : m_workingSets) {
DestroyBufferView(workingSetPair.second.sortDistances);
DestroyBufferView(workingSetPair.second.orderIndices);
DestroyBufferView(workingSetPair.second.viewData);
}
m_workingSets.clear();
DestroyAccumulationSurface(m_accumulationSurface);
m_device = nullptr;
}
const BuiltinGaussianSplatPassResources::WorkingSet* BuiltinGaussianSplatPassResources::FindWorkingSet(
const Components::GaussianSplatRendererComponent* renderer) const {
const auto workingSetIt = m_workingSets.find(renderer);
return workingSetIt != m_workingSets.end() ? &workingSetIt->second : nullptr;
}
const BuiltinGaussianSplatPassResources::AccumulationSurface* BuiltinGaussianSplatPassResources::GetAccumulationSurface() const {
return m_accumulationSurface.texture != nullptr ? &m_accumulationSurface : nullptr;
}
void BuiltinGaussianSplatPassResources::DestroyBufferView(CachedBufferView& bufferView) {
if (bufferView.shaderResourceView != nullptr) {
bufferView.shaderResourceView->Shutdown();
delete bufferView.shaderResourceView;
bufferView.shaderResourceView = nullptr;
}
if (bufferView.unorderedAccessView != nullptr) {
bufferView.unorderedAccessView->Shutdown();
delete bufferView.unorderedAccessView;
bufferView.unorderedAccessView = nullptr;
}
if (bufferView.buffer != nullptr) {
bufferView.buffer->Shutdown();
delete bufferView.buffer;
bufferView.buffer = nullptr;
}
bufferView.elementCount = 0u;
bufferView.elementStride = 0u;
bufferView.currentState = RHI::ResourceStates::Common;
}
void BuiltinGaussianSplatPassResources::DestroyAccumulationSurface(AccumulationSurface& surface) {
if (surface.renderTargetView != nullptr) {
surface.renderTargetView->Shutdown();
delete surface.renderTargetView;
surface.renderTargetView = nullptr;
}
if (surface.shaderResourceView != nullptr) {
surface.shaderResourceView->Shutdown();
delete surface.shaderResourceView;
surface.shaderResourceView = nullptr;
}
if (surface.texture != nullptr) {
surface.texture->Shutdown();
delete surface.texture;
surface.texture = nullptr;
}
surface.width = 0u;
surface.height = 0u;
surface.format = RHI::Format::Unknown;
surface.currentColorState = RHI::ResourceStates::Common;
}
bool BuiltinGaussianSplatPassResources::ResetForDevice(RHI::RHIDevice* device) {
if (m_device == nullptr) {
m_device = device;
return true;
}
if (m_device == device) {
return true;
}
Shutdown();
m_device = device;
return true;
}
bool BuiltinGaussianSplatPassResources::RecreateWorkingSet(
RHI::RHIDevice* device,
Core::uint32 splatCapacity,
WorkingSet& workingSet) {
if (!CreateStructuredBufferView(device, splatCapacity, kSortDistanceStride, workingSet.sortDistances) ||
!CreateStructuredBufferView(device, splatCapacity, kOrderIndexStride, workingSet.orderIndices) ||
!CreateStructuredBufferView(device, splatCapacity, kViewDataStride, workingSet.viewData)) {
DestroyBufferView(workingSet.sortDistances);
DestroyBufferView(workingSet.orderIndices);
DestroyBufferView(workingSet.viewData);
workingSet.renderer = nullptr;
workingSet.splatCapacity = 0u;
return false;
}
workingSet.splatCapacity = splatCapacity;
return true;
}
bool BuiltinGaussianSplatPassResources::CreateStructuredBufferView(
RHI::RHIDevice* device,
Core::uint32 elementCount,
Core::uint32 elementStride,
CachedBufferView& bufferView) {
return CreateStructuredBufferViews(device, elementCount, elementStride, bufferView);
}
bool BuiltinGaussianSplatPassResources::RecreateAccumulationSurface(
RHI::RHIDevice* device,
Core::uint32 width,
Core::uint32 height,
RHI::Format format) {
DestroyAccumulationSurface(m_accumulationSurface);
RHI::TextureDesc textureDesc = {};
textureDesc.width = width;
textureDesc.height = height;
textureDesc.depth = 1u;
textureDesc.mipLevels = 1u;
textureDesc.arraySize = 1u;
textureDesc.format = static_cast<uint32_t>(format);
textureDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
textureDesc.sampleCount = 1u;
textureDesc.sampleQuality = 0u;
textureDesc.flags = 0u;
m_accumulationSurface.texture = device->CreateTexture(textureDesc);
if (m_accumulationSurface.texture == nullptr) {
return false;
}
RHI::ResourceViewDesc viewDesc = {};
viewDesc.format = static_cast<uint32_t>(format);
viewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
viewDesc.mipLevel = 0u;
m_accumulationSurface.renderTargetView = device->CreateRenderTargetView(m_accumulationSurface.texture, viewDesc);
if (m_accumulationSurface.renderTargetView == nullptr) {
DestroyAccumulationSurface(m_accumulationSurface);
return false;
}
m_accumulationSurface.shaderResourceView = device->CreateShaderResourceView(m_accumulationSurface.texture, viewDesc);
if (m_accumulationSurface.shaderResourceView == nullptr) {
DestroyAccumulationSurface(m_accumulationSurface);
return false;
}
m_accumulationSurface.width = width;
m_accumulationSurface.height = height;
m_accumulationSurface.format = format;
m_accumulationSurface.currentColorState = RHI::ResourceStates::Common;
return true;
}
} // namespace Internal
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,104 @@
#pragma once
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Rendering/FrameData/VisibleGaussianSplatItem.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
#include <unordered_map>
namespace XCEngine {
namespace Rendering {
namespace Passes {
namespace Internal {
struct GaussianSplatViewData {
Math::Vector4 clipCenter = Math::Vector4::Zero();
Math::Vector4 ellipseAxisU = Math::Vector4::Zero();
Math::Vector4 ellipseAxisV = Math::Vector4::Zero();
Math::Vector4 colorOpacity = Math::Vector4::Zero();
};
class BuiltinGaussianSplatPassResources final {
public:
struct CachedBufferView {
RHI::RHIBuffer* buffer = nullptr;
RHI::RHIResourceView* shaderResourceView = nullptr;
RHI::RHIResourceView* unorderedAccessView = nullptr;
Core::uint32 elementCount = 0u;
Core::uint32 elementStride = 0u;
RHI::ResourceStates currentState = RHI::ResourceStates::Common;
};
struct WorkingSet {
const Components::GaussianSplatRendererComponent* renderer = nullptr;
Core::uint32 splatCapacity = 0u;
CachedBufferView sortDistances = {};
CachedBufferView orderIndices = {};
CachedBufferView viewData = {};
};
struct AccumulationSurface {
Core::uint32 width = 0u;
Core::uint32 height = 0u;
RHI::Format format = RHI::Format::Unknown;
RHI::RHITexture* texture = nullptr;
RHI::RHIResourceView* renderTargetView = nullptr;
RHI::RHIResourceView* shaderResourceView = nullptr;
RHI::ResourceStates currentColorState = RHI::ResourceStates::Common;
};
~BuiltinGaussianSplatPassResources();
bool EnsureWorkingSet(
RHI::RHIDevice* device,
const VisibleGaussianSplatItem& visibleGaussianSplat,
WorkingSet*& outWorkingSet);
bool EnsureAccumulationSurface(
RHI::RHIDevice* device,
Core::uint32 width,
Core::uint32 height,
RHI::Format format,
AccumulationSurface*& outSurface);
void Shutdown();
size_t GetWorkingSetCount() const { return m_workingSets.size(); }
const WorkingSet* FindWorkingSet(
const Components::GaussianSplatRendererComponent* renderer) const;
const AccumulationSurface* GetAccumulationSurface() const;
private:
static constexpr Core::uint32 kSortDistanceStride = sizeof(float);
static constexpr Core::uint32 kOrderIndexStride = sizeof(Core::uint32);
static constexpr Core::uint32 kViewDataStride = sizeof(GaussianSplatViewData);
static void DestroyBufferView(CachedBufferView& bufferView);
static void DestroyAccumulationSurface(AccumulationSurface& surface);
bool ResetForDevice(RHI::RHIDevice* device);
bool RecreateWorkingSet(
RHI::RHIDevice* device,
Core::uint32 splatCapacity,
WorkingSet& workingSet);
bool CreateStructuredBufferView(
RHI::RHIDevice* device,
Core::uint32 elementCount,
Core::uint32 elementStride,
CachedBufferView& bufferView);
bool RecreateAccumulationSurface(
RHI::RHIDevice* device,
Core::uint32 width,
Core::uint32 height,
RHI::Format format);
RHI::RHIDevice* m_device = nullptr;
std::unordered_map<const Components::GaussianSplatRendererComponent*, WorkingSet> m_workingSets;
AccumulationSurface m_accumulationSurface = {};
};
} // namespace Internal
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine