Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d12_texture.cpp
7328 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "d3d12_texture.h"
5
#include "d3d12_builders.h"
6
#include "d3d12_device.h"
7
#include "d3d_common.h"
8
9
#include "common/align.h"
10
#include "common/assert.h"
11
#include "common/bitutils.h"
12
#include "common/error.h"
13
#include "common/log.h"
14
#include "common/string_util.h"
15
16
#include "D3D12MemAlloc.h"
17
18
LOG_CHANNEL(GPUDevice);
19
20
D3D12Texture::D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
21
GPUTextureFormat format, Flags flags, DXGI_FORMAT dxgi_format,
22
ComPtr<ID3D12Resource> resource, ComPtr<D3D12MA::Allocation> allocation,
23
const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor,
24
const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype,
25
D3D12_RESOURCE_STATES resource_state)
26
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
27
static_cast<u8>(samples), type, format, flags),
28
m_resource(std::move(resource)), m_allocation(std::move(allocation)), m_srv_descriptor(srv_descriptor),
29
m_write_descriptor(write_descriptor), m_uav_descriptor(uav_descriptor), m_dxgi_format(dxgi_format),
30
m_resource_state(resource_state), m_write_descriptor_type(wdtype)
31
{
32
}
33
34
D3D12Texture::~D3D12Texture()
35
{
36
Destroy(true);
37
}
38
39
std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
40
GPUTexture::Type type, GPUTextureFormat format,
41
GPUTexture::Flags flags, const void* data /* = nullptr */,
42
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
43
{
44
if (!GPUTexture::ValidateConfig(width, height, layers, levels, samples, type, format, flags, error))
45
return {};
46
47
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format);
48
49
D3D12_RESOURCE_DESC desc = {};
50
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
51
desc.Width = width;
52
desc.Height = height;
53
desc.DepthOrArraySize = 1;
54
desc.MipLevels = static_cast<u8>(levels);
55
desc.Format = fm.resource_format;
56
desc.SampleDesc.Count = samples;
57
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
58
59
D3D12MA::ALLOCATION_DESC allocationDesc = {};
60
allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET;
61
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
62
63
D3D12_CLEAR_VALUE optimized_clear_value = {};
64
D3D12_RESOURCE_STATES state;
65
66
switch (type)
67
{
68
case GPUTexture::Type::Texture:
69
{
70
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
71
state = D3D12_RESOURCE_STATE_COPY_DEST;
72
}
73
break;
74
75
case GPUTexture::Type::RenderTarget:
76
{
77
// RT's tend to be larger, so we'll keep them committed for speed.
78
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
79
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
80
optimized_clear_value.Format = fm.rtv_format;
81
state = D3D12_RESOURCE_STATE_RENDER_TARGET;
82
}
83
break;
84
85
case GPUTexture::Type::DepthStencil:
86
{
87
DebugAssert(levels == 1);
88
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
89
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
90
optimized_clear_value.Format = fm.dsv_format;
91
state = D3D12_RESOURCE_STATE_DEPTH_WRITE;
92
}
93
break;
94
95
DefaultCaseIsUnreachable();
96
}
97
98
if ((flags & GPUTexture::Flags::AllowBindAsImage) != GPUTexture::Flags::None)
99
{
100
DebugAssert(levels == 1);
101
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
102
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
103
}
104
105
if ((flags & GPUTexture::Flags::AllowGenerateMipmaps) != GPUTexture::Flags::None)
106
{
107
// requires RTs since we need to draw the mips
108
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
109
}
110
111
ComPtr<ID3D12Resource> resource;
112
ComPtr<D3D12MA::Allocation> allocation;
113
HRESULT hr = m_allocator->CreateResource(
114
&allocationDesc, &desc, state,
115
(type == GPUTexture::Type::RenderTarget || type == GPUTexture::Type::DepthStencil) ? &optimized_clear_value :
116
nullptr,
117
allocation.GetAddressOf(), IID_PPV_ARGS(resource.GetAddressOf()));
118
if (FAILED(hr)) [[unlikely]]
119
{
120
Error::SetHResult(error, "CreateResource() failed: ", hr);
121
return {};
122
}
123
124
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor;
125
D3D12Texture::WriteDescriptorType write_descriptor_type = D3D12Texture::WriteDescriptorType::None;
126
if (fm.srv_format != DXGI_FORMAT_UNKNOWN)
127
{
128
if (!CreateSRVDescriptor(resource.Get(), layers, levels, samples, fm.srv_format, &srv_descriptor, error))
129
return {};
130
}
131
132
switch (type)
133
{
134
case GPUTexture::Type::Texture:
135
break;
136
137
case GPUTexture::Type::RenderTarget:
138
{
139
write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV;
140
if (!CreateRTVDescriptor(resource.Get(), samples, fm.rtv_format, &write_descriptor, error))
141
{
142
m_descriptor_heap_manager.Free(&srv_descriptor);
143
return {};
144
}
145
}
146
break;
147
148
case GPUTexture::Type::DepthStencil:
149
{
150
write_descriptor_type = D3D12Texture::WriteDescriptorType::DSV;
151
if (!CreateDSVDescriptor(resource.Get(), samples, fm.dsv_format, &write_descriptor, error))
152
{
153
m_descriptor_heap_manager.Free(&srv_descriptor);
154
return {};
155
}
156
}
157
break;
158
159
DefaultCaseIsUnreachable();
160
}
161
162
if ((flags & GPUTexture::Flags::AllowBindAsImage) != GPUTexture::Flags::None)
163
{
164
if (!CreateUAVDescriptor(resource.Get(), samples, fm.srv_format, &uav_descriptor, error))
165
{
166
if (write_descriptor_type != D3D12Texture::WriteDescriptorType::None)
167
m_descriptor_heap_manager.Free(&write_descriptor);
168
169
m_descriptor_heap_manager.Free(&srv_descriptor);
170
return {};
171
}
172
}
173
174
std::unique_ptr<D3D12Texture> tex(new D3D12Texture(
175
width, height, layers, levels, samples, type, format, flags, fm.resource_format, std::move(resource),
176
std::move(allocation), srv_descriptor, write_descriptor, uav_descriptor, write_descriptor_type, state));
177
178
if (data)
179
{
180
tex->Update(0, 0, width, height, data, data_stride);
181
182
if (type == GPUTexture::Type::Texture)
183
{
184
// resource barrier must be in the same command buffer as the upload
185
tex->TransitionSubresourceToState(GetInitCommandList(), 0u, 0u, D3D12_RESOURCE_STATE_COPY_DEST,
186
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
187
tex->m_resource_state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
188
}
189
}
190
191
return tex;
192
}
193
194
bool D3D12Device::CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
195
D3D12DescriptorHandle* dh, Error* error)
196
{
197
if (!m_descriptor_heap_manager.Allocate(dh))
198
{
199
Error::SetStringView(error, "Failed to allocate SRV descriptor");
200
return false;
201
}
202
203
D3D12_SHADER_RESOURCE_VIEW_DESC desc;
204
desc.Format = format;
205
desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
206
207
if (layers > 1)
208
{
209
if (samples > 1)
210
{
211
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
212
desc.Texture2DMSArray = {0u, layers};
213
}
214
else
215
{
216
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
217
desc.Texture2DArray = {0u, levels, 0u, layers, 0u, 0.0f};
218
}
219
}
220
else
221
{
222
if (samples > 1)
223
{
224
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
225
}
226
else
227
{
228
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
229
desc.Texture2D = {0u, levels, 0u, 0.0f};
230
}
231
}
232
233
m_device->CreateShaderResourceView(resource, &desc, dh->cpu_handle);
234
return true;
235
}
236
237
bool D3D12Device::CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format,
238
D3D12DescriptorHandle* dh, Error* error)
239
{
240
if (!m_rtv_heap_manager.Allocate(dh))
241
{
242
Error::SetStringView(error, "Failed to allocate SRV descriptor");
243
return false;
244
}
245
246
const D3D12_RENDER_TARGET_VIEW_DESC desc = {
247
format, (samples > 1) ? D3D12_RTV_DIMENSION_TEXTURE2DMS : D3D12_RTV_DIMENSION_TEXTURE2D, {}};
248
m_device->CreateRenderTargetView(resource, &desc, dh->cpu_handle);
249
return true;
250
}
251
252
bool D3D12Device::CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format,
253
D3D12DescriptorHandle* dh, Error* error)
254
{
255
if (!m_dsv_heap_manager.Allocate(dh))
256
{
257
Error::SetStringView(error, "Failed to allocate SRV descriptor");
258
return false;
259
}
260
261
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {
262
format, (samples > 1) ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE, {}};
263
m_device->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
264
return true;
265
}
266
267
bool D3D12Device::CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format,
268
D3D12DescriptorHandle* dh, Error* error)
269
{
270
if (!m_descriptor_heap_manager.Allocate(dh))
271
{
272
Error::SetStringView(error, "Failed to allocate UAV descriptor");
273
return false;
274
}
275
276
DebugAssert(samples == 1);
277
const D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {format, D3D12_UAV_DIMENSION_TEXTURE2D, {}};
278
m_device->CreateUnorderedAccessView(resource, nullptr, &desc, dh->cpu_handle);
279
return true;
280
}
281
282
void D3D12Texture::Destroy(bool defer)
283
{
284
D3D12Device& dev = D3D12Device::GetInstance();
285
dev.UnbindTexture(this);
286
287
if (defer)
288
{
289
dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_srv_descriptor);
290
291
switch (m_write_descriptor_type)
292
{
293
case WriteDescriptorType::RTV:
294
dev.DeferDescriptorDestruction(dev.GetRTVHeapManager(), &m_write_descriptor);
295
break;
296
case WriteDescriptorType::DSV:
297
dev.DeferDescriptorDestruction(dev.GetDSVHeapManager(), &m_write_descriptor);
298
break;
299
case WriteDescriptorType::None:
300
default:
301
break;
302
}
303
304
if (m_uav_descriptor)
305
dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_uav_descriptor);
306
307
dev.DeferResourceDestruction(std::move(m_allocation), std::move(m_resource));
308
}
309
else
310
{
311
dev.GetDescriptorHeapManager().Free(&m_srv_descriptor);
312
313
switch (m_write_descriptor_type)
314
{
315
case WriteDescriptorType::RTV:
316
dev.GetRTVHeapManager().Free(&m_write_descriptor);
317
break;
318
case WriteDescriptorType::DSV:
319
dev.GetDSVHeapManager().Free(&m_write_descriptor);
320
break;
321
case WriteDescriptorType::None:
322
default:
323
break;
324
}
325
326
if (m_uav_descriptor)
327
dev.GetDescriptorHeapManager().Free(&m_uav_descriptor);
328
329
m_resource.Reset();
330
m_allocation.Reset();
331
}
332
333
m_write_descriptor_type = WriteDescriptorType::None;
334
}
335
336
ID3D12GraphicsCommandList4* D3D12Texture::GetCommandBufferForUpdate()
337
{
338
D3D12Device& dev = D3D12Device::GetInstance();
339
if (m_type != Type::Texture || m_use_fence_counter == dev.GetCurrentFenceValue())
340
{
341
// DEV_LOG("Texture update within frame, can't use do beforehand");
342
if (dev.InRenderPass())
343
dev.EndRenderPass();
344
return dev.GetCommandList();
345
}
346
347
return dev.GetInitCommandList();
348
}
349
350
ID3D12Resource* D3D12Texture::AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 width,
351
u32 height, u32 buffer_size) const
352
{
353
ComPtr<ID3D12Resource> resource;
354
ComPtr<D3D12MA::Allocation> allocation;
355
356
const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD,
357
D3D12_HEAP_FLAG_NONE, nullptr, nullptr};
358
const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER,
359
0,
360
buffer_size,
361
1,
362
1,
363
1,
364
DXGI_FORMAT_UNKNOWN,
365
{1, 0},
366
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
367
D3D12_RESOURCE_FLAG_NONE};
368
HRESULT hr = D3D12Device::GetInstance().GetAllocator()->CreateResource(
369
&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.GetAddressOf(),
370
IID_PPV_ARGS(resource.GetAddressOf()));
371
if (FAILED(hr)) [[unlikely]]
372
{
373
ERROR_LOG("CreateResource() failed with {:08X}", static_cast<unsigned>(hr));
374
return nullptr;
375
}
376
377
void* map_ptr;
378
hr = resource->Map(0, nullptr, &map_ptr);
379
if (FAILED(hr)) [[unlikely]]
380
{
381
ERROR_LOG("Map() failed with {:08X}", static_cast<unsigned>(hr));
382
return nullptr;
383
}
384
385
CopyTextureDataForUpload(width, height, m_format, map_ptr, upload_pitch, data, pitch);
386
387
const D3D12_RANGE write_range = {0, buffer_size};
388
resource->Unmap(0, &write_range);
389
390
// Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
391
// This adds the reference needed to keep the buffer alive.
392
ID3D12Resource* ret = resource.Get();
393
D3D12Device::GetInstance().DeferResourceDestruction(std::move(allocation), std::move(resource));
394
return ret;
395
}
396
397
bool D3D12Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer, u32 level)
398
{
399
DebugAssert(layer < m_layers && level < m_levels);
400
DebugAssert((x + width) <= GetMipWidth(level) && (y + height) <= GetMipHeight(level));
401
402
D3D12Device& dev = D3D12Device::GetInstance();
403
D3D12StreamBuffer& sbuffer = dev.GetTextureUploadBuffer();
404
405
const u32 upload_pitch = Common::AlignUpPow2<u32>(CalcUploadPitch(width), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
406
const u32 required_size = CalcUploadSize(height, upload_pitch);
407
408
D3D12_TEXTURE_COPY_LOCATION srcloc;
409
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
410
srcloc.PlacedFootprint.Footprint.Width = width;
411
srcloc.PlacedFootprint.Footprint.Height = height;
412
srcloc.PlacedFootprint.Footprint.Depth = 1;
413
srcloc.PlacedFootprint.Footprint.Format = m_dxgi_format;
414
srcloc.PlacedFootprint.Footprint.RowPitch = upload_pitch;
415
416
// If the texture is larger than half our streaming buffer size, use a separate buffer.
417
// Otherwise allocation will either fail, or require lots of cmdbuffer submissions.
418
if (required_size > (sbuffer.GetSize() / 2))
419
{
420
srcloc.pResource = AllocateUploadStagingBuffer(data, pitch, upload_pitch, width, height, required_size);
421
if (!srcloc.pResource)
422
return false;
423
424
srcloc.PlacedFootprint.Offset = 0;
425
}
426
else
427
{
428
if (!sbuffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) [[unlikely]]
429
{
430
D3D12Device::GetInstance().SubmitCommandList(
431
false, TinyString::from_format("Needs {} bytes in texture upload buffer", required_size));
432
if (!sbuffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) [[unlikely]]
433
{
434
ERROR_LOG("Failed to reserve texture upload memory ({} bytes).", required_size);
435
return false;
436
}
437
}
438
439
srcloc.pResource = sbuffer.GetBuffer();
440
srcloc.PlacedFootprint.Offset = sbuffer.GetCurrentOffset();
441
CopyTextureDataForUpload(width, height, m_format, sbuffer.GetCurrentHostPointer(), upload_pitch, data, pitch);
442
sbuffer.CommitMemory(required_size);
443
}
444
445
ID3D12GraphicsCommandList4* cmdlist = GetCommandBufferForUpdate();
446
447
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
448
if (m_type == Type::RenderTarget)
449
{
450
if (x != 0 || y != 0 || width != m_width || height != m_height)
451
CommitClear(cmdlist);
452
else
453
m_state = State::Dirty;
454
}
455
456
GPUDevice::GetStatistics().buffer_streamed += required_size;
457
GPUDevice::GetStatistics().num_uploads++;
458
459
// first time the texture is used? don't leave it undefined
460
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
461
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
462
else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
463
TransitionSubresourceToState(cmdlist, layer, level, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST);
464
465
D3D12_TEXTURE_COPY_LOCATION dstloc;
466
dstloc.pResource = m_resource.Get();
467
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
468
dstloc.SubresourceIndex = layer;
469
470
const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1u};
471
cmdlist->CopyTextureRegion(&dstloc, x, y, 0, &srcloc, &srcbox);
472
473
if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
474
TransitionSubresourceToState(cmdlist, layer, level, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state);
475
476
return true;
477
}
478
479
bool D3D12Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer, u32 level)
480
{
481
// TODO: linear textures for dynamic?
482
if ((x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || layer > m_layers || level > m_levels)
483
{
484
return false;
485
}
486
487
D3D12Device& dev = D3D12Device::GetInstance();
488
if (m_state == State::Cleared && (x != 0 || y != 0 || width != m_width || height != m_height))
489
CommitClear(GetCommandBufferForUpdate());
490
491
// see note in Update() for the reason why.
492
const u32 aligned_pitch = Common::AlignUpPow2(CalcUploadPitch(m_width), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
493
const u32 req_size = CalcUploadSize(m_height, aligned_pitch);
494
D3D12StreamBuffer& buffer = dev.GetTextureUploadBuffer();
495
if (req_size >= (buffer.GetSize() / 2))
496
return false;
497
498
if (!buffer.ReserveMemory(req_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) [[unlikely]]
499
{
500
dev.SubmitCommandList(false, TinyString::from_format("Needs {} bytes in texture upload buffer", req_size));
501
if (!buffer.ReserveMemory(req_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) [[unlikely]]
502
Panic("Failed to reserve texture upload memory");
503
}
504
505
// map for writing
506
*map = buffer.GetCurrentHostPointer();
507
*map_stride = aligned_pitch;
508
m_map_x = static_cast<u16>(x);
509
m_map_y = static_cast<u16>(y);
510
m_map_width = static_cast<u16>(width);
511
m_map_height = static_cast<u16>(height);
512
m_map_layer = static_cast<u8>(layer);
513
m_map_level = static_cast<u8>(level);
514
m_state = State::Dirty;
515
return true;
516
}
517
518
void D3D12Texture::Unmap()
519
{
520
D3D12Device& dev = D3D12Device::GetInstance();
521
D3D12StreamBuffer& sb = dev.GetTextureUploadBuffer();
522
const u32 aligned_pitch = Common::AlignUpPow2(CalcUploadPitch(m_width), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
523
const u32 req_size = CalcUploadSize(m_map_height, aligned_pitch);
524
const u32 offset = sb.GetCurrentOffset();
525
sb.CommitMemory(req_size);
526
527
GPUDevice::GetStatistics().buffer_streamed += req_size;
528
GPUDevice::GetStatistics().num_uploads++;
529
530
ID3D12GraphicsCommandList4* cmdlist = GetCommandBufferForUpdate();
531
532
// first time the texture is used? don't leave it undefined
533
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
534
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
535
else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
536
TransitionSubresourceToState(cmdlist, m_map_layer, m_map_level, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST);
537
538
D3D12_TEXTURE_COPY_LOCATION srcloc;
539
srcloc.pResource = sb.GetBuffer();
540
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
541
srcloc.PlacedFootprint.Offset = offset;
542
srcloc.PlacedFootprint.Footprint.Width = m_map_width;
543
srcloc.PlacedFootprint.Footprint.Height = m_map_height;
544
srcloc.PlacedFootprint.Footprint.Depth = 1;
545
srcloc.PlacedFootprint.Footprint.Format = m_dxgi_format;
546
srcloc.PlacedFootprint.Footprint.RowPitch = aligned_pitch;
547
548
D3D12_TEXTURE_COPY_LOCATION dstloc;
549
dstloc.pResource = m_resource.Get();
550
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
551
dstloc.SubresourceIndex = m_map_level;
552
553
const D3D12_BOX srcbox{0u, 0u, 0u, m_map_width, m_map_height, 1};
554
cmdlist->CopyTextureRegion(&dstloc, m_map_x, m_map_y, 0, &srcloc, &srcbox);
555
556
if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
557
TransitionSubresourceToState(cmdlist, m_map_layer, m_map_level, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state);
558
559
m_map_x = 0;
560
m_map_y = 0;
561
m_map_width = 0;
562
m_map_height = 0;
563
m_map_layer = 0;
564
m_map_level = 0;
565
}
566
567
void D3D12Texture::GenerateMipmaps()
568
{
569
for (u32 layer = 0; layer < m_layers; layer++)
570
{
571
for (u32 dst_level = 1; dst_level < m_levels; dst_level++)
572
{
573
const u32 src_level = dst_level - 1;
574
const u32 src_width = std::max<u32>(m_width >> src_level, 1u);
575
const u32 src_height = std::max<u32>(m_height >> src_level, 1u);
576
const u32 dst_width = std::max<u32>(m_width >> dst_level, 1u);
577
const u32 dst_height = std::max<u32>(m_height >> dst_level, 1u);
578
579
D3D12Device::GetInstance().RenderTextureMipmap(this, dst_level, dst_width, dst_height, src_level, src_width,
580
src_height);
581
}
582
}
583
584
SetUseFenceValue(D3D12Device::GetInstance().GetCurrentFenceValue());
585
}
586
587
void D3D12Texture::CommitClear()
588
{
589
if (m_state != GPUTexture::State::Cleared)
590
return;
591
592
D3D12Device& dev = D3D12Device::GetInstance();
593
if (dev.InRenderPass())
594
dev.EndRenderPass();
595
596
ActuallyCommitClear(dev.GetCommandList());
597
}
598
599
void D3D12Texture::CommitClear(ID3D12GraphicsCommandList* cmdlist)
600
{
601
if (m_state != GPUTexture::State::Cleared)
602
return;
603
604
ActuallyCommitClear(cmdlist);
605
}
606
607
void D3D12Texture::ActuallyCommitClear(ID3D12GraphicsCommandList* cmdlist)
608
{
609
if (IsDepthStencil())
610
{
611
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
612
cmdlist->ClearDepthStencilView(GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
613
}
614
else
615
{
616
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
617
cmdlist->ClearRenderTargetView(GetWriteDescriptor(), D3D12Device::RGBA8ToFloat(m_clear_value.color).data(), 0,
618
nullptr);
619
}
620
621
SetState(State::Dirty);
622
}
623
624
#ifdef ENABLE_GPU_OBJECT_NAMES
625
626
void D3D12Texture::SetDebugName(std::string_view name)
627
{
628
D3D12::SetObjectName(m_resource.Get(), name);
629
}
630
631
#endif
632
633
u32 D3D12Texture::CalculateSubresource(u32 layer, u32 level, u32 num_levels)
634
{
635
// D3D11CalcSubresource
636
return level + layer * num_levels;
637
}
638
639
u32 D3D12Texture::CalculateSubresource(u32 layer, u32 level) const
640
{
641
return CalculateSubresource(layer, level, m_levels);
642
}
643
644
void D3D12Texture::TransitionToState(D3D12_RESOURCE_STATES state)
645
{
646
TransitionToState(D3D12Device::GetInstance().GetCommandList(), state);
647
}
648
649
void D3D12Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
650
{
651
if (m_resource_state == state)
652
return;
653
654
const D3D12_RESOURCE_STATES prev_state = m_resource_state;
655
m_resource_state = state;
656
657
const D3D12_RESOURCE_BARRIER barrier = {
658
D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
659
D3D12_RESOURCE_BARRIER_FLAG_NONE,
660
{{m_resource.Get(), D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, prev_state, state}}};
661
cmdlist->ResourceBarrier(1, &barrier);
662
}
663
664
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 layer, u32 level,
665
D3D12_RESOURCE_STATES before_state,
666
D3D12_RESOURCE_STATES after_state) const
667
{
668
TransitionSubresourceToState(cmdlist, m_resource.Get(), CalculateSubresource(layer, level), before_state,
669
after_state);
670
}
671
672
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 subresource,
673
D3D12_RESOURCE_STATES before_state,
674
D3D12_RESOURCE_STATES after_state) const
675
{
676
TransitionSubresourceToState(cmdlist, m_resource.Get(), subresource, before_state, after_state);
677
}
678
679
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource,
680
u32 subresource, D3D12_RESOURCE_STATES before_state,
681
D3D12_RESOURCE_STATES after_state)
682
{
683
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
684
D3D12_RESOURCE_BARRIER_FLAG_NONE,
685
{{resource, subresource, before_state, after_state}}};
686
cmdlist->ResourceBarrier(1, &barrier);
687
}
688
689
void D3D12Texture::MakeReadyForSampling()
690
{
691
if (m_resource_state == D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE && m_state != State::Cleared)
692
return;
693
694
ID3D12GraphicsCommandList4* const cmdlist = GetCommandBufferForUpdate();
695
CommitClear(cmdlist);
696
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
697
}
698
699
D3D12Sampler::D3D12Sampler(D3D12DescriptorHandle descriptor) : m_descriptor(descriptor)
700
{
701
}
702
703
D3D12Sampler::~D3D12Sampler()
704
{
705
D3D12Device& dev = D3D12Device::GetInstance();
706
dev.DeferDescriptorDestruction(dev.GetSamplerHeapManager(), &m_descriptor);
707
}
708
709
#ifdef ENABLE_GPU_OBJECT_NAMES
710
711
void D3D12Sampler::SetDebugName(std::string_view name)
712
{
713
}
714
715
#endif
716
717
D3D12_SAMPLER_DESC D3D12Sampler::GetD3DSamplerDesc(const GPUSampler::Config& config)
718
{
719
static constexpr std::array<D3D12_TEXTURE_ADDRESS_MODE, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{
720
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // Repeat
721
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // ClampToEdge
722
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // ClampToBorder
723
D3D12_TEXTURE_ADDRESS_MODE_MIRROR, // MirrorRepeat
724
}};
725
726
static constexpr u8 filter_count = static_cast<u8>(GPUSampler::Filter::MaxCount);
727
static constexpr D3D12_FILTER filters[filter_count][filter_count][filter_count] = {
728
{
729
{D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT},
730
{D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT, D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT},
731
},
732
{
733
{D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR, D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR},
734
{D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, D3D12_FILTER_MIN_MAG_MIP_LINEAR},
735
}};
736
737
D3D12_SAMPLER_DESC desc = {};
738
desc.AddressU = ta[static_cast<u8>(config.address_u.GetValue())];
739
desc.AddressV = ta[static_cast<u8>(config.address_v.GetValue())];
740
desc.AddressW = ta[static_cast<u8>(config.address_w.GetValue())];
741
std::memcpy(desc.BorderColor, GPUDevice::RGBA8ToFloat(config.border_color).data(), sizeof(desc.BorderColor));
742
desc.MinLOD = static_cast<float>(config.min_lod);
743
desc.MaxLOD = static_cast<float>(config.max_lod);
744
745
if (config.anisotropy > 1)
746
{
747
desc.Filter = D3D12_FILTER_ANISOTROPIC;
748
desc.MaxAnisotropy = config.anisotropy;
749
}
750
else
751
{
752
desc.Filter = filters[static_cast<u8>(config.mip_filter.GetValue())][static_cast<u8>(config.min_filter.GetValue())]
753
[static_cast<u8>(config.mag_filter.GetValue())];
754
desc.MaxAnisotropy = 1;
755
}
756
757
return desc;
758
}
759
760
std::unique_ptr<GPUSampler> D3D12Device::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */)
761
{
762
D3D12DescriptorHandle handle;
763
if (m_sampler_heap_manager.Allocate(&handle)) [[likely]]
764
{
765
const D3D12_SAMPLER_DESC desc = D3D12Sampler::GetD3DSamplerDesc(config);
766
m_device->CreateSampler(&desc, handle);
767
return std::unique_ptr<GPUSampler>(new D3D12Sampler(handle));
768
}
769
else
770
{
771
Error::SetStringView(error, "Failed to allocate sampler handle.");
772
return {};
773
}
774
}
775
776
D3D12TextureBuffer::D3D12TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements)
777
{
778
}
779
780
D3D12TextureBuffer::~D3D12TextureBuffer()
781
{
782
Destroy(true);
783
}
784
785
bool D3D12TextureBuffer::Create(D3D12Device& dev, Error* error)
786
{
787
static constexpr std::array<DXGI_FORMAT, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{
788
DXGI_FORMAT_R16_UINT, // R16UI
789
}};
790
791
if (!m_buffer.Create(GetSizeInBytes(), error)) [[unlikely]]
792
return false;
793
794
if (!dev.GetDescriptorHeapManager().Allocate(&m_descriptor)) [[unlikely]]
795
{
796
Error::SetStringView(error, "Failed to allocate descriptor.");
797
return {};
798
}
799
800
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format_mapping[static_cast<u8>(m_format)],
801
D3D12_SRV_DIMENSION_BUFFER,
802
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
803
{}};
804
desc.Buffer.NumElements = m_size_in_elements;
805
dev.GetDevice()->CreateShaderResourceView(m_buffer.GetBuffer(), &desc, m_descriptor);
806
return true;
807
}
808
809
void D3D12TextureBuffer::Destroy(bool defer)
810
{
811
D3D12Device& dev = D3D12Device::GetInstance();
812
if (m_descriptor)
813
{
814
if (defer)
815
dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_descriptor);
816
else
817
dev.GetDescriptorHeapManager().Free(&m_descriptor);
818
}
819
}
820
821
void* D3D12TextureBuffer::Map(u32 required_elements)
822
{
823
const u32 esize = GetElementSize(m_format);
824
const u32 req_size = esize * required_elements;
825
if (!m_buffer.ReserveMemory(req_size, esize))
826
{
827
D3D12Device::GetInstance().SubmitCommandListAndRestartRenderPass("out of space in texture buffer");
828
if (!m_buffer.ReserveMemory(req_size, esize))
829
Panic("Failed to allocate texture buffer space.");
830
}
831
832
m_current_position = m_buffer.GetCurrentOffset() / esize;
833
return m_buffer.GetCurrentHostPointer();
834
}
835
836
void D3D12TextureBuffer::Unmap(u32 used_elements)
837
{
838
const u32 size = GetElementSize(m_format) * used_elements;
839
GPUDevice::GetStatistics().buffer_streamed += size;
840
GPUDevice::GetStatistics().num_uploads++;
841
m_buffer.CommitMemory(size);
842
}
843
844
#ifdef ENABLE_GPU_OBJECT_NAMES
845
846
void D3D12TextureBuffer::SetDebugName(std::string_view name)
847
{
848
D3D12::SetObjectName(m_buffer.GetBuffer(), name);
849
}
850
851
#endif
852
853
std::unique_ptr<GPUTextureBuffer> D3D12Device::CreateTextureBuffer(GPUTextureBuffer::Format format,
854
u32 size_in_elements, Error* error /* = nullptr */)
855
{
856
857
std::unique_ptr<D3D12TextureBuffer> tb = std::make_unique<D3D12TextureBuffer>(format, size_in_elements);
858
if (!tb->Create(*this, error))
859
tb.reset();
860
861
return tb;
862
}
863
864
D3D12DownloadTexture::D3D12DownloadTexture(u32 width, u32 height, GPUTextureFormat format,
865
ComPtr<D3D12MA::Allocation> allocation, ComPtr<ID3D12Resource> buffer)
866
: GPUDownloadTexture(width, height, format, false), m_allocation(std::move(allocation)), m_buffer(std::move(buffer))
867
{
868
}
869
870
D3D12DownloadTexture::~D3D12DownloadTexture()
871
{
872
if (IsMapped())
873
D3D12DownloadTexture::Unmap();
874
875
if (m_buffer)
876
D3D12Device::GetInstance().DeferResourceDestruction(m_allocation.Get(), m_buffer.Get());
877
}
878
879
std::unique_ptr<D3D12DownloadTexture> D3D12DownloadTexture::Create(u32 width, u32 height, GPUTextureFormat format,
880
Error* error)
881
{
882
const u32 buffer_size = GetBufferSize(width, height, format, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
883
884
D3D12MA::ALLOCATION_DESC allocation_desc = {};
885
allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK;
886
887
const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER,
888
0,
889
buffer_size,
890
1,
891
1,
892
1,
893
DXGI_FORMAT_UNKNOWN,
894
{1, 0},
895
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
896
D3D12_RESOURCE_FLAG_NONE};
897
898
ComPtr<D3D12MA::Allocation> allocation;
899
ComPtr<ID3D12Resource> buffer;
900
901
const HRESULT hr = D3D12Device::GetInstance().GetAllocator()->CreateResource(
902
&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, allocation.GetAddressOf(),
903
IID_PPV_ARGS(buffer.GetAddressOf()));
904
if (FAILED(hr))
905
{
906
Error::SetHResult(error, "CreateResource() failed: ", hr);
907
return {};
908
}
909
910
return std::unique_ptr<D3D12DownloadTexture>(
911
new D3D12DownloadTexture(width, height, format, std::move(allocation), std::move(buffer)));
912
}
913
914
void D3D12DownloadTexture::CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width,
915
u32 height, u32 src_layer, u32 src_level, bool use_transfer_pitch)
916
{
917
D3D12Texture* const src12 = static_cast<D3D12Texture*>(src);
918
D3D12Device& dev = D3D12Device::GetInstance();
919
920
DebugAssert(src12->GetFormat() == m_format);
921
DebugAssert(src_level < src12->GetLevels());
922
DebugAssert((src_x + width) <= src12->GetMipWidth(src_level) && (src_y + height) <= src12->GetMipHeight(src_level));
923
DebugAssert((dst_x + width) <= m_width && (dst_y + height) <= m_height);
924
DebugAssert((dst_x == 0 && dst_y == 0) || !use_transfer_pitch);
925
926
u32 copy_offset, copy_size, copy_rows;
927
m_current_pitch = GetTransferPitch(use_transfer_pitch ? width : m_width, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
928
GetTransferSize(dst_x, dst_y, width, height, m_current_pitch, &copy_offset, &copy_size, &copy_rows);
929
930
dev.GetStatistics().num_downloads++;
931
if (dev.InRenderPass())
932
dev.EndRenderPass();
933
src12->CommitClear();
934
935
if (IsMapped())
936
Unmap();
937
938
ID3D12GraphicsCommandList* cmdlist = dev.GetCommandList();
939
GL_INS_FMT("ReadbackTexture: {{{},{}}} {}x{} => {{{},{}}}", src_x, src_y, width, height, dst_x, dst_y);
940
941
D3D12_TEXTURE_COPY_LOCATION srcloc;
942
srcloc.pResource = src12->GetResource();
943
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
944
srcloc.SubresourceIndex = src12->CalculateSubresource(src_layer, src_level);
945
946
D3D12_TEXTURE_COPY_LOCATION dstloc;
947
dstloc.pResource = m_buffer.Get();
948
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
949
dstloc.PlacedFootprint.Offset = copy_offset;
950
dstloc.PlacedFootprint.Footprint.Format = src12->GetDXGIFormat();
951
dstloc.PlacedFootprint.Footprint.Width = width;
952
dstloc.PlacedFootprint.Footprint.Height = height;
953
dstloc.PlacedFootprint.Footprint.Depth = 1;
954
dstloc.PlacedFootprint.Footprint.RowPitch = m_current_pitch;
955
956
const D3D12_RESOURCE_STATES old_layout = src12->GetResourceState();
957
if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE)
958
src12->TransitionSubresourceToState(cmdlist, src_level, old_layout, D3D12_RESOURCE_STATE_COPY_SOURCE);
959
960
// TODO: Rules for depth buffers here?
961
const D3D12_BOX srcbox{static_cast<UINT>(src_x), static_cast<UINT>(src_y), 0u,
962
static_cast<UINT>(src_x + width), static_cast<UINT>(src_y + height), 1u};
963
cmdlist->CopyTextureRegion(&dstloc, 0, 0, 0, &srcloc, &srcbox);
964
965
if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE)
966
src12->TransitionSubresourceToState(cmdlist, src_level, D3D12_RESOURCE_STATE_COPY_SOURCE, old_layout);
967
968
m_copy_fence_value = dev.GetCurrentFenceValue();
969
m_needs_flush = true;
970
}
971
972
bool D3D12DownloadTexture::Map(u32 x, u32 y, u32 width, u32 height)
973
{
974
if (IsMapped())
975
return true;
976
977
// Never populated?
978
if (!m_current_pitch)
979
return false;
980
981
u32 copy_offset, copy_size, copy_rows;
982
GetTransferSize(x, y, width, height, m_current_pitch, &copy_offset, &copy_size, &copy_rows);
983
984
const D3D12_RANGE read_range{copy_offset, copy_offset + m_current_pitch * copy_rows};
985
const HRESULT hr = m_buffer->Map(0, &read_range, reinterpret_cast<void**>(const_cast<u8**>(&m_map_pointer)));
986
if (FAILED(hr))
987
{
988
ERROR_LOG("Map() failed with HRESULT {:08X}", hr);
989
return false;
990
}
991
992
return true;
993
}
994
995
void D3D12DownloadTexture::Unmap()
996
{
997
if (!IsMapped())
998
return;
999
1000
const D3D12_RANGE write_range = {};
1001
m_buffer->Unmap(0, &write_range);
1002
m_map_pointer = nullptr;
1003
}
1004
1005
void D3D12DownloadTexture::Flush()
1006
{
1007
if (!m_needs_flush)
1008
return;
1009
1010
m_needs_flush = false;
1011
1012
D3D12Device& dev = D3D12Device::GetInstance();
1013
if (dev.GetCompletedFenceValue() >= m_copy_fence_value)
1014
return;
1015
1016
// Need to execute command buffer.
1017
if (dev.GetCurrentFenceValue() == m_copy_fence_value)
1018
{
1019
if (dev.InRenderPass())
1020
dev.EndRenderPass();
1021
dev.SubmitCommandList(true);
1022
}
1023
else
1024
{
1025
dev.WaitForFence(m_copy_fence_value);
1026
}
1027
}
1028
1029
#ifdef ENABLE_GPU_OBJECT_NAMES
1030
1031
void D3D12DownloadTexture::SetDebugName(std::string_view name)
1032
{
1033
if (name.empty())
1034
return;
1035
1036
D3D12::SetObjectName(m_buffer.Get(), name);
1037
}
1038
1039
#endif
1040
1041
std::unique_ptr<GPUDownloadTexture> D3D12Device::CreateDownloadTexture(u32 width, u32 height, GPUTextureFormat format,
1042
Error* error /* = nullptr */)
1043
{
1044
return D3D12DownloadTexture::Create(width, height, format, error);
1045
}
1046
1047
std::unique_ptr<GPUDownloadTexture> D3D12Device::CreateDownloadTexture(u32 width, u32 height, GPUTextureFormat format,
1048
void* memory, size_t memory_size,
1049
u32 memory_stride, Error* error /* = nullptr */)
1050
{
1051
Error::SetStringView(error, "D3D12 cannot import memory for download textures");
1052
return {};
1053
}
1054
1055