#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/D3D12Shader.h" #include "XCEngine/RHI/D3D12/D3D12RootSignature.h" #include "XCEngine/RHI/D3D12/D3D12PipelineState.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") D3D12Device gDevice; D3D12CommandQueue gCommandQueue; D3D12SwapChain gSwapChain; D3D12CommandAllocator gCommandAllocator; D3D12CommandList gCommandList; D3D12Texture gDepthStencil; D3D12DescriptorHeap gRTVHeap; D3D12DescriptorHeap gDSVHeap; D3D12RenderTargetView gRTVs[2]; D3D12DepthStencilView gDSV; D3D12Shader gVertexShader; D3D12Shader gPixelShader; D3D12RootSignature gRootSignature; D3D12PipelineState gPipelineState; D3D12Buffer gVertexBuffer; UINT gRTVDescriptorSize = 0; UINT gDSVDescriptorSize = 0; int gCurrentRTIndex = 0; HWND gHWND = nullptr; int gWidth = 1280; int gHeight = 720; 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)); } 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); } bool InitD3D12() { 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(); if (!gCommandQueue.Initialize(device, CommandQueueType::Direct)) { Log("[ERROR] Failed to initialize command queue"); return false; } if (!gSwapChain.Initialize(factory, gCommandQueue.GetCommandQueue(), gHWND, gWidth, gHeight, 2)) { Log("[ERROR] Failed to initialize swap chain"); return false; } gDepthStencil.InitializeDepthStencil(device, gWidth, gHeight); gRTVHeap.Initialize(device, DescriptorHeapType::RTV, 2); gRTVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::RTV); gDSVHeap.Initialize(device, DescriptorHeapType::DSV, 1); gDSVDescriptorSize = gDevice.GetDescriptorHandleIncrementSize(DescriptorHeapType::DSV); for (int i = 0; i < 2; i++) { D3D12Texture& backBuffer = gSwapChain.GetBackBuffer(i); CPUDescriptorHandle rtvCpuHandle = gRTVHeap.GetCPUDescriptorHandle(i); D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr }; gRTVs[i].InitializeAt(device, backBuffer.GetResource(), rtvHandle, nullptr); } D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12DepthStencilView::CreateDesc(Format::D24_UNorm_S8_UInt); CPUDescriptorHandle dsvCpuHandle = gDSVHeap.GetCPUDescriptorHandle(0); D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { dsvCpuHandle.ptr }; gDSV.InitializeAt(device, gDepthStencil.GetResource(), dsvHandle, &dsvDesc); gCommandAllocator.Initialize(device, CommandQueueType::Direct); gCommandList.Initialize(device, CommandQueueType::Direct, gCommandAllocator.GetCommandAllocator()); if (!gVertexShader.CompileFromFile(L"Res/Shader/triangle.hlsl", "MainVS", "vs_5_1")) { Log("[ERROR] Failed to compile vertex shader"); return false; } Log("[INFO] Vertex shader compiled, bytecode size: %zu", gVertexShader.GetBytecodeSize()); if (!gPixelShader.CompileFromFile(L"Res/Shader/triangle.hlsl", "MainPS", "ps_5_1")) { Log("[ERROR] Failed to compile pixel shader"); return false; } Log("[INFO] Pixel shader compiled, bytecode size: %zu", gPixelShader.GetBytecodeSize()); D3D12_ROOT_SIGNATURE_DESC rsDesc = D3D12RootSignature::CreateDesc(nullptr, 0); if (!gRootSignature.Initialize(device, rsDesc)) { Log("[ERROR] Failed to initialize root signature"); return false; } D3D12_INPUT_ELEMENT_DESC inputElements[] = { D3D12PipelineState::CreateInputElement("POSITION", 0, Format::R32G32B32A32_Float, 0, 0), D3D12PipelineState::CreateInputElement("COLOR", 0, Format::R32G32B32A32_Float, 0, 16), }; D3D12_SHADER_BYTECODE emptyGs = {}; D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; psoDesc.pRootSignature = gRootSignature.GetRootSignature(); psoDesc.VS = gVertexShader.GetD3D12Bytecode(); psoDesc.PS = gPixelShader.GetD3D12Bytecode(); psoDesc.GS = emptyGs; psoDesc.InputLayout.NumElements = 2; psoDesc.InputLayout.pInputElementDescs = inputElements; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; psoDesc.SampleDesc.Count = 1; psoDesc.SampleDesc.Quality = 0; psoDesc.SampleMask = 0xffffffff; psoDesc.NumRenderTargets = 1; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; psoDesc.RasterizerState.FrontCounterClockwise = FALSE; psoDesc.RasterizerState.DepthClipEnable = TRUE; psoDesc.DepthStencilState.DepthEnable = FALSE; psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS; psoDesc.BlendState.RenderTarget[0].BlendEnable = FALSE; psoDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_ONE; psoDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_ZERO; psoDesc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; psoDesc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; psoDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO; psoDesc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; psoDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; if (!gPipelineState.Initialize(device, psoDesc)) { Log("[ERROR] Failed to initialize pipeline state"); return false; } struct Vertex { float pos[4]; float col[4]; }; Vertex vertices[] = { { { 0.0f, 0.5f, 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, { { -0.5f, -0.5f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, { { 0.5f, -0.5f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }, }; if (!gVertexBuffer.InitializeWithData(device, gCommandList.GetCommandList(), vertices, sizeof(vertices), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)) { Log("[ERROR] Failed to initialize vertex buffer"); return false; } gVertexBuffer.SetStride(sizeof(Vertex)); gVertexBuffer.SetBufferType(BufferType::Vertex); Log("[INFO] D3D12 initialized successfully"); return true; } void WaitForGPU() { gCommandQueue.WaitForIdle(); } void ExecuteCommandList() { gCommandList.Close(); void* commandLists[] = { gCommandList.GetCommandList() }; gCommandQueue.ExecuteCommandLists(1, commandLists); } void BeginRender() { gCurrentRTIndex = gSwapChain.GetCurrentBackBufferIndex(); D3D12Texture& currentBackBuffer = gSwapChain.GetBackBuffer(gCurrentRTIndex); gCommandList.TransitionBarrier(currentBackBuffer.GetResource(), ResourceStates::Present, ResourceStates::RenderTarget); CPUDescriptorHandle rtvCpuHandle = gRTVHeap.GetCPUDescriptorHandle(gCurrentRTIndex); CPUDescriptorHandle dsvCpuHandle = gDSVHeap.GetCPUDescriptorHandle(0); D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvCpuHandle.ptr }; D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = { dsvCpuHandle.ptr }; gCommandList.SetRenderTargetsHandle(1, &rtvHandle, &dsvHandle); 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); float clearColor[] = { 0.0f, 0.0f, 1.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); } void EndRender() { D3D12Texture& currentBackBuffer = gSwapChain.GetBackBuffer(gCurrentRTIndex); gCommandList.TransitionBarrier(currentBackBuffer.GetResource(), ResourceStates::RenderTarget, ResourceStates::Present); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { Logger::Get().Initialize(); Logger::Get().AddSink(std::make_unique()); Logger::Get().SetMinimumLevel(LogLevel::Debug); Log("[INFO] D3D12 Triangle Test Starting"); WNDCLASSEX wc = {}; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = L"D3D12TriangleTest"; if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Failed to register window class", L"Error", MB_OK); return -1; } RECT rect = { 0, 0, gWidth, gHeight }; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); gHWND = CreateWindowEx(0, L"D3D12TriangleTest", L"D3D12 Triangle 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; } if (!InitD3D12()) { MessageBox(NULL, L"Failed to initialize D3D12", L"Error", MB_OK); return -1; } ShowWindow(gHWND, nShowCmd); UpdateWindow(gHWND); 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 { if (frameCount > 0) { gCommandQueue.WaitForPreviousFrame(); } gCommandAllocator.Reset(); gCommandList.Reset(); BeginRender(); gCommandList.SetPipelineState(gPipelineState.GetPipelineState()); gCommandList.SetRootSignature(gRootSignature.GetRootSignature()); gCommandList.SetPrimitiveTopology(PrimitiveTopology::TriangleList); gCommandList.SetVertexBuffer(0, gVertexBuffer.GetResource(), 0, gVertexBuffer.GetStride()); gCommandList.Draw(3, 1, 0, 0); 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, gCommandQueue, gSwapChain.GetBackBuffer(gCurrentRTIndex), "triangle.ppm" ); if (screenshotResult) { Log("[INFO] Screenshot saved to triangle.ppm"); } else { Log("[ERROR] Screenshot failed"); } break; } EndRender(); ExecuteCommandList(); gSwapChain.Present(0, 0); } } gCommandList.Shutdown(); gCommandAllocator.Shutdown(); gSwapChain.Shutdown(); gDevice.Shutdown(); Logger::Get().Shutdown(); Log("[INFO] D3D12 Triangle Test Finished"); return 0; }