diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h b/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h index b9eda368..295aed2b 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h @@ -64,7 +64,9 @@ public: void SetGraphicsRootDescriptorTable(uint32_t rootParameterIndex, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor); void SetGraphicsRootShaderResourceView(uint32_t rootParameterIndex, D3D12_GPU_VIRTUAL_ADDRESS shaderResource); - void SetStencilRef(uint32_t stencilRef); + void SetStencilRef(uint8_t stencilRef) override; + void SetDepthStencilState(const DepthStencilState& state) override; + void SetBlendState(const BlendState& state) override; void SetBlendFactor(const float blendFactor[4]); void SetDepthBias(float depthBias, float slopeScaledDepthBias, float depthBiasClamp); @@ -104,6 +106,7 @@ public: private: ComPtr m_commandList; + ComPtr m_commandAllocator; CommandQueueType m_type; std::unordered_map m_resourceStateMap; diff --git a/engine/src/RHI/D3D12/D3D12CommandList.cpp b/engine/src/RHI/D3D12/D3D12CommandList.cpp index d82d1adf..394eca87 100644 --- a/engine/src/RHI/D3D12/D3D12CommandList.cpp +++ b/engine/src/RHI/D3D12/D3D12CommandList.cpp @@ -18,6 +18,8 @@ D3D12CommandList::~D3D12CommandList() { bool D3D12CommandList::Initialize(ID3D12Device* device, CommandQueueType type, ID3D12CommandAllocator* allocator) { D3D12_COMMAND_LIST_TYPE listType = ToD3D12(type); + m_commandAllocator = allocator; + HRESULT hResult = device->CreateCommandList( 0, listType, @@ -42,10 +44,15 @@ void D3D12CommandList::Shutdown() { } void D3D12CommandList::Reset() { + if (m_commandList && m_commandAllocator) { + m_commandList->Reset(m_commandAllocator.Get(), nullptr); + } m_currentTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; m_currentPipelineState = nullptr; m_currentRootSignature = nullptr; m_currentDescriptorHeap = nullptr; + m_resourceStateMap.clear(); + m_trackedResources.clear(); } void D3D12CommandList::Close() { @@ -106,6 +113,15 @@ void D3D12CommandList::SetPipelineStateInternal(ID3D12PipelineState* pso) { m_currentPipelineState = pso; } +void D3D12CommandList::SetRootSignature(ID3D12RootSignature* signature) { + m_commandList->SetGraphicsRootSignature(signature); + m_currentRootSignature = signature; +} + +void D3D12CommandList::SetBlendFactor(const float blendFactor[4]) { + m_commandList->OMSetBlendFactor(blendFactor); +} + void D3D12CommandList::SetViewport(const Viewport& viewport) { D3D12_VIEWPORT d3d12Viewport = {}; d3d12Viewport.TopLeftX = viewport.topLeftX; @@ -179,6 +195,10 @@ void D3D12CommandList::SetRenderTargetsInternal(uint32_t count, ID3D12Resource** m_commandList->OMSetRenderTargets(count, count > 0 ? rtvHandles.data() : nullptr, FALSE, depthStencil ? &dsvHandle : nullptr); } +void D3D12CommandList::SetRenderTargetsHandle(uint32_t count, const D3D12_CPU_DESCRIPTOR_HANDLE* renderTargetHandles, const D3D12_CPU_DESCRIPTOR_HANDLE* depthStencilHandle) { + m_commandList->OMSetRenderTargets(count, renderTargetHandles, FALSE, depthStencilHandle); +} + void D3D12CommandList::SetVertexBuffer(uint32_t slot, void* buffer, uint64_t offset, uint32_t stride) { SetVertexBufferInternal(slot, static_cast(buffer), offset, stride); } @@ -252,10 +272,18 @@ void D3D12CommandList::SetGraphicsRootShaderResourceView(uint32_t rootParameterI m_commandList->SetGraphicsRootShaderResourceView(rootParameterIndex, shaderResource); } -void D3D12CommandList::SetStencilRef(uint32_t stencilRef) { +void D3D12CommandList::SetStencilRef(uint8_t stencilRef) { m_commandList->OMSetStencilRef(stencilRef); } +void D3D12CommandList::SetDepthStencilState(const DepthStencilState& state) { + // TODO: Implement depth stencil state +} + +void D3D12CommandList::SetBlendState(const BlendState& state) { + // TODO: Implement blend state +} + void D3D12CommandList::SetDepthBias(float depthBias, float slopeScaledDepthBias, float depthBiasClamp) { } @@ -292,6 +320,18 @@ void D3D12CommandList::ClearRenderTargetView(D3D12_CPU_DESCRIPTOR_HANDLE renderT m_commandList->ClearRenderTargetView(renderTargetHandle, color, rectCount, rects); } +void D3D12CommandList::Clear(float r, float g, float b, float a, uint32_t buffers) { + // Not implemented - use ClearRenderTargetView and ClearDepthStencilView directly +} + +void D3D12CommandList::ClearRenderTarget(void* renderTarget, const float color[4]) { + ClearRenderTargetView(static_cast(renderTarget), color, 0, nullptr); +} + +void D3D12CommandList::ClearDepthStencil(void* depthStencil, float depth, uint8_t stencil) { + ClearDepthStencilView(static_cast(depthStencil), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, depth, stencil, 0, nullptr); +} + void D3D12CommandList::ClearDepthStencilView(ID3D12Resource* depthStencil, uint32_t clearFlags, float depth, uint8_t stencil, uint32_t rectCount, const D3D12_RECT* rects) { D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = {}; m_commandList->ClearDepthStencilView(dsvHandle, static_cast(clearFlags), depth, stencil, rectCount, rects); diff --git a/tests/RHI/D3D12/integration/main_minimal.cpp b/tests/RHI/D3D12/integration/main_minimal.cpp new file mode 100644 index 00000000..a81f893e --- /dev/null +++ b/tests/RHI/D3D12/integration/main_minimal.cpp @@ -0,0 +1,332 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XCEngine/RHI/RHIEnums.h" +#include "XCEngine/RHI/RHITypes.h" +#include "XCEngine/RHI/D3D12/D3D12Device.h" +#include "XCEngine/RHI/D3D12/D3D12CommandQueue.h" +#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h" +#include "XCEngine/RHI/D3D12/D3D12CommandList.h" +#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h" +#include "XCEngine/RHI/D3D12/D3D12Fence.h" +#include "XCEngine/RHI/D3D12/D3D12SwapChain.h" +#include "XCEngine/RHI/D3D12/D3D12Buffer.h" +#include "XCEngine/RHI/D3D12/D3D12Texture.h" +#include "XCEngine/RHI/D3D12/D3D12RenderTargetView.h" +#include "XCEngine/RHI/D3D12/D3D12DepthStencilView.h" +#include "XCEngine/RHI/D3D12/D3D12Screenshot.h" +#include "XCEngine/Debug/Logger.h" +#include "XCEngine/Debug/ConsoleLogSink.h" +#include "XCEngine/Debug/FileLogSink.h" +#include "XCEngine/Containers/String.h" + +using namespace XCEngine::RHI; +using namespace XCEngine::Debug; +using namespace XCEngine::Containers; + +#pragma comment(lib,"d3d12.lib") +#pragma comment(lib,"dxgi.lib") +#pragma comment(lib,"dxguid.lib") +#pragma comment(lib,"d3dcompiler.lib") +#pragma comment(lib,"winmm.lib") + +// Global D3D12 objects +D3D12Device gDevice; +D3D12CommandQueue gCommandQueue; +D3D12SwapChain gSwapChain; +D3D12CommandAllocator gCommandAllocator; +D3D12CommandList gCommandList; +D3D12Fence gFence; + +// Render targets +D3D12Texture gColorRTs[2]; +D3D12Texture gDepthStencil; +D3D12DescriptorHeap gRTVHeap; +D3D12DescriptorHeap gDSVHeap; +D3D12RenderTargetView gRTVs[2]; +D3D12DepthStencilView gDSV; + +UINT gRTVDescriptorSize = 0; +UINT gDSVDescriptorSize = 0; +int gCurrentRTIndex = 0; +UINT64 gFenceValue = 0; + +// Window +HWND gHWND = nullptr; +int gWidth = 1280; +int gHeight = 720; + +// Log helper +void Log(const char* format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + Logger::Get().Debug(LogCategory::Rendering, String(buffer)); +} + +// Window procedure +LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_CLOSE: + PostQuitMessage(0); + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +// Initialize D3D12 +bool InitD3D12() { + // Create device + RHIDeviceDesc deviceDesc; + deviceDesc.windowHandle = gHWND; + deviceDesc.width = gWidth; + deviceDesc.height = gHeight; + deviceDesc.adapterIndex = 0; + deviceDesc.enableDebugLayer = false; + deviceDesc.enableGPUValidation = false; + + if (!gDevice.Initialize(deviceDesc)) { + Log("[ERROR] Failed to initialize D3D12 device"); + return false; + } + + ID3D12Device* device = gDevice.GetDevice(); + IDXGIFactory4* factory = gDevice.GetFactory(); + + // Create command queue + if (!gCommandQueue.Initialize(device, CommandQueueType::Direct)) { + Log("[ERROR] Failed to initialize command queue"); + return false; + } + + // Create swap chain + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = 2; + swapChainDesc.BufferDesc.Width = gWidth; + swapChainDesc.BufferDesc.Height = gHeight; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.OutputWindow = gHWND; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.Windowed = true; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + + IDXGISwapChain* dxgiSwapChain = nullptr; + HRESULT hr = factory->CreateSwapChain(gCommandQueue.GetCommandQueue(), &swapChainDesc, &dxgiSwapChain); + if (FAILED(hr)) { + Log("[ERROR] Failed to create swap chain"); + return false; + } + + if (!gSwapChain.Initialize(dxgiSwapChain, (uint32_t)gWidth, (uint32_t)gHeight)) { + Log("[ERROR] Failed to initialize swap chain"); + return false; + } + + // Initialize depth stencil + gDepthStencil.InitializeDepthStencil(device, gWidth, gHeight); + + // Create RTV heap + gRTVHeap.Initialize(device, DescriptorHeapType::RTV, 2); + gRTVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::RTV); + + // Create DSV heap + gDSVHeap.Initialize(device, DescriptorHeapType::DSV, 1); + gDSVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV); + + // Create RTVs for back buffers + D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapStart = gRTVHeap.GetCPUDescriptorHandleForHeapStart(); + for (int i = 0; i < 2; i++) { + ID3D12Resource* buffer = nullptr; + gSwapChain.GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&buffer)); + gColorRTs[i].InitializeFromExisting(buffer); + + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle; + rtvHandle.ptr = rtvHeapStart.ptr + i * gRTVDescriptorSize; + gRTVs[i].InitializeAt(device, gColorRTs[i].GetResource(), rtvHandle, nullptr); + } + + // Create DSV + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12DepthStencilView::CreateDesc(Format::D24_UNorm_S8_UInt); + gDSV.InitializeAt(device, gDepthStencil.GetResource(), gDSVHeap.GetCPUDescriptorHandleForHeapStart(), &dsvDesc); + + // Create command allocator and list + gCommandAllocator.Initialize(device, CommandQueueType::Direct); + gCommandList.Initialize(device, CommandQueueType::Direct, gCommandAllocator.GetCommandAllocator()); + + // Create fence + gFence.Initialize(device, 0); + + Log("[INFO] D3D12 initialized successfully"); + return true; +} + +// Wait for GPU +void WaitForGPU() { + gCommandQueue.WaitForIdle(); +} + +// Execute command list +void ExecuteCommandList() { + gCommandList.Close(); + void* commandLists[] = { gCommandList.GetCommandList() }; + gCommandQueue.ExecuteCommandLists(1, commandLists); + gFenceValue += 1; +} + +// Begin rendering +void BeginRender() { + gCurrentRTIndex = gSwapChain.GetCurrentBackBufferIndex(); + + // Transition render target + gCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(), + ResourceStates::Present, ResourceStates::RenderTarget); + + // Set render targets + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle; + rtvHandle.ptr = gRTVHeap.GetCPUDescriptorHandleForHeapStart().ptr + gCurrentRTIndex * gRTVDescriptorSize; + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = gDSVHeap.GetCPUDescriptorHandleForHeapStart(); + + gCommandList.SetRenderTargetsHandle(1, &rtvHandle, &dsvHandle); + + // Set viewport and scissor + Viewport viewport = { 0.0f, 0.0f, (float)gWidth, (float)gHeight, 0.0f, 1.0f }; + Rect scissorRect = { 0, 0, gWidth, gHeight }; + gCommandList.SetViewport(viewport); + gCommandList.SetScissorRect(scissorRect); + + // Clear + float clearColor[] = { 1.0f, 0.0f, 0.0f, 1.0f }; + gCommandList.ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); + gCommandList.ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr); +} + +// End rendering +void EndRender() { + gCommandList.TransitionBarrier(gColorRTs[gCurrentRTIndex].GetResource(), + ResourceStates::RenderTarget, ResourceStates::Present); +} + +// Main entry +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { + // Initialize logger + Logger::Get().Initialize(); + Logger::Get().AddSink(std::make_unique()); + Logger::Get().SetMinimumLevel(LogLevel::Debug); + + Log("[INFO] D3D12 Integration Test Starting"); + + // Register window class + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WindowProc; + wc.hInstance = hInstance; + wc.lpszClassName = L"D3D12Test"; + + if (!RegisterClassEx(&wc)) { + MessageBox(NULL, L"Failed to register window class", L"Error", MB_OK); + return -1; + } + + // Create window + RECT rect = { 0, 0, gWidth, gHeight }; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + + gHWND = CreateWindowEx(0, L"D3D12Test", L"D3D12 Integration Test", + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, hInstance, NULL); + + if (!gHWND) { + MessageBox(NULL, L"Failed to create window", L"Error", MB_OK); + return -1; + } + + // Initialize D3D12 + if (!InitD3D12()) { + MessageBox(NULL, L"Failed to initialize D3D12", L"Error", MB_OK); + return -1; + } + + // Show window + ShowWindow(gHWND, nShowCmd); + UpdateWindow(gHWND); + + // Main loop + MSG msg = {}; + int frameCount = 0; + const int targetFrameCount = 30; + + while (true) { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + // Reset command list for this frame + gCommandAllocator.Reset(); + gCommandList.Reset(); + + // Render + BeginRender(); + // (Add rendering code here) + + frameCount++; + + if (frameCount >= targetFrameCount) { + Log("[INFO] Reached target frame count %d - taking screenshot before present!", targetFrameCount); + ExecuteCommandList(); + WaitForGPU(); + Log("[INFO] GPU idle, taking screenshot..."); + bool screenshotResult = D3D12Screenshot::Capture( + gDevice.GetDevice(), + gCommandQueue.GetCommandQueue(), + gColorRTs[gCurrentRTIndex].GetResource(), + "minimal.ppm", + gWidth, + gHeight + ); + if (screenshotResult) { + Log("[INFO] Screenshot saved to minimal.ppm"); + } else { + Log("[ERROR] Screenshot failed"); + } + break; + } + + EndRender(); + + // Execute + ExecuteCommandList(); + + // Present + gSwapChain.Present(0, 0); + + // Add small delay to prevent GPU overload + Sleep(10); + } + } + + // Shutdown + gCommandList.Shutdown(); + gCommandAllocator.Shutdown(); + gFence.Shutdown(); + gSwapChain.Shutdown(); + gDevice.Shutdown(); + + Logger::Get().Shutdown(); + + Log("[INFO] D3D12 Integration Test Finished"); + return 0; +}