Add 3DGS D3D12 MVS bootstrap and PLY loader
This commit is contained in:
472
MVS/3DGS-D3D12/src/App.cpp
Normal file
472
MVS/3DGS-D3D12/src/App.cpp
Normal 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
|
||||
Reference in New Issue
Block a user