SleakEngine 0.1.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX11Renderer.cpp
Go to the documentation of this file.
2#ifdef PLATFORM_WIN
3
4#include <Core/Window.hpp>
5#include <SDL3/SDL.h>
6#include <dxgidebug.h>
11#include <Runtime/MeshData.hpp>
13#include <imgui_internal.h>
14#include <windows.h>
15#include <psapi.h>
16#include <d3dcompiler.h>
17#include <cstring>
19
20namespace Sleak {
21 namespace RenderEngine {
22
23/// Registers this backend's buffer/shader/texture factories with ResourceManager.
24DirectX11Renderer::DirectX11Renderer(Window* window)
25 : window(window),
26 device(nullptr),
27 deviceContext(nullptr),
28 swapChain(nullptr),
29 renderTargetView(nullptr) {
30 this->Type = RendererType::DirectX11;
31 this->cull = D3D11_CULL_FRONT;
32
33 ResourceManager::RegisterCreateBuffer(this,&DirectX11Renderer::CreateBuffer);
34 ResourceManager::RegisterCreateShader(this,&DirectX11Renderer::CreateShader);
35 ResourceManager::RegisterCreateTexture(this,&DirectX11Renderer::CreateTexture);
36 ResourceManager::RegisterCreateCubemapTexture(this,&DirectX11Renderer::CreateCubemapTexture);
37 ResourceManager::RegisterCreateCubemapTextureFromPanorama(this,&DirectX11Renderer::CreateCubemapTextureFromPanorama);
38 ResourceManager::RegisterCreateTextureFromMemory(
39 [this](const void* data, uint32_t w, uint32_t h, TextureFormat fmt, uint32_t) -> Texture* {
40 auto* tex = new DirectX11Texture(device);
41 if (tex->LoadFromMemory(data, w, h, fmt)) return tex;
42 delete tex;
43 return nullptr;
44 });
45
46 }
47
48DirectX11Renderer::~DirectX11Renderer() {
49 Cleanup();
50}
51
52bool DirectX11Renderer::Initialize() {
53 HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window->GetSDLWindow()),
54 SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
55 // Create a DirectX 11 device and swap chain
56 DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
57 swapChainDesc.BufferCount = 2;
58 swapChainDesc.BufferDesc.Width = Window::GetWidth();
59 swapChainDesc.BufferDesc.Height = Window::GetHeight();
60 swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
61 swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
62 swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
63 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
64 swapChainDesc.OutputWindow = hwnd;
65 swapChainDesc.SampleDesc.Count = 1;
66 swapChainDesc.SampleDesc.Quality = 0;
67 swapChainDesc.Windowed = TRUE;
68 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
69
70 UINT createDeviceFlags = 0;
71 #ifdef _DEBUG
72 createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
73 #endif
74
75 D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
76 HRESULT hr = D3D11CreateDeviceAndSwapChain(
77 nullptr, // Use default adapter
78 D3D_DRIVER_TYPE_HARDWARE, // Use hardware rendering
79 nullptr, // No software device
80 createDeviceFlags, // No creation flags
81 featureLevels, // Feature levels
82 1, // Number of feature levels
83 D3D11_SDK_VERSION, // SDK version
84 &swapChainDesc, // Swap chain description
85 &swapChain, // Swap chain
86 &device, // Device
87 nullptr, // Supported feature level
88 &deviceContext // Device context
89 );
90
91 if (FAILED(hr)) {
92 return false;
93 }
94
95 CheckMSAASupport();
96
97 if (!CreateRenderTargetView() ||
98 !CreateDepthStencilBuffer(window->GetWidth(), window->GetHeight())) {
99 return false;
100 }
101
102 SetViewport(0, 0, window->GetWidth(), window->GetHeight());
103
104 SLEAK_INFO("DirectX 11 has successfully created!");
105
106 #ifdef _DEBUG
107 ID3D11InfoQueue* infoQueue;
108 if (SUCCEEDED(device->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&infoQueue))) {
109 // Only break on corruption (critical), not on errors — breaking on
110 // errors without a debugger attached silently drops draw calls.
111 infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
112
113 D3D11_MESSAGE_SEVERITY severities[] = {
114 D3D11_MESSAGE_SEVERITY_INFO
115 };
116
117 D3D11_INFO_QUEUE_FILTER filter = {};
118 filter.DenyList.NumSeverities = _countof(severities);
119 filter.DenyList.pSeverityList = severities;
120
121 infoQueue->AddStorageFilterEntries(&filter);
122 infoQueue->Release();
123 }
124 #endif
125
126 SetPerformanceCounter(true);
127 SetRenderMode(RenderMode::Fill);
128 SetRenderFace(RenderFace::Front);
129
130 ConfigureRenderMode();
131
132 SetDepthStencilState(true, true);
133
134 msaaSampleCount = 1;
135
136 D3D11_QUERY_DESC queryDesc;
137 queryDesc.Query = D3D11_QUERY::D3D11_QUERY_PIPELINE_STATISTICS;
138 queryDesc.MiscFlags = 0;
139 if(FAILED(device->CreateQuery(&queryDesc,&query)))
140 SLEAK_WARN("Could not create pipeline statics object!");
141
142
143 if(bEnabledPerformanceCounter){
144 if(query)
145 while (deviceContext->GetData(query, nullptr, 0, 0) == S_FALSE);
146 deviceContext->Begin(query);
147 }
148
149 if (!CreateBlendState())
150 return false;
151
152 return true;
153}
154
155void DirectX11Renderer::BeginRender() {
156 if (m_msaaChangeRequested)
157 ApplyMSAAChange();
158
159 // Commit staged lightVP before shadow pass so shadow + main agree.
160 if (m_hasPendingLightVP) {
161 std::memcpy(m_lightVP, m_pendingLightVP, sizeof(m_lightVP));
162 }
163
164 // Create tonemap resources on demand
165 if (m_tonemapEnabled && !m_tonemapResourcesCreated)
166 CreateTonemapResources();
167
168 // When tonemapping is enabled and no MSAA, render to HDR target
169 if (m_tonemapEnabled && m_tonemapResourcesCreated && msaaSampleCount <= 1) {
170 deviceContext->OMSetRenderTargets(1, &m_hdrRTV, depthStencilView);
171 } else if (msaaSampleCount > 1 && msaaRenderTargetView && msaaDepthStencilView) {
172 deviceContext->OMSetRenderTargets(1, &msaaRenderTargetView, msaaDepthStencilView);
173 } else {
174 deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
175 }
176
177 ClearRenderTarget(0.39f, 0.58f, 0.93f, 1.0f);
178 ClearDepthStencil(true, false, 1.0, 0);
179
180 // Shadow pass: render shadow depth map before main pass
181 if (m_shadowPassEnabled) {
182 RenderShadowPass();
183 }
184
185 // Ensure shadow map SRV + comparison sampler stay bound at t3/s3
186 // for the main draw calls (guards against any intermediate state
187 // clearing the slots between shadow pass and ExecuteCommands).
188 if (m_shadowMapCreated && m_shadowSRV && m_shadowSampler) {
189 deviceContext->PSSetShaderResources(3, 1, &m_shadowSRV);
190 deviceContext->PSSetSamplers(3, 1, &m_shadowSampler);
191 }
192
193 if (bIsLayoutCreated)
194 deviceContext->IASetInputLayout(layout);
195
196 if(bImInitialized) {
197 ImGui_ImplDX11_NewFrame();
198 ImGui_ImplSDL3_NewFrame();
199 ImGui::NewFrame();
200 }
201}
202
203void DirectX11Renderer::EndRender() {
204 // Resolve MSAA before presenting
205 if (msaaSampleCount > 1) {
206 // Switch back to default render target for ImGui
207 deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
208 ResolveMSAA();
209 }
210
211 // Run tonemapping post-process pass (HDR -> backbuffer)
212 if (m_tonemapEnabled && m_tonemapResourcesCreated && msaaSampleCount <= 1) {
213 ExecuteTonemapPass();
214 // Restore input layout after fullscreen pass
215 if (bIsLayoutCreated)
216 deviceContext->IASetInputLayout(layout);
217 }
218
219 // ImGui renders on top of tonemapped output
220 if (bImInitialized) {
221 // Ensure we're rendering to the backbuffer
222 deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);
223 ImGui::Render();
224 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
225 }
226
227 swapChain->Present(m_vsync ? 1 : 0, 0);
228
229 UpdateFrameMetrics();
230}
231
232void DirectX11Renderer::Cleanup() {
233 bImInitialized = false;
234
235 // Cleanup post-process resources
236 CleanupTonemapResources();
237 CleanupShadowResources();
238
239 // Cleanup MSAA resources
240 if (msaaRenderTarget) { msaaRenderTarget->Release(); msaaRenderTarget = nullptr; }
241 if (msaaRenderTargetView) { msaaRenderTargetView->Release(); msaaRenderTargetView = nullptr; }
242 if (msaaDepthStencilBuffer) { msaaDepthStencilBuffer->Release(); msaaDepthStencilBuffer = nullptr; }
243 if (msaaDepthStencilView) { msaaDepthStencilView->Release(); msaaDepthStencilView = nullptr; }
244
245 if (renderTargetView) {
246 renderTargetView->Release();
247 renderTargetView = nullptr;
248 }
249 if (swapChain) {
250 swapChain->Release();
251 swapChain = nullptr;
252 }
253 if (deviceContext) {
254 deviceContext->Release();
255 deviceContext = nullptr;
256 }
257
258 if (depthStencilView) {
259 depthStencilView->Release();
260 depthStencilView = nullptr;
261 }
262 if (depthStencilBuffer) {
263 depthStencilBuffer->Release();
264 depthStencilBuffer = nullptr;
265 }
266 if (depthStencilState) {
267 depthStencilState->Release();
268 depthStencilState = nullptr;
269 }
270
271 if (blendState) {
272 blendState->Release();
273 blendState = nullptr;
274 }
275
276 if(query)
277 {
278 query->Release();
279 query = nullptr;
280 }
281
282 if (bImInitialized) {
283 ImGui_ImplDX11_Shutdown();
284 ImGui_ImplSDL3_Shutdown();
285 ImGui::DestroyContext();
286 bImInitialized = false;
287 }
288
289 #ifdef _DEBUG
290 if(device) {
291 ID3D11Debug* debugDevice;
292 device->QueryInterface(__uuidof(ID3D11Debug), (void**)&debugDevice);
293 if (debugDevice) {
294 debugDevice->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
295 debugDevice->Release();
296 }
297 }
298 #endif
299
300 if (device) {
301 device->Release();
302 device = nullptr;
303 }
304}
305
306bool DirectX11Renderer::CreateDepthStencilBuffer(uint32_t width,uint32_t height) {
307
308 D3D11_TEXTURE2D_DESC depthDesc = {};
309 depthDesc.Width = width;
310 depthDesc.Height = height;
311 depthDesc.MipLevels = 1;
312 depthDesc.ArraySize = 1;
313 depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
314 depthDesc.SampleDesc.Count = 1;
315 depthDesc.SampleDesc.Quality = 0;
316 depthDesc.Usage = D3D11_USAGE_DEFAULT;
317 depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
318
319 HRESULT hr =
320 device->CreateTexture2D(&depthDesc, nullptr, &depthStencilBuffer);
321 if (FAILED(hr)) {
322 SLEAK_ERROR("Failed to create depth stencil buffer!");
323 return false;
324 }
325
326 hr = device->CreateDepthStencilView(depthStencilBuffer, nullptr,
327 &depthStencilView);
328 if (FAILED(hr)) {
329 SLEAK_ERROR("Failed to create depth stencil view!");
330 return false;
331 }
332
333 deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
334 return true;
335}
336
337void DirectX11Renderer::SetDepthStencilState(bool enableDepth,
338 bool enableStencil) {
339 D3D11_DEPTH_STENCIL_DESC depthStencilDesc = {};
340 depthStencilDesc.DepthEnable = enableDepth;
341 depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
342 depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
343 depthStencilDesc.StencilEnable = enableStencil;
344 depthStencilDesc.StencilReadMask = 0xFF;
345 depthStencilDesc.StencilWriteMask = 0xFF;
346
347 // Front face stencil operations
348 depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
349 depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
350 depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
351 depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
352
353 // Back face stencil operations
354 depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
355 depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
356 depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
357 depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
358
359 HRESULT hr = device->CreateDepthStencilState(&depthStencilDesc, &depthStencilState);
360 if (FAILED(hr)) {
361 SLEAK_ERROR("Failed to create depth stencil state!");
362 return;
363 }
364
365 deviceContext->OMSetDepthStencilState(depthStencilState, 1);
366}
367
368void DirectX11Renderer::Resize(uint32_t width, uint32_t height) {
369 if (swapChain == nullptr || device == nullptr || deviceContext == nullptr) {
370 return;
371 }
372 if (width == 0 || height == 0) return;
373
374 // Unbind all pipeline resources so DXGI can release the back-buffer references
375 deviceContext->OMSetRenderTargets(0, nullptr, nullptr);
376 deviceContext->ClearState();
377 deviceContext->Flush();
378
379 // Release all references to the swap chain's buffers
380 if (renderTargetView) {
381 renderTargetView->Release();
382 renderTargetView = nullptr;
383 }
384
385 if (depthStencilView) {
386 depthStencilView->Release();
387 depthStencilView = nullptr;
388 }
389 if (depthStencilBuffer) {
390 depthStencilBuffer->Release();
391 depthStencilBuffer = nullptr;
392 }
393 if (depthStencilState) {
394 depthStencilState->Release();
395 depthStencilState = nullptr;
396 }
397
398 // Use 0 to preserve the existing buffer count (FLIP_DISCARD requires >=2)
399 // Use DXGI_FORMAT_UNKNOWN to preserve the existing format
400 HRESULT hr = swapChain->ResizeBuffers(0,
401 width, height,
402 DXGI_FORMAT_UNKNOWN,
403 0);
404
405 if (FAILED(hr)) {
406 SLEAK_ERROR("Failed to resize swap chain buffers! HRESULT: 0x{:X}", (unsigned)hr);
407 return;
408 }
409
410 // Recreate the render target view
411 if (!CreateRenderTargetView() || !CreateDepthStencilBuffer(width, height)) {
412 return;
413 }
414
415 // Recreate MSAA targets if MSAA is active
416 if (msaaSampleCount > 1) {
417 CreateMSAARenderTarget();
418 CreateMSAADepthStencil();
419 }
420
421 // Recreate HDR tonemap target at new size
422 if (m_tonemapResourcesCreated) {
423 CleanupTonemapResources();
424 CreateTonemapResources();
425 }
426
427 // Update the viewport
428 SetViewport(0, 0, width, height);
429
430 ImCon->IO.DisplaySize = ImVec2(width,height);
431
432 SLEAK_INFO("DirectX 11 resized to {}x{}", width, height);
433}
434
435void DirectX11Renderer::Draw(uint32_t vertexCount) { Draw(vertexCount, 0); }
436
437void DirectX11Renderer::Draw(uint32_t vertexCount, uint32_t firstVertex) {
438 deviceContext->Draw(vertexCount, firstVertex);
439}
440
441void DirectX11Renderer::DrawIndexed(uint32_t indexCount) {
442 DrawIndexed(indexCount, 0, 0);
443}
444
445void DirectX11Renderer::DrawIndexed(uint32_t indexCount, uint32_t firstIndex,
446 int32_t baseVertex) {
447 deviceContext->DrawIndexed(indexCount, firstIndex, baseVertex);
448 DrawnVertices += indexCount;
449}
450
451void DirectX11Renderer::DrawInstance(uint32_t instanceCount,
452 uint32_t vertexPerInstance) {
453 deviceContext->DrawInstanced(vertexPerInstance, instanceCount, 0, 0);
454}
455
456void DirectX11Renderer::DrawIndexedInstance(uint32_t instanceCount,
457 uint32_t indexPerInstance) {
458 DrawIndexedInstance(instanceCount, indexPerInstance, 0, 0);
459}
460
461void DirectX11Renderer::DrawIndexedInstance(uint32_t instanceCount,
462 uint32_t indexPerInstance,
463 uint32_t firstIndex,
464 int32_t baseVertex) {
465 deviceContext->DrawIndexedInstanced(indexPerInstance, instanceCount,
466 firstIndex, baseVertex, 0);
467}
468
469void DirectX11Renderer::ClearRenderTarget(float r, float g, float b, float a) {
470 const float clearColor[4] = {r, g, b, a};
471
472 ID3D11RenderTargetView* target;
473 if (m_tonemapEnabled && m_tonemapResourcesCreated && msaaSampleCount <= 1)
474 target = m_hdrRTV;
475 else if (msaaSampleCount > 1)
476 target = msaaRenderTargetView;
477 else
478 target = renderTargetView;
479
480 if (target)
481 deviceContext->ClearRenderTargetView(target, clearColor);
482}
483
484void DirectX11Renderer::ClearDepthStencil(bool clearDepth, bool clearStencil,
485 float depth, uint8_t stencil) {
486 UINT clearFlags = 0;
487 if (clearDepth) clearFlags |= D3D11_CLEAR_DEPTH;
488 if (clearStencil) clearFlags |= D3D11_CLEAR_STENCIL;
489
490 ID3D11DepthStencilView* target = (msaaSampleCount > 1 && msaaDepthStencilView)
491 ? msaaDepthStencilView : depthStencilView;
492 if (target) {
493 deviceContext->ClearDepthStencilView(target, clearFlags, depth, stencil);
494 }
495}
496
497// Buffer binding
498void DirectX11Renderer::BindVertexBuffer(RefPtr<BufferBase> buffer, uint32_t slot) {
499 if (!buffer)
500 return;
501
502 UINT stride = sizeof(Sleak::Vertex);
503 UINT offset = 0;
504
505 try
506 {
507 auto d3d11Buffer = dynamic_cast<DirectX11Buffer*>
508 (buffer.get())
509 ->GetD3DBuffer();
510
511 deviceContext->IASetVertexBuffers(slot, 1, &d3d11Buffer, &stride, &offset);
512
513 }
514 catch (std::exception& e)
515 {
516 SLEAK_ERROR("Failed to cast Vertex Buffer! {}", e.what());
517 }
518}
519
520void DirectX11Renderer::BindIndexBuffer(RefPtr<BufferBase> buffer,
521 uint32_t slot) {
522 if (!buffer) return;
523
524 UINT offset = 0;
525
526 try {
527 auto d3d11Buffer =
528 dynamic_cast<DirectX11Buffer*>(buffer.get())->GetD3DBuffer();
529
530 deviceContext->IASetIndexBuffer(d3d11Buffer, DXGI_FORMAT_R32_UINT, 0);
531
532 } catch (std::exception& e) {
533 SLEAK_ERROR("Failed to cast Index Buffer! {}", e.what());
534 }
535}
536
537void DirectX11Renderer::BindConstantBuffer(RefPtr<BufferBase> buffer,
538 uint32_t slot) {
539 try {
540 auto* dx11Buf = dynamic_cast<DirectX11Buffer*>(buffer.get());
541 if (!dx11Buf) return;
542
543 // Shadow pass: use dedicated shadow CB with LightVP*World for slot 0
544 if (m_inShadowPass && slot == 0 && dx11Buf->GetCPUShadowCopySize() >= 128) {
545 const float* srcWorld = reinterpret_cast<const float*>(
546 static_cast<const char*>(dx11Buf->GetCPUShadowCopy()) + 64);
547
548 float shadowPC[32];
549 for (int r = 0; r < 4; ++r) {
550 for (int c = 0; c < 4; ++c) {
551 float sum = 0.0f;
552 for (int k = 0; k < 4; ++k) {
553 sum += srcWorld[r * 4 + k] * m_lightVP[k * 4 + c];
554 }
555 shadowPC[r * 4 + c] = sum;
556 }
557 }
558 std::memcpy(&shadowPC[16], srcWorld, sizeof(float) * 16);
559
560 // Create dedicated shadow transform CB on first use
561 if (!m_shadowTransformCB) {
562 D3D11_BUFFER_DESC desc{};
563 desc.ByteWidth = 128;
564 desc.Usage = D3D11_USAGE_DYNAMIC;
565 desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
566 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
567 device->CreateBuffer(&desc, nullptr, &m_shadowTransformCB);
568 }
569
570 // Update and bind the shadow CB instead of the original
571 D3D11_MAPPED_SUBRESOURCE mapped{};
572 if (SUCCEEDED(deviceContext->Map(m_shadowTransformCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
573 std::memcpy(mapped.pData, shadowPC, sizeof(shadowPC));
574 deviceContext->Unmap(m_shadowTransformCB, 0);
575 }
576 deviceContext->VSSetConstantBuffers(slot, 1, &m_shadowTransformCB);
577 deviceContext->PSSetConstantBuffers(slot, 1, &m_shadowTransformCB);
578 return;
579 }
580
581 auto d3d11Buffer = dx11Buf->GetD3DBuffer();
582 deviceContext->VSSetConstantBuffers(slot, 1, &d3d11Buffer);
583 deviceContext->PSSetConstantBuffers(slot, 1, &d3d11Buffer);
584
585 } catch (std::exception& e) {
586 SLEAK_ERROR("Failed to cast Constant Buffer! {}", e.what());
587 }
588}
589
590void DirectX11Renderer::ConfigureRenderMode() {
591 switch (Mode)
592 {
593 case RenderMode::Fill:
594 topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
595 fillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
596 break;
597 case RenderMode::Points:
598 topology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
599 fillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
600 break;
601 case RenderMode::Wireframe :
602 topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
603 fillMode = D3D11_FILL_MODE::D3D11_FILL_WIREFRAME;
604 break;
605 default:
606 SLEAK_ERROR("Unable to set render mode!");
607 break;
608 }
609
610 if (deviceContext)
611 deviceContext->IASetPrimitiveTopology(topology);
612
613 SetRasterState();
614}
615
616void DirectX11Renderer::ConfigureRenderFace() {
617 switch(Face) {
618 case RenderFace::None : cull = D3D11_CULL_MODE::D3D11_CULL_NONE; break;
619 case RenderFace::Front : cull = D3D11_CULL_MODE::D3D11_CULL_FRONT; break;
620 case RenderFace::Back : cull = D3D11_CULL_MODE::D3D11_CULL_BACK; break;
621 default: cull = D3D11_CULL_MODE::D3D11_CULL_FRONT;
622 }
623}
624
625bool DirectX11Renderer::SetRasterState() {
626 ID3D11RasterizerState* rasterState;
627 D3D11_RASTERIZER_DESC RasterDesc = {};
628 RasterDesc.AntialiasedLineEnable = true;
629 RasterDesc.MultisampleEnable = true;
630 RasterDesc.CullMode = cull;
631 RasterDesc.DepthBias = 0;
632 RasterDesc.DepthBiasClamp = 0.0f;
633 RasterDesc.SlopeScaledDepthBias = 0.0f;
634 RasterDesc.DepthClipEnable = true;
635 RasterDesc.ScissorEnable = false;
636 RasterDesc.FillMode = fillMode;
637 RasterDesc.FrontCounterClockwise = false;
638
639 if (FAILED(device->CreateRasterizerState(&RasterDesc, &rasterState))) {
640 return false;
641 } else {
642 deviceContext->RSSetState(rasterState);
643 rasterState->Release();
644 }
645
646 return true;
647}
648
649void DirectX11Renderer::SetRenderMode(RenderMode mode) {
650 this->Mode = mode;
651 ConfigureRenderMode();
652}
653
654void DirectX11Renderer::SetRenderFace(RenderFace face) {
655 this->Face = face;
656 ConfigureRenderFace();
657}
658
659void DirectX11Renderer::SetViewport(float x, float y, float width, float height,
660 float minDepth,
661 float maxDepth) {
662 D3D11_VIEWPORT viewport = {};
663 viewport.Width = static_cast<float>(width);
664 viewport.Height = static_cast<float>(height);
665 viewport.MinDepth = minDepth;
666 viewport.MaxDepth = maxDepth;
667 viewport.TopLeftX = x;
668 viewport.TopLeftY = y;
669 deviceContext->RSSetViewports(1, &viewport);
670
671}
672
673bool DirectX11Renderer::CreateRenderTargetView() {
674 // Release the existing render target view if it exists
675 if (renderTargetView) {
676 renderTargetView->Release();
677 renderTargetView = nullptr;
678 }
679
680 // Get the back buffer from the swap chain
681 ID3D11Texture2D* backBuffer = nullptr;
682 HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
683 reinterpret_cast<void**>(&backBuffer));
684 if (FAILED(hr)) {
685 SLEAK_ERROR("Failed to get back buffer!");
686 return false;
687 }
688
689 // Create the render target view
690 hr = device->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView);
691 backBuffer->Release();
692 if (FAILED(hr)) {
693 SLEAK_ERROR("Failed to create render target view!");
694 return false;
695 }
696
697 // Set the render target
698 deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);
699
700 SLEAK_INFO("Render target view created successfully.");
701 return true;
702}
703
704Texture* DirectX11Renderer::CreateTextureFromData(uint32_t width, uint32_t height, void* data) {
705 DirectX11Texture* texture = new DirectX11Texture(device);
706 if (texture->LoadFromMemory(data,width,height,TextureFormat::RGBA8)) {
707 return texture;
708 }
709 return nullptr;
710}
711
712Texture* DirectX11Renderer::CreateTexture(const std::string& TexturePath) {
713 DirectX11Texture* texture = new DirectX11Texture(device);
714 if (texture->LoadFromFile(TexturePath)) {
715 return texture;
716 }
717 return nullptr;
718}
719
720Texture* DirectX11Renderer::CreateCubemapTexture(
721 const std::array<std::string, 6>& facePaths) {
722 auto* texture = new DirectX11CubemapTexture(device);
723 if (texture->LoadCubemap(facePaths)) {
724 return texture;
725 }
726 delete texture;
727 return nullptr;
728}
729
730Texture* DirectX11Renderer::CreateCubemapTextureFromPanorama(
731 const std::string& panoramaPath) {
732 auto* texture = new DirectX11CubemapTexture(device);
733 if (texture->LoadEquirectangular(panoramaPath)) {
734 return texture;
735 }
736 delete texture;
737 return nullptr;
738}
739
740void DirectX11Renderer::SetDepthWrite(bool enabled) {
741 m_depthWriteEnabled = enabled;
742
743 D3D11_DEPTH_STENCIL_DESC desc = {};
744 desc.DepthEnable = TRUE;
745 desc.DepthWriteMask = enabled ? D3D11_DEPTH_WRITE_MASK_ALL
746 : D3D11_DEPTH_WRITE_MASK_ZERO;
747 desc.DepthFunc = m_depthFunc;
748 desc.StencilEnable = FALSE;
749
750 ID3D11DepthStencilState* state = nullptr;
751 if (SUCCEEDED(device->CreateDepthStencilState(&desc, &state))) {
752 deviceContext->OMSetDepthStencilState(state, 1);
753 state->Release();
754 }
755}
756
757void DirectX11Renderer::SetDepthCompare(DepthCompare compare) {
758 switch (compare) {
759 case DepthCompare::Less: m_depthFunc = D3D11_COMPARISON_LESS; break;
760 case DepthCompare::LessEqual: m_depthFunc = D3D11_COMPARISON_LESS_EQUAL; break;
761 case DepthCompare::Greater: m_depthFunc = D3D11_COMPARISON_GREATER; break;
762 case DepthCompare::GreaterEqual: m_depthFunc = D3D11_COMPARISON_GREATER_EQUAL; break;
763 case DepthCompare::Equal: m_depthFunc = D3D11_COMPARISON_EQUAL; break;
764 case DepthCompare::NotEqual: m_depthFunc = D3D11_COMPARISON_NOT_EQUAL; break;
765 case DepthCompare::Always: m_depthFunc = D3D11_COMPARISON_ALWAYS; break;
766 case DepthCompare::Never: m_depthFunc = D3D11_COMPARISON_NEVER; break;
767 }
768
769 D3D11_DEPTH_STENCIL_DESC desc = {};
770 desc.DepthEnable = TRUE;
771 desc.DepthWriteMask = m_depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL
772 : D3D11_DEPTH_WRITE_MASK_ZERO;
773 desc.DepthFunc = m_depthFunc;
774 desc.StencilEnable = FALSE;
775
776 ID3D11DepthStencilState* state = nullptr;
777 if (SUCCEEDED(device->CreateDepthStencilState(&desc, &state))) {
778 deviceContext->OMSetDepthStencilState(state, 1);
779 state->Release();
780 }
781}
782
783void DirectX11Renderer::SetCullEnabled(bool enabled) {
784 if (enabled) {
785 cull = D3D11_CULL_FRONT;
786 } else {
787 cull = D3D11_CULL_NONE;
788 }
789 SetRasterState();
790}
791
792void DirectX11Renderer::BindTexture(RefPtr<Sleak::Texture> texture,
793 uint32_t slot) {
794 if (texture.IsValid()) {
795 texture->Bind(slot);
796 }
797}
798
799void DirectX11Renderer::BeginSkyboxPass() {
800 // Save current state
801 m_savedCullMode = cull;
802 m_savedDepthStencilState = depthStencilState;
803 if (m_savedDepthStencilState) {
804 m_savedDepthStencilState->AddRef();
805 }
806}
807
808void DirectX11Renderer::EndSkyboxPass() {
809 // Restore depth stencil state
810 if (m_savedDepthStencilState) {
811 deviceContext->OMSetDepthStencilState(m_savedDepthStencilState, 1);
812 m_savedDepthStencilState->Release();
813 m_savedDepthStencilState = nullptr;
814 }
815
816 // Restore cull mode
817 cull = m_savedCullMode;
818 SetRasterState();
819
820 // Reset depth tracking
821 m_depthWriteEnabled = true;
822 m_depthFunc = D3D11_COMPARISON_LESS;
823}
824
825void DirectX11Renderer::BeginDebugLinePass() {
826 m_savedTopology = topology;
827 topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
828 deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
829}
830
831void DirectX11Renderer::EndDebugLinePass() {
832 topology = m_savedTopology;
833 deviceContext->IASetPrimitiveTopology(topology);
834}
835
836BufferBase* DirectX11Renderer::CreateBuffer(BufferType Type, uint32_t size, void* data) {
837 assert(size > 0);
838
839 DirectX11Buffer* buffer = new DirectX11Buffer(device, size, Type);
840 if (!buffer->Initialize(data)) {
841 delete buffer;
842 return nullptr;
843 }
844 return static_cast<BufferBase*>(buffer);
845}
846
847Shader* DirectX11Renderer::CreateShader(const std::string& shaderSource) {
848 DirectX11Shader* shader = new DirectX11Shader(device);
849 if (shader->compile(shaderSource)) {
850 // Each shader creates its own input layout matching its VS inputs.
851 // The layout is stored per-shader and set in bind().
852 auto* il = shader->createInputLayout();
853 if (!bIsLayoutCreated && il) {
854 layout = il;
855 bIsLayoutCreated = true;
856 }
857 return static_cast<Shader*>(shader);
858 }
859 return nullptr;
860}
861
862bool DirectX11Renderer::CreateBlendState() {
863 //(+) : Binary Operator
864 // Color = SourceP (X) SourceFactor (+) DestP (X) DestFactor
865 // Alpha = SourceA SourceFactor (+) DestA DestFactor
866
867 D3D11_BLEND_DESC desc;
868 desc.AlphaToCoverageEnable = false;
869 desc.IndependentBlendEnable = false;
870
871 desc.RenderTarget[0].BlendEnable = true;
872
873 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
874 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
875
876 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
877 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
878
879 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
880 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
881
882 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
883
884 if (FAILED(device->CreateBlendState(&desc, &blendState)))
885 return false;
886
887 return true;
888}
889
890void DirectX11Renderer::SetBlendState(float r, float g, float b, float a, UINT Mask) {
891 float BlendFactor[4] = {r,g,b,a};
892 deviceContext->OMSetBlendState(blendState, BlendFactor, Mask);
893}
894
895void DirectX11Renderer::CheckMSAASupport() {
896 m_maxMsaaSampleCount = 1;
897 for (UINT sampleCount = 1;
898 sampleCount <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT;
899 sampleCount *= 2)
900 {
901 UINT quality = 0;
902 HRESULT hr = device->CheckMultisampleQualityLevels(
903 DXGI_FORMAT_R8G8B8A8_UNORM, sampleCount, &quality);
904 if (SUCCEEDED(hr) && quality > 0) {
905 SLEAK_INFO("MSAA {}x supported with {} quality levels.",
906 sampleCount, quality);
907 if (sampleCount <= 8)
908 m_maxMsaaSampleCount = sampleCount;
909 }
910 }
911}
912
913bool DirectX11Renderer::CreateMSAARenderTarget() {
914
915 if (msaaRenderTarget) msaaRenderTarget->Release();
916 if (msaaRenderTargetView) msaaRenderTargetView->Release();
917
918 D3D11_TEXTURE2D_DESC texDesc;
919 texDesc.Width = window->GetWidth();
920 texDesc.Height = window->GetHeight();
921 texDesc.MipLevels = 1;
922 texDesc.ArraySize = 1;
923 texDesc.SampleDesc.Count = msaaSampleCount;
924 texDesc.SampleDesc.Quality = msaaQualityLevel;
925 texDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
926 texDesc.CPUAccessFlags = 0;
927 texDesc.MiscFlags = 0;
928 texDesc.Usage = D3D11_USAGE_DEFAULT;
929 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
930
931 if (FAILED(device->CreateTexture2D(&texDesc, nullptr, &msaaRenderTarget)))
932 return false;
933
934 if (FAILED(device->CreateRenderTargetView(msaaRenderTarget,nullptr,&msaaRenderTargetView)))
935 return false;
936
937 return true;
938}
939
940bool DirectX11Renderer::CreateMSAADepthStencil() {
941 if (msaaDepthStencilBuffer) msaaDepthStencilBuffer->Release();
942 if (msaaDepthStencilView) msaaDepthStencilView->Release();
943
944 D3D11_TEXTURE2D_DESC texDesc;
945 texDesc.Width = window->GetWidth();
946 texDesc.Height = window->GetHeight();
947 texDesc.MipLevels = 1;
948 texDesc.ArraySize = 1;
949 texDesc.SampleDesc.Count = msaaSampleCount;
950 texDesc.SampleDesc.Quality = msaaQualityLevel;
951 texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
952 texDesc.CPUAccessFlags = 0;
953 texDesc.MiscFlags = 0;
954 texDesc.Usage = D3D11_USAGE_DEFAULT;
955 texDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
956
957 if (FAILED(device->CreateTexture2D(&texDesc,nullptr,&msaaDepthStencilBuffer))) return false;
958
959 if (FAILED(device->CreateDepthStencilView(msaaDepthStencilBuffer,nullptr,&msaaDepthStencilView))) return false;
960
961
962 return true;
963}
964
965void DirectX11Renderer::ResolveMSAA() {
966 if (msaaSampleCount <= 1 || msaaSampleCount % 2 != 0)
967 return;
968
969 ID3D11Texture2D* back;
970 if (FAILED(swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&back))) {
971 SLEAK_ERROR("Failed to get back buffer for MSAA!");
972 return;
973 }
974 deviceContext->ResolveSubresource(back,0,msaaRenderTarget,0,DXGI_FORMAT_R8G8B8A8_UNORM);
975
976 back->Release();
977}
978
979void DirectX11Renderer::ApplyMSAAChange() {
980 if (!m_msaaChangeRequested)
981 return;
982 m_msaaChangeRequested = false;
983
984 m_msaaSampleCount = m_pendingMsaaSampleCount;
985 msaaSampleCount = m_pendingMsaaSampleCount;
986
987 // Query quality level for new sample count
988 UINT quality = 0;
989 device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM,
990 msaaSampleCount, &quality);
991 msaaQualityLevel = (quality > 0) ? quality - 1 : 0;
992
993 // Cleanup old MSAA resources
994 if (msaaRenderTarget) { msaaRenderTarget->Release(); msaaRenderTarget = nullptr; }
995 if (msaaRenderTargetView) { msaaRenderTargetView->Release(); msaaRenderTargetView = nullptr; }
996 if (msaaDepthStencilBuffer) { msaaDepthStencilBuffer->Release(); msaaDepthStencilBuffer = nullptr; }
997 if (msaaDepthStencilView) { msaaDepthStencilView->Release(); msaaDepthStencilView = nullptr; }
998
999 if (msaaSampleCount > 1) {
1000 CreateMSAARenderTarget();
1001 CreateMSAADepthStencil();
1002 } else {
1003 // Re-bind non-MSAA render targets since pipeline still references released MSAA views
1004 deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
1005 }
1006
1007 SLEAK_INFO("DX11 MSAA changed to {}x", msaaSampleCount);
1008}
1009
1010bool DirectX11Renderer::CreateImGUI()
1011{
1012 IMGUI_CHECKVERSION();
1013 ImCon = ImGui::CreateContext();
1014 ImCon->IO.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
1015
1016 ImGui::StyleColorsDark();
1017
1018 if(!ImGui_ImplSDL3_InitForD3D(window->GetSDLWindow()))
1019 return false;
1020
1021 if(!ImGui_ImplDX11_Init(device, deviceContext))
1022 return false;
1023
1024 if(!ImGui_ImplDX11_CreateDeviceObjects())
1025 return false;
1026
1027
1028 bImInitialized = true;
1029
1030 return true;
1031}
1032
1033
1034// ---- Shadow Constant Buffer ----
1035
1036bool DirectX11Renderer::CreateShadowConstantBuffer() {
1037 if (m_shadowCBCreated) return true;
1038
1039 D3D11_BUFFER_DESC desc{};
1040 desc.ByteWidth = sizeof(PCSSShadowGPUData);
1041 desc.Usage = D3D11_USAGE_DYNAMIC;
1042 desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1043 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1044
1045 HRESULT hr = device->CreateBuffer(&desc, nullptr, &m_shadowCB);
1046 if (FAILED(hr)) {
1047 SLEAK_ERROR("Failed to create shadow constant buffer!");
1048 return false;
1049 }
1050
1051 m_shadowCBCreated = true;
1052 return true;
1053}
1054
1055void DirectX11Renderer::CleanupShadowResources() {
1056 if (m_shadowCB) { m_shadowCB->Release(); m_shadowCB = nullptr; }
1057 m_shadowCBCreated = false;
1058 if (m_shadowTransformCB) { m_shadowTransformCB->Release(); m_shadowTransformCB = nullptr; }
1059 if (m_shadowRasterState) { m_shadowRasterState->Release(); m_shadowRasterState = nullptr; }
1060 if (m_shadowSampler) { m_shadowSampler->Release(); m_shadowSampler = nullptr; }
1061 if (m_shadowSRV) { m_shadowSRV->Release(); m_shadowSRV = nullptr; }
1062 if (m_shadowDSV) { m_shadowDSV->Release(); m_shadowDSV = nullptr; }
1063 if (m_shadowDepthTex) { m_shadowDepthTex->Release(); m_shadowDepthTex = nullptr; }
1064 m_shadowMapCreated = false;
1065}
1066
1067void DirectX11Renderer::SetLightVP(const float* mat) {
1068 if (mat) {
1069 std::memcpy(m_pendingLightVP, mat, sizeof(m_pendingLightVP));
1070 m_hasPendingLightVP = true;
1071 }
1072}
1073
1074bool DirectX11Renderer::CreateShadowMapResources() {
1075 if (m_shadowMapCreated) return true;
1076
1077 // Create depth texture
1078 D3D11_TEXTURE2D_DESC texDesc{};
1079 texDesc.Width = m_shadowMapResolution;
1080 texDesc.Height = m_shadowMapResolution;
1081 texDesc.MipLevels = 1;
1082 texDesc.ArraySize = 1;
1083 texDesc.Format = DXGI_FORMAT_R32_TYPELESS;
1084 texDesc.SampleDesc.Count = 1;
1085 texDesc.SampleDesc.Quality = 0;
1086 texDesc.Usage = D3D11_USAGE_DEFAULT;
1087 texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
1088
1089 HRESULT hr = device->CreateTexture2D(&texDesc, nullptr, &m_shadowDepthTex);
1090 if (FAILED(hr)) {
1091 SLEAK_ERROR("Failed to create shadow depth texture!");
1092 return false;
1093 }
1094
1095 // Create DSV
1096 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc{};
1097 dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
1098 dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
1099 hr = device->CreateDepthStencilView(m_shadowDepthTex, &dsvDesc, &m_shadowDSV);
1100 if (FAILED(hr)) {
1101 SLEAK_ERROR("Failed to create shadow DSV!");
1102 return false;
1103 }
1104
1105 // Create SRV for sampling
1106 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};
1107 srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
1108 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
1109 srvDesc.Texture2D.MipLevels = 1;
1110 hr = device->CreateShaderResourceView(m_shadowDepthTex, &srvDesc, &m_shadowSRV);
1111 if (FAILED(hr)) {
1112 SLEAK_ERROR("Failed to create shadow SRV!");
1113 return false;
1114 }
1115
1116 // Create comparison sampler
1117 D3D11_SAMPLER_DESC sampDesc{};
1118 sampDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
1119 sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
1120 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
1121 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
1122 sampDesc.BorderColor[0] = 1.0f;
1123 sampDesc.BorderColor[1] = 1.0f;
1124 sampDesc.BorderColor[2] = 1.0f;
1125 sampDesc.BorderColor[3] = 1.0f;
1126 sampDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
1127 sampDesc.MinLOD = 0;
1128 sampDesc.MaxLOD = 0;
1129 hr = device->CreateSamplerState(&sampDesc, &m_shadowSampler);
1130 if (FAILED(hr)) {
1131 SLEAK_ERROR("Failed to create shadow sampler!");
1132 return false;
1133 }
1134
1135 // Create shadow rasterizer state with depth bias
1136 D3D11_RASTERIZER_DESC rasterDesc{};
1137 rasterDesc.FillMode = D3D11_FILL_SOLID;
1138 rasterDesc.CullMode = D3D11_CULL_NONE; // all faces cast shadow regardless of orientation
1139 rasterDesc.FrontCounterClockwise = FALSE;
1140 rasterDesc.DepthBias = 100;
1141 rasterDesc.SlopeScaledDepthBias = 2.0f;
1142 rasterDesc.DepthBiasClamp = 0.01f;
1143 rasterDesc.DepthClipEnable = TRUE;
1144 hr = device->CreateRasterizerState(&rasterDesc, &m_shadowRasterState);
1145 if (FAILED(hr)) {
1146 SLEAK_ERROR("Failed to create shadow rasterizer state!");
1147 return false;
1148 }
1149
1150 m_shadowMapCreated = true;
1151 m_shadowResourcesCreated = true;
1152 SLEAK_INFO("D3D11 shadow map resources created ({}x{})", m_shadowMapResolution, m_shadowMapResolution);
1153 return true;
1154}
1155
1156void DirectX11Renderer::RenderShadowPass() {
1157 // Skip if no cached draws — preserve previous frame's shadow map
1158 auto* queue = RenderCommandQueue::GetInstance();
1159 if (!queue || !queue->HasCachedShadowDraws()) return;
1160
1161 if (!m_shadowMapCreated) {
1162 if (!CreateShadowMapResources()) return;
1163 }
1164
1165 // Save current render targets and viewport
1166 ID3D11RenderTargetView* savedRTV = nullptr;
1167 ID3D11DepthStencilView* savedDSV = nullptr;
1168 deviceContext->OMGetRenderTargets(1, &savedRTV, &savedDSV);
1169
1170 D3D11_VIEWPORT savedViewport;
1171 UINT numViewports = 1;
1172 deviceContext->RSGetViewports(&numViewports, &savedViewport);
1173
1174 // Unbind shadow SRV to avoid D3D11 warning (resource bound as both input and output)
1175 ID3D11ShaderResourceView* nullSRV = nullptr;
1176 deviceContext->PSSetShaderResources(3, 1, &nullSRV);
1177
1178 // Set shadow render target (depth only, no color)
1179 ID3D11RenderTargetView* nullRTV = nullptr;
1180 deviceContext->OMSetRenderTargets(1, &nullRTV, m_shadowDSV);
1181
1182 // Set shadow viewport
1183 D3D11_VIEWPORT shadowViewport{};
1184 shadowViewport.Width = static_cast<float>(m_shadowMapResolution);
1185 shadowViewport.Height = static_cast<float>(m_shadowMapResolution);
1186 shadowViewport.MinDepth = 0.0f;
1187 shadowViewport.MaxDepth = 1.0f;
1188 deviceContext->RSSetViewports(1, &shadowViewport);
1189
1190 // Clear shadow depth
1191 deviceContext->ClearDepthStencilView(m_shadowDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);
1192
1193 // Set shadow rasterizer state
1194 deviceContext->RSSetState(m_shadowRasterState);
1195
1196 // Set primitive topology (same as main pass)
1197 deviceContext->IASetPrimitiveTopology(topology);
1198 if (bIsLayoutCreated)
1199 deviceContext->IASetInputLayout(layout);
1200
1201 // Execute shadow draw commands
1202 m_inShadowPass = true;
1203 if (queue) {
1204 queue->ExecuteShadowPass(this);
1205 }
1206 m_inShadowPass = false;
1207
1208 // Restore raster state
1209 SetRasterState();
1210
1211 // Restore render targets and viewport
1212 deviceContext->OMSetRenderTargets(1, &savedRTV, savedDSV);
1213 deviceContext->RSSetViewports(1, &savedViewport);
1214 if (savedRTV) savedRTV->Release();
1215 if (savedDSV) savedDSV->Release();
1216
1217 // Bind shadow map SRV and sampler for main pass
1218 deviceContext->PSSetShaderResources(3, 1, &m_shadowSRV);
1219 deviceContext->PSSetSamplers(3, 1, &m_shadowSampler);
1220}
1221
1222void DirectX11Renderer::UpdateShadowLightUBO(const void* data, uint32_t size) {
1223 if (!device || !deviceContext) return;
1224
1225 // Create shadow CB on first call
1226 if (!m_shadowCBCreated) {
1227 if (!CreateShadowConstantBuffer()) return;
1228 }
1229
1230 // Map the ShadowLightUBO data into our PCSSShadowGPUData format
1231 const auto* ubo = static_cast<const ShadowLightUBO*>(data);
1232
1233 PCSSShadowGPUData shadowData{};
1234 std::memcpy(shadowData.LightVP, ubo->LightVP, sizeof(float) * 16);
1235 shadowData.ShadowBias = ubo->ShadowBias;
1236 shadowData.ShadowStrength = ubo->ShadowStrength;
1237 shadowData.ShadowTexelSize = ubo->ShadowTexelSize;
1238 shadowData.ShadowLightSize = ubo->LightSize;
1239 shadowData.PCSSEnabled = m_pcssEnabled ? 1 : 0;
1240 shadowData.ShadowMapEnabled = m_shadowPassEnabled ? 1 : 0;
1241
1242 D3D11_MAPPED_SUBRESOURCE mapped{};
1243 HRESULT hr = deviceContext->Map(m_shadowCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
1244 if (SUCCEEDED(hr)) {
1245 std::memcpy(mapped.pData, &shadowData, sizeof(shadowData));
1246 deviceContext->Unmap(m_shadowCB, 0);
1247 }
1248
1249 // Bind shadow CB at slot 5 (both VS and PS)
1250 deviceContext->VSSetConstantBuffers(5, 1, &m_shadowCB);
1251 deviceContext->PSSetConstantBuffers(5, 1, &m_shadowCB);
1252}
1253
1254// ---- Post-Process: Tonemapping ----
1255
1256bool DirectX11Renderer::CreateTonemapResources() {
1257 if (m_tonemapResourcesCreated) return true;
1258 if (!device) return false;
1259
1260 // Get back buffer dimensions
1261 ID3D11Texture2D* backBuffer = nullptr;
1262 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
1263 if (!backBuffer) return false;
1264
1265 D3D11_TEXTURE2D_DESC bbDesc{};
1266 backBuffer->GetDesc(&bbDesc);
1267 backBuffer->Release();
1268
1269 // Create HDR render target (R16G16B16A16_FLOAT)
1270 D3D11_TEXTURE2D_DESC texDesc{};
1271 texDesc.Width = bbDesc.Width;
1272 texDesc.Height = bbDesc.Height;
1273 texDesc.MipLevels = 1;
1274 texDesc.ArraySize = 1;
1275 texDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
1276 texDesc.SampleDesc.Count = 1;
1277 texDesc.SampleDesc.Quality = 0;
1278 texDesc.Usage = D3D11_USAGE_DEFAULT;
1279 texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
1280
1281 HRESULT hr = device->CreateTexture2D(&texDesc, nullptr, &m_hdrTexture);
1282 if (FAILED(hr)) {
1283 SLEAK_ERROR("Failed to create HDR texture for tonemapping!");
1284 return false;
1285 }
1286
1287 hr = device->CreateRenderTargetView(m_hdrTexture, nullptr, &m_hdrRTV);
1288 if (FAILED(hr)) return false;
1289
1290 hr = device->CreateShaderResourceView(m_hdrTexture, nullptr, &m_hdrSRV);
1291 if (FAILED(hr)) return false;
1292
1293 // Create point sampler
1294 D3D11_SAMPLER_DESC sampDesc{};
1295 sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
1296 sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
1297 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
1298 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
1299 hr = device->CreateSamplerState(&sampDesc, &m_pointSampler);
1300 if (FAILED(hr)) return false;
1301
1302 // Create post-process constant buffer
1303 D3D11_BUFFER_DESC cbDesc{};
1304 cbDesc.ByteWidth = sizeof(PostProcessGPUData);
1305 cbDesc.Usage = D3D11_USAGE_DYNAMIC;
1306 cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1307 cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1308 hr = device->CreateBuffer(&cbDesc, nullptr, &m_postProcessCB);
1309 if (FAILED(hr)) return false;
1310
1311 // Compile tonemap shaders
1312 ID3DBlob* vsBlob = nullptr;
1313 ID3DBlob* psBlob = nullptr;
1314 ID3DBlob* errorBlob = nullptr;
1315
1316 // Build path to shader (relative to working directory = bin/)
1317 std::string narrowPath = "assets/shaders/tonemap.hlsl";
1318 std::wstring shaderPath(narrowPath.begin(), narrowPath.end());
1319
1320 hr = D3DCompileFromFile(shaderPath.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
1321 "VS_Main", "vs_5_0", D3DCOMPILE_DEBUG, 0, &vsBlob, &errorBlob);
1322 if (FAILED(hr)) {
1323 if (errorBlob) {
1324 SLEAK_ERROR("Tonemap VS compile error: {}",
1325 (const char*)errorBlob->GetBufferPointer());
1326 errorBlob->Release();
1327 }
1328 return false;
1329 }
1330
1331 hr = D3DCompileFromFile(shaderPath.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
1332 "PS_Main", "ps_5_0", D3DCOMPILE_DEBUG, 0, &psBlob, &errorBlob);
1333 if (FAILED(hr)) {
1334 if (errorBlob) {
1335 SLEAK_ERROR("Tonemap PS compile error: {}",
1336 (const char*)errorBlob->GetBufferPointer());
1337 errorBlob->Release();
1338 }
1339 vsBlob->Release();
1340 return false;
1341 }
1342
1343 hr = device->CreateVertexShader(vsBlob->GetBufferPointer(),
1344 vsBlob->GetBufferSize(), nullptr, &m_tonemapVS);
1345 vsBlob->Release();
1346 if (FAILED(hr)) { psBlob->Release(); return false; }
1347
1348 hr = device->CreatePixelShader(psBlob->GetBufferPointer(),
1349 psBlob->GetBufferSize(), nullptr, &m_tonemapPS);
1350 psBlob->Release();
1351 if (FAILED(hr)) return false;
1352
1353 m_tonemapResourcesCreated = true;
1354 SLEAK_INFO("Tonemapping post-process resources created successfully");
1355 return true;
1356}
1357
1358void DirectX11Renderer::CleanupTonemapResources() {
1359 if (m_tonemapVS) { m_tonemapVS->Release(); m_tonemapVS = nullptr; }
1360 if (m_tonemapPS) { m_tonemapPS->Release(); m_tonemapPS = nullptr; }
1361 if (m_postProcessCB) { m_postProcessCB->Release(); m_postProcessCB = nullptr; }
1362 if (m_hdrSRV) { m_hdrSRV->Release(); m_hdrSRV = nullptr; }
1363 if (m_hdrRTV) { m_hdrRTV->Release(); m_hdrRTV = nullptr; }
1364 if (m_hdrTexture) { m_hdrTexture->Release(); m_hdrTexture = nullptr; }
1365 if (m_pointSampler) { m_pointSampler->Release(); m_pointSampler = nullptr; }
1366 m_tonemapResourcesCreated = false;
1367}
1368
1369void DirectX11Renderer::ExecuteTonemapPass() {
1370 if (!m_tonemapResourcesCreated || !m_tonemapVS || !m_tonemapPS)
1371 return;
1372
1373 // Update post-process constant buffer
1374 PostProcessGPUData ppData{};
1375 ppData.Exposure = m_exposure;
1376 ppData.Gamma = m_gamma;
1377 ppData.TonemapEnabled = m_tonemapEnabled ? 1 : 0;
1378
1379 D3D11_MAPPED_SUBRESOURCE mapped{};
1380 HRESULT hr = deviceContext->Map(m_postProcessCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
1381 if (SUCCEEDED(hr)) {
1382 std::memcpy(mapped.pData, &ppData, sizeof(ppData));
1383 deviceContext->Unmap(m_postProcessCB, 0);
1384 }
1385
1386 // Switch render target to backbuffer
1387 deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);
1388
1389 // Bind HDR texture as input
1390 deviceContext->PSSetShaderResources(0, 1, &m_hdrSRV);
1391 deviceContext->PSSetSamplers(0, 1, &m_pointSampler);
1392 deviceContext->PSSetConstantBuffers(0, 1, &m_postProcessCB);
1393
1394 // Set tonemap shaders
1395 deviceContext->VSSetShader(m_tonemapVS, nullptr, 0);
1396 deviceContext->PSSetShader(m_tonemapPS, nullptr, 0);
1397
1398 // Draw fullscreen triangle (no input layout needed for SV_VertexID)
1399 deviceContext->IASetInputLayout(nullptr);
1400 deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1401 deviceContext->Draw(3, 0);
1402
1403 // Unbind HDR SRV to prevent read/write conflict
1404 ID3D11ShaderResourceView* nullSRV = nullptr;
1405 deviceContext->PSSetShaderResources(0, 1, &nullSRV);
1406}
1407
1408} // namespace RenderEngine
1409} // namespace Sleak
1410
1411#else
1412namespace Sleak::RenderEngine {class DirectX11Renderer;}
1413
1414#endif
int width
int height
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
#define SLEAK_INFO(...)
Definition Logger.hpp:20
#define SLEAK_WARN(...)
Definition Logger.hpp:21
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10
class ENGINE_API Window