Formalize GaussianSplat transient pass resources
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user