Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d12_texture.cpp
4214 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, Format format,
21
Flags flags, DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource,
22
ComPtr<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
23
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& uav_descriptor,
24
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
25
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
26
static_cast<u8>(samples), type, format, flags),
27
m_resource(std::move(resource)), m_allocation(std::move(allocation)), m_srv_descriptor(srv_descriptor),
28
m_write_descriptor(write_descriptor), m_uav_descriptor(uav_descriptor), m_dxgi_format(dxgi_format),
29
m_resource_state(resource_state), m_write_descriptor_type(wdtype)
30
{
31
}
32
33
D3D12Texture::~D3D12Texture()
34
{
35
Destroy(true);
36
}
37
38
std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
39
GPUTexture::Type type, GPUTexture::Format format,
40
GPUTexture::Flags flags, const void* data /* = nullptr */,
41
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
42
{
43
if (!GPUTexture::ValidateConfig(width, height, layers, levels, samples, type, format, flags, error))
44
return {};
45
46
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format);
47
48
D3D12_RESOURCE_DESC desc = {};
49
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
50
desc.Width = width;
51
desc.Height = height;
52
desc.DepthOrArraySize = 1;
53
desc.MipLevels = static_cast<u8>(levels);
54
desc.Format = fm.resource_format;
55
desc.SampleDesc.Count = samples;
56
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
57
58
D3D12MA::ALLOCATION_DESC allocationDesc = {};
59
allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET;
60
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
61
62
D3D12_CLEAR_VALUE optimized_clear_value = {};
63
D3D12_RESOURCE_STATES state;
64
65
switch (type)
66
{
67
case GPUTexture::Type::Texture:
68
{
69
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
70
state = D3D12_RESOURCE_STATE_COPY_DEST;
71
}
72
break;
73
74
case GPUTexture::Type::RenderTarget:
75
{
76
// RT's tend to be larger, so we'll keep them committed for speed.
77
DebugAssert(levels == 1);
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
Panic("Not implemented");
570
571
for (u32 layer = 0; layer < m_layers; layer++)
572
{
573
for (u32 dst_level = 1; dst_level < m_levels; dst_level++)
574
{
575
const u32 src_level = dst_level - 1;
576
const u32 src_width = std::max<u32>(m_width >> src_level, 1u);
577
const u32 src_height = std::max<u32>(m_height >> src_level, 1u);
578
const u32 dst_width = std::max<u32>(m_width >> dst_level, 1u);
579
const u32 dst_height = std::max<u32>(m_height >> dst_level, 1u);
580
581
D3D12Device::GetInstance().RenderTextureMipmap(this, dst_level, dst_width, dst_height, src_level, src_width,
582
src_height);
583
}
584
}
585
586
SetUseFenceValue(D3D12Device::GetInstance().GetCurrentFenceValue());
587
}
588
589
void D3D12Texture::CommitClear()
590
{
591
if (m_state != GPUTexture::State::Cleared)
592
return;
593
594
D3D12Device& dev = D3D12Device::GetInstance();
595
if (dev.InRenderPass())
596
dev.EndRenderPass();
597
598
ActuallyCommitClear(dev.GetCommandList());
599
}
600
601
void D3D12Texture::CommitClear(ID3D12GraphicsCommandList* cmdlist)
602
{
603
if (m_state != GPUTexture::State::Cleared)
604
return;
605
606
ActuallyCommitClear(cmdlist);
607
}
608
609
void D3D12Texture::ActuallyCommitClear(ID3D12GraphicsCommandList* cmdlist)
610
{
611
if (IsDepthStencil())
612
{
613
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
614
cmdlist->ClearDepthStencilView(GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
615
}
616
else
617
{
618
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
619
cmdlist->ClearRenderTargetView(GetWriteDescriptor(), D3D12Device::RGBA8ToFloat(m_clear_value.color).data(), 0,
620
nullptr);
621
}
622
623
SetState(State::Dirty);
624
}
625
626
#ifdef ENABLE_GPU_OBJECT_NAMES
627
628
void D3D12Texture::SetDebugName(std::string_view name)
629
{
630
D3D12::SetObjectName(m_resource.Get(), name);
631
}
632
633
#endif
634
635
u32 D3D12Texture::CalculateSubresource(u32 layer, u32 level, u32 num_levels)
636
{
637
// D3D11CalcSubresource
638
return level + layer * num_levels;
639
}
640
641
u32 D3D12Texture::CalculateSubresource(u32 layer, u32 level) const
642
{
643
return CalculateSubresource(layer, level, m_levels);
644
}
645
646
void D3D12Texture::TransitionToState(D3D12_RESOURCE_STATES state)
647
{
648
TransitionToState(D3D12Device::GetInstance().GetCommandList(), state);
649
}
650
651
void D3D12Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
652
{
653
if (m_resource_state == state)
654
return;
655
656
const D3D12_RESOURCE_STATES prev_state = m_resource_state;
657
m_resource_state = state;
658
659
const D3D12_RESOURCE_BARRIER barrier = {
660
D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
661
D3D12_RESOURCE_BARRIER_FLAG_NONE,
662
{{m_resource.Get(), D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, prev_state, state}}};
663
cmdlist->ResourceBarrier(1, &barrier);
664
}
665
666
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 layer, u32 level,
667
D3D12_RESOURCE_STATES before_state,
668
D3D12_RESOURCE_STATES after_state) const
669
{
670
TransitionSubresourceToState(cmdlist, m_resource.Get(), CalculateSubresource(layer, level), before_state,
671
after_state);
672
}
673
674
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 subresource,
675
D3D12_RESOURCE_STATES before_state,
676
D3D12_RESOURCE_STATES after_state) const
677
{
678
TransitionSubresourceToState(cmdlist, m_resource.Get(), subresource, before_state, after_state);
679
}
680
681
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource,
682
u32 subresource, D3D12_RESOURCE_STATES before_state,
683
D3D12_RESOURCE_STATES after_state)
684
{
685
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
686
D3D12_RESOURCE_BARRIER_FLAG_NONE,
687
{{resource, subresource, before_state, after_state}}};
688
cmdlist->ResourceBarrier(1, &barrier);
689
}
690
691
void D3D12Texture::MakeReadyForSampling()
692
{
693
if (m_resource_state == D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
694
return;
695
696
D3D12Device& dev = D3D12Device::GetInstance();
697
if (dev.InRenderPass())
698
dev.EndRenderPass();
699
700
TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
701
}
702
703
D3D12Sampler::D3D12Sampler(D3D12DescriptorHandle descriptor) : m_descriptor(descriptor)
704
{
705
}
706
707
D3D12Sampler::~D3D12Sampler()
708
{
709
D3D12Device& dev = D3D12Device::GetInstance();
710
dev.DeferDescriptorDestruction(dev.GetSamplerHeapManager(), &m_descriptor);
711
}
712
713
#ifdef ENABLE_GPU_OBJECT_NAMES
714
715
void D3D12Sampler::SetDebugName(std::string_view name)
716
{
717
}
718
719
#endif
720
721
std::unique_ptr<GPUSampler> D3D12Device::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */)
722
{
723
static constexpr std::array<D3D12_TEXTURE_ADDRESS_MODE, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{
724
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // Repeat
725
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // ClampToEdge
726
D3D12_TEXTURE_ADDRESS_MODE_BORDER, // ClampToBorder
727
D3D12_TEXTURE_ADDRESS_MODE_MIRROR, // MirrorRepeat
728
}};
729
730
static constexpr u8 filter_count = static_cast<u8>(GPUSampler::Filter::MaxCount);
731
static constexpr D3D12_FILTER filters[filter_count][filter_count][filter_count] = {
732
{
733
{D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT},
734
{D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT, D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT},
735
},
736
{
737
{D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR, D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR},
738
{D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, D3D12_FILTER_MIN_MAG_MIP_LINEAR},
739
}};
740
741
D3D12_SAMPLER_DESC desc = {};
742
desc.AddressU = ta[static_cast<u8>(config.address_u.GetValue())];
743
desc.AddressV = ta[static_cast<u8>(config.address_v.GetValue())];
744
desc.AddressW = ta[static_cast<u8>(config.address_w.GetValue())];
745
std::memcpy(desc.BorderColor, RGBA8ToFloat(config.border_color).data(), sizeof(desc.BorderColor));
746
desc.MinLOD = static_cast<float>(config.min_lod);
747
desc.MaxLOD = static_cast<float>(config.max_lod);
748
749
if (config.anisotropy > 1)
750
{
751
desc.Filter = D3D12_FILTER_ANISOTROPIC;
752
desc.MaxAnisotropy = config.anisotropy;
753
}
754
else
755
{
756
desc.Filter = filters[static_cast<u8>(config.mip_filter.GetValue())][static_cast<u8>(config.min_filter.GetValue())]
757
[static_cast<u8>(config.mag_filter.GetValue())];
758
desc.MaxAnisotropy = 1;
759
}
760
761
D3D12DescriptorHandle handle;
762
if (m_sampler_heap_manager.Allocate(&handle)) [[likely]]
763
{
764
m_device->CreateSampler(&desc, handle);
765
return std::unique_ptr<GPUSampler>(new D3D12Sampler(handle));
766
}
767
else
768
{
769
Error::SetStringView(error, "Failed to allocate sampler handle.");
770
return {};
771
}
772
}
773
774
D3D12TextureBuffer::D3D12TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements)
775
{
776
}
777
778
D3D12TextureBuffer::~D3D12TextureBuffer()
779
{
780
Destroy(true);
781
}
782
783
bool D3D12TextureBuffer::Create(D3D12Device& dev, Error* error)
784
{
785
static constexpr std::array<DXGI_FORMAT, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{
786
DXGI_FORMAT_R16_UINT, // R16UI
787
}};
788
789
if (!m_buffer.Create(GetSizeInBytes(), error)) [[unlikely]]
790
return false;
791
792
if (!dev.GetDescriptorHeapManager().Allocate(&m_descriptor)) [[unlikely]]
793
{
794
Error::SetStringView(error, "Failed to allocate descriptor.");
795
return {};
796
}
797
798
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format_mapping[static_cast<u8>(m_format)],
799
D3D12_SRV_DIMENSION_BUFFER,
800
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
801
{}};
802
desc.Buffer.NumElements = m_size_in_elements;
803
dev.GetDevice()->CreateShaderResourceView(m_buffer.GetBuffer(), &desc, m_descriptor);
804
return true;
805
}
806
807
void D3D12TextureBuffer::Destroy(bool defer)
808
{
809
D3D12Device& dev = D3D12Device::GetInstance();
810
if (m_descriptor)
811
{
812
if (defer)
813
dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_descriptor);
814
else
815
dev.GetDescriptorHeapManager().Free(&m_descriptor);
816
}
817
}
818
819
void* D3D12TextureBuffer::Map(u32 required_elements)
820
{
821
const u32 esize = GetElementSize(m_format);
822
const u32 req_size = esize * required_elements;
823
if (!m_buffer.ReserveMemory(req_size, esize))
824
{
825
D3D12Device::GetInstance().SubmitCommandListAndRestartRenderPass("out of space in texture buffer");
826
if (!m_buffer.ReserveMemory(req_size, esize))
827
Panic("Failed to allocate texture buffer space.");
828
}
829
830
m_current_position = m_buffer.GetCurrentOffset() / esize;
831
return m_buffer.GetCurrentHostPointer();
832
}
833
834
void D3D12TextureBuffer::Unmap(u32 used_elements)
835
{
836
const u32 size = GetElementSize(m_format) * used_elements;
837
GPUDevice::GetStatistics().buffer_streamed += size;
838
GPUDevice::GetStatistics().num_uploads++;
839
m_buffer.CommitMemory(size);
840
}
841
842
#ifdef ENABLE_GPU_OBJECT_NAMES
843
844
void D3D12TextureBuffer::SetDebugName(std::string_view name)
845
{
846
D3D12::SetObjectName(m_buffer.GetBuffer(), name);
847
}
848
849
#endif
850
851
std::unique_ptr<GPUTextureBuffer> D3D12Device::CreateTextureBuffer(GPUTextureBuffer::Format format,
852
u32 size_in_elements, Error* error /* = nullptr */)
853
{
854
855
std::unique_ptr<D3D12TextureBuffer> tb = std::make_unique<D3D12TextureBuffer>(format, size_in_elements);
856
if (!tb->Create(*this, error))
857
tb.reset();
858
859
return tb;
860
}
861
862
D3D12DownloadTexture::D3D12DownloadTexture(u32 width, u32 height, GPUTexture::Format format,
863
ComPtr<D3D12MA::Allocation> allocation, ComPtr<ID3D12Resource> buffer,
864
size_t buffer_size)
865
: GPUDownloadTexture(width, height, format, false), m_allocation(std::move(allocation)), m_buffer(std::move(buffer)),
866
m_buffer_size(buffer_size)
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, GPUTexture::Format 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), buffer_size));
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, GPUTexture::Format 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, GPUTexture::Format 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