Files
XCEngine/editor/src/Platform/D3D12WindowRenderer.h

334 lines
10 KiB
C++

#pragma once
#include "UI/ImGuiBackendBridge.h"
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/RHI/RHICommandList.h>
#include <XCEngine/RHI/RHICommandQueue.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIFactory.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHISwapChain.h>
#include <XCEngine/RHI/D3D12/D3D12CommandList.h>
#include <XCEngine/RHI/D3D12/D3D12CommandQueue.h>
#include <XCEngine/RHI/D3D12/D3D12DescriptorHeap.h>
#include <XCEngine/RHI/D3D12/D3D12Device.h>
#include <XCEngine/RHI/D3D12/D3D12SwapChain.h>
#include <d3d12.h>
#include <functional>
#include <vector>
#include <windows.h>
namespace XCEngine {
namespace Editor {
namespace Platform {
class D3D12WindowRenderer {
public:
static constexpr UINT kSrvDescriptorCount = 64;
static constexpr uint32_t kSwapChainBufferCount = 3;
bool Initialize(HWND hwnd, int width, int height) {
Shutdown();
if (hwnd == nullptr || width <= 0 || height <= 0) {
return false;
}
m_hwnd = hwnd;
m_width = width;
m_height = height;
m_device = RHI::RHIFactory::CreateRHIDevice(RHI::RHIType::D3D12);
if (m_device == nullptr) {
Shutdown();
return false;
}
RHI::RHIDeviceDesc deviceDesc = {};
if (!m_device->Initialize(deviceDesc)) {
Shutdown();
return false;
}
RHI::CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(RHI::CommandQueueType::Direct);
m_commandQueue = m_device->CreateCommandQueue(queueDesc);
if (m_commandQueue == nullptr || GetD3D12CommandQueue() == nullptr) {
Shutdown();
return false;
}
RHI::CommandListDesc commandListDesc = {};
commandListDesc.commandListType = static_cast<uint32_t>(RHI::CommandQueueType::Direct);
m_commandList = m_device->CreateCommandList(commandListDesc);
if (m_commandList == nullptr || GetD3D12CommandList() == nullptr) {
Shutdown();
return false;
}
m_commandList->Close();
RHI::SwapChainDesc swapChainDesc = {};
swapChainDesc.windowHandle = hwnd;
swapChainDesc.width = static_cast<uint32_t>(width);
swapChainDesc.height = static_cast<uint32_t>(height);
swapChainDesc.bufferCount = kSwapChainBufferCount;
m_swapChain = m_device->CreateSwapChain(swapChainDesc, m_commandQueue);
if (m_swapChain == nullptr || GetD3D12SwapChain() == nullptr) {
Shutdown();
return false;
}
RHI::DescriptorPoolDesc srvPoolDesc = {};
srvPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
srvPoolDesc.descriptorCount = kSrvDescriptorCount;
srvPoolDesc.shaderVisible = true;
m_srvPool = m_device->CreateDescriptorPool(srvPoolDesc);
m_srvHeap = dynamic_cast<RHI::D3D12DescriptorHeap*>(m_srvPool);
if (m_srvPool == nullptr || m_srvHeap == nullptr) {
Shutdown();
return false;
}
m_srvDescriptorSize = m_srvHeap->GetDescriptorSize();
if (!RecreateBackBufferViews()) {
Shutdown();
return false;
}
return true;
}
void Shutdown() {
WaitForGpuIdle();
ReleaseBackBufferViews();
if (m_srvPool) {
m_srvPool->Shutdown();
delete m_srvPool;
m_srvPool = nullptr;
}
m_srvHeap = nullptr;
if (m_swapChain) {
m_swapChain->Shutdown();
delete m_swapChain;
m_swapChain = nullptr;
}
if (m_commandList) {
m_commandList->Shutdown();
delete m_commandList;
m_commandList = nullptr;
}
if (m_commandQueue) {
m_commandQueue->Shutdown();
delete m_commandQueue;
m_commandQueue = nullptr;
}
if (m_device) {
m_device->Shutdown();
delete m_device;
m_device = nullptr;
}
m_hwnd = nullptr;
m_width = 0;
m_height = 0;
m_srvDescriptorSize = 0;
}
void Resize(int width, int height) {
if (width <= 0 || height <= 0 || m_swapChain == nullptr) {
return;
}
WaitForGpuIdle();
ReleaseBackBufferViews();
m_swapChain->Resize(static_cast<uint32_t>(width), static_cast<uint32_t>(height));
m_width = width;
m_height = height;
RecreateBackBufferViews();
}
bool BeginFrame() {
auto* d3d12Queue = GetD3D12CommandQueue();
auto* d3d12CommandList = GetD3D12CommandList();
if (m_swapChain == nullptr ||
d3d12Queue == nullptr ||
d3d12CommandList == nullptr ||
m_srvHeap == nullptr) {
return false;
}
// Viewport panels can resize and recreate render targets while building ImGui.
// Make sure the previous frame is fully retired before any CPU-side resource destruction.
d3d12Queue->WaitForPreviousFrame();
m_commandList->Reset();
return true;
}
void Render(
UI::ImGuiBackendBridge& imguiBackend,
const float clearColor[4],
const std::function<void(const Rendering::RenderContext&)>& beforeUiRender = {}) {
auto* d3d12Queue = GetD3D12CommandQueue();
auto* d3d12CommandList = GetD3D12CommandList();
if (m_swapChain == nullptr ||
d3d12Queue == nullptr ||
d3d12CommandList == nullptr ||
m_srvHeap == nullptr) {
return;
}
if (beforeUiRender) {
beforeUiRender(GetRenderContext());
}
const uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
if (backBufferIndex >= m_backBufferViews.size() || m_backBufferViews[backBufferIndex] == nullptr) {
return;
}
RHI::RHIResourceView* renderTargetView = m_backBufferViews[backBufferIndex];
d3d12CommandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::Present,
RHI::ResourceStates::RenderTarget);
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
d3d12CommandList->ClearRenderTarget(renderTargetView, clearColor);
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap->GetDescriptorHeap() };
d3d12CommandList->SetDescriptorHeaps(1, descriptorHeaps);
imguiBackend.RenderDrawData(d3d12CommandList->GetCommandList());
d3d12CommandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::RenderTarget,
RHI::ResourceStates::Present);
m_commandList->Close();
void* commandLists[] = { m_commandList };
m_commandQueue->ExecuteCommandLists(1, commandLists);
m_swapChain->Present(1, 0);
}
ID3D12Device* GetDevice() const {
const auto* device = GetD3D12Device();
return device ? device->GetDevice() : nullptr;
}
ID3D12DescriptorHeap* GetSrvHeap() const {
return m_srvHeap ? m_srvHeap->GetDescriptorHeap() : nullptr;
}
ID3D12CommandQueue* GetCommandQueue() const {
const auto* queue = GetD3D12CommandQueue();
return queue ? queue->GetCommandQueue() : nullptr;
}
UINT GetSrvDescriptorSize() const {
return m_srvDescriptorSize;
}
UINT GetSrvDescriptorCount() const {
return kSrvDescriptorCount;
}
RHI::RHIDevice* GetRHIDevice() const {
return m_device;
}
RHI::RHISwapChain* GetSwapChain() const {
return m_swapChain;
}
Rendering::RenderContext GetRenderContext() const {
Rendering::RenderContext context = {};
context.device = m_device;
context.commandList = m_commandList;
context.commandQueue = m_commandQueue;
context.backendType = RHI::RHIType::D3D12;
return context;
}
private:
RHI::D3D12Device* GetD3D12Device() const {
return m_device ? static_cast<RHI::D3D12Device*>(m_device) : nullptr;
}
RHI::D3D12CommandQueue* GetD3D12CommandQueue() const {
return m_commandQueue ? static_cast<RHI::D3D12CommandQueue*>(m_commandQueue) : nullptr;
}
RHI::D3D12CommandList* GetD3D12CommandList() const {
return m_commandList ? static_cast<RHI::D3D12CommandList*>(m_commandList) : nullptr;
}
RHI::D3D12SwapChain* GetD3D12SwapChain() const {
return m_swapChain ? static_cast<RHI::D3D12SwapChain*>(m_swapChain) : nullptr;
}
void WaitForGpuIdle() {
if (m_commandQueue != nullptr) {
m_commandQueue->WaitForIdle();
}
}
void ReleaseBackBufferViews() {
for (RHI::RHIResourceView* view : m_backBufferViews) {
if (view != nullptr) {
view->Shutdown();
delete view;
}
}
m_backBufferViews.clear();
}
bool RecreateBackBufferViews() {
auto* d3d12SwapChain = GetD3D12SwapChain();
if (m_device == nullptr || d3d12SwapChain == nullptr) {
return false;
}
m_backBufferViews.resize(kSwapChainBufferCount, nullptr);
RHI::ResourceViewDesc viewDesc = {};
viewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
viewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
for (uint32_t backBufferIndex = 0; backBufferIndex < kSwapChainBufferCount; ++backBufferIndex) {
m_backBufferViews[backBufferIndex] = m_device->CreateRenderTargetView(
&d3d12SwapChain->GetBackBuffer(backBufferIndex),
viewDesc);
if (m_backBufferViews[backBufferIndex] == nullptr) {
ReleaseBackBufferViews();
return false;
}
}
return true;
}
HWND m_hwnd = nullptr;
int m_width = 0;
int m_height = 0;
RHI::RHIDevice* m_device = nullptr;
RHI::RHICommandQueue* m_commandQueue = nullptr;
RHI::RHICommandList* m_commandList = nullptr;
RHI::RHISwapChain* m_swapChain = nullptr;
RHI::RHIDescriptorPool* m_srvPool = nullptr;
RHI::D3D12DescriptorHeap* m_srvHeap = nullptr;
std::vector<RHI::RHIResourceView*> m_backBufferViews;
UINT m_srvDescriptorSize = 0;
};
} // namespace Platform
} // namespace Editor
} // namespace XCEngine