SleakEngine 0.1.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX12Renderer.hpp
Go to the documentation of this file.
1#include <Core/OSDef.hpp>
2
3#ifdef PLATFORM_WIN
4
5#ifndef _DIRECTX12RENDERER_H
6#define _DIRECTX12RENDERER_H
7
8#include <backends/imgui_impl_dx12.h>
9#include <d3d12.h>
10#include <d3dcompiler.h>
11#include <dxgi1_4.h>
12#include <imgui.h>
13#include <wrl/client.h>
14
15#include <Core/Window.hpp>
16#include <cstdint>
17#include <utility>
18#include <vector>
19
28
29namespace Sleak {
30namespace RenderEngine {
31
32/// D3D12 backend: explicit command lists, per-frame allocators, and a shared SRV descriptor heap.
33class ENGINE_API DirectX12Renderer : public Renderer, public RenderContext {
34public:
35 DirectX12Renderer(Window* window);
36 virtual ~DirectX12Renderer();
37
38 bool Initialize() override;
39 void BeginRender() override;
40 void EndRender() override;
41 void Cleanup() override;
42 void WaitIdle() override { WaitForGPU(); }
43
44 virtual void Resize(uint32_t width, uint32_t height) override;
45
46 /// True if the current adapter/driver supports D3D12 feature level 11_0.
47 static bool IsSupport();
48
49 virtual bool CreateImGUI() override;
50
51 virtual RenderContext* GetContext() override { return this; }
52
53 // Feature capability mask
54 virtual uint32_t GetFeatureCaps() const override { return CapShadows; }
55
56 // RenderContext interface
57 virtual void Draw(uint32_t vertexCount) override;
58 virtual void Draw(uint32_t vertexCount, uint32_t firstVertex) override;
59 virtual void DrawIndexed(uint32_t indexCount) override;
60 virtual void DrawIndexed(uint32_t indexCount, uint32_t firstIndex,
61 int32_t baseVertex) override;
62 virtual void DrawInstance(uint32_t instanceCount,
63 uint32_t vertexPerInstance) override;
64 virtual void DrawIndexedInstance(uint32_t instanceCount,
65 uint32_t indexPerInstance) override;
66 virtual void DrawIndexedInstance(uint32_t instanceCount,
67 uint32_t indexPerInstance,
68 uint32_t firstIndex,
69 int32_t baseVertex) override;
70
71 virtual void SetRenderFace(RenderFace face) override;
72 virtual void SetRenderMode(RenderMode mode) override;
73 virtual void SetViewport(float x, float y, float width, float height,
74 float minDepth = 0.0f,
75 float maxDepth = 1.0f) override;
76 virtual void ClearRenderTarget(float r, float g, float b,
77 float a) override;
78 virtual void ClearDepthStencil(bool clearDepth, bool clearStencil,
79 float depth, uint8_t stencil) override;
80
81 virtual void BindVertexBuffer(RefPtr<BufferBase> buffer,
82 uint32_t slot = 0) override;
83 virtual void BindIndexBuffer(RefPtr<BufferBase> buffer,
84 uint32_t slot = 0) override;
85 virtual void BindConstantBuffer(RefPtr<BufferBase> buffer,
86 uint32_t slot = 0) override;
87
88 virtual BufferBase* CreateBuffer(BufferType Type, uint32_t size,
89 void* data) override;
90 virtual Shader* CreateShader(const std::string& shaderSource) override;
91 virtual Texture* CreateTexture(const std::string& TexturePath) override;
92 virtual Texture* CreateTextureFromData(uint32_t width, uint32_t height,
93 void* data) override;
94
95 /// Loads a cubemap from six face image paths.
96 Texture* CreateCubemapTexture(const std::array<std::string, 6>& facePaths);
97 /// Loads a cubemap by converting a single equirectangular panorama.
98 Texture* CreateCubemapTextureFromPanorama(const std::string& panoramaPath);
99
100 // Lighting/fog UBO (matches ShadowLightUBO from Vulkan path)
101 void UpdateShadowLightUBO(const void* data, uint32_t size) override;
102
103 // Skybox state management
104 virtual void BindTexture(RefPtr<Sleak::Texture> texture, uint32_t slot = 0) override;
105 virtual void BindTextureRaw(Sleak::Texture* texture, uint32_t slot = 0) override;
106 virtual void BeginSkyboxPass() override;
107 virtual void EndSkyboxPass() override;
108 virtual void BeginDebugLinePass() override;
109 virtual void EndDebugLinePass() override;
110
111 /// Sets a shader's PSO unless a skybox or debug line pass owns it.
112 void BindShaderPipeline(ID3D12PipelineState* pso);
113
114 private:
115 /// Fixed-function state that, with the shader bytecode, identifies a PSO.
116 struct PipelineKey {
117 uint64_t vsHash = 0;
118 uint64_t psHash = 0;
119 uint32_t vertexFormat = 0;
120 D3D12_CULL_MODE cullMode = D3D12_CULL_MODE_FRONT;
121 BOOL depthClip = TRUE;
122 INT depthBias = 0;
123 float depthBiasClamp = 0.0f;
124 float slopeScaledDepthBias = 0.0f;
125 D3D12_DEPTH_WRITE_MASK depthWrite = D3D12_DEPTH_WRITE_MASK_ALL;
126 D3D12_COMPARISON_FUNC depthFunc = D3D12_COMPARISON_FUNC_LESS;
127 BOOL blendEnable = FALSE;
128 D3D12_PRIMITIVE_TOPOLOGY_TYPE topology =
129 D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
130 DXGI_FORMAT rtvFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
131 DXGI_FORMAT dsvFormat = DXGI_FORMAT_D32_FLOAT;
132
133 bool operator==(const PipelineKey&) const = default;
134 };
135
136 /// Returns the cached PSO for the key, building it on a miss.
137 ID3D12PipelineState* GetPipeline(const PipelineKey& key, ID3DBlob* vs,
138 ID3DBlob* ps);
139 /// Reapplies the last shader PSO after a pass override ends.
140 void RestoreShaderPipeline();
141 /// Copies the light/fog constants into the ring and binds them at b2.
142 void BindLightConstants();
143
144 /// Creates the DXGI factory, enumerates adapters, and creates the D3D12 device.
145 bool CreateDevice();
146 bool CreateCommandQueue();
147 /// Creates the swapchain and its per-frame buffers.
148 bool CreateSwapChain();
149 /// Creates per-frame command allocators and the shared graphics command list.
150 bool CreateCommandAllocatorAndList();
151 /// Creates the RTV heap and a render target view for each swapchain buffer.
152 bool CreateRenderTargetViews();
153 bool CreateDepthStencilView();
154 bool CreateFence();
155 bool CreateRootSignature();
156
157 virtual void ConfigureRenderMode() override;
158 virtual void ConfigureRenderFace() override;
159
160 /// Signals the fence and blocks the CPU until the GPU catches up.
161 void WaitForGPU();
162 /// Logs available DXGI adapters and picks the current one for device creation.
163 void EnumerateDevices(Microsoft::WRL::ComPtr<IDXGIFactory4> factory);
164
165 // Frame count (must be declared before arrays that use it)
166 static constexpr UINT FrameCount = 2;
167
168 // Device and swap chain
169 Microsoft::WRL::ComPtr<ID3D12Device> device;
170 Microsoft::WRL::ComPtr<IDXGISwapChain3> swapChain;
171 Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
172
173 // Command objects (per-frame allocators for CPU/GPU overlap)
174 Microsoft::WRL::ComPtr<ID3D12CommandQueue> commandQueue;
175 Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocators[FrameCount];
176 Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> commandList;
177
178 // Descriptor heaps
179 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> rtvHeap;
180 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> dsvHeap;
181
182 // Render targets and depth
183 Microsoft::WRL::ComPtr<ID3D12Resource> renderTargets[FrameCount];
184 Microsoft::WRL::ComPtr<ID3D12Resource> depthStencilBuffer;
185
186 // Pipeline state (PSOs are owned by the cache)
187 Microsoft::WRL::ComPtr<ID3D12RootSignature> rootSignature;
188 std::vector<
189 std::pair<PipelineKey, Microsoft::WRL::ComPtr<ID3D12PipelineState>>>
190 m_pipelineCache;
191 ID3D12PipelineState* m_defaultPipelineState = nullptr;
192 ID3D12PipelineState* m_shaderPipeline = nullptr;
193 ID3D12PipelineState* m_passPipeline = nullptr;
194
195 // Synchronization: one monotonically increasing fence, and the value
196 // that marks the end of the last frame recorded into each slot
197 Microsoft::WRL::ComPtr<ID3D12Fence> fence;
198 HANDLE fenceEvent = nullptr;
199 UINT64 m_fenceValue = 0;
200 UINT64 fenceValues[FrameCount] = {};
201 uint64_t m_frameSerial = 0;
202 bool m_frameActive = false;
203
204 // Descriptor sizes
205 UINT rtvDescriptorSize = 0;
206 UINT frameIndex = 0;
207
208 // Window
209 Window* window;
210
211 // Viewport and scissor rect
212 D3D12_VIEWPORT viewport = {};
213 D3D12_RECT scissorRect = {};
214
215 bool m_Initialized = false;
216
217 // Default 1x1 white texture (fallback when no texture is bound)
218 DirectX12Texture* m_defaultTexture = nullptr;
219
220 // Skybox PSO (depth write off, LEQUAL, no cull)
221 ID3D12PipelineState* m_skyboxPipelineState = nullptr;
222 bool m_skyboxPipelineFailed = false;
223 /// Builds the skybox PSO (depth write disabled, LEQUAL compare, no culling).
224 bool CreateSkyboxPipelineState();
225
226 // Debug line PSO (line topology)
227 ID3D12PipelineState* m_debugLinePipelineState = nullptr;
228 bool m_debugLinePipelineFailed = false;
229 /// Builds the line-topology PSO used for debug line rendering.
230 bool CreateDebugLinePipelineState();
231
232 // Transient per-frame constant data (constant buffers, light/fog)
233 DirectX12UploadRing m_uploadRing;
234 // Batched init-time and dynamic buffer/texture uploads
235 DirectX12UploadContext m_uploader;
236
237 // Latest light/fog constants, copied into the ring when bound
238 std::vector<uint8_t> m_lightData;
239
240 // Shadow mapping
241 Microsoft::WRL::ComPtr<ID3D12Resource> m_shadowDepthBuffer;
242 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_shadowDsvHeap;
243 D3D12_CPU_DESCRIPTOR_HANDLE m_shadowDsvHandle = {};
244 UINT m_shadowSrvIndex = 0;
245 bool m_shadowMapCreated = false;
246 float m_lightVP[16] = {};
247 float m_pendingLightVP[16] = {};
248 bool m_hasPendingLightVP = false;
249 bool m_inShadowPass = false;
250 // Depth-only PSO for shadow pass (no PS, no RTV, CULL_NONE)
251 ID3D12PipelineState* m_shadowPassPSO = nullptr;
252 Microsoft::WRL::ComPtr<ID3DBlob> m_shadowVSBlob;
253 bool m_shadowVSCompiled = false;
254 // First shader VS, fallback for the shadow PSO
255 Microsoft::WRL::ComPtr<ID3DBlob> m_cachedVSBlob;
256 void SetLightVP(const float* mat) override;
257 /// Allocates the shadow-pass depth buffer, DSV, and shared-heap SRV slot.
258 bool CreateShadowMapResources();
259 /// Builds the depth-only PSO (no pixel shader, no RTV, no culling) used for the shadow pass.
260 bool CreateShadowPassPSO();
261 /// Replays cached shadow-caster draws into the depth-only shadow map.
262 void RenderShadowPass();
263
264 // ImGUI
265 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> imguiSrvHeap;
266
267 // Shared SRV descriptor heap (all textures live here — set once per frame)
268 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_sharedSrvHeap;
269 UINT m_srvDescriptorSize = 0;
270 UINT m_nextSrvSlot = 1; // slot 0 reserved for imgui/default
271 static constexpr UINT MAX_SRV_DESCRIPTORS = 512;
272
273 /// Creates the shared, shader-visible SRV descriptor heap used by all textures/cubemaps.
274 bool CreateSharedSrvHeap();
275public:
276 // Allocate a slot in the shared SRV heap; returns the slot index
277 UINT AllocateSRVSlot();
278 D3D12_CPU_DESCRIPTOR_HANDLE GetSharedSrvCPUHandle(UINT slot) const;
279 D3D12_GPU_DESCRIPTOR_HANDLE GetSharedSrvGPUHandle(UINT slot) const;
280 ID3D12DescriptorHeap* GetSharedSrvHeap() const { return m_sharedSrvHeap.Get(); }
281};
282
283} // namespace RenderEngine
284} // namespace Sleak
285
286#endif // _DIRECTX12RENDERER_H
287
288#else
289namespace Sleak::RenderEngine {
290class DirectX12Renderer;
291}
292
293#endif
int width
int height
Abstract interface for high-level graphics command execution and resource management.
Abstract render backend: swapchain, frame lifecycle, and post-effect toggles.
Definition Renderer.hpp:37
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10
bool operator==(const Pair< T1, T2 > &lhs, const Pair< T1, T2 > &rhs)
Definition Pair.hpp:115
class ENGINE_API Window