SleakEngine 1.0.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) {
436 deviceContext->Draw(vertexCount,0);
437}
438
439void DirectX11Renderer::DrawIndexed(uint32_t indexCount) {
440 deviceContext->DrawIndexed(indexCount, 0, 0);
441 DrawnVertices += indexCount;
442}
443
444void DirectX11Renderer::DrawInstance(uint32_t instanceCount,
445 uint32_t vertexPerInstance) {
446 deviceContext->DrawInstanced(vertexPerInstance, instanceCount, 0, 0);
447}
448
449void DirectX11Renderer::DrawIndexedInstance(uint32_t instanceCount,
450 uint32_t indexPerInstance) {
451 deviceContext->DrawIndexedInstanced(indexPerInstance, instanceCount, 0, 0, 0);
452}
453
454void DirectX11Renderer::ClearRenderTarget(float r, float g, float b, float a) {
455 const float clearColor[4] = {r, g, b, a};
456
457 ID3D11RenderTargetView* target;
458 if (m_tonemapEnabled && m_tonemapResourcesCreated && msaaSampleCount <= 1)
459 target = m_hdrRTV;
460 else if (msaaSampleCount > 1)
461 target = msaaRenderTargetView;
462 else
463 target = renderTargetView;
464
465 if (target)
466 deviceContext->ClearRenderTargetView(target, clearColor);
467}
468
469void DirectX11Renderer::ClearDepthStencil(bool clearDepth, bool clearStencil,
470 float depth, uint8_t stencil) {
471 UINT clearFlags = 0;
472 if (clearDepth) clearFlags |= D3D11_CLEAR_DEPTH;
473 if (clearStencil) clearFlags |= D3D11_CLEAR_STENCIL;
474
475 ID3D11DepthStencilView* target = (msaaSampleCount > 1 && msaaDepthStencilView)
476 ? msaaDepthStencilView : depthStencilView;
477 if (target) {
478 deviceContext->ClearDepthStencilView(target, clearFlags, depth, stencil);
479 }
480}
481
482// Buffer binding
483void DirectX11Renderer::BindVertexBuffer(RefPtr<BufferBase> buffer, uint32_t slot) {
484 if (!buffer)
485 return;
486
487 UINT stride = sizeof(Sleak::Vertex);
488 UINT offset = 0;
489
490 try
491 {
492 auto d3d11Buffer = dynamic_cast<DirectX11Buffer*>
493 (buffer.get())
494 ->GetD3DBuffer();
495
496 deviceContext->IASetVertexBuffers(slot, 1, &d3d11Buffer, &stride, &offset);
497
498 }
499 catch (std::exception& e)
500 {
501 SLEAK_ERROR("Failed to cast Vertex Buffer! {}", e.what());
502 }
503}
504
505void DirectX11Renderer::BindIndexBuffer(RefPtr<BufferBase> buffer,
506 uint32_t slot) {
507 if (!buffer) return;
508
509 UINT offset = 0;
510
511 try {
512 auto d3d11Buffer =
513 dynamic_cast<DirectX11Buffer*>(buffer.get())->GetD3DBuffer();
514
515 deviceContext->IASetIndexBuffer(d3d11Buffer, DXGI_FORMAT_R32_UINT, 0);
516
517 } catch (std::exception& e) {
518 SLEAK_ERROR("Failed to cast Index Buffer! {}", e.what());
519 }
520}
521
522void DirectX11Renderer::BindConstantBuffer(RefPtr<BufferBase> buffer,
523 uint32_t slot) {
524 try {
525 auto* dx11Buf = dynamic_cast<DirectX11Buffer*>(buffer.get());
526 if (!dx11Buf) return;
527
528 // Shadow pass: use dedicated shadow CB with LightVP*World for slot 0
529 if (m_inShadowPass && slot == 0 && dx11Buf->GetCPUShadowCopySize() >= 128) {
530 const float* srcWorld = reinterpret_cast<const float*>(
531 static_cast<const char*>(dx11Buf->GetCPUShadowCopy()) + 64);
532
533 float shadowPC[32];
534 for (int r = 0; r < 4; ++r) {
535 for (int c = 0; c < 4; ++c) {
536 float sum = 0.0f;
537 for (int k = 0; k < 4; ++k) {
538 sum += srcWorld[r * 4 + k] * m_lightVP[k * 4 + c];
539 }
540 shadowPC[r * 4 + c] = sum;
541 }
542 }
543 std::memcpy(&shadowPC[16], srcWorld, sizeof(float) * 16);
544
545 // Create dedicated shadow transform CB on first use
546 if (!m_shadowTransformCB) {
547 D3D11_BUFFER_DESC desc{};
548 desc.ByteWidth = 128;
549 desc.Usage = D3D11_USAGE_DYNAMIC;
550 desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
551 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
552 device->CreateBuffer(&desc, nullptr, &m_shadowTransformCB);
553 }
554
555 // Update and bind the shadow CB instead of the original
556 D3D11_MAPPED_SUBRESOURCE mapped{};
557 if (SUCCEEDED(deviceContext->Map(m_shadowTransformCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
558 std::memcpy(mapped.pData, shadowPC, sizeof(shadowPC));
559 deviceContext->Unmap(m_shadowTransformCB, 0);
560 }
561 deviceContext->VSSetConstantBuffers(slot, 1, &m_shadowTransformCB);
562 deviceContext->PSSetConstantBuffers(slot, 1, &m_shadowTransformCB);
563 return;
564 }
565
566 auto d3d11Buffer = dx11Buf->GetD3DBuffer();
567 deviceContext->VSSetConstantBuffers(slot, 1, &d3d11Buffer);
568 deviceContext->PSSetConstantBuffers(slot, 1, &d3d11Buffer);
569
570 } catch (std::exception& e) {
571 SLEAK_ERROR("Failed to cast Constant Buffer! {}", e.what());
572 }
573}
574
575void DirectX11Renderer::ConfigureRenderMode() {
576 switch (Mode)
577 {
578 case RenderMode::Fill:
579 topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
580 fillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
581 break;
582 case RenderMode::Points:
583 topology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
584 fillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
585 break;
586 case RenderMode::Wireframe :
587 topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
588 fillMode = D3D11_FILL_MODE::D3D11_FILL_WIREFRAME;
589 break;
590 default:
591 SLEAK_ERROR("Unable to set render mode!");
592 break;
593 }
594
595 if (deviceContext)
596 deviceContext->IASetPrimitiveTopology(topology);
597
598 SetRasterState();
599}
600
601void DirectX11Renderer::ConfigureRenderFace() {
602 switch(Face) {
603 case RenderFace::None : cull = D3D11_CULL_MODE::D3D11_CULL_NONE; break;
604 case RenderFace::Front : cull = D3D11_CULL_MODE::D3D11_CULL_FRONT; break;
605 case RenderFace::Back : cull = D3D11_CULL_MODE::D3D11_CULL_BACK; break;
606 default: cull = D3D11_CULL_MODE::D3D11_CULL_FRONT;
607 }
608}
609
610bool DirectX11Renderer::SetRasterState() {
611 ID3D11RasterizerState* rasterState;
612 D3D11_RASTERIZER_DESC RasterDesc = {};
613 RasterDesc.AntialiasedLineEnable = true;
614 RasterDesc.MultisampleEnable = true;
615 RasterDesc.CullMode = cull;
616 RasterDesc.DepthBias = 0;
617 RasterDesc.DepthBiasClamp = 0.0f;
618 RasterDesc.SlopeScaledDepthBias = 0.0f;
619 RasterDesc.DepthClipEnable = true;
620 RasterDesc.ScissorEnable = false;
621 RasterDesc.FillMode = fillMode;
622 RasterDesc.FrontCounterClockwise = false;
623
624 if (FAILED(device->CreateRasterizerState(&RasterDesc, &rasterState))) {
625 return false;
626 } else {
627 deviceContext->RSSetState(rasterState);
628 rasterState->Release();
629 }
630
631 return true;
632}
633
634void DirectX11Renderer::SetRenderMode(RenderMode mode) {
635 this->Mode = mode;
636 ConfigureRenderMode();
637}
638
639void DirectX11Renderer::SetRenderFace(RenderFace face) {
640 this->Face = face;
641 ConfigureRenderFace();
642}
643
644void DirectX11Renderer::SetViewport(float x, float y, float width, float height,
645 float minDepth,
646 float maxDepth) {
647 D3D11_VIEWPORT viewport = {};
648 viewport.Width = static_cast<float>(width);
649 viewport.Height = static_cast<float>(height);
650 viewport.MinDepth = minDepth;
651 viewport.MaxDepth = maxDepth;
652 viewport.TopLeftX = x;
653 viewport.TopLeftY = y;
654 deviceContext->RSSetViewports(1, &viewport);
655
656}
657
658bool DirectX11Renderer::CreateRenderTargetView() {
659 // Release the existing render target view if it exists
660 if (renderTargetView) {
661 renderTargetView->Release();
662 renderTargetView = nullptr;
663 }
664
665 // Get the back buffer from the swap chain
666 ID3D11Texture2D* backBuffer = nullptr;
667 HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
668 reinterpret_cast<void**>(&backBuffer));
669 if (FAILED(hr)) {
670 SLEAK_ERROR("Failed to get back buffer!");
671 return false;
672 }
673
674 // Create the render target view
675 hr = device->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView);
676 backBuffer->Release();
677 if (FAILED(hr)) {
678 SLEAK_ERROR("Failed to create render target view!");
679 return false;
680 }
681
682 // Set the render target
683 deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);
684
685 SLEAK_INFO("Render target view created successfully.");
686 return true;
687}
688
689Texture* DirectX11Renderer::CreateTextureFromData(uint32_t width, uint32_t height, void* data) {
690 DirectX11Texture* texture = new DirectX11Texture(device);
691 if (texture->LoadFromMemory(data,width,height,TextureFormat::RGBA8)) {
692 return texture;
693 }
694 return nullptr;
695}
696
697Texture* DirectX11Renderer::CreateTexture(const std::string& TexturePath) {
698 DirectX11Texture* texture = new DirectX11Texture(device);
699 if (texture->LoadFromFile(TexturePath)) {
700 return texture;
701 }
702 return nullptr;
703}
704
705Texture* DirectX11Renderer::CreateCubemapTexture(
706 const std::array<std::string, 6>& facePaths) {
707 auto* texture = new DirectX11CubemapTexture(device);
708 if (texture->LoadCubemap(facePaths)) {
709 return texture;
710 }
711 delete texture;
712 return nullptr;
713}
714
715Texture* DirectX11Renderer::CreateCubemapTextureFromPanorama(
716 const std::string& panoramaPath) {
717 auto* texture = new DirectX11CubemapTexture(device);
718 if (texture->LoadEquirectangular(panoramaPath)) {
719 return texture;
720 }
721 delete texture;
722 return nullptr;
723}
724
725void DirectX11Renderer::SetDepthWrite(bool enabled) {
726 m_depthWriteEnabled = enabled;
727
728 D3D11_DEPTH_STENCIL_DESC desc = {};
729 desc.DepthEnable = TRUE;
730 desc.DepthWriteMask = enabled ? D3D11_DEPTH_WRITE_MASK_ALL
731 : D3D11_DEPTH_WRITE_MASK_ZERO;
732 desc.DepthFunc = m_depthFunc;
733 desc.StencilEnable = FALSE;
734
735 ID3D11DepthStencilState* state = nullptr;
736 if (SUCCEEDED(device->CreateDepthStencilState(&desc, &state))) {
737 deviceContext->OMSetDepthStencilState(state, 1);
738 state->Release();
739 }
740}
741
742void DirectX11Renderer::SetDepthCompare(DepthCompare compare) {
743 switch (compare) {
744 case DepthCompare::Less: m_depthFunc = D3D11_COMPARISON_LESS; break;
745 case DepthCompare::LessEqual: m_depthFunc = D3D11_COMPARISON_LESS_EQUAL; break;
746 case DepthCompare::Greater: m_depthFunc = D3D11_COMPARISON_GREATER; break;
747 case DepthCompare::GreaterEqual: m_depthFunc = D3D11_COMPARISON_GREATER_EQUAL; break;
748 case DepthCompare::Equal: m_depthFunc = D3D11_COMPARISON_EQUAL; break;
749 case DepthCompare::NotEqual: m_depthFunc = D3D11_COMPARISON_NOT_EQUAL; break;
750 case DepthCompare::Always: m_depthFunc = D3D11_COMPARISON_ALWAYS; break;
751 case DepthCompare::Never: m_depthFunc = D3D11_COMPARISON_NEVER; break;
752 }
753
754 D3D11_DEPTH_STENCIL_DESC desc = {};
755 desc.DepthEnable = TRUE;
756 desc.DepthWriteMask = m_depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL
757 : D3D11_DEPTH_WRITE_MASK_ZERO;
758 desc.DepthFunc = m_depthFunc;
759 desc.StencilEnable = FALSE;
760
761 ID3D11DepthStencilState* state = nullptr;
762 if (SUCCEEDED(device->CreateDepthStencilState(&desc, &state))) {
763 deviceContext->OMSetDepthStencilState(state, 1);
764 state->Release();
765 }
766}
767
768void DirectX11Renderer::SetCullEnabled(bool enabled) {
769 if (enabled) {
770 cull = D3D11_CULL_FRONT;
771 } else {
772 cull = D3D11_CULL_NONE;
773 }
774 SetRasterState();
775}
776
777void DirectX11Renderer::BindTexture(RefPtr<Sleak::Texture> texture,
778 uint32_t slot) {
779 if (texture.IsValid()) {
780 texture->Bind(slot);
781 }
782}
783
784void DirectX11Renderer::BeginSkyboxPass() {
785 // Save current state
786 m_savedCullMode = cull;
787 m_savedDepthStencilState = depthStencilState;
788 if (m_savedDepthStencilState) {
789 m_savedDepthStencilState->AddRef();
790 }
791}
792
793void DirectX11Renderer::EndSkyboxPass() {
794 // Restore depth stencil state
795 if (m_savedDepthStencilState) {
796 deviceContext->OMSetDepthStencilState(m_savedDepthStencilState, 1);
797 m_savedDepthStencilState->Release();
798 m_savedDepthStencilState = nullptr;
799 }
800
801 // Restore cull mode
802 cull = m_savedCullMode;
803 SetRasterState();
804
805 // Reset depth tracking
806 m_depthWriteEnabled = true;
807 m_depthFunc = D3D11_COMPARISON_LESS;
808}
809
810void DirectX11Renderer::BeginDebugLinePass() {
811 m_savedTopology = topology;
812 topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
813 deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
814}
815
816void DirectX11Renderer::EndDebugLinePass() {
817 topology = m_savedTopology;
818 deviceContext->IASetPrimitiveTopology(topology);
819}
820
821BufferBase* DirectX11Renderer::CreateBuffer(BufferType Type, uint32_t size, void* data) {
822 assert(size > 0);
823
824 DirectX11Buffer* buffer = new DirectX11Buffer(device, size, Type);
825 if (!buffer->Initialize(data)) {
826 delete buffer;
827 return nullptr;
828 }
829 return static_cast<BufferBase*>(buffer);
830}
831
832Shader* DirectX11Renderer::CreateShader(const std::string& shaderSource) {
833 DirectX11Shader* shader = new DirectX11Shader(device);
834 if (shader->compile(shaderSource)) {
835 // Each shader creates its own input layout matching its VS inputs.
836 // The layout is stored per-shader and set in bind().
837 auto* il = shader->createInputLayout();
838 if (!bIsLayoutCreated && il) {
839 layout = il;
840 bIsLayoutCreated = true;
841 }
842 return static_cast<Shader*>(shader);
843 }
844 return nullptr;
845}
846
847bool DirectX11Renderer::CreateBlendState() {
848 //(+) : Binary Operator
849 // Color = SourceP (X) SourceFactor (+) DestP (X) DestFactor
850 // Alpha = SourceA SourceFactor (+) DestA DestFactor
851
852 D3D11_BLEND_DESC desc;
853 desc.AlphaToCoverageEnable = false;
854 desc.IndependentBlendEnable = false;
855
856 desc.RenderTarget[0].BlendEnable = true;
857
858 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
859 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
860
861 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
862 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
863
864 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
865 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
866
867 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
868
869 if (FAILED(device->CreateBlendState(&desc, &blendState)))
870 return false;
871
872 return true;
873}
874
875void DirectX11Renderer::SetBlendState(float r, float g, float b, float a, UINT Mask) {
876 float BlendFactor[4] = {r,g,b,a};
877 deviceContext->OMSetBlendState(blendState, BlendFactor, Mask);
878}
879
880void DirectX11Renderer::CheckMSAASupport() {
881 m_maxMsaaSampleCount = 1;
882 for (UINT sampleCount = 1;
883 sampleCount <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT;
884 sampleCount *= 2)
885 {
886 UINT quality = 0;
887 HRESULT hr = device->CheckMultisampleQualityLevels(
888 DXGI_FORMAT_R8G8B8A8_UNORM, sampleCount, &quality);
889 if (SUCCEEDED(hr) && quality > 0) {
890 SLEAK_INFO("MSAA {}x supported with {} quality levels.",
891 sampleCount, quality);
892 if (sampleCount <= 8)
893 m_maxMsaaSampleCount = sampleCount;
894 }
895 }
896}
897
898bool DirectX11Renderer::CreateMSAARenderTarget() {
899
900 if (msaaRenderTarget) msaaRenderTarget->Release();
901 if (msaaRenderTargetView) msaaRenderTargetView->Release();
902
903 D3D11_TEXTURE2D_DESC texDesc;
904 texDesc.Width = window->GetWidth();
905 texDesc.Height = window->GetHeight();
906 texDesc.MipLevels = 1;
907 texDesc.ArraySize = 1;
908 texDesc.SampleDesc.Count = msaaSampleCount;
909 texDesc.SampleDesc.Quality = msaaQualityLevel;
910 texDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
911 texDesc.CPUAccessFlags = 0;
912 texDesc.MiscFlags = 0;
913 texDesc.Usage = D3D11_USAGE_DEFAULT;
914 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
915
916 if (FAILED(device->CreateTexture2D(&texDesc, nullptr, &msaaRenderTarget)))
917 return false;
918
919 if (FAILED(device->CreateRenderTargetView(msaaRenderTarget,nullptr,&msaaRenderTargetView)))
920 return false;
921
922 return true;
923}
924
925bool DirectX11Renderer::CreateMSAADepthStencil() {
926 if (msaaDepthStencilBuffer) msaaDepthStencilBuffer->Release();
927 if (msaaDepthStencilView) msaaDepthStencilView->Release();
928
929 D3D11_TEXTURE2D_DESC texDesc;
930 texDesc.Width = window->GetWidth();
931 texDesc.Height = window->GetHeight();
932 texDesc.MipLevels = 1;
933 texDesc.ArraySize = 1;
934 texDesc.SampleDesc.Count = msaaSampleCount;
935 texDesc.SampleDesc.Quality = msaaQualityLevel;
936 texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
937 texDesc.CPUAccessFlags = 0;
938 texDesc.MiscFlags = 0;
939 texDesc.Usage = D3D11_USAGE_DEFAULT;
940 texDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
941
942 if (FAILED(device->CreateTexture2D(&texDesc,nullptr,&msaaDepthStencilBuffer))) return false;
943
944 if (FAILED(device->CreateDepthStencilView(msaaDepthStencilBuffer,nullptr,&msaaDepthStencilView))) return false;
945
946
947 return true;
948}
949
950void DirectX11Renderer::ResolveMSAA() {
951 if (msaaSampleCount <= 1 || msaaSampleCount % 2 != 0)
952 return;
953
954 ID3D11Texture2D* back;
955 if (FAILED(swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&back))) {
956 SLEAK_ERROR("Failed to get back buffer for MSAA!");
957 return;
958 }
959 deviceContext->ResolveSubresource(back,0,msaaRenderTarget,0,DXGI_FORMAT_R8G8B8A8_UNORM);
960
961 back->Release();
962}
963
964void DirectX11Renderer::ApplyMSAAChange() {
965 if (!m_msaaChangeRequested)
966 return;
967 m_msaaChangeRequested = false;
968
969 m_msaaSampleCount = m_pendingMsaaSampleCount;
970 msaaSampleCount = m_pendingMsaaSampleCount;
971
972 // Query quality level for new sample count
973 UINT quality = 0;
974 device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM,
975 msaaSampleCount, &quality);
976 msaaQualityLevel = (quality > 0) ? quality - 1 : 0;
977
978 // Cleanup old MSAA resources
979 if (msaaRenderTarget) { msaaRenderTarget->Release(); msaaRenderTarget = nullptr; }
980 if (msaaRenderTargetView) { msaaRenderTargetView->Release(); msaaRenderTargetView = nullptr; }
981 if (msaaDepthStencilBuffer) { msaaDepthStencilBuffer->Release(); msaaDepthStencilBuffer = nullptr; }
982 if (msaaDepthStencilView) { msaaDepthStencilView->Release(); msaaDepthStencilView = nullptr; }
983
984 if (msaaSampleCount > 1) {
985 CreateMSAARenderTarget();
986 CreateMSAADepthStencil();
987 } else {
988 // Re-bind non-MSAA render targets since pipeline still references released MSAA views
989 deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
990 }
991
992 SLEAK_INFO("DX11 MSAA changed to {}x", msaaSampleCount);
993}
994
995bool DirectX11Renderer::CreateImGUI()
996{
997 IMGUI_CHECKVERSION();
998 ImCon = ImGui::CreateContext();
999 ImCon->IO.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
1000
1001 ImGui::StyleColorsDark();
1002
1003 if(!ImGui_ImplSDL3_InitForD3D(window->GetSDLWindow()))
1004 return false;
1005
1006 if(!ImGui_ImplDX11_Init(device, deviceContext))
1007 return false;
1008
1009 if(!ImGui_ImplDX11_CreateDeviceObjects())
1010 return false;
1011
1012
1013 bImInitialized = true;
1014
1015 return true;
1016}
1017
1018
1019// ---- Shadow Constant Buffer ----
1020
1021bool DirectX11Renderer::CreateShadowConstantBuffer() {
1022 if (m_shadowCBCreated) return true;
1023
1024 D3D11_BUFFER_DESC desc{};
1025 desc.ByteWidth = sizeof(PCSSShadowGPUData);
1026 desc.Usage = D3D11_USAGE_DYNAMIC;
1027 desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1028 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1029
1030 HRESULT hr = device->CreateBuffer(&desc, nullptr, &m_shadowCB);
1031 if (FAILED(hr)) {
1032 SLEAK_ERROR("Failed to create shadow constant buffer!");
1033 return false;
1034 }
1035
1036 m_shadowCBCreated = true;
1037 return true;
1038}
1039
1040void DirectX11Renderer::CleanupShadowResources() {
1041 if (m_shadowCB) { m_shadowCB->Release(); m_shadowCB = nullptr; }
1042 m_shadowCBCreated = false;
1043 if (m_shadowTransformCB) { m_shadowTransformCB->Release(); m_shadowTransformCB = nullptr; }
1044 if (m_shadowRasterState) { m_shadowRasterState->Release(); m_shadowRasterState = nullptr; }
1045 if (m_shadowSampler) { m_shadowSampler->Release(); m_shadowSampler = nullptr; }
1046 if (m_shadowSRV) { m_shadowSRV->Release(); m_shadowSRV = nullptr; }
1047 if (m_shadowDSV) { m_shadowDSV->Release(); m_shadowDSV = nullptr; }
1048 if (m_shadowDepthTex) { m_shadowDepthTex->Release(); m_shadowDepthTex = nullptr; }
1049 m_shadowMapCreated = false;
1050}
1051
1052void DirectX11Renderer::SetLightVP(const float* mat) {
1053 if (mat) {
1054 std::memcpy(m_pendingLightVP, mat, sizeof(m_pendingLightVP));
1055 m_hasPendingLightVP = true;
1056 }
1057}
1058
1059bool DirectX11Renderer::CreateShadowMapResources() {
1060 if (m_shadowMapCreated) return true;
1061
1062 // Create depth texture
1063 D3D11_TEXTURE2D_DESC texDesc{};
1064 texDesc.Width = m_shadowMapResolution;
1065 texDesc.Height = m_shadowMapResolution;
1066 texDesc.MipLevels = 1;
1067 texDesc.ArraySize = 1;
1068 texDesc.Format = DXGI_FORMAT_R32_TYPELESS;
1069 texDesc.SampleDesc.Count = 1;
1070 texDesc.SampleDesc.Quality = 0;
1071 texDesc.Usage = D3D11_USAGE_DEFAULT;
1072 texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
1073
1074 HRESULT hr = device->CreateTexture2D(&texDesc, nullptr, &m_shadowDepthTex);
1075 if (FAILED(hr)) {
1076 SLEAK_ERROR("Failed to create shadow depth texture!");
1077 return false;
1078 }
1079
1080 // Create DSV
1081 D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc{};
1082 dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
1083 dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
1084 hr = device->CreateDepthStencilView(m_shadowDepthTex, &dsvDesc, &m_shadowDSV);
1085 if (FAILED(hr)) {
1086 SLEAK_ERROR("Failed to create shadow DSV!");
1087 return false;
1088 }
1089
1090 // Create SRV for sampling
1091 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};
1092 srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
1093 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
1094 srvDesc.Texture2D.MipLevels = 1;
1095 hr = device->CreateShaderResourceView(m_shadowDepthTex, &srvDesc, &m_shadowSRV);
1096 if (FAILED(hr)) {
1097 SLEAK_ERROR("Failed to create shadow SRV!");
1098 return false;
1099 }
1100
1101 // Create comparison sampler
1102 D3D11_SAMPLER_DESC sampDesc{};
1103 sampDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
1104 sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
1105 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
1106 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
1107 sampDesc.BorderColor[0] = 1.0f;
1108 sampDesc.BorderColor[1] = 1.0f;
1109 sampDesc.BorderColor[2] = 1.0f;
1110 sampDesc.BorderColor[3] = 1.0f;
1111 sampDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
1112 sampDesc.MinLOD = 0;
1113 sampDesc.MaxLOD = 0;
1114 hr = device->CreateSamplerState(&sampDesc, &m_shadowSampler);
1115 if (FAILED(hr)) {
1116 SLEAK_ERROR("Failed to create shadow sampler!");
1117 return false;
1118 }
1119
1120 // Create shadow rasterizer state with depth bias
1121 D3D11_RASTERIZER_DESC rasterDesc{};
1122 rasterDesc.FillMode = D3D11_FILL_SOLID;
1123 rasterDesc.CullMode = D3D11_CULL_NONE; // all faces cast shadow regardless of orientation
1124 rasterDesc.FrontCounterClockwise = FALSE;
1125 rasterDesc.DepthBias = 100;
1126 rasterDesc.SlopeScaledDepthBias = 2.0f;
1127 rasterDesc.DepthBiasClamp = 0.01f;
1128 rasterDesc.DepthClipEnable = TRUE;
1129 hr = device->CreateRasterizerState(&rasterDesc, &m_shadowRasterState);
1130 if (FAILED(hr)) {
1131 SLEAK_ERROR("Failed to create shadow rasterizer state!");
1132 return false;
1133 }
1134
1135 m_shadowMapCreated = true;
1136 m_shadowResourcesCreated = true;
1137 SLEAK_INFO("D3D11 shadow map resources created ({}x{})", m_shadowMapResolution, m_shadowMapResolution);
1138 return true;
1139}
1140
1141void DirectX11Renderer::RenderShadowPass() {
1142 // Skip if no cached draws — preserve previous frame's shadow map
1143 auto* queue = RenderCommandQueue::GetInstance();
1144 if (!queue || !queue->HasCachedShadowDraws()) return;
1145
1146 if (!m_shadowMapCreated) {
1147 if (!CreateShadowMapResources()) return;
1148 }
1149
1150 // Save current render targets and viewport
1151 ID3D11RenderTargetView* savedRTV = nullptr;
1152 ID3D11DepthStencilView* savedDSV = nullptr;
1153 deviceContext->OMGetRenderTargets(1, &savedRTV, &savedDSV);
1154
1155 D3D11_VIEWPORT savedViewport;
1156 UINT numViewports = 1;
1157 deviceContext->RSGetViewports(&numViewports, &savedViewport);
1158
1159 // Unbind shadow SRV to avoid D3D11 warning (resource bound as both input and output)
1160 ID3D11ShaderResourceView* nullSRV = nullptr;
1161 deviceContext->PSSetShaderResources(3, 1, &nullSRV);
1162
1163 // Set shadow render target (depth only, no color)
1164 ID3D11RenderTargetView* nullRTV = nullptr;
1165 deviceContext->OMSetRenderTargets(1, &nullRTV, m_shadowDSV);
1166
1167 // Set shadow viewport
1168 D3D11_VIEWPORT shadowViewport{};
1169 shadowViewport.Width = static_cast<float>(m_shadowMapResolution);
1170 shadowViewport.Height = static_cast<float>(m_shadowMapResolution);
1171 shadowViewport.MinDepth = 0.0f;
1172 shadowViewport.MaxDepth = 1.0f;
1173 deviceContext->RSSetViewports(1, &shadowViewport);
1174
1175 // Clear shadow depth
1176 deviceContext->ClearDepthStencilView(m_shadowDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);
1177
1178 // Set shadow rasterizer state
1179 deviceContext->RSSetState(m_shadowRasterState);
1180
1181 // Set primitive topology (same as main pass)
1182 deviceContext->IASetPrimitiveTopology(topology);
1183 if (bIsLayoutCreated)
1184 deviceContext->IASetInputLayout(layout);
1185
1186 // Execute shadow draw commands
1187 m_inShadowPass = true;
1188 if (queue) {
1189 queue->ExecuteShadowPass(this);
1190 }
1191 m_inShadowPass = false;
1192
1193 // Restore raster state
1194 SetRasterState();
1195
1196 // Restore render targets and viewport
1197 deviceContext->OMSetRenderTargets(1, &savedRTV, savedDSV);
1198 deviceContext->RSSetViewports(1, &savedViewport);
1199 if (savedRTV) savedRTV->Release();
1200 if (savedDSV) savedDSV->Release();
1201
1202 // Bind shadow map SRV and sampler for main pass
1203 deviceContext->PSSetShaderResources(3, 1, &m_shadowSRV);
1204 deviceContext->PSSetSamplers(3, 1, &m_shadowSampler);
1205}
1206
1207void DirectX11Renderer::UpdateShadowLightUBO(const void* data, uint32_t size) {
1208 if (!device || !deviceContext) return;
1209
1210 // Create shadow CB on first call
1211 if (!m_shadowCBCreated) {
1212 if (!CreateShadowConstantBuffer()) return;
1213 }
1214
1215 // Map the ShadowLightUBO data into our PCSSShadowGPUData format
1216 const auto* ubo = static_cast<const ShadowLightUBO*>(data);
1217
1218 PCSSShadowGPUData shadowData{};
1219 std::memcpy(shadowData.LightVP, ubo->LightVP, sizeof(float) * 16);
1220 shadowData.ShadowBias = ubo->ShadowBias;
1221 shadowData.ShadowStrength = ubo->ShadowStrength;
1222 shadowData.ShadowTexelSize = ubo->ShadowTexelSize;
1223 shadowData.ShadowLightSize = ubo->LightSize;
1224 shadowData.PCSSEnabled = m_pcssEnabled ? 1 : 0;
1225 shadowData.ShadowMapEnabled = m_shadowPassEnabled ? 1 : 0;
1226
1227 D3D11_MAPPED_SUBRESOURCE mapped{};
1228 HRESULT hr = deviceContext->Map(m_shadowCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
1229 if (SUCCEEDED(hr)) {
1230 std::memcpy(mapped.pData, &shadowData, sizeof(shadowData));
1231 deviceContext->Unmap(m_shadowCB, 0);
1232 }
1233
1234 // Bind shadow CB at slot 5 (both VS and PS)
1235 deviceContext->VSSetConstantBuffers(5, 1, &m_shadowCB);
1236 deviceContext->PSSetConstantBuffers(5, 1, &m_shadowCB);
1237}
1238
1239// ---- Post-Process: Tonemapping ----
1240
1241bool DirectX11Renderer::CreateTonemapResources() {
1242 if (m_tonemapResourcesCreated) return true;
1243 if (!device) return false;
1244
1245 // Get back buffer dimensions
1246 ID3D11Texture2D* backBuffer = nullptr;
1247 swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
1248 if (!backBuffer) return false;
1249
1250 D3D11_TEXTURE2D_DESC bbDesc{};
1251 backBuffer->GetDesc(&bbDesc);
1252 backBuffer->Release();
1253
1254 // Create HDR render target (R16G16B16A16_FLOAT)
1255 D3D11_TEXTURE2D_DESC texDesc{};
1256 texDesc.Width = bbDesc.Width;
1257 texDesc.Height = bbDesc.Height;
1258 texDesc.MipLevels = 1;
1259 texDesc.ArraySize = 1;
1260 texDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
1261 texDesc.SampleDesc.Count = 1;
1262 texDesc.SampleDesc.Quality = 0;
1263 texDesc.Usage = D3D11_USAGE_DEFAULT;
1264 texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
1265
1266 HRESULT hr = device->CreateTexture2D(&texDesc, nullptr, &m_hdrTexture);
1267 if (FAILED(hr)) {
1268 SLEAK_ERROR("Failed to create HDR texture for tonemapping!");
1269 return false;
1270 }
1271
1272 hr = device->CreateRenderTargetView(m_hdrTexture, nullptr, &m_hdrRTV);
1273 if (FAILED(hr)) return false;
1274
1275 hr = device->CreateShaderResourceView(m_hdrTexture, nullptr, &m_hdrSRV);
1276 if (FAILED(hr)) return false;
1277
1278 // Create point sampler
1279 D3D11_SAMPLER_DESC sampDesc{};
1280 sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
1281 sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
1282 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
1283 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
1284 hr = device->CreateSamplerState(&sampDesc, &m_pointSampler);
1285 if (FAILED(hr)) return false;
1286
1287 // Create post-process constant buffer
1288 D3D11_BUFFER_DESC cbDesc{};
1289 cbDesc.ByteWidth = sizeof(PostProcessGPUData);
1290 cbDesc.Usage = D3D11_USAGE_DYNAMIC;
1291 cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1292 cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1293 hr = device->CreateBuffer(&cbDesc, nullptr, &m_postProcessCB);
1294 if (FAILED(hr)) return false;
1295
1296 // Compile tonemap shaders
1297 ID3DBlob* vsBlob = nullptr;
1298 ID3DBlob* psBlob = nullptr;
1299 ID3DBlob* errorBlob = nullptr;
1300
1301 // Build path to shader (relative to working directory = bin/)
1302 std::string narrowPath = "assets/shaders/tonemap.hlsl";
1303 std::wstring shaderPath(narrowPath.begin(), narrowPath.end());
1304
1305 hr = D3DCompileFromFile(shaderPath.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
1306 "VS_Main", "vs_5_0", D3DCOMPILE_DEBUG, 0, &vsBlob, &errorBlob);
1307 if (FAILED(hr)) {
1308 if (errorBlob) {
1309 SLEAK_ERROR("Tonemap VS compile error: {}",
1310 (const char*)errorBlob->GetBufferPointer());
1311 errorBlob->Release();
1312 }
1313 return false;
1314 }
1315
1316 hr = D3DCompileFromFile(shaderPath.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
1317 "PS_Main", "ps_5_0", D3DCOMPILE_DEBUG, 0, &psBlob, &errorBlob);
1318 if (FAILED(hr)) {
1319 if (errorBlob) {
1320 SLEAK_ERROR("Tonemap PS compile error: {}",
1321 (const char*)errorBlob->GetBufferPointer());
1322 errorBlob->Release();
1323 }
1324 vsBlob->Release();
1325 return false;
1326 }
1327
1328 hr = device->CreateVertexShader(vsBlob->GetBufferPointer(),
1329 vsBlob->GetBufferSize(), nullptr, &m_tonemapVS);
1330 vsBlob->Release();
1331 if (FAILED(hr)) { psBlob->Release(); return false; }
1332
1333 hr = device->CreatePixelShader(psBlob->GetBufferPointer(),
1334 psBlob->GetBufferSize(), nullptr, &m_tonemapPS);
1335 psBlob->Release();
1336 if (FAILED(hr)) return false;
1337
1338 m_tonemapResourcesCreated = true;
1339 SLEAK_INFO("Tonemapping post-process resources created successfully");
1340 return true;
1341}
1342
1343void DirectX11Renderer::CleanupTonemapResources() {
1344 if (m_tonemapVS) { m_tonemapVS->Release(); m_tonemapVS = nullptr; }
1345 if (m_tonemapPS) { m_tonemapPS->Release(); m_tonemapPS = nullptr; }
1346 if (m_postProcessCB) { m_postProcessCB->Release(); m_postProcessCB = nullptr; }
1347 if (m_hdrSRV) { m_hdrSRV->Release(); m_hdrSRV = nullptr; }
1348 if (m_hdrRTV) { m_hdrRTV->Release(); m_hdrRTV = nullptr; }
1349 if (m_hdrTexture) { m_hdrTexture->Release(); m_hdrTexture = nullptr; }
1350 if (m_pointSampler) { m_pointSampler->Release(); m_pointSampler = nullptr; }
1351 m_tonemapResourcesCreated = false;
1352}
1353
1354void DirectX11Renderer::ExecuteTonemapPass() {
1355 if (!m_tonemapResourcesCreated || !m_tonemapVS || !m_tonemapPS)
1356 return;
1357
1358 // Update post-process constant buffer
1359 PostProcessGPUData ppData{};
1360 ppData.Exposure = m_exposure;
1361 ppData.Gamma = m_gamma;
1362 ppData.TonemapEnabled = m_tonemapEnabled ? 1 : 0;
1363
1364 D3D11_MAPPED_SUBRESOURCE mapped{};
1365 HRESULT hr = deviceContext->Map(m_postProcessCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
1366 if (SUCCEEDED(hr)) {
1367 std::memcpy(mapped.pData, &ppData, sizeof(ppData));
1368 deviceContext->Unmap(m_postProcessCB, 0);
1369 }
1370
1371 // Switch render target to backbuffer
1372 deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);
1373
1374 // Bind HDR texture as input
1375 deviceContext->PSSetShaderResources(0, 1, &m_hdrSRV);
1376 deviceContext->PSSetSamplers(0, 1, &m_pointSampler);
1377 deviceContext->PSSetConstantBuffers(0, 1, &m_postProcessCB);
1378
1379 // Set tonemap shaders
1380 deviceContext->VSSetShader(m_tonemapVS, nullptr, 0);
1381 deviceContext->PSSetShader(m_tonemapPS, nullptr, 0);
1382
1383 // Draw fullscreen triangle (no input layout needed for SV_VertexID)
1384 deviceContext->IASetInputLayout(nullptr);
1385 deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1386 deviceContext->Draw(3, 0);
1387
1388 // Unbind HDR SRV to prevent read/write conflict
1389 ID3D11ShaderResourceView* nullSRV = nullptr;
1390 deviceContext->PSSetShaderResources(0, 1, &nullSRV);
1391}
1392
1393} // namespace RenderEngine
1394} // namespace Sleak
1395
1396#else
1397namespace Sleak::RenderEngine {class DirectX11Renderer;}
1398
1399#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