334 lines
10 KiB
C++
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
|