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