SleakEngine 0.1.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 <stb_image.h>
6
7#include <Core/Logger.hpp>
10#include <algorithm>
11#include <cstring>
12#include <utility>
13#include <vector>
14
15namespace Sleak {
16namespace RenderEngine {
17
18namespace {
19uint32_t MipCount(uint32_t width, uint32_t height) {
20 uint32_t count = 1;
21 uint32_t size = (std::max)(width, height);
22 while (size > 1) {
23 size /= 2;
24 ++count;
25 }
26 return count;
27}
28
29void DownsampleRGBA8(const uint8_t* src, uint32_t width, uint32_t height,
30 std::vector<uint8_t>& dst) {
31 const uint32_t dstWidth = (std::max)(1u, width / 2);
32 const uint32_t dstHeight = (std::max)(1u, height / 2);
33 dst.resize(static_cast<size_t>(dstWidth) * dstHeight * 4);
34
35 for (uint32_t y = 0; y < dstHeight; ++y) {
36 const uint32_t y0 = (std::min)(y * 2, height - 1);
37 const uint32_t y1 = (std::min)(y * 2 + 1, height - 1);
38 for (uint32_t x = 0; x < dstWidth; ++x) {
39 const uint32_t x0 = (std::min)(x * 2, width - 1);
40 const uint32_t x1 = (std::min)(x * 2 + 1, width - 1);
41 for (uint32_t c = 0; c < 4; ++c) {
42 const uint32_t sum = src[(y0 * width + x0) * 4 + c] +
43 src[(y0 * width + x1) * 4 + c] +
44 src[(y1 * width + x0) * 4 + c] +
45 src[(y1 * width + x1) * 4 + c];
46 dst[(static_cast<size_t>(y) * dstWidth + x) * 4 + c] =
47 static_cast<uint8_t>((sum + 2) / 4);
48 }
49 }
50 }
51}
52} // namespace
53
54DirectX12Texture::DirectX12Texture(ID3D12Device* device,
55 ID3D12CommandQueue* commandQueue,
56 ID3D12GraphicsCommandList* commandList,
57 DirectX12UploadContext* uploader)
58 : m_device(device),
59 m_commandQueue(commandQueue),
60 m_commandList(commandList),
61 m_uploader(uploader) {}
62
63DirectX12Texture::~DirectX12Texture() {
64 DirectX12Buffer::DeferCleanup(std::move(m_srvHeap));
65 DirectX12Buffer::DeferCleanup(std::move(m_texture));
66}
67
68bool DirectX12Texture::LoadFromMemory(const void* data, uint32_t width,
69 uint32_t height,
70 TextureFormat format) {
71 if (!data || !m_device || width == 0 || height == 0) return false;
72
73 m_width = width;
74 m_height = height;
75 m_format = format;
76 m_mipLevels = MipCount(width, height);
77
78 DXGI_FORMAT dxgiFormat = GetDXGIFormat(format);
79
80 if (!CreateTextureResource(width, height, dxgiFormat)) return false;
81 if (!UploadTextureData(data, width, height)) return false;
82 if (!CreateSRV(dxgiFormat)) return false;
83
84 return true;
85}
86
87bool DirectX12Texture::LoadFromFile(const std::string& filePath) {
88 int w, h, channels;
89 unsigned char* pixels =
90 stbi_load(filePath.c_str(), &w, &h, &channels, 4);
91 if (!pixels) {
92 SLEAK_ERROR("DirectX12Texture: Failed to load image: {}",
93 filePath);
94 return false;
95 }
96
97 bool result = LoadFromMemory(pixels, static_cast<uint32_t>(w),
98 static_cast<uint32_t>(h),
99 TextureFormat::RGBA8);
100 stbi_image_free(pixels);
101 return result;
102}
103
104void DirectX12Texture::Bind(uint32_t slot) const {
105 if (m_commandList && m_srvHeap) {
106 BindToCommandList(m_commandList, 2); // root param 2 = SRV table
107 }
108}
109
110void DirectX12Texture::Unbind() const {
111 // No-op in DX12
112}
113
114void DirectX12Texture::SetFilter(TextureFilter filter) {
115 m_filter = filter;
116 // Sampler is baked into the root signature as a static sampler.
117 // A full implementation would recreate the PSO with a new sampler.
118}
119
120void DirectX12Texture::SetWrapMode(TextureWrapMode wrapMode) {
121 m_wrapMode = wrapMode;
122}
123
124void DirectX12Texture::BindToCommandList(
125 ID3D12GraphicsCommandList* cmdList, UINT rootParameterIndex) const {
126 if (!cmdList) return;
127
128 if (m_usesSharedHeap) {
129 // Fast path: shared heap already bound by BeginRender, just set the table
130 cmdList->SetGraphicsRootDescriptorTable(rootParameterIndex, m_srvGpuHandle);
131 } else if (m_srvHeap) {
132 // Legacy fallback: per-texture heap (should not happen in normal flow)
133 ID3D12DescriptorHeap* heaps[] = {m_srvHeap.Get()};
134 cmdList->SetDescriptorHeaps(1, heaps);
135 cmdList->SetGraphicsRootDescriptorTable(
136 rootParameterIndex,
137 m_srvHeap->GetGPUDescriptorHandleForHeapStart());
138 }
139}
140
141bool DirectX12Texture::CreateTextureResource(uint32_t width,
142 uint32_t height,
143 DXGI_FORMAT format) {
144 D3D12_RESOURCE_DESC texDesc{};
145 texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
146 texDesc.Alignment = 0;
147 texDesc.Width = width;
148 texDesc.Height = height;
149 texDesc.DepthOrArraySize = 1;
150 texDesc.MipLevels = static_cast<UINT16>(m_mipLevels);
151 texDesc.Format = format;
152 texDesc.SampleDesc.Count = 1;
153 texDesc.SampleDesc.Quality = 0;
154 texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
155 texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
156
157 D3D12_HEAP_PROPERTIES heapProps{};
158 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
159 heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
160 heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
161 heapProps.CreationNodeMask = 1;
162 heapProps.VisibleNodeMask = 1;
163
164 HRESULT hr = m_device->CreateCommittedResource(
165 &heapProps, D3D12_HEAP_FLAG_NONE, &texDesc,
166 D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
167 IID_PPV_ARGS(&m_texture));
168
169 if (FAILED(hr)) {
171 "DirectX12Texture: Failed to create texture resource "
172 "HRESULT: 0x{:08X}",
173 static_cast<unsigned int>(hr));
174 return false;
175 }
176
177 return true;
178}
179
180bool DirectX12Texture::UploadTextureData(const void* data,
181 uint32_t width,
182 uint32_t height) {
183 const UINT mipCount = m_mipLevels;
184 std::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> footprints(mipCount);
185 std::vector<UINT> numRows(mipCount);
186 std::vector<UINT64> rowSizes(mipCount);
187 UINT64 totalBytes = 0;
188 D3D12_RESOURCE_DESC texDesc = m_texture->GetDesc();
189 m_device->GetCopyableFootprints(&texDesc, 0, mipCount, 0, footprints.data(),
190 numRows.data(), rowSizes.data(),
191 &totalBytes);
192
193 DirectX12UploadContext local;
194 DirectX12UploadContext* uploader = m_uploader;
195 if (!uploader) {
196 if (!local.Initialize(m_device, m_commandQueue)) return false;
197 uploader = &local;
198 }
199
200 ID3D12Resource* staging = nullptr;
201 BYTE* mapped =
202 static_cast<BYTE*>(uploader->AllocateStaging(totalBytes, &staging));
203 if (!mapped) {
204 SLEAK_ERROR("DirectX12Texture: Failed to allocate staging memory");
205 return false;
206 }
207
208 const uint8_t* level = static_cast<const uint8_t*>(data);
209 std::vector<uint8_t> current;
210 std::vector<uint8_t> next;
211 uint32_t levelWidth = width;
212 uint32_t levelHeight = height;
213
214 for (UINT mip = 0; mip < mipCount; ++mip) {
215 const UINT rowBytes = levelWidth * 4; // RGBA8 = 4 bytes per pixel
216 BYTE* destRow = mapped + footprints[mip].Offset;
217 for (UINT row = 0; row < numRows[mip]; ++row) {
218 memcpy(destRow + row * footprints[mip].Footprint.RowPitch,
219 level + row * rowBytes, rowBytes);
220 }
221
222 if (mip + 1 < mipCount) {
223 DownsampleRGBA8(level, levelWidth, levelHeight, next);
224 current.swap(next);
225 level = current.data();
226 levelWidth = (std::max)(1u, levelWidth / 2);
227 levelHeight = (std::max)(1u, levelHeight / 2);
228 }
229 }
230
231 ID3D12GraphicsCommandList* cmdList = uploader->GetCommandList();
232 for (UINT mip = 0; mip < mipCount; ++mip) {
233 D3D12_TEXTURE_COPY_LOCATION dst{};
234 dst.pResource = m_texture.Get();
235 dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
236 dst.SubresourceIndex = mip;
237
238 D3D12_TEXTURE_COPY_LOCATION src{};
239 src.pResource = staging;
240 src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
241 src.PlacedFootprint = footprints[mip];
242
243 cmdList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
244 }
245
246 // Transition from COPY_DEST to PIXEL_SHADER_RESOURCE
247 D3D12_RESOURCE_BARRIER barrier{};
248 barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
249 barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
250 barrier.Transition.pResource = m_texture.Get();
251 barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
252 barrier.Transition.StateAfter =
253 D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
254 barrier.Transition.Subresource =
255 D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
256 cmdList->ResourceBarrier(1, &barrier);
257
258 if (uploader == &local) local.Flush();
259 return true;
260}
261
262bool DirectX12Texture::CreateSRV(DXGI_FORMAT format) {
263 // Create SRV descriptor heap
264 D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc{};
265 srvHeapDesc.NumDescriptors = 1;
266 srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
267 srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
268
269 HRESULT hr = m_device->CreateDescriptorHeap(
270 &srvHeapDesc, IID_PPV_ARGS(&m_srvHeap));
271 if (FAILED(hr)) {
273 "DirectX12Texture: Failed to create SRV descriptor heap");
274 return false;
275 }
276
277 // Create the SRV
278 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{};
279 srvDesc.Shader4ComponentMapping =
280 D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
281 srvDesc.Format = format;
282 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
283 srvDesc.Texture2D.MipLevels = m_mipLevels;
284
285 m_device->CreateShaderResourceView(
286 m_texture.Get(), &srvDesc,
287 m_srvHeap->GetCPUDescriptorHandleForHeapStart());
288
289 return true;
290}
291
292bool DirectX12Texture::CreateSRVIntoHandle(DXGI_FORMAT format,
293 D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle) {
294 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{};
295 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
296 srvDesc.Format = format;
297 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
298 srvDesc.Texture2D.MipLevels = m_mipLevels;
299 m_device->CreateShaderResourceView(m_texture.Get(), &srvDesc, cpuHandle);
300 return true;
301}
302
303DXGI_FORMAT DirectX12Texture::GetDXGIFormat(TextureFormat format) const {
304 switch (format) {
305 case TextureFormat::RGBA8:
306 return DXGI_FORMAT_R8G8B8A8_UNORM;
307 case TextureFormat::BGRA8:
308 return DXGI_FORMAT_B8G8R8A8_UNORM;
309 case TextureFormat::RGB8:
310 return DXGI_FORMAT_R8G8B8A8_UNORM; // No 3-component format
311 default:
312 return DXGI_FORMAT_R8G8B8A8_UNORM;
313 }
314}
315
316} // namespace RenderEngine
317} // namespace Sleak
318
319#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