Add 3DGS D3D12 MVS bootstrap and PLY loader

This commit is contained in:
2026-04-13 00:36:50 +08:00
parent 6f876678f5
commit 64212a53c7
8 changed files with 1534 additions and 0 deletions

472
MVS/3DGS-D3D12/src/App.cpp Normal file
View File

@@ -0,0 +1,472 @@
#include "XC3DGSD3D12/App.h"
#include <d3d12.h>
#include <dxgi1_4.h>
#include <filesystem>
namespace XC3DGSD3D12 {
using namespace XCEngine::RHI;
namespace {
constexpr wchar_t kWindowClassName[] = L"XC3DGSD3D12WindowClass";
constexpr wchar_t kWindowTitle[] = L"XC 3DGS D3D12 MVS - Phase 1";
constexpr float kClearColor[4] = { 0.08f, 0.12f, 0.18f, 1.0f };
std::filesystem::path GetExecutableDirectory() {
std::wstring pathBuffer;
pathBuffer.resize(MAX_PATH);
const DWORD pathLength = GetModuleFileNameW(nullptr, pathBuffer.data(), static_cast<DWORD>(pathBuffer.size()));
pathBuffer.resize(pathLength);
return std::filesystem::path(pathBuffer).parent_path();
}
std::filesystem::path ResolveNearExecutable(const std::wstring& path) {
const std::filesystem::path inputPath(path);
if (inputPath.is_absolute()) {
return inputPath;
}
return GetExecutableDirectory() / inputPath;
}
}
App::App() = default;
App::~App() {
Shutdown();
}
bool App::Initialize(HINSTANCE instance, int showCommand) {
m_instance = instance;
m_lastErrorMessage.clear();
if (!LoadGaussianScene()) {
return false;
}
if (!RegisterWindowClass(instance)) {
m_lastErrorMessage = L"Failed to register the Win32 window class.";
return false;
}
if (!CreateMainWindow(instance, showCommand)) {
m_lastErrorMessage = L"Failed to create the main window.";
return false;
}
if (!InitializeRhi()) {
if (m_lastErrorMessage.empty()) {
m_lastErrorMessage = L"Failed to initialize the D3D12 RHI objects.";
}
return false;
}
if (!InitializeGaussianGpuResources()) {
return false;
}
m_isInitialized = true;
m_running = true;
return true;
}
void App::SetFrameLimit(unsigned int frameLimit) {
m_frameLimit = frameLimit;
}
void App::SetGaussianScenePath(std::wstring scenePath) {
m_gaussianScenePath = std::move(scenePath);
}
void App::SetSummaryPath(std::wstring summaryPath) {
m_summaryPath = std::move(summaryPath);
}
const std::wstring& App::GetLastErrorMessage() const {
return m_lastErrorMessage;
}
int App::Run() {
MSG message = {};
while (m_running) {
while (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
if (message.message == WM_QUIT) {
m_running = false;
break;
}
TranslateMessage(&message);
DispatchMessage(&message);
}
if (!m_running) {
break;
}
RenderFrame();
++m_renderedFrameCount;
if (m_frameLimit > 0 && m_renderedFrameCount >= m_frameLimit) {
m_running = false;
PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
}
}
return static_cast<int>(message.wParam);
}
LRESULT CALLBACK App::StaticWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
App* app = nullptr;
if (message == WM_NCCREATE) {
CREATESTRUCTW* createStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
app = reinterpret_cast<App*>(createStruct->lpCreateParams);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(app));
} else {
app = reinterpret_cast<App*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
}
if (app != nullptr) {
return app->WindowProc(hwnd, message, wParam, lParam);
}
return DefWindowProcW(hwnd, message, wParam, lParam);
}
LRESULT App::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProcW(hwnd, message, wParam, lParam);
}
}
bool App::RegisterWindowClass(HINSTANCE instance) {
WNDCLASSEXW windowClass = {};
windowClass.cbSize = sizeof(WNDCLASSEXW);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = &App::StaticWindowProc;
windowClass.hInstance = instance;
windowClass.hCursor = LoadCursorW(nullptr, IDC_ARROW);
windowClass.lpszClassName = kWindowClassName;
return RegisterClassExW(&windowClass) != 0;
}
bool App::CreateMainWindow(HINSTANCE instance, int showCommand) {
RECT windowRect = { 0, 0, m_width, m_height };
AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
m_hwnd = CreateWindowExW(
0,
kWindowClassName,
kWindowTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
nullptr,
nullptr,
instance,
this);
if (m_hwnd == nullptr) {
return false;
}
ShowWindow(m_hwnd, showCommand);
UpdateWindow(m_hwnd);
return true;
}
bool App::LoadGaussianScene() {
std::string errorMessage;
const std::filesystem::path scenePath = ResolveNearExecutable(m_gaussianScenePath);
if (!LoadGaussianSceneFromPly(scenePath, m_gaussianSceneData, errorMessage)) {
m_lastErrorMessage.assign(errorMessage.begin(), errorMessage.end());
return false;
}
if (!m_summaryPath.empty()) {
const std::filesystem::path summaryPath = ResolveNearExecutable(m_summaryPath);
if (!WriteGaussianSceneSummary(summaryPath, m_gaussianSceneData, errorMessage)) {
m_lastErrorMessage.assign(errorMessage.begin(), errorMessage.end());
return false;
}
}
return true;
}
bool App::InitializeRhi() {
RHIDeviceDesc deviceDesc = {};
deviceDesc.adapterIndex = 0;
deviceDesc.enableDebugLayer = false;
deviceDesc.enableGPUValidation = false;
if (!m_device.Initialize(deviceDesc)) {
m_lastErrorMessage = L"Failed to initialize the XCEngine D3D12 device.";
return false;
}
ID3D12Device* device = m_device.GetDevice();
IDXGIFactory4* factory = m_device.GetFactory();
if (!m_commandQueue.Initialize(device, CommandQueueType::Direct)) {
m_lastErrorMessage = L"Failed to initialize the direct command queue.";
return false;
}
if (!m_swapChain.Initialize(factory, m_commandQueue.GetCommandQueue(), m_hwnd, m_width, m_height, kBackBufferCount)) {
m_lastErrorMessage = L"Failed to initialize the swap chain.";
return false;
}
if (!m_depthStencil.InitializeDepthStencil(device, m_width, m_height)) {
m_lastErrorMessage = L"Failed to initialize the depth stencil texture.";
return false;
}
if (!m_rtvHeap.Initialize(device, DescriptorHeapType::RTV, kBackBufferCount)) {
m_lastErrorMessage = L"Failed to initialize the RTV descriptor heap.";
return false;
}
if (!m_dsvHeap.Initialize(device, DescriptorHeapType::DSV, 1)) {
m_lastErrorMessage = L"Failed to initialize the DSV descriptor heap.";
return false;
}
for (int index = 0; index < kBackBufferCount; ++index) {
D3D12Texture& backBuffer = m_swapChain.GetBackBuffer(index);
D3D12_RENDER_TARGET_VIEW_DESC renderTargetDesc =
D3D12ResourceView::CreateRenderTargetDesc(Format::R8G8B8A8_UNorm, D3D12_RTV_DIMENSION_TEXTURE2D);
m_rtvs[index].InitializeAsRenderTarget(device, backBuffer.GetResource(), &renderTargetDesc, &m_rtvHeap, index);
}
D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc =
D3D12ResourceView::CreateDepthStencilDesc(Format::D24_UNorm_S8_UInt, D3D12_DSV_DIMENSION_TEXTURE2D);
m_dsv.InitializeAsDepthStencil(device, m_depthStencil.GetResource(), &depthStencilDesc, &m_dsvHeap, 0);
if (!m_commandAllocator.Initialize(device, CommandQueueType::Direct)) {
m_lastErrorMessage = L"Failed to initialize the command allocator.";
return false;
}
if (!m_commandList.Initialize(device, CommandQueueType::Direct, m_commandAllocator.GetCommandAllocator())) {
m_lastErrorMessage = L"Failed to initialize the command list.";
return false;
}
return true;
}
bool App::InitializeGaussianGpuResources() {
ID3D12Device* device = m_device.GetDevice();
ID3D12GraphicsCommandList* commandList = m_commandList.GetCommandList();
m_commandAllocator.Reset();
m_commandList.Reset();
const D3D12_RESOURCE_STATES shaderResourceState =
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
m_gaussianPositionBuffer.SetStride(4);
if (!m_gaussianPositionBuffer.InitializeWithData(
device,
commandList,
m_gaussianSceneData.positionData.data(),
static_cast<uint64_t>(m_gaussianSceneData.positionData.size()),
shaderResourceState)) {
m_lastErrorMessage = L"Failed to upload the Gaussian position buffer.";
return false;
}
m_gaussianOtherBuffer.SetStride(4);
if (!m_gaussianOtherBuffer.InitializeWithData(
device,
commandList,
m_gaussianSceneData.otherData.data(),
static_cast<uint64_t>(m_gaussianSceneData.otherData.size()),
shaderResourceState)) {
m_lastErrorMessage = L"Failed to upload the Gaussian other buffer.";
return false;
}
m_gaussianShBuffer.SetStride(4);
if (!m_gaussianShBuffer.InitializeWithData(
device,
commandList,
m_gaussianSceneData.shData.data(),
static_cast<uint64_t>(m_gaussianSceneData.shData.size()),
shaderResourceState)) {
m_lastErrorMessage = L"Failed to upload the Gaussian SH buffer.";
return false;
}
D3D12_RESOURCE_DESC colorTextureDesc = {};
colorTextureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
colorTextureDesc.Alignment = 0;
colorTextureDesc.Width = m_gaussianSceneData.colorTextureWidth;
colorTextureDesc.Height = m_gaussianSceneData.colorTextureHeight;
colorTextureDesc.DepthOrArraySize = 1;
colorTextureDesc.MipLevels = 1;
colorTextureDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
colorTextureDesc.SampleDesc.Count = 1;
colorTextureDesc.SampleDesc.Quality = 0;
colorTextureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
colorTextureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
Microsoft::WRL::ComPtr<ID3D12Resource> colorUploadBuffer;
if (!m_gaussianColorTexture.InitializeFromData(
device,
commandList,
colorTextureDesc,
TextureType::Texture2D,
m_gaussianSceneData.colorData.data(),
m_gaussianSceneData.colorData.size(),
m_gaussianSceneData.colorTextureWidth * GaussianSplatRuntimeData::kColorStride,
&colorUploadBuffer)) {
m_lastErrorMessage = L"Failed to upload the Gaussian color texture.";
return false;
}
m_gaussianUploadBuffers.push_back(colorUploadBuffer);
ResourceViewDesc rawBufferViewDesc = {};
rawBufferViewDesc.dimension = ResourceViewDimension::RawBuffer;
m_gaussianPositionView.reset(static_cast<D3D12ResourceView*>(m_device.CreateShaderResourceView(&m_gaussianPositionBuffer, rawBufferViewDesc)));
if (!m_gaussianPositionView) {
m_lastErrorMessage = L"Failed to create the Gaussian position SRV.";
return false;
}
m_gaussianOtherView.reset(static_cast<D3D12ResourceView*>(m_device.CreateShaderResourceView(&m_gaussianOtherBuffer, rawBufferViewDesc)));
if (!m_gaussianOtherView) {
m_lastErrorMessage = L"Failed to create the Gaussian other SRV.";
return false;
}
m_gaussianShView.reset(static_cast<D3D12ResourceView*>(m_device.CreateShaderResourceView(&m_gaussianShBuffer, rawBufferViewDesc)));
if (!m_gaussianShView) {
m_lastErrorMessage = L"Failed to create the Gaussian SH SRV.";
return false;
}
ResourceViewDesc textureViewDesc = {};
textureViewDesc.dimension = ResourceViewDimension::Texture2D;
textureViewDesc.format = static_cast<uint32_t>(Format::R32G32B32A32_Float);
m_gaussianColorView.reset(static_cast<D3D12ResourceView*>(m_device.CreateShaderResourceView(&m_gaussianColorTexture, textureViewDesc)));
if (!m_gaussianColorView) {
m_lastErrorMessage = L"Failed to create the Gaussian color texture SRV.";
return false;
}
m_commandList.Close();
void* commandLists[] = { &m_commandList };
m_commandQueue.ExecuteCommandLists(1, commandLists);
m_commandQueue.WaitForIdle();
m_gaussianUploadBuffers.clear();
return true;
}
void App::ShutdownGaussianGpuResources() {
m_gaussianColorView.reset();
m_gaussianShView.reset();
m_gaussianOtherView.reset();
m_gaussianPositionView.reset();
m_gaussianUploadBuffers.clear();
m_gaussianColorTexture.Shutdown();
m_gaussianShBuffer.Shutdown();
m_gaussianOtherBuffer.Shutdown();
m_gaussianPositionBuffer.Shutdown();
}
void App::Shutdown() {
if (!m_isInitialized && m_hwnd == nullptr) {
return;
}
m_running = false;
if (m_commandQueue.GetCommandQueue() != nullptr) {
m_commandQueue.WaitForIdle();
}
ShutdownGaussianGpuResources();
m_commandList.Shutdown();
m_commandAllocator.Shutdown();
m_dsv.Shutdown();
for (D3D12ResourceView& rtv : m_rtvs) {
rtv.Shutdown();
}
m_dsvHeap.Shutdown();
m_rtvHeap.Shutdown();
m_depthStencil.Shutdown();
m_swapChain.Shutdown();
m_commandQueue.Shutdown();
m_device.Shutdown();
if (m_hwnd != nullptr) {
DestroyWindow(m_hwnd);
m_hwnd = nullptr;
}
if (m_instance != nullptr) {
UnregisterClassW(kWindowClassName, m_instance);
m_instance = nullptr;
}
m_isInitialized = false;
}
void App::RenderFrame() {
if (m_hasRenderedAtLeastOneFrame) {
m_commandQueue.WaitForPreviousFrame();
}
m_commandAllocator.Reset();
m_commandList.Reset();
const int currentBackBufferIndex = m_swapChain.GetCurrentBackBufferIndex();
D3D12Texture& backBuffer = m_swapChain.GetBackBuffer(currentBackBufferIndex);
m_commandList.TransitionBarrier(backBuffer.GetResource(), ResourceStates::Present, ResourceStates::RenderTarget);
const CPUDescriptorHandle rtvCpuHandle = m_rtvHeap.GetCPUDescriptorHandle(currentBackBufferIndex);
const CPUDescriptorHandle dsvCpuHandle = m_dsvHeap.GetCPUDescriptorHandle(0);
const D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr };
const D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { dsvCpuHandle.ptr };
m_commandList.SetRenderTargetsHandle(1, &rtvHandle, &dsvHandle);
const Viewport viewport = { 0.0f, 0.0f, static_cast<float>(m_width), static_cast<float>(m_height), 0.0f, 1.0f };
const Rect scissorRect = { 0, 0, m_width, m_height };
m_commandList.SetViewport(viewport);
m_commandList.SetScissorRect(scissorRect);
m_commandList.ClearRenderTargetView(rtvHandle, kClearColor, 0, nullptr);
m_commandList.ClearDepthStencilView(
dsvHandle,
D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,
1.0f,
0,
0,
nullptr);
m_commandList.TransitionBarrier(backBuffer.GetResource(), ResourceStates::RenderTarget, ResourceStates::Present);
m_commandList.Close();
void* commandLists[] = { &m_commandList };
m_commandQueue.ExecuteCommandLists(1, commandLists);
m_swapChain.Present(1, 0);
m_hasRenderedAtLeastOneFrame = true;
}
} // namespace XC3DGSD3D12