#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; }