SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX12CubemapTexture.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#include <cmath>
9#include <algorithm>
10#include <vector>
11
12namespace Sleak {
13namespace RenderEngine {
14
15DirectX12CubemapTexture::DirectX12CubemapTexture(
16 ID3D12Device* device, ID3D12CommandQueue* commandQueue)
17 : m_device(device), m_commandQueue(commandQueue) {}
18
19DirectX12CubemapTexture::~DirectX12CubemapTexture() {
20 m_uploadBuffer.Reset();
21 m_srvHeap.Reset();
22 m_texture.Reset();
23}
24
25bool DirectX12CubemapTexture::LoadCubemap(
26 const std::array<std::string, 6>& facePaths) {
27 stbi_set_flip_vertically_on_load(false);
28
29 struct FaceData {
30 unsigned char* pixels = nullptr;
31 int w = 0, h = 0;
32 };
33 std::array<FaceData, 6> faces;
34
35 for (int i = 0; i < 6; i++) {
36 int channels;
37 faces[i].pixels = stbi_load(facePaths[i].c_str(), &faces[i].w,
38 &faces[i].h, &channels, 4);
39 if (!faces[i].pixels) {
41 "DirectX12CubemapTexture: Failed to load face {}: {}", i,
42 facePaths[i]);
43 for (int j = 0; j < i; j++)
44 stbi_image_free(faces[j].pixels);
45 return false;
46 }
47 }
48
49 for (int i = 1; i < 6; i++) {
50 if (faces[i].w != faces[0].w || faces[i].h != faces[0].h) {
52 "DirectX12CubemapTexture: Face {} size ({}x{}) doesn't match "
53 "face 0 ({}x{})",
54 i, faces[i].w, faces[i].h, faces[0].w, faces[0].h);
55 for (int j = 0; j < 6; j++)
56 stbi_image_free(faces[j].pixels);
57 return false;
58 }
59 }
60
61 uint32_t faceSize = static_cast<uint32_t>(faces[0].w);
62 std::vector<unsigned char*> facePointers;
63 for (int i = 0; i < 6; i++)
64 facePointers.push_back(faces[i].pixels);
65
66 bool result = CreateCubemapFromFaces(facePointers, faceSize);
67
68 for (int i = 0; i < 6; i++)
69 stbi_image_free(faces[i].pixels);
70
71 if (result) {
73 "DirectX12CubemapTexture: Loaded cubemap ({}x{}, 6 faces)",
74 faceSize, faceSize);
75 }
76 return result;
77}
78
79bool DirectX12CubemapTexture::LoadEquirectangular(const std::string& path,
80 uint32_t faceSize) {
81 stbi_set_flip_vertically_on_load(false);
82
83 int panW, panH, channels;
84 unsigned char* panorama =
85 stbi_load(path.c_str(), &panW, &panH, &channels, 4);
86 if (!panorama) {
88 "DirectX12CubemapTexture: Failed to load panorama: {}", path);
89 return false;
90 }
91
92 size_t faceBytes = static_cast<size_t>(faceSize) * faceSize * 4;
93 std::vector<std::vector<unsigned char>> allFaces(6);
94 for (int i = 0; i < 6; i++)
95 allFaces[i].resize(faceBytes);
96
97 for (int face = 0; face < 6; ++face) {
98 unsigned char* faceData = allFaces[face].data();
99
100 for (uint32_t y = 0; y < faceSize; ++y) {
101 for (uint32_t x = 0; x < faceSize; ++x) {
102 float u = (2.0f * (x + 0.5f) / faceSize) - 1.0f;
103 float v = (2.0f * (y + 0.5f) / faceSize) - 1.0f;
104
105 float dx, dy, dz;
106 switch (face) {
107 case 0: dx = 1.0f; dy = -v; dz = -u; break; // +X
108 case 1: dx = -1.0f; dy = -v; dz = u; break; // -X
109 case 2: dx = u; dy = 1.0f; dz = v; break; // +Y
110 case 3: dx = u; dy = -1.0f; dz = -v; break; // -Y
111 case 4: dx = u; dy = -v; dz = 1.0f; break; // +Z
112 case 5: dx = -u; dy = -v; dz = -1.0f; break; // -Z
113 default: dx = dy = dz = 0.0f; break;
114 }
115
116 float len = std::sqrt(dx * dx + dy * dy + dz * dz);
117 dx /= len; dy /= len; dz /= len;
118
119 float lon = std::atan2(dz, dx);
120 float lat = std::asin(std::clamp(dy, -1.0f, 1.0f));
121
122 float panU = 0.5f + lon / (2.0f * 3.14159265f);
123 float panV = 0.5f - lat / 3.14159265f;
124
125 float srcX = panU * (panW - 1);
126 float srcY = panV * (panH - 1);
127 int x0 = static_cast<int>(srcX);
128 int y0 = static_cast<int>(srcY);
129 int x1 = std::min(x0 + 1, panW - 1);
130 int y1 = std::min(y0 + 1, panH - 1);
131 x0 = std::clamp(x0, 0, panW - 1);
132 y0 = std::clamp(y0, 0, panH - 1);
133 float fx = srcX - x0;
134 float fy = srcY - y0;
135
136 size_t idx = (y * faceSize + x) * 4;
137 for (int c = 0; c < 4; ++c) {
138 float c00 = panorama[(y0 * panW + x0) * 4 + c];
139 float c10 = panorama[(y0 * panW + x1) * 4 + c];
140 float c01 = panorama[(y1 * panW + x0) * 4 + c];
141 float c11 = panorama[(y1 * panW + x1) * 4 + c];
142 float val = c00 * (1 - fx) * (1 - fy) +
143 c10 * fx * (1 - fy) +
144 c01 * (1 - fx) * fy +
145 c11 * fx * fy;
146 faceData[idx + c] = static_cast<unsigned char>(
147 std::clamp(val, 0.0f, 255.0f));
148 }
149 }
150 }
151 }
152
153 stbi_image_free(panorama);
154
155 std::vector<unsigned char*> facePointers;
156 for (int i = 0; i < 6; i++)
157 facePointers.push_back(allFaces[i].data());
158
159 bool result = CreateCubemapFromFaces(facePointers, faceSize);
160
161 if (result) {
163 "DirectX12CubemapTexture: Loaded equirectangular panorama "
164 "({}x{} face)",
165 faceSize, faceSize);
166 }
167 return result;
168}
169
170bool DirectX12CubemapTexture::CreateCubemapFromFaces(
171 const std::vector<unsigned char*>& faceData, uint32_t faceSize) {
172 if (!m_device || faceData.size() != 6) return false;
173
174 m_width = faceSize;
175 m_height = faceSize;
176
177 // Create cubemap texture resource (6 array slices)
178 D3D12_RESOURCE_DESC texDesc = {};
179 texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
180 texDesc.Alignment = 0;
181 texDesc.Width = faceSize;
182 texDesc.Height = faceSize;
183 texDesc.DepthOrArraySize = 6;
184 texDesc.MipLevels = 1;
185 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
186 texDesc.SampleDesc.Count = 1;
187 texDesc.SampleDesc.Quality = 0;
188 texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
189 texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
190
191 D3D12_HEAP_PROPERTIES heapProps = {};
192 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
193 heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
194 heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
195 heapProps.CreationNodeMask = 1;
196 heapProps.VisibleNodeMask = 1;
197
198 HRESULT hr = m_device->CreateCommittedResource(
199 &heapProps, D3D12_HEAP_FLAG_NONE, &texDesc,
200 D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
201 IID_PPV_ARGS(&m_texture));
202
203 if (FAILED(hr)) {
205 "DirectX12CubemapTexture: Failed to create texture resource "
206 "HRESULT: 0x{:08X}",
207 static_cast<unsigned int>(hr));
208 return false;
209 }
210
211 // Get copyable footprints for all 6 subresources
212 D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprints[6];
213 UINT numRows[6];
214 UINT64 rowSizeInBytes[6];
215 UINT64 totalBytes;
216 m_device->GetCopyableFootprints(&texDesc, 0, 6, 0, footprints, numRows,
217 rowSizeInBytes, &totalBytes);
218
219 // Create upload buffer
220 D3D12_HEAP_PROPERTIES uploadHeapProps = {};
221 uploadHeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
222 uploadHeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
223 uploadHeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
224 uploadHeapProps.CreationNodeMask = 1;
225 uploadHeapProps.VisibleNodeMask = 1;
226
227 D3D12_RESOURCE_DESC uploadDesc = {};
228 uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
229 uploadDesc.Alignment = 0;
230 uploadDesc.Width = totalBytes;
231 uploadDesc.Height = 1;
232 uploadDesc.DepthOrArraySize = 1;
233 uploadDesc.MipLevels = 1;
234 uploadDesc.Format = DXGI_FORMAT_UNKNOWN;
235 uploadDesc.SampleDesc.Count = 1;
236 uploadDesc.SampleDesc.Quality = 0;
237 uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
238 uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
239
240 hr = m_device->CreateCommittedResource(
241 &uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadDesc,
242 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
243 IID_PPV_ARGS(&m_uploadBuffer));
244
245 if (FAILED(hr)) {
247 "DirectX12CubemapTexture: Failed to create upload buffer");
248 return false;
249 }
250
251 // Map and copy data for all 6 faces
252 BYTE* mapped = nullptr;
253 hr = m_uploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mapped));
254 if (FAILED(hr)) {
255 SLEAK_ERROR("DirectX12CubemapTexture: Failed to map upload buffer");
256 return false;
257 }
258
259 uint32_t srcRowPitch = faceSize * 4;
260 for (int face = 0; face < 6; face++) {
261 BYTE* destBase = mapped + footprints[face].Offset;
262 const BYTE* srcData = reinterpret_cast<const BYTE*>(faceData[face]);
263
264 for (UINT row = 0; row < numRows[face]; row++) {
265 memcpy(destBase + row * footprints[face].Footprint.RowPitch,
266 srcData + row * srcRowPitch, srcRowPitch);
267 }
268 }
269
270 m_uploadBuffer->Unmap(0, nullptr);
271
272 // Create command allocator and list for the upload
273 Microsoft::WRL::ComPtr<ID3D12CommandAllocator> cmdAllocator;
274 Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> cmdList;
275
276 hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
277 IID_PPV_ARGS(&cmdAllocator));
278 if (FAILED(hr)) return false;
279
280 hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
281 cmdAllocator.Get(), nullptr,
282 IID_PPV_ARGS(&cmdList));
283 if (FAILED(hr)) return false;
284
285 // Copy each face from upload buffer to texture
286 for (UINT face = 0; face < 6; face++) {
287 D3D12_TEXTURE_COPY_LOCATION dst = {};
288 dst.pResource = m_texture.Get();
289 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
290 dst.SubresourceIndex = face;
291
292 D3D12_TEXTURE_COPY_LOCATION src = {};
293 src.pResource = m_uploadBuffer.Get();
294 src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
295 src.PlacedFootprint = footprints[face];
296
297 cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
298 }
299
300 // Transition to PIXEL_SHADER_RESOURCE
301 D3D12_RESOURCE_BARRIER barrier = {};
302 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
303 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
304 barrier.Transition.pResource = m_texture.Get();
305 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
306 barrier.Transition.StateAfter =
307 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
308 barrier.Transition.Subresource =
309 D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
310 cmdList->ResourceBarrier(1, &barrier);
311
312 cmdList->Close();
313
314 ID3D12CommandList* ppCmdLists[] = {cmdList.Get()};
315 m_commandQueue->ExecuteCommandLists(1, ppCmdLists);
316 WaitForUpload();
317
318 // Create SRV descriptor heap
319 D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
320 srvHeapDesc.NumDescriptors = 1;
321 srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
322 srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
323
324 hr = m_device->CreateDescriptorHeap(&srvHeapDesc,
325 IID_PPV_ARGS(&m_srvHeap));
326 if (FAILED(hr)) {
328 "DirectX12CubemapTexture: Failed to create SRV descriptor heap");
329 return false;
330 }
331
332 // Create SRV for TextureCube
333 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
334 srvDesc.Shader4ComponentMapping =
335 D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
336 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
337 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
338 srvDesc.TextureCube.MipLevels = 1;
339 srvDesc.TextureCube.MostDetailedMip = 0;
340 srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
341
342 m_device->CreateShaderResourceView(
343 m_texture.Get(), &srvDesc,
344 m_srvHeap->GetCPUDescriptorHandleForHeapStart());
345
346 return true;
347}
348
349void DirectX12CubemapTexture::WaitForUpload() {
350 Microsoft::WRL::ComPtr<ID3D12Fence> fence;
351 HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
352 IID_PPV_ARGS(&fence));
353 if (FAILED(hr)) return;
354
355 HANDLE event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
356 if (!event) return;
357
358 m_commandQueue->Signal(fence.Get(), 1);
359 if (fence->GetCompletedValue() < 1) {
360 fence->SetEventOnCompletion(1, event);
361 WaitForSingleObject(event, INFINITE);
362 }
363
364 CloseHandle(event);
365}
366
367bool DirectX12CubemapTexture::LoadFromMemory(const void* data, uint32_t width,
368 uint32_t height,
369 TextureFormat format) {
370 (void)data;
371 (void)width;
372 (void)height;
373 (void)format;
374 return false;
375}
376
377bool DirectX12CubemapTexture::LoadFromFile(const std::string& filePath) {
378 (void)filePath;
379 return false;
380}
381
382void DirectX12CubemapTexture::Bind(uint32_t slot) const {
383 (void)slot;
384 // Binding in DX12 requires a command list — use BindToCommandList instead
385}
386
387void DirectX12CubemapTexture::Unbind() const {
388 // No-op in DX12
389}
390
391void DirectX12CubemapTexture::SetFilter(TextureFilter filter) {
392 (void)filter;
393}
394
395void DirectX12CubemapTexture::SetWrapMode(TextureWrapMode wrapMode) {
396 (void)wrapMode;
397}
398
399void DirectX12CubemapTexture::BindToCommandList(
400 ID3D12GraphicsCommandList* cmdList, UINT rootParameterIndex) const {
401 if (!cmdList) return;
402
403 if (m_usesSharedHeap) {
404 // Fast path: shared heap already bound by BeginRender
405 cmdList->SetGraphicsRootDescriptorTable(rootParameterIndex, m_srvGpuHandle);
406 } else if (m_srvHeap) {
407 // Legacy fallback
408 ID3D12DescriptorHeap* heaps[] = {m_srvHeap.Get()};
409 cmdList->SetDescriptorHeaps(1, heaps);
410 cmdList->SetGraphicsRootDescriptorTable(
411 rootParameterIndex,
412 m_srvHeap->GetGPUDescriptorHandleForHeapStart());
413 }
414}
415
416void DirectX12CubemapTexture::CreateSRVIntoHandle(
417 D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle) {
418 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
419 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
420 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
421 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
422 srvDesc.TextureCube.MipLevels = 1;
423 srvDesc.TextureCube.MostDetailedMip = 0;
424 srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
425 m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, cpuHandle);
426}
427
428} // namespace RenderEngine
429} // namespace Sleak
430
431#endif // PLATFORM_WIN
int width
int height
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
#define SLEAK_INFO(...)
Definition Logger.hpp:20
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10