SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX12Texture.cpp
Go to the documentation of this file.
2
3#ifdef PLATFORM_WIN
4
5#include <Core/Logger.hpp>
6#include <stb_image.h>
7#include <cstring>
8
9namespace Sleak {
10namespace RenderEngine {
11
12DirectX12Texture::DirectX12Texture(ID3D12Device* device,
13 ID3D12CommandQueue* commandQueue,
14 ID3D12GraphicsCommandList* commandList)
15 : m_device(device), m_commandQueue(commandQueue),
16 m_commandList(commandList) {}
17
18DirectX12Texture::~DirectX12Texture() {
19 m_uploadBuffer.Reset();
20 m_srvHeap.Reset();
21 m_texture.Reset();
22}
23
24bool DirectX12Texture::LoadFromMemory(const void* data, uint32_t width,
25 uint32_t height,
26 TextureFormat format) {
27 if (!data || !m_device || width == 0 || height == 0) return false;
28
29 m_width = width;
30 m_height = height;
31 m_format = format;
32
33 DXGI_FORMAT dxgiFormat = GetDXGIFormat(format);
34
35 if (!CreateTextureResource(width, height, dxgiFormat)) return false;
36 if (!UploadTextureData(data, width, height)) return false;
37 if (!CreateSRV(dxgiFormat)) return false;
38
39 return true;
40}
41
42bool DirectX12Texture::LoadFromFile(const std::string& filePath) {
43 int w, h, channels;
44 unsigned char* pixels =
45 stbi_load(filePath.c_str(), &w, &h, &channels, 4);
46 if (!pixels) {
47 SLEAK_ERROR("DirectX12Texture: Failed to load image: {}",
48 filePath);
49 return false;
50 }
51
52 bool result = LoadFromMemory(pixels, static_cast<uint32_t>(w),
53 static_cast<uint32_t>(h),
54 TextureFormat::RGBA8);
55 stbi_image_free(pixels);
56 return result;
57}
58
59void DirectX12Texture::Bind(uint32_t slot) const {
60 if (m_commandList && m_srvHeap) {
61 BindToCommandList(m_commandList, 2); // root param 2 = SRV table
62 }
63}
64
65void DirectX12Texture::Unbind() const {
66 // No-op in DX12
67}
68
69void DirectX12Texture::SetFilter(TextureFilter filter) {
70 m_filter = filter;
71 // Sampler is baked into the root signature as a static sampler.
72 // A full implementation would recreate the PSO with a new sampler.
73}
74
75void DirectX12Texture::SetWrapMode(TextureWrapMode wrapMode) {
76 m_wrapMode = wrapMode;
77}
78
79void DirectX12Texture::BindToCommandList(
80 ID3D12GraphicsCommandList* cmdList, UINT rootParameterIndex) const {
81 if (!cmdList) return;
82
83 if (m_usesSharedHeap) {
84 // Fast path: shared heap already bound by BeginRender, just set the table
85 cmdList->SetGraphicsRootDescriptorTable(rootParameterIndex, m_srvGpuHandle);
86 } else if (m_srvHeap) {
87 // Legacy fallback: per-texture heap (should not happen in normal flow)
88 ID3D12DescriptorHeap* heaps[] = {m_srvHeap.Get()};
89 cmdList->SetDescriptorHeaps(1, heaps);
90 cmdList->SetGraphicsRootDescriptorTable(
91 rootParameterIndex,
92 m_srvHeap->GetGPUDescriptorHandleForHeapStart());
93 }
94}
95
96bool DirectX12Texture::CreateTextureResource(uint32_t width,
97 uint32_t height,
98 DXGI_FORMAT format) {
99 D3D12_RESOURCE_DESC texDesc{};
100 texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
101 texDesc.Alignment = 0;
102 texDesc.Width = width;
103 texDesc.Height = height;
104 texDesc.DepthOrArraySize = 1;
105 texDesc.MipLevels = 1;
106 texDesc.Format = format;
107 texDesc.SampleDesc.Count = 1;
108 texDesc.SampleDesc.Quality = 0;
109 texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
110 texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
111
112 D3D12_HEAP_PROPERTIES heapProps{};
113 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
114 heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
115 heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
116 heapProps.CreationNodeMask = 1;
117 heapProps.VisibleNodeMask = 1;
118
119 HRESULT hr = m_device->CreateCommittedResource(
120 &heapProps, D3D12_HEAP_FLAG_NONE, &texDesc,
121 D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
122 IID_PPV_ARGS(&m_texture));
123
124 if (FAILED(hr)) {
126 "DirectX12Texture: Failed to create texture resource "
127 "HRESULT: 0x{:08X}",
128 static_cast<unsigned int>(hr));
129 return false;
130 }
131
132 return true;
133}
134
135bool DirectX12Texture::UploadTextureData(const void* data,
136 uint32_t width,
137 uint32_t height) {
138 // Get the required upload buffer size (with row pitch alignment)
139 D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
140 UINT numRows;
141 UINT64 rowSizeInBytes;
142 UINT64 totalBytes;
143 D3D12_RESOURCE_DESC texDesc = m_texture->GetDesc();
144 m_device->GetCopyableFootprints(&texDesc, 0, 1, 0, &footprint,
145 &numRows, &rowSizeInBytes,
146 &totalBytes);
147
148 // Create upload buffer
149 D3D12_HEAP_PROPERTIES uploadHeapProps{};
150 uploadHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
151 uploadHeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
152 uploadHeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
153 uploadHeapProps.CreationNodeMask = 1;
154 uploadHeapProps.VisibleNodeMask = 1;
155
156 D3D12_RESOURCE_DESC uploadDesc{};
157 uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
158 uploadDesc.Alignment = 0;
159 uploadDesc.Width = totalBytes;
160 uploadDesc.Height = 1;
161 uploadDesc.DepthOrArraySize = 1;
162 uploadDesc.MipLevels = 1;
163 uploadDesc.Format = DXGI_FORMAT_UNKNOWN;
164 uploadDesc.SampleDesc.Count = 1;
165 uploadDesc.SampleDesc.Quality = 0;
166 uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
167 uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
168
169 HRESULT hr = m_device->CreateCommittedResource(
170 &uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
171 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
172 IID_PPV_ARGS(&m_uploadBuffer));
173
174 if (FAILED(hr)) {
176 "DirectX12Texture: Failed to create upload buffer");
177 return false;
178 }
179
180 // Map and copy data row-by-row (respecting row pitch alignment)
181 BYTE* mapped = nullptr;
182 hr = m_uploadBuffer->Map(0, nullptr,
183 reinterpret_cast<void**>(&mapped));
184 if (FAILED(hr)) {
185 SLEAK_ERROR("DirectX12Texture: Failed to map upload buffer");
186 return false;
187 }
188
189 const BYTE* srcData = static_cast<const BYTE*>(data);
190 UINT srcRowPitch = width * 4; // RGBA8 = 4 bytes per pixel
191 BYTE* destRow = mapped + footprint.Offset;
192
193 for (UINT row = 0; row < numRows; row++) {
194 memcpy(destRow + row * footprint.Footprint.RowPitch,
195 srcData + row * srcRowPitch,
196 srcRowPitch);
197 }
198
199 m_uploadBuffer->Unmap(0, nullptr);
200
201 // Create command allocator and list for the upload
202 Microsoft::WRL::ComPtr<ID3D12CommandAllocator> cmdAllocator;
203 Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> cmdList;
204
205 hr = m_device->CreateCommandAllocator(
206 D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAllocator));
207 if (FAILED(hr)) return false;
208
209 hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
210 cmdAllocator.Get(), nullptr,
211 IID_PPV_ARGS(&cmdList));
212 if (FAILED(hr)) return false;
213
214 // Copy from upload buffer to texture
215 D3D12_TEXTURE_COPY_LOCATION dst{};
216 dst.pResource = m_texture.Get();
217 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
218 dst.SubresourceIndex = 0;
219
220 D3D12_TEXTURE_COPY_LOCATION src{};
221 src.pResource = m_uploadBuffer.Get();
222 src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
223 src.PlacedFootprint = footprint;
224
225 cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
226
227 // Transition from COPY_DEST to PIXEL_SHADER_RESOURCE
228 D3D12_RESOURCE_BARRIER barrier{};
229 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
230 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
231 barrier.Transition.pResource = m_texture.Get();
232 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
233 barrier.Transition.StateAfter =
234 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
235 barrier.Transition.Subresource =
236 D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
237 cmdList->ResourceBarrier(1, &barrier);
238
239 cmdList->Close();
240
241 // Execute and wait
242 ID3D12CommandList* ppCmdLists[] = {cmdList.Get()};
243 m_commandQueue->ExecuteCommandLists(1, ppCmdLists);
244 WaitForUpload();
245
246 return true;
247}
248
249bool DirectX12Texture::CreateSRV(DXGI_FORMAT format) {
250 // Create SRV descriptor heap
251 D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc{};
252 srvHeapDesc.NumDescriptors = 1;
253 srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
254 srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
255
256 HRESULT hr = m_device->CreateDescriptorHeap(
257 &srvHeapDesc, IID_PPV_ARGS(&m_srvHeap));
258 if (FAILED(hr)) {
260 "DirectX12Texture: Failed to create SRV descriptor heap");
261 return false;
262 }
263
264 // Create the SRV
265 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{};
266 srvDesc.Shader4ComponentMapping =
267 D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
268 srvDesc.Format = format;
269 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
270 srvDesc.Texture2D.MipLevels = 1;
271
272 m_device->CreateShaderResourceView(
273 m_texture.Get(), &srvDesc,
274 m_srvHeap->GetCPUDescriptorHandleForHeapStart());
275
276 return true;
277}
278
279bool DirectX12Texture::CreateSRVIntoHandle(DXGI_FORMAT format,
280 D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle) {
281 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{};
282 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
283 srvDesc.Format = format;
284 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
285 srvDesc.Texture2D.MipLevels = 1;
286 m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, cpuHandle);
287 return true;
288}
289
290DXGI_FORMAT DirectX12Texture::GetDXGIFormat(TextureFormat format) const {
291 switch (format) {
292 case TextureFormat::RGBA8:
293 return DXGI_FORMAT_R8G8B8A8_UNORM;
294 case TextureFormat::BGRA8:
295 return DXGI_FORMAT_B8G8R8A8_UNORM;
296 case TextureFormat::RGB8:
297 return DXGI_FORMAT_R8G8B8A8_UNORM; // No 3-component format
298 default:
299 return DXGI_FORMAT_R8G8B8A8_UNORM;
300 }
301}
302
303void DirectX12Texture::WaitForUpload() {
304 Microsoft::WRL::ComPtr<ID3D12Fence> fence;
305 HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
306 IID_PPV_ARGS(&fence));
307 if (FAILED(hr)) return;
308
309 HANDLE event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
310 if (!event) return;
311
312 m_commandQueue->Signal(fence.Get(), 1);
313 if (fence->GetCompletedValue() < 1) {
314 fence->SetEventOnCompletion(1, event);
315 WaitForSingleObject(event, INFINITE);
316 }
317
318 CloseHandle(event);
319}
320
321} // namespace RenderEngine
322} // namespace Sleak
323
324#endif // PLATFORM_WIN
int width
int height
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10