5#include <SDL3/SDL_system.h>
14#include <d3dcompiler.h>
22DirectX12Renderer::DirectX12Renderer(
Window* window) : window(window) {
23 fenceEvent = CreateEvent(
nullptr, FALSE, FALSE,
nullptr);
24 if (fenceEvent ==
nullptr) {
25 throw std::runtime_error(
"Failed to create fence event.");
27 this->Type = RendererType::DirectX12;
29 ResourceManager::RegisterCreateBuffer(
30 this, &DirectX12Renderer::CreateBuffer);
31 ResourceManager::RegisterCreateShader(
32 this, &DirectX12Renderer::CreateShader);
33 ResourceManager::RegisterCreateTexture(
34 this, &DirectX12Renderer::CreateTexture);
35 ResourceManager::RegisterCreateCubemapTexture(
36 this, &DirectX12Renderer::CreateCubemapTexture);
37 ResourceManager::RegisterCreateCubemapTextureFromPanorama(
38 this, &DirectX12Renderer::CreateCubemapTextureFromPanorama);
39 ResourceManager::RegisterCreateTextureFromMemory(
40 [
this](
const void* data, uint32_t w, uint32_t h, TextureFormat fmt, uint32_t) -> Texture* {
41 auto* tex =
new DirectX12Texture(device.Get(), commandQueue.Get(), commandList.Get());
42 if (!tex->LoadFromMemory(data, w, h, fmt)) {
delete tex;
return nullptr; }
43 UINT slot = AllocateSRVSlot();
44 tex->CreateSRVIntoHandle(DXGI_FORMAT_R8G8B8A8_UNORM,
45 GetSharedSrvCPUHandle(slot));
46 tex->SetSharedSrvGPUHandle(GetSharedSrvGPUHandle(slot));
51DirectX12Renderer::~DirectX12Renderer() {
53 delete m_defaultTexture;
54 m_defaultTexture =
nullptr;
57 CloseHandle(fenceEvent);
62bool DirectX12Renderer::Initialize() {
63 if (m_Initialized)
return true;
65 if (!CreateDevice())
return false;
66 if (!CreateCommandQueue())
return false;
67 if (!CreateCommandAllocatorAndList())
return false;
68 if (!CreateSwapChain())
return false;
69 if (!CreateRenderTargetViews())
return false;
70 if (!CreateDepthStencilView())
return false;
71 if (!CreateFence())
return false;
72 if (!CreateRootSignature())
return false;
73 if (!CreateSharedSrvHeap())
return false;
78 uint32_t whitePixel = 0xFFFFFFFF;
79 m_defaultTexture =
new DirectX12Texture(
80 device.Get(), commandQueue.Get(), commandList.Get());
81 if (!m_defaultTexture->LoadFromMemory(
82 &whitePixel, 1, 1, TextureFormat::RGBA8)) {
83 SLEAK_WARN(
"Failed to create default white texture for DX12");
84 delete m_defaultTexture;
85 m_defaultTexture =
nullptr;
87 UINT slot = AllocateSRVSlot();
88 m_defaultTexture->CreateSRVIntoHandle(DXGI_FORMAT_R8G8B8A8_UNORM,
89 GetSharedSrvCPUHandle(slot));
90 m_defaultTexture->SetSharedSrvGPUHandle(GetSharedSrvGPUHandle(slot));
95 int w = Window::GetWidth();
96 int h = Window::GetHeight();
97 viewport.TopLeftX = 0.0f;
98 viewport.TopLeftY = 0.0f;
99 viewport.Width =
static_cast<float>(w);
100 viewport.Height =
static_cast<float>(h);
101 viewport.MinDepth = 0.0f;
102 viewport.MaxDepth = 1.0f;
104 scissorRect.left = 0;
106 scissorRect.right = w;
107 scissorRect.bottom = h;
109 SetPerformanceCounter(
true);
111 m_Initialized =
true;
112 SLEAK_INFO(
"DirectX 12 has been initialized successfully!");
117bool DirectX12Renderer::CreateDevice() {
119 if (SUCCEEDED(D3D12CreateDevice(
nullptr, D3D_FEATURE_LEVEL_12_0,
120 IID_PPV_ARGS(&device)))) {
121 SLEAK_INFO(
"DirectX 12 device created with feature level 12.0");
126 if (SUCCEEDED(D3D12CreateDevice(
nullptr, D3D_FEATURE_LEVEL_11_0,
127 IID_PPV_ARGS(&device)))) {
128 SLEAK_INFO(
"DirectX 12 device created with feature level 11.0");
132 SLEAK_ERROR(
"Failed to create DirectX 12 device!");
136bool DirectX12Renderer::CreateCommandQueue() {
137 D3D12_COMMAND_QUEUE_DESC queueDesc = {};
138 queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
139 queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
141 if (FAILED(device->CreateCommandQueue(&queueDesc,
142 IID_PPV_ARGS(&commandQueue)))) {
149bool DirectX12Renderer::CreateCommandAllocatorAndList() {
152 for (UINT i = 0; i < FrameCount; i++) {
153 if (FAILED(device->CreateCommandAllocator(
154 D3D12_COMMAND_LIST_TYPE_DIRECT,
155 IID_PPV_ARGS(&commandAllocators[i])))) {
156 SLEAK_ERROR(
"Failed to create command allocator {}!", i);
161 if (FAILED(device->CreateCommandList(
162 0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocators[0].Get(),
163 nullptr, IID_PPV_ARGS(&commandList)))) {
169 commandList->Close();
173bool DirectX12Renderer::CreateSwapChain() {
174 HWND hwnd = (HWND)SDL_GetPointerProperty(
175 SDL_GetWindowProperties(window->GetSDLWindow()),
176 SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
178 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
179 swapChainDesc.BufferCount = FrameCount;
180 swapChainDesc.Width = 0;
181 swapChainDesc.Height = 0;
182 swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
183 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
184 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
185 swapChainDesc.SampleDesc.Count = 1;
187 Microsoft::WRL::ComPtr<IDXGIFactory4> factory;
188 if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) {
193 HRESULT hr = factory->CreateSwapChainForHwnd(
194 commandQueue.Get(), hwnd, &swapChainDesc,
nullptr,
nullptr,
195 reinterpret_cast<IDXGISwapChain1**
>(swapChain.GetAddressOf()));
202 frameIndex = swapChain->GetCurrentBackBufferIndex();
204 EnumerateDevices(factory);
209bool DirectX12Renderer::CreateRenderTargetViews() {
210 D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
211 rtvHeapDesc.NumDescriptors = FrameCount;
212 rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
213 rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
215 if (FAILED(device->CreateDescriptorHeap(&rtvHeapDesc,
216 IID_PPV_ARGS(&rtvHeap)))) {
217 SLEAK_ERROR(
"Failed to create RTV descriptor heap!");
221 rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(
222 D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
224 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle =
225 rtvHeap->GetCPUDescriptorHandleForHeapStart();
227 for (UINT i = 0; i < FrameCount; i++) {
228 if (FAILED(swapChain->GetBuffer(i,
229 IID_PPV_ARGS(&renderTargets[i])))) {
230 SLEAK_ERROR(
"Failed to get swap chain buffer {}!", i);
233 device->CreateRenderTargetView(renderTargets[i].Get(),
nullptr,
235 rtvHandle.ptr += rtvDescriptorSize;
241bool DirectX12Renderer::CreateDepthStencilView() {
243 D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
244 dsvHeapDesc.NumDescriptors = 1;
245 dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
246 dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
248 if (FAILED(device->CreateDescriptorHeap(&dsvHeapDesc,
249 IID_PPV_ARGS(&dsvHeap)))) {
250 SLEAK_ERROR(
"Failed to create DSV descriptor heap!");
255 DXGI_SWAP_CHAIN_DESC1 scDesc;
256 swapChain->GetDesc1(&scDesc);
257 UINT
width = scDesc.Width;
258 UINT
height = scDesc.Height;
261 D3D12_RESOURCE_DESC depthDesc = {};
262 depthDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
263 depthDesc.Width =
width;
264 depthDesc.Height =
height;
265 depthDesc.DepthOrArraySize = 1;
266 depthDesc.MipLevels = 1;
267 depthDesc.Format = DXGI_FORMAT_D32_FLOAT;
268 depthDesc.SampleDesc.Count = 1;
269 depthDesc.SampleDesc.Quality = 0;
270 depthDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
272 D3D12_CLEAR_VALUE clearValue = {};
273 clearValue.Format = DXGI_FORMAT_D32_FLOAT;
274 clearValue.DepthStencil.Depth = 1.0f;
275 clearValue.DepthStencil.Stencil = 0;
277 D3D12_HEAP_PROPERTIES heapProps = {};
278 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
280 if (FAILED(device->CreateCommittedResource(
281 &heapProps, D3D12_HEAP_FLAG_NONE, &depthDesc,
282 D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue,
283 IID_PPV_ARGS(&depthStencilBuffer)))) {
284 SLEAK_ERROR(
"Failed to create depth stencil buffer!");
289 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
290 dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
291 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
292 dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
294 device->CreateDepthStencilView(
295 depthStencilBuffer.Get(), &dsvDesc,
296 dsvHeap->GetCPUDescriptorHandleForHeapStart());
301bool DirectX12Renderer::CreateFence() {
302 if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
303 IID_PPV_ARGS(&fence)))) {
307 for (UINT i = 0; i < FrameCount; i++)
312bool DirectX12Renderer::CreateRootSignature() {
318 D3D12_ROOT_PARAMETER rootParams[5] = {};
319 rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
320 rootParams[0].Descriptor.ShaderRegister = 0;
321 rootParams[0].Descriptor.RegisterSpace = 0;
322 rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
324 rootParams[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
325 rootParams[1].Descriptor.ShaderRegister = 1;
326 rootParams[1].Descriptor.RegisterSpace = 0;
327 rootParams[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
329 D3D12_DESCRIPTOR_RANGE srvRange = {};
330 srvRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
331 srvRange.NumDescriptors = 1;
332 srvRange.BaseShaderRegister = 0;
333 srvRange.RegisterSpace = 0;
334 srvRange.OffsetInDescriptorsFromTableStart =
335 D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
337 rootParams[2].ParameterType =
338 D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
339 rootParams[2].DescriptorTable.NumDescriptorRanges = 1;
340 rootParams[2].DescriptorTable.pDescriptorRanges = &srvRange;
341 rootParams[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
343 rootParams[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
344 rootParams[3].Descriptor.ShaderRegister = 2;
345 rootParams[3].Descriptor.RegisterSpace = 0;
346 rootParams[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
348 D3D12_DESCRIPTOR_RANGE shadowSrvRange = {};
349 shadowSrvRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
350 shadowSrvRange.NumDescriptors = 1;
351 shadowSrvRange.BaseShaderRegister = 3;
352 shadowSrvRange.RegisterSpace = 0;
353 shadowSrvRange.OffsetInDescriptorsFromTableStart =
354 D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
356 rootParams[4].ParameterType =
357 D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
358 rootParams[4].DescriptorTable.NumDescriptorRanges = 1;
359 rootParams[4].DescriptorTable.pDescriptorRanges = &shadowSrvRange;
360 rootParams[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
363 D3D12_STATIC_SAMPLER_DESC staticSamplers[3] = {};
366 staticSamplers[0].Filter = D3D12_FILTER_ANISOTROPIC;
367 staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
368 staticSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
369 staticSamplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
370 staticSamplers[0].MipLODBias = 0.0f;
371 staticSamplers[0].MaxAnisotropy = 16;
372 staticSamplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
373 staticSamplers[0].BorderColor =
374 D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
375 staticSamplers[0].MinLOD = 0.0f;
376 staticSamplers[0].MaxLOD = D3D12_FLOAT32_MAX;
377 staticSamplers[0].ShaderRegister = 0;
378 staticSamplers[0].RegisterSpace = 0;
379 staticSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
382 staticSamplers[1].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
383 staticSamplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
384 staticSamplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
385 staticSamplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
386 staticSamplers[1].MipLODBias = 0.0f;
387 staticSamplers[1].MaxAnisotropy = 1;
388 staticSamplers[1].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
389 staticSamplers[1].BorderColor =
390 D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
391 staticSamplers[1].MinLOD = 0.0f;
392 staticSamplers[1].MaxLOD = D3D12_FLOAT32_MAX;
393 staticSamplers[1].ShaderRegister = 1;
394 staticSamplers[1].RegisterSpace = 0;
395 staticSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
398 staticSamplers[2].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
399 staticSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
400 staticSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
401 staticSamplers[2].AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER;
402 staticSamplers[2].MipLODBias = 0.0f;
403 staticSamplers[2].MaxAnisotropy = 1;
404 staticSamplers[2].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
405 staticSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
406 staticSamplers[2].MinLOD = 0.0f;
407 staticSamplers[2].MaxLOD = 0.0f;
408 staticSamplers[2].ShaderRegister = 3;
409 staticSamplers[2].RegisterSpace = 0;
410 staticSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
412 D3D12_ROOT_SIGNATURE_DESC rootSigDesc = {};
413 rootSigDesc.NumParameters = 5;
414 rootSigDesc.pParameters = rootParams;
415 rootSigDesc.NumStaticSamplers = 3;
416 rootSigDesc.pStaticSamplers = staticSamplers;
418 D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
420 Microsoft::WRL::ComPtr<ID3DBlob> serializedRootSig;
421 Microsoft::WRL::ComPtr<ID3DBlob> errorBlob;
423 HRESULT hr = D3D12SerializeRootSignature(
424 &rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
425 &serializedRootSig, &errorBlob);
429 SLEAK_ERROR(
"Root signature serialization error: {}",
430 (
char*)errorBlob->GetBufferPointer());
435 hr = device->CreateRootSignature(
436 0, serializedRootSig->GetBufferPointer(),
437 serializedRootSig->GetBufferSize(),
438 IID_PPV_ARGS(&rootSignature));
448bool DirectX12Renderer::CreatePipelineState() {
456bool DirectX12Renderer::CreatePipelineStateFromShader(
457 ID3DBlob* vertexShaderBlob, ID3DBlob* pixelShaderBlob) {
458 if (!vertexShaderBlob || !pixelShaderBlob)
return false;
461 D3D12_INPUT_ELEMENT_DESC inputLayout[] = {
462 {
"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
464 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
465 {
"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
467 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
468 {
"TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
470 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
471 {
"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
473 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
474 {
"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0,
476 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
479 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
480 psoDesc.InputLayout = {inputLayout, _countof(inputLayout)};
481 psoDesc.pRootSignature = rootSignature.Get();
482 psoDesc.VS = {vertexShaderBlob->GetBufferPointer(),
483 vertexShaderBlob->GetBufferSize()};
484 psoDesc.PS = {pixelShaderBlob->GetBufferPointer(),
485 pixelShaderBlob->GetBufferSize()};
488 D3D12_RASTERIZER_DESC rasterDesc = {};
489 rasterDesc.FillMode = D3D12_FILL_MODE_SOLID;
490 rasterDesc.CullMode = D3D12_CULL_MODE_FRONT;
491 rasterDesc.FrontCounterClockwise = FALSE;
492 rasterDesc.DepthBias = 0;
493 rasterDesc.DepthBiasClamp = 0.0f;
494 rasterDesc.SlopeScaledDepthBias = 0.0f;
495 rasterDesc.DepthClipEnable = TRUE;
496 rasterDesc.MultisampleEnable = FALSE;
497 rasterDesc.AntialiasedLineEnable = FALSE;
498 rasterDesc.ForcedSampleCount = 0;
499 rasterDesc.ConservativeRaster =
500 D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
501 psoDesc.RasterizerState = rasterDesc;
504 D3D12_BLEND_DESC blendDesc = {};
505 blendDesc.AlphaToCoverageEnable = FALSE;
506 blendDesc.IndependentBlendEnable = FALSE;
507 D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc = {};
508 rtBlendDesc.BlendEnable = FALSE;
509 rtBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
510 for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
511 blendDesc.RenderTarget[i] = rtBlendDesc;
512 psoDesc.BlendState = blendDesc;
515 D3D12_DEPTH_STENCIL_DESC depthStencilDesc = {};
516 depthStencilDesc.DepthEnable = TRUE;
517 depthStencilDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
518 depthStencilDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
519 depthStencilDesc.StencilEnable = FALSE;
520 psoDesc.DepthStencilState = depthStencilDesc;
522 psoDesc.SampleMask = UINT_MAX;
523 psoDesc.PrimitiveTopologyType =
524 D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
525 psoDesc.NumRenderTargets = 1;
526 psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
527 psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
528 psoDesc.SampleDesc.Count = 1;
530 HRESULT hr = device->CreateGraphicsPipelineState(
531 &psoDesc, IID_PPV_ARGS(&pipelineState));
533 SLEAK_ERROR(
"Failed to create pipeline state! HRESULT: 0x{:08X}",
534 static_cast<unsigned int>(hr));
539 m_cachedVSBlob =
nullptr;
540 vertexShaderBlob->AddRef();
541 m_cachedVSBlob.Attach(vertexShaderBlob);
543 SLEAK_INFO(
"DirectX 12 pipeline state created successfully");
547bool DirectX12Renderer::CreateShadowPassPSO() {
548 if (m_shadowPassPSO)
return true;
549 if (!m_cachedVSBlob) {
550 SLEAK_WARN(
"CreateShadowPassPSO: no cached VS blob yet");
554 D3D12_INPUT_ELEMENT_DESC inputLayout[] = {
555 {
"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
557 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
558 {
"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
560 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
561 {
"TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
563 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
564 {
"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
566 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
567 {
"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0,
569 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
572 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
573 psoDesc.InputLayout = {inputLayout, _countof(inputLayout)};
574 psoDesc.pRootSignature = rootSignature.Get();
575 psoDesc.VS = {m_cachedVSBlob->GetBufferPointer(),
576 m_cachedVSBlob->GetBufferSize()};
578 psoDesc.PS = {
nullptr, 0};
580 D3D12_RASTERIZER_DESC rasterDesc = {};
581 rasterDesc.FillMode = D3D12_FILL_MODE_SOLID;
582 rasterDesc.CullMode = D3D12_CULL_MODE_NONE;
583 rasterDesc.FrontCounterClockwise = FALSE;
584 rasterDesc.DepthBias = 1000;
585 rasterDesc.DepthBiasClamp = 0.01f;
586 rasterDesc.SlopeScaledDepthBias = 2.0f;
587 rasterDesc.DepthClipEnable = TRUE;
588 psoDesc.RasterizerState = rasterDesc;
591 D3D12_BLEND_DESC blendDesc = {};
592 blendDesc.AlphaToCoverageEnable = FALSE;
593 blendDesc.IndependentBlendEnable = FALSE;
594 for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) {
595 blendDesc.RenderTarget[i].RenderTargetWriteMask = 0;
597 psoDesc.BlendState = blendDesc;
599 D3D12_DEPTH_STENCIL_DESC dsDesc = {};
600 dsDesc.DepthEnable = TRUE;
601 dsDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
602 dsDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
603 dsDesc.StencilEnable = FALSE;
604 psoDesc.DepthStencilState = dsDesc;
606 psoDesc.SampleMask = UINT_MAX;
607 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
608 psoDesc.NumRenderTargets = 0;
609 psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
610 psoDesc.SampleDesc.Count = 1;
612 HRESULT hr = device->CreateGraphicsPipelineState(
613 &psoDesc, IID_PPV_ARGS(&m_shadowPassPSO));
615 SLEAK_ERROR(
"Failed to create shadow pass PSO! HRESULT: 0x{:08X}",
616 static_cast<unsigned int>(hr));
623void DirectX12Renderer::BeginRender() {
624 frameIndex = swapChain->GetCurrentBackBufferIndex();
627 if (m_hasPendingLightVP) {
628 memcpy(m_lightVP, m_pendingLightVP,
sizeof(m_lightVP));
635 UINT64 waitValue = 0;
636 for (UINT i = 0; i < FrameCount; i++) {
637 if (fenceValues[i] > waitValue) waitValue = fenceValues[i];
639 if (fence->GetCompletedValue() < waitValue) {
640 fence->SetEventOnCompletion(waitValue, fenceEvent);
641 WaitForSingleObject(fenceEvent, INFINITE);
645 DirectX12Buffer::ProcessDeferredCleanup();
648 commandAllocators[frameIndex]->Reset();
649 commandList->Reset(commandAllocators[frameIndex].Get(),
650 pipelineState ? pipelineState.Get() :
nullptr);
654 commandList->SetPipelineState(pipelineState.Get());
656 SLEAK_WARN(
"BeginRender: pipeline state is null — nothing will draw");
660 commandList->SetGraphicsRootSignature(rootSignature.Get());
663 ID3D12DescriptorHeap* srvHeaps[] = {m_sharedSrvHeap.Get()};
664 commandList->SetDescriptorHeaps(1, srvHeaps);
666 commandList->RSSetViewports(1, &viewport);
667 commandList->RSSetScissorRects(1, &scissorRect);
670 D3D12_RESOURCE_BARRIER barrier = {};
671 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
672 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
673 barrier.Transition.pResource = renderTargets[frameIndex].Get();
674 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
675 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
676 barrier.Transition.Subresource =
677 D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
678 commandList->ResourceBarrier(1, &barrier);
681 const float clearColor[] = {0.39f, 0.58f, 0.93f, 1.0f};
682 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle =
683 rtvHeap->GetCPUDescriptorHandleForHeapStart();
684 rtvHandle.ptr += frameIndex * rtvDescriptorSize;
686 D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle =
687 dsvHeap->GetCPUDescriptorHandleForHeapStart();
689 commandList->ClearRenderTargetView(rtvHandle, clearColor, 0,
nullptr);
690 commandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH,
691 1.0f, 0, 0,
nullptr);
694 if (m_shadowPassEnabled) {
699 commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
701 commandList->RSSetViewports(1, &viewport);
702 commandList->RSSetScissorRects(1, &scissorRect);
705 commandList->IASetPrimitiveTopology(
706 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
710 commandList->SetGraphicsRootSignature(rootSignature.Get());
712 ID3D12DescriptorHeap* heaps[] = {m_sharedSrvHeap.Get()};
713 commandList->SetDescriptorHeaps(1, heaps);
717 if (m_defaultTexture) {
718 m_defaultTexture->Bind(0);
722 if (m_lightUBOCreated && m_lightUBO) {
723 commandList->SetGraphicsRootConstantBufferView(
724 3, m_lightUBO->GetGPUVirtualAddress());
728 if (m_shadowMapCreated) {
729 commandList->SetGraphicsRootDescriptorTable(
730 4, GetSharedSrvGPUHandle(m_shadowSrvIndex));
733 bImFrameActive =
false;
734 if (bImInitialized) {
735 ImGui_ImplDX12_NewFrame();
736 ImGui_ImplSDL3_NewFrame();
738 auto& io = ImGui::GetIO();
739 if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f) {
741 bImFrameActive =
true;
746void DirectX12Renderer::EndRender() {
747 if (bImFrameActive) {
750 ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(),
755 D3D12_RESOURCE_BARRIER barrier = {};
756 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
757 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
758 barrier.Transition.pResource = renderTargets[frameIndex].Get();
759 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
760 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
761 barrier.Transition.Subresource =
762 D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
763 commandList->ResourceBarrier(1, &barrier);
766 commandList->Close();
767 ID3D12CommandList* ppCommandLists[] = {commandList.Get()};
768 commandQueue->ExecuteCommandLists(_countof(ppCommandLists),
771 swapChain->Present(m_vsync ? 1 : 0, 0);
776 fenceValues[frameIndex]++;
777 commandQueue->Signal(fence.Get(), fenceValues[frameIndex]);
779 UpdateFrameMetrics();
782void DirectX12Renderer::WaitForGPU() {
783 if (!commandQueue || !fence || !fenceEvent)
return;
787 for (UINT i = 0; i < FrameCount; i++) {
788 if (fenceValues[i] > maxFence) maxFence = fenceValues[i];
790 const UINT64 waitValue = maxFence + 1;
791 commandQueue->Signal(fence.Get(), waitValue);
792 for (UINT i = 0; i < FrameCount; i++)
793 fenceValues[i] = waitValue;
795 if (fence->GetCompletedValue() < waitValue) {
796 fence->SetEventOnCompletion(waitValue, fenceEvent);
797 WaitForSingleObject(fenceEvent, INFINITE);
801bool DirectX12Renderer::CreateSharedSrvHeap() {
802 D3D12_DESCRIPTOR_HEAP_DESC heapDesc{};
803 heapDesc.NumDescriptors = MAX_SRV_DESCRIPTORS;
804 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
805 heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
807 HRESULT hr = device->CreateDescriptorHeap(
808 &heapDesc, IID_PPV_ARGS(&m_sharedSrvHeap));
810 SLEAK_ERROR(
"Failed to create shared SRV descriptor heap!");
813 m_srvDescriptorSize = device->GetDescriptorHandleIncrementSize(
814 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
819UINT DirectX12Renderer::AllocateSRVSlot() {
820 if (m_nextSrvSlot >= MAX_SRV_DESCRIPTORS) {
821 SLEAK_ERROR(
"Shared SRV heap full! Increase MAX_SRV_DESCRIPTORS.");
824 return m_nextSrvSlot++;
827D3D12_CPU_DESCRIPTOR_HANDLE DirectX12Renderer::GetSharedSrvCPUHandle(UINT slot)
const {
828 D3D12_CPU_DESCRIPTOR_HANDLE handle = m_sharedSrvHeap->GetCPUDescriptorHandleForHeapStart();
829 handle.ptr +=
static_cast<SIZE_T
>(slot) * m_srvDescriptorSize;
833D3D12_GPU_DESCRIPTOR_HANDLE DirectX12Renderer::GetSharedSrvGPUHandle(UINT slot)
const {
834 D3D12_GPU_DESCRIPTOR_HANDLE handle = m_sharedSrvHeap->GetGPUDescriptorHandleForHeapStart();
835 handle.ptr +=
static_cast<UINT64
>(slot) * m_srvDescriptorSize;
839void DirectX12Renderer::Cleanup() {
840 if (!m_Initialized)
return;
844 if (bImInitialized) {
845 ImGui_ImplDX12_Shutdown();
846 ImGui_ImplSDL3_Shutdown();
847 ImGui::DestroyContext();
848 bImInitialized =
false;
850 if (m_lightUBO && m_lightUBOMapped) {
851 m_lightUBO->Unmap(0,
nullptr);
852 m_lightUBOMapped =
nullptr;
855 m_lightUBOCreated =
false;
857 imguiSrvHeap.Reset();
858 m_sharedSrvHeap.Reset();
860 m_skyboxPipelineState.Reset();
861 pipelineState.Reset();
862 rootSignature.Reset();
863 depthStencilBuffer.Reset();
865 for (
auto& rt : renderTargets) {
871 for (UINT i = 0; i < FrameCount; i++)
872 commandAllocators[i].Reset();
873 commandQueue.Reset();
877 m_Initialized =
false;
880void DirectX12Renderer::Resize(uint32_t
width, uint32_t
height) {
881 if (!m_Initialized || !swapChain)
return;
886 for (
auto& rt : renderTargets) {
889 depthStencilBuffer.Reset();
894 HRESULT hr = swapChain->ResizeBuffers(FrameCount,
width,
height,
895 DXGI_FORMAT_R8G8B8A8_UNORM, 0);
897 SLEAK_ERROR(
"Failed to resize swap chain buffers!");
901 frameIndex = swapChain->GetCurrentBackBufferIndex();
904 if (!CreateRenderTargetViews() || !CreateDepthStencilView()) {
905 SLEAK_ERROR(
"Failed to recreate views after resize!");
910 viewport.Width =
static_cast<float>(
width);
911 viewport.Height =
static_cast<float>(
height);
912 scissorRect.right =
width;
913 scissorRect.bottom =
height;
918void DirectX12Renderer::Draw(uint32_t vertexCount) {
919 commandList->DrawInstanced(vertexCount, 1, 0, 0);
920 DrawnVertices += vertexCount;
921 DrawnTriangles += vertexCount / 3;
924void DirectX12Renderer::DrawIndexed(uint32_t indexCount) {
925 commandList->DrawIndexedInstanced(indexCount, 1, 0, 0, 0);
926 DrawnVertices += indexCount;
927 DrawnTriangles += indexCount / 3;
930void DirectX12Renderer::DrawInstance(uint32_t instanceCount,
931 uint32_t vertexPerInstance) {
932 commandList->DrawInstanced(vertexPerInstance, instanceCount, 0, 0);
933 DrawnVertices += vertexPerInstance * instanceCount;
934 DrawnTriangles += (vertexPerInstance / 3) * instanceCount;
937void DirectX12Renderer::DrawIndexedInstance(uint32_t instanceCount,
938 uint32_t indexPerInstance) {
939 commandList->DrawIndexedInstanced(indexPerInstance, instanceCount, 0,
941 DrawnVertices += indexPerInstance * instanceCount;
942 DrawnTriangles += (indexPerInstance / 3) * instanceCount;
945void DirectX12Renderer::SetRenderFace(RenderFace face) {
947 ConfigureRenderFace();
950void DirectX12Renderer::SetRenderMode(RenderMode mode) {
952 ConfigureRenderMode();
955void DirectX12Renderer::SetViewport(
float x,
float y,
float width,
956 float height,
float minDepth,
958 viewport.TopLeftX = x;
959 viewport.TopLeftY = y;
960 viewport.Width =
width;
962 viewport.MinDepth = minDepth;
963 viewport.MaxDepth = maxDepth;
965 scissorRect.left =
static_cast<LONG
>(x);
966 scissorRect.top =
static_cast<LONG
>(y);
967 scissorRect.right =
static_cast<LONG
>(x +
width);
968 scissorRect.bottom =
static_cast<LONG
>(y +
height);
971 commandList->RSSetViewports(1, &viewport);
972 commandList->RSSetScissorRects(1, &scissorRect);
976void DirectX12Renderer::ClearRenderTarget(
float r,
float g,
float b,
978 const float clearColor[] = {r, g, b, a};
979 D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle =
980 rtvHeap->GetCPUDescriptorHandleForHeapStart();
981 rtvHandle.ptr += frameIndex * rtvDescriptorSize;
982 commandList->ClearRenderTargetView(rtvHandle, clearColor, 0,
nullptr);
985void DirectX12Renderer::ClearDepthStencil(
bool clearDepth,
986 bool clearStencil,
float depth,
988 D3D12_CLEAR_FLAGS flags = {};
990 flags =
static_cast<D3D12_CLEAR_FLAGS
>(
991 flags | D3D12_CLEAR_FLAG_DEPTH);
993 flags =
static_cast<D3D12_CLEAR_FLAGS
>(
994 flags | D3D12_CLEAR_FLAG_STENCIL);
996 if (flags && dsvHeap) {
997 D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle =
998 dsvHeap->GetCPUDescriptorHandleForHeapStart();
999 commandList->ClearDepthStencilView(dsvHandle, flags, depth,
1000 stencil, 0,
nullptr);
1004void DirectX12Renderer::BindVertexBuffer(RefPtr<BufferBase> buffer,
1006 auto* dx12Buf =
static_cast<DirectX12Buffer*
>(buffer.get());
1007 if (!dx12Buf)
return;
1009 D3D12_VERTEX_BUFFER_VIEW vbView = {};
1010 vbView.BufferLocation =
1011 dx12Buf->GetD3DBuffer()->GetGPUVirtualAddress();
1013 vbView.SizeInBytes =
static_cast<UINT
>(dx12Buf->GetSize());
1015 commandList->IASetVertexBuffers(slot, 1, &vbView);
1018void DirectX12Renderer::BindIndexBuffer(RefPtr<BufferBase> buffer,
1020 auto* dx12Buf =
static_cast<DirectX12Buffer*
>(buffer.get());
1021 if (!dx12Buf)
return;
1023 D3D12_INDEX_BUFFER_VIEW ibView = {};
1024 ibView.BufferLocation =
1025 dx12Buf->GetD3DBuffer()->GetGPUVirtualAddress();
1026 ibView.Format = DXGI_FORMAT_R32_UINT;
1027 ibView.SizeInBytes =
static_cast<UINT
>(dx12Buf->GetSize());
1029 commandList->IASetIndexBuffer(&ibView);
1032void DirectX12Renderer::BindConstantBuffer(RefPtr<BufferBase> buffer,
1034 auto* dx12Buf =
static_cast<DirectX12Buffer*
>(buffer.get());
1035 if (!dx12Buf)
return;
1038 if (m_inShadowPass && slot == 0 && dx12Buf->GetCPUShadowCopySize() >= 128) {
1039 const float* srcWorld =
reinterpret_cast<const float*
>(
1040 static_cast<const char*
>(dx12Buf->GetCPUShadowCopy()) + 64);
1043 for (
int r = 0; r < 4; ++r) {
1044 for (
int c = 0; c < 4; ++c) {
1046 for (
int k = 0; k < 4; ++k) {
1047 sum += srcWorld[r * 4 + k] * m_lightVP[k * 4 + c];
1049 shadowPC[r * 4 + c] = sum;
1052 memcpy(&shadowPC[16], srcWorld,
sizeof(
float) * 16);
1055 if (!m_shadowTransformCB) {
1056 const UINT cbSize = 256;
1057 D3D12_HEAP_PROPERTIES heapProps = {};
1058 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
1059 D3D12_RESOURCE_DESC desc = {};
1060 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
1061 desc.Width = cbSize;
1063 desc.DepthOrArraySize = 1;
1065 desc.Format = DXGI_FORMAT_UNKNOWN;
1066 desc.SampleDesc.Count = 1;
1067 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1068 device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc,
1069 D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
1070 IID_PPV_ARGS(&m_shadowTransformCB));
1071 m_shadowTransformCB->Map(0,
nullptr, &m_shadowTransformMapped);
1074 memcpy(m_shadowTransformMapped, shadowPC,
sizeof(shadowPC));
1075 commandList->SetGraphicsRootConstantBufferView(
1076 slot, m_shadowTransformCB->GetGPUVirtualAddress());
1080 commandList->SetGraphicsRootConstantBufferView(
1081 slot, dx12Buf->GetD3DBuffer()->GetGPUVirtualAddress());
1084BufferBase* DirectX12Renderer::CreateBuffer(BufferType Type, uint32_t size,
1087 auto* buffer =
new DirectX12Buffer(device.Get(), commandQueue.Get(), size, Type);
1088 if (!buffer->Initialize(data)) {
1098 if (buffer->HasPendingCommands()) {
1099 ID3D12CommandList* ppCmdLists[] = {buffer->GetCommandList()};
1100 commandQueue->ExecuteCommandLists(1, ppCmdLists);
1106Shader* DirectX12Renderer::CreateShader(
const std::string& shaderSource) {
1107 auto* shader =
new DirectX12Shader(device.Get());
1108 if (shader->compile(shaderSource)) {
1110 if (shader->getVertexShaderBlob()) {
1111 CreatePipelineStateFromShader(
1112 shader->getVertexShaderBlob(),
1113 shader->getPixelShaderBlob());
1115 shader->SetPipelineState(pipelineState);
1118 shader->SetCommandList(commandList.Get());
1125Texture* DirectX12Renderer::CreateTexture(
1126 const std::string& TexturePath) {
1128 new DirectX12Texture(device.Get(), commandQueue.Get(),
1130 if (!texture->LoadFromFile(TexturePath)) {
1135 UINT slot = AllocateSRVSlot();
1136 texture->CreateSRVIntoHandle(
1137 texture->GetFormat() == TextureFormat::RGBA8 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM,
1138 GetSharedSrvCPUHandle(slot));
1139 texture->SetSharedSrvGPUHandle(GetSharedSrvGPUHandle(slot));
1143Texture* DirectX12Renderer::CreateTextureFromData(uint32_t
width,
1147 new DirectX12Texture(device.Get(), commandQueue.Get(),
1150 TextureFormat::RGBA8)) {
1155 UINT slot = AllocateSRVSlot();
1156 texture->CreateSRVIntoHandle(DXGI_FORMAT_R8G8B8A8_UNORM,
1157 GetSharedSrvCPUHandle(slot));
1158 texture->SetSharedSrvGPUHandle(GetSharedSrvGPUHandle(slot));
1162Texture* DirectX12Renderer::CreateCubemapTexture(
1163 const std::array<std::string, 6>& facePaths) {
1164 auto* texture =
new DirectX12CubemapTexture(device.Get(),
1165 commandQueue.Get());
1166 if (!texture->LoadCubemap(facePaths)) {
1170 UINT slot = AllocateSRVSlot();
1171 texture->CreateSRVIntoHandle(GetSharedSrvCPUHandle(slot));
1172 texture->SetSharedSrvGPUHandle(GetSharedSrvGPUHandle(slot));
1176Texture* DirectX12Renderer::CreateCubemapTextureFromPanorama(
1177 const std::string& panoramaPath) {
1178 auto* texture =
new DirectX12CubemapTexture(device.Get(),
1179 commandQueue.Get());
1180 if (!texture->LoadEquirectangular(panoramaPath)) {
1184 UINT slot = AllocateSRVSlot();
1185 texture->CreateSRVIntoHandle(GetSharedSrvCPUHandle(slot));
1186 texture->SetSharedSrvGPUHandle(GetSharedSrvGPUHandle(slot));
1190void DirectX12Renderer::BindTexture(RefPtr<Sleak::Texture> texture,
1192 if (!texture.IsValid() || !commandList)
return;
1194 if (texture->GetType() == TextureType::TextureCube) {
1195 static_cast<DirectX12CubemapTexture*
>(texture.get())
1196 ->BindToCommandList(commandList.Get(), 2);
1198 static_cast<DirectX12Texture*
>(texture.get())
1199 ->BindToCommandList(commandList.Get(), 2);
1203void DirectX12Renderer::BindTextureRaw(
Sleak::Texture* texture, uint32_t slot) {
1204 if (!texture || !commandList)
return;
1206 if (texture->
GetType() == TextureType::TextureCube) {
1207 static_cast<DirectX12CubemapTexture*
>(texture)
1208 ->BindToCommandList(commandList.Get(), 2);
1212 auto* dx12Tex =
static_cast<DirectX12Texture*
>(texture);
1214 dx12Tex->BindToCommandList(commandList.Get(), 2);
1218bool DirectX12Renderer::CreateLightUBO() {
1219 if (m_lightUBOCreated)
return true;
1222 const UINT uboSize = (
sizeof(RenderEngine::ShadowLightUBO) + 255) & ~255;
1224 D3D12_HEAP_PROPERTIES heapProps = {};
1225 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
1227 D3D12_RESOURCE_DESC desc = {};
1228 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
1229 desc.Width = uboSize;
1231 desc.DepthOrArraySize = 1;
1233 desc.Format = DXGI_FORMAT_UNKNOWN;
1234 desc.SampleDesc.Count = 1;
1235 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
1237 HRESULT hr = device->CreateCommittedResource(
1238 &heapProps, D3D12_HEAP_FLAG_NONE, &desc,
1239 D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
1240 IID_PPV_ARGS(&m_lightUBO));
1242 SLEAK_ERROR(
"Failed to create light UBO! HRESULT: 0x{:08X}",
1243 static_cast<unsigned int>(hr));
1247 hr = m_lightUBO->Map(0,
nullptr, &m_lightUBOMapped);
1253 memset(m_lightUBOMapped, 0, uboSize);
1254 m_lightUBOCreated =
true;
1258void DirectX12Renderer::UpdateShadowLightUBO(
const void* data, uint32_t size) {
1259 if (!m_lightUBOCreated && !CreateLightUBO())
return;
1262 uint32_t copySize = size;
1263 if (copySize >
sizeof(RenderEngine::ShadowLightUBO))
1264 copySize =
sizeof(RenderEngine::ShadowLightUBO);
1265 memcpy(m_lightUBOMapped, data, copySize);
1268void DirectX12Renderer::SetLightVP(
const float* mat) {
1270 memcpy(m_pendingLightVP, mat,
sizeof(m_pendingLightVP));
1271 m_hasPendingLightVP =
true;
1275bool DirectX12Renderer::CreateShadowMapResources() {
1276 if (m_shadowMapCreated)
return true;
1279 D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
1280 dsvHeapDesc.NumDescriptors = 1;
1281 dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
1282 dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
1283 if (FAILED(device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&m_shadowDsvHeap)))) {
1287 m_shadowDsvHandle = m_shadowDsvHeap->GetCPUDescriptorHandleForHeapStart();
1290 D3D12_RESOURCE_DESC depthDesc = {};
1291 depthDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
1292 depthDesc.Width = m_shadowMapResolution;
1293 depthDesc.Height = m_shadowMapResolution;
1294 depthDesc.DepthOrArraySize = 1;
1295 depthDesc.MipLevels = 1;
1296 depthDesc.Format = DXGI_FORMAT_R32_TYPELESS;
1297 depthDesc.SampleDesc.Count = 1;
1298 depthDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
1300 D3D12_CLEAR_VALUE clearValue = {};
1301 clearValue.Format = DXGI_FORMAT_D32_FLOAT;
1302 clearValue.DepthStencil.Depth = 1.0f;
1304 D3D12_HEAP_PROPERTIES heapProps = {};
1305 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
1307 if (FAILED(device->CreateCommittedResource(
1308 &heapProps, D3D12_HEAP_FLAG_NONE, &depthDesc,
1309 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, &clearValue,
1310 IID_PPV_ARGS(&m_shadowDepthBuffer)))) {
1311 SLEAK_ERROR(
"Failed to create shadow depth buffer!");
1316 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
1317 dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
1318 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
1319 device->CreateDepthStencilView(m_shadowDepthBuffer.Get(), &dsvDesc, m_shadowDsvHandle);
1322 m_shadowSrvIndex = AllocateSRVSlot();
1323 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
1324 srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
1325 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
1326 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
1327 srvDesc.Texture2D.MipLevels = 1;
1328 device->CreateShaderResourceView(m_shadowDepthBuffer.Get(), &srvDesc,
1329 GetSharedSrvCPUHandle(m_shadowSrvIndex));
1331 m_shadowMapCreated =
true;
1332 m_shadowResourcesCreated =
true;
1333 SLEAK_INFO(
"D3D12 shadow map resources created ({}x{})", m_shadowMapResolution, m_shadowMapResolution);
1337void DirectX12Renderer::RenderShadowPass() {
1339 auto* queue = RenderCommandQueue::GetInstance();
1340 if (!queue || !queue->HasCachedShadowDraws())
return;
1342 if (!m_shadowMapCreated) {
1343 if (!CreateShadowMapResources())
return;
1347 D3D12_RESOURCE_BARRIER barrier = {};
1348 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1349 barrier.Transition.pResource = m_shadowDepthBuffer.Get();
1350 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
1351 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
1352 barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1353 commandList->ResourceBarrier(1, &barrier);
1356 commandList->OMSetRenderTargets(0,
nullptr, FALSE, &m_shadowDsvHandle);
1357 commandList->ClearDepthStencilView(m_shadowDsvHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0,
nullptr);
1360 D3D12_VIEWPORT shadowViewport = {};
1361 shadowViewport.Width =
static_cast<float>(m_shadowMapResolution);
1362 shadowViewport.Height =
static_cast<float>(m_shadowMapResolution);
1363 shadowViewport.MinDepth = 0.0f;
1364 shadowViewport.MaxDepth = 1.0f;
1365 commandList->RSSetViewports(1, &shadowViewport);
1367 D3D12_RECT shadowScissor = {};
1368 shadowScissor.right = m_shadowMapResolution;
1369 shadowScissor.bottom = m_shadowMapResolution;
1370 commandList->RSSetScissorRects(1, &shadowScissor);
1373 if (!m_shadowPassPSO) CreateShadowPassPSO();
1374 if (m_shadowPassPSO) {
1375 commandList->SetPipelineState(m_shadowPassPSO.Get());
1376 }
else if (pipelineState) {
1377 commandList->SetPipelineState(pipelineState.Get());
1379 commandList->SetGraphicsRootSignature(rootSignature.Get());
1380 commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1383 ID3D12DescriptorHeap* srvHeaps[] = {m_sharedSrvHeap.Get()};
1384 commandList->SetDescriptorHeaps(1, srvHeaps);
1387 if (m_defaultTexture) {
1388 m_defaultTexture->Bind(0);
1392 m_inShadowPass =
true;
1394 queue->ExecuteShadowPass(
this);
1396 m_inShadowPass =
false;
1399 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE;
1400 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
1401 commandList->ResourceBarrier(1, &barrier);
1404bool DirectX12Renderer::CreateSkyboxPipelineState() {
1405 if (m_skyboxPipelineState)
return true;
1406 if (!pipelineState)
return false;
1411 Microsoft::WRL::ComPtr<ID3DBlob> vsBlob, psBlob, errorBlob;
1413 HRESULT hr = D3DCompileFromFile(
1414 L
"assets/shaders/skybox_dx12.hlsl",
nullptr,
nullptr,
1415 "VS_Main",
"vs_5_0", 0, 0, &vsBlob, &errorBlob);
1419 (
char*)errorBlob->GetBufferPointer());
1421 SLEAK_WARN(
"Failed to compile skybox VS, using main PSO blobs");
1424 hr = D3DCompileFromFile(
1425 L
"assets/shaders/skybox_dx12.hlsl",
nullptr,
nullptr,
1426 "PS_Main",
"ps_5_0", 0, 0, &psBlob, &errorBlob);
1430 (
char*)errorBlob->GetBufferPointer());
1435 if (!vsBlob || !psBlob)
return false;
1438 D3D12_INPUT_ELEMENT_DESC inputLayout[] = {
1439 {
"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
1441 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1442 {
"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
1444 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1445 {
"TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
1447 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1448 {
"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
1450 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1451 {
"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0,
1453 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1456 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
1457 psoDesc.InputLayout = {inputLayout, _countof(inputLayout)};
1458 psoDesc.pRootSignature = rootSignature.Get();
1459 psoDesc.VS = {vsBlob->GetBufferPointer(), vsBlob->GetBufferSize()};
1460 psoDesc.PS = {psBlob->GetBufferPointer(), psBlob->GetBufferSize()};
1464 D3D12_RASTERIZER_DESC rasterDesc = {};
1465 rasterDesc.FillMode = D3D12_FILL_MODE_SOLID;
1466 rasterDesc.CullMode = D3D12_CULL_MODE_NONE;
1467 rasterDesc.FrontCounterClockwise = FALSE;
1468 rasterDesc.DepthClipEnable = FALSE;
1469 psoDesc.RasterizerState = rasterDesc;
1472 D3D12_BLEND_DESC blendDesc = {};
1473 blendDesc.AlphaToCoverageEnable = FALSE;
1474 blendDesc.IndependentBlendEnable = FALSE;
1475 D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc = {};
1476 rtBlendDesc.BlendEnable = FALSE;
1477 rtBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
1478 for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
1479 blendDesc.RenderTarget[i] = rtBlendDesc;
1480 psoDesc.BlendState = blendDesc;
1483 D3D12_DEPTH_STENCIL_DESC depthStencilDesc = {};
1484 depthStencilDesc.DepthEnable = TRUE;
1485 depthStencilDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
1486 depthStencilDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
1487 depthStencilDesc.StencilEnable = FALSE;
1488 psoDesc.DepthStencilState = depthStencilDesc;
1490 psoDesc.SampleMask = UINT_MAX;
1491 psoDesc.PrimitiveTopologyType =
1492 D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
1493 psoDesc.NumRenderTargets = 1;
1494 psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
1495 psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
1496 psoDesc.SampleDesc.Count = 1;
1498 hr = device->CreateGraphicsPipelineState(
1499 &psoDesc, IID_PPV_ARGS(&m_skyboxPipelineState));
1502 "Failed to create skybox pipeline state! HRESULT: 0x{:08X}",
1503 static_cast<unsigned int>(hr));
1507 SLEAK_INFO(
"DirectX 12 skybox pipeline state created successfully");
1511void DirectX12Renderer::BeginSkyboxPass() {
1512 if (!CreateSkyboxPipelineState()) {
1513 SLEAK_WARN(
"BeginSkyboxPass: failed to create skybox PSO");
1516 commandList->SetPipelineState(m_skyboxPipelineState.Get());
1519void DirectX12Renderer::EndSkyboxPass() {
1520 if (pipelineState) {
1521 commandList->SetPipelineState(pipelineState.Get());
1525bool DirectX12Renderer::CreateDebugLinePipelineState() {
1526 if (m_debugLinePipelineState)
return true;
1527 if (!pipelineState)
return false;
1529 Microsoft::WRL::ComPtr<ID3DBlob> vsBlob, psBlob, errorBlob;
1531 HRESULT hr = D3DCompileFromFile(
1532 L
"assets/shaders/debug_line_dx12.hlsl",
nullptr,
nullptr,
1533 "VS_Main",
"vs_5_0", 0, 0, &vsBlob, &errorBlob);
1537 (
char*)errorBlob->GetBufferPointer());
1541 hr = D3DCompileFromFile(
1542 L
"assets/shaders/debug_line_dx12.hlsl",
nullptr,
nullptr,
1543 "PS_Main",
"ps_5_0", 0, 0, &psBlob, &errorBlob);
1547 (
char*)errorBlob->GetBufferPointer());
1551 if (!vsBlob || !psBlob)
return false;
1553 D3D12_INPUT_ELEMENT_DESC inputLayout[] = {
1554 {
"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
1556 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1557 {
"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,
1559 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1560 {
"TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
1562 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1563 {
"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,
1565 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1566 {
"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0,
1568 D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
1571 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
1572 psoDesc.InputLayout = {inputLayout, _countof(inputLayout)};
1573 psoDesc.pRootSignature = rootSignature.Get();
1574 psoDesc.VS = {vsBlob->GetBufferPointer(), vsBlob->GetBufferSize()};
1575 psoDesc.PS = {psBlob->GetBufferPointer(), psBlob->GetBufferSize()};
1577 D3D12_RASTERIZER_DESC rasterDesc = {};
1578 rasterDesc.FillMode = D3D12_FILL_MODE_SOLID;
1579 rasterDesc.CullMode = D3D12_CULL_MODE_NONE;
1580 rasterDesc.FrontCounterClockwise = FALSE;
1581 rasterDesc.DepthClipEnable = TRUE;
1582 psoDesc.RasterizerState = rasterDesc;
1584 D3D12_BLEND_DESC blendDesc = {};
1585 blendDesc.AlphaToCoverageEnable = FALSE;
1586 blendDesc.IndependentBlendEnable = FALSE;
1587 D3D12_RENDER_TARGET_BLEND_DESC rtBlendDesc = {};
1588 rtBlendDesc.BlendEnable = FALSE;
1589 rtBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
1590 for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
1591 blendDesc.RenderTarget[i] = rtBlendDesc;
1592 psoDesc.BlendState = blendDesc;
1594 D3D12_DEPTH_STENCIL_DESC depthStencilDesc = {};
1595 depthStencilDesc.DepthEnable = TRUE;
1596 depthStencilDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
1597 depthStencilDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
1598 depthStencilDesc.StencilEnable = FALSE;
1599 psoDesc.DepthStencilState = depthStencilDesc;
1601 psoDesc.SampleMask = UINT_MAX;
1602 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
1603 psoDesc.NumRenderTargets = 1;
1604 psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
1605 psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
1606 psoDesc.SampleDesc.Count = 1;
1608 hr = device->CreateGraphicsPipelineState(
1609 &psoDesc, IID_PPV_ARGS(&m_debugLinePipelineState));
1612 "Failed to create debug line pipeline state! HRESULT: 0x{:08X}",
1613 static_cast<unsigned int>(hr));
1617 SLEAK_INFO(
"DirectX 12 debug line pipeline state created successfully");
1621void DirectX12Renderer::BeginDebugLinePass() {
1622 if (!CreateDebugLinePipelineState()) {
1623 SLEAK_WARN(
"BeginDebugLinePass: failed to create debug line PSO");
1626 commandList->SetPipelineState(m_debugLinePipelineState.Get());
1627 commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_LINELIST);
1630void DirectX12Renderer::EndDebugLinePass() {
1631 if (pipelineState) {
1632 commandList->SetPipelineState(pipelineState.Get());
1634 commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1637void DirectX12Renderer::ConfigureRenderMode() {
1643void DirectX12Renderer::ConfigureRenderFace() {
1648void DirectX12Renderer::EnumerateDevices(
1649 Microsoft::WRL::ComPtr<IDXGIFactory4> factory) {
1650 UINT adapter_index = 0;
1651 IDXGIAdapter1* tempadapter;
1652 while (factory->EnumAdapters1(adapter_index, &tempadapter) !=
1653 DXGI_ERROR_NOT_FOUND) {
1654 DXGI_ADAPTER_DESC1 desc;
1655 tempadapter->GetDesc1(&desc);
1658 std::wstring wgpu_name = L
"Found GPU: ";
1659 wgpu_name += desc.Description;
1660 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
1661 std::string gpu_name = converter.to_bytes(wgpu_name);
1665 if (adapter ==
nullptr &&
1666 (desc.VendorId == 0x10DE || desc.VendorId == 0x1002)) {
1667 adapter.Attach(tempadapter);
1669 tempadapter->Release();
1678bool DirectX12Renderer::IsSupport() {
1679 Microsoft::WRL::ComPtr<ID3D12Device> testDevice;
1680 HRESULT hr = D3D12CreateDevice(
nullptr, D3D_FEATURE_LEVEL_11_0,
1681 IID_PPV_ARGS(&testDevice));
1682 return SUCCEEDED(hr);
1685bool DirectX12Renderer::CreateImGUI() {
1686 if (!device || !commandQueue || !m_sharedSrvHeap)
1690 UINT imguiSlot = AllocateSRVSlot();
1691 D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = GetSharedSrvCPUHandle(imguiSlot);
1692 D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = GetSharedSrvGPUHandle(imguiSlot);
1694 IMGUI_CHECKVERSION();
1695 ImGui::CreateContext();
1696 ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
1697 ImGui::StyleColorsDark();
1699 if (!ImGui_ImplSDL3_InitForD3D(window->GetSDLWindow()))
1702 if (!ImGui_ImplDX12_Init(
1703 device.Get(), FrameCount, DXGI_FORMAT_R8G8B8A8_UNORM,
1704 m_sharedSrvHeap.Get(),
1709 bImInitialized =
true;
virtual TextureType GetType() const =0
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.