diff --git a/Source/Core/VideoBackends/D3D12/BoundingBox.cpp b/Source/Core/VideoBackends/D3D12/BoundingBox.cpp index d847924cf2..48cb3ce613 100644 --- a/Source/Core/VideoBackends/D3D12/BoundingBox.cpp +++ b/Source/Core/VideoBackends/D3D12/BoundingBox.cpp @@ -2,43 +2,160 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include + #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" #include "VideoBackends/D3D12/BoundingBox.h" +#include "VideoBackends/D3D12/D3DBase.h" +#include "VideoBackends/D3D12/D3DCommandListManager.h" +#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h" +#include "VideoBackends/D3D12/D3DStreamBuffer.h" +#include "VideoBackends/D3D12/D3DUtil.h" +#include "VideoBackends/D3D12/FramebufferManager.h" +#include "VideoBackends/D3D12/Render.h" #include "VideoCommon/VideoConfig.h" -// D3D12TODO: Support bounding box behavior. namespace DX12 { -ID3D11UnorderedAccessView* BBox::GetUAV() -{ - // D3D12TODO: Implement this; - return nullptr; -} +constexpr size_t BBOX_BUFFER_SIZE = sizeof(int) * 4; +constexpr size_t BBOX_STREAM_BUFFER_SIZE = BBOX_BUFFER_SIZE * 128; + +static ID3D12Resource* s_bbox_buffer; +static ID3D12Resource* s_bbox_staging_buffer; +static void* s_bbox_staging_buffer_map; +static std::unique_ptr s_bbox_stream_buffer; +static D3D12_GPU_DESCRIPTOR_HANDLE s_bbox_descriptor_handle; void BBox::Init() { - if (g_ActiveConfig.backend_info.bSupportsBBox) - { - // D3D12TODO: Implement this; - } + if (!g_ActiveConfig.backend_info.bSupportsBBox) + return; + + CD3DX12_RESOURCE_DESC buffer_desc(CD3DX12_RESOURCE_DESC::Buffer(BBOX_BUFFER_SIZE, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, 0)); + CD3DX12_RESOURCE_DESC staging_buffer_desc(CD3DX12_RESOURCE_DESC::Buffer(BBOX_BUFFER_SIZE, D3D12_RESOURCE_FLAG_NONE, 0)); + + CheckHR(D3D::device12->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), + D3D12_HEAP_FLAG_NONE, + &buffer_desc, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + nullptr, + IID_PPV_ARGS(&s_bbox_buffer))); + + CheckHR(D3D::device12->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK), + D3D12_HEAP_FLAG_NONE, + &staging_buffer_desc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(&s_bbox_staging_buffer))); + + s_bbox_stream_buffer = std::make_unique(BBOX_STREAM_BUFFER_SIZE, BBOX_STREAM_BUFFER_SIZE, nullptr); + + // D3D12 root signature UAV must be raw or structured buffers, not typed. Since we used a typed buffer, + // we have to use a descriptor table. Luckily, we only have to allocate this once, and it never changes. + D3D12_CPU_DESCRIPTOR_HANDLE cpu_descriptor_handle; + if (!D3D::gpu_descriptor_heap_mgr->Allocate(&cpu_descriptor_handle, &s_bbox_descriptor_handle, nullptr, false)) + PanicAlert("Failed to create bounding box UAV descriptor"); + + D3D12_UNORDERED_ACCESS_VIEW_DESC view_desc = { DXGI_FORMAT_R32_SINT, D3D12_UAV_DIMENSION_BUFFER }; + view_desc.Buffer.FirstElement = 0; + view_desc.Buffer.NumElements = 4; + view_desc.Buffer.StructureByteStride = 0; + view_desc.Buffer.CounterOffsetInBytes = 0; + view_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; + D3D::device12->CreateUnorderedAccessView(s_bbox_buffer, nullptr, &view_desc, cpu_descriptor_handle); + + Bind(); +} + +void BBox::Bind() +{ + if (s_bbox_buffer) + D3D::current_command_list->SetGraphicsRootDescriptorTable(DESCRIPTOR_TABLE_PS_UAV, s_bbox_descriptor_handle); +} + +void BBox::Invalidate() +{ + if (!s_bbox_staging_buffer_map) + return; + + s_bbox_staging_buffer->Unmap(0, nullptr); + s_bbox_staging_buffer_map = nullptr; } void BBox::Shutdown() { - // D3D12TODO: Implement this; + Invalidate(); + + if (s_bbox_buffer) + { + D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(s_bbox_buffer); + s_bbox_buffer = nullptr; + } + + if (s_bbox_staging_buffer) + { + D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(s_bbox_staging_buffer); + s_bbox_staging_buffer = nullptr; + } + + s_bbox_stream_buffer.reset(); } void BBox::Set(int index, int value) { - // D3D12TODO: Implement this; + // If the buffer is currently mapped, compare the value, and update the staging buffer. + if (s_bbox_staging_buffer_map) + { + int current_value; + memcpy(¤t_value, reinterpret_cast(s_bbox_staging_buffer_map) + (index * sizeof(int)), sizeof(int)); + if (current_value == value) + { + // Value hasn't changed. So skip updating completely. + return; + } + + memcpy(reinterpret_cast(s_bbox_staging_buffer_map) + (index * sizeof(int)), &value, sizeof(int)); + } + + if (s_bbox_stream_buffer->AllocateSpaceInBuffer(sizeof(int), sizeof(int))) + { + // Command list was executed, reset state + g_renderer->SetViewport(); + FramebufferManager::RestoreEFBRenderTargets(); + } + + // Allocate temporary bytes in upload buffer, then copy to real buffer. + memcpy(s_bbox_stream_buffer->GetCPUAddressOfCurrentAllocation(), &value, sizeof(int)); + D3D::ResourceBarrier(D3D::current_command_list, s_bbox_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST, 0); + D3D::current_command_list->CopyBufferRegion(s_bbox_buffer, index * sizeof(int), s_bbox_stream_buffer->GetBuffer(), s_bbox_stream_buffer->GetOffsetOfCurrentAllocation(), sizeof(int)); + D3D::ResourceBarrier(D3D::current_command_list, s_bbox_buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, 0); } int BBox::Get(int index) { - // D3D12TODO: Implement this; - return 0; + if (!s_bbox_staging_buffer_map) + { + D3D::command_list_mgr->CPUAccessNotify(); + + // Copy from real buffer to staging buffer, then block until we have the results. + D3D::ResourceBarrier(D3D::current_command_list, s_bbox_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE, 0); + D3D::current_command_list->CopyBufferRegion(s_bbox_staging_buffer, 0, s_bbox_buffer, 0, BBOX_BUFFER_SIZE); + D3D::ResourceBarrier(D3D::current_command_list, s_bbox_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, 0); + + D3D::command_list_mgr->ExecuteQueuedWork(true); + g_renderer->SetViewport(); + FramebufferManager::RestoreEFBRenderTargets(); + + CheckHR(s_bbox_staging_buffer->Map(0, nullptr, &s_bbox_staging_buffer_map)); + } + + int value; + memcpy(&value, &reinterpret_cast(s_bbox_staging_buffer_map)[index], sizeof(int)); + return value; } }; diff --git a/Source/Core/VideoBackends/D3D12/BoundingBox.h b/Source/Core/VideoBackends/D3D12/BoundingBox.h index 05126810db..5a762e7b59 100644 --- a/Source/Core/VideoBackends/D3D12/BoundingBox.h +++ b/Source/Core/VideoBackends/D3D12/BoundingBox.h @@ -11,8 +11,9 @@ namespace DX12 class BBox { public: - static ID3D11UnorderedAccessView* GetUAV(); static void Init(); + static void Bind(); + static void Invalidate(); static void Shutdown(); static void Set(int index, int value); diff --git a/Source/Core/VideoBackends/D3D12/D3DBase.cpp b/Source/Core/VideoBackends/D3D12/D3DBase.cpp index 637b11b059..a2f2d06fac 100644 --- a/Source/Core/VideoBackends/D3D12/D3DBase.cpp +++ b/Source/Core/VideoBackends/D3D12/D3DBase.cpp @@ -637,7 +637,15 @@ void CreateRootSignatures() D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart; }; - D3D12_ROOT_PARAMETER root_parameters[6]; + D3D12_DESCRIPTOR_RANGE desc_range_uav = { + D3D12_DESCRIPTOR_RANGE_TYPE_UAV, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType; + 1, // UINT NumDescriptors; + 2, // UINT BaseShaderRegister; + 0, // UINT RegisterSpace; + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart; + }; + + D3D12_ROOT_PARAMETER root_parameters[NUM_GRAPHICS_ROOT_PARAMETERS]; root_parameters[DESCRIPTOR_TABLE_PS_SRV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.NumDescriptorRanges = 1; @@ -669,7 +677,10 @@ void CreateRootSignatures() root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.ShaderRegister = 1; root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - // D3D12TODO: Add bounding box UAV to root signature. + root_parameters[DESCRIPTOR_TABLE_PS_UAV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + root_parameters[DESCRIPTOR_TABLE_PS_UAV].DescriptorTable.NumDescriptorRanges = 1; + root_parameters[DESCRIPTOR_TABLE_PS_UAV].DescriptorTable.pDescriptorRanges = &desc_range_uav; + root_parameters[DESCRIPTOR_TABLE_PS_UAV].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {}; root_signature_desc.pParameters = root_parameters; diff --git a/Source/Core/VideoBackends/D3D12/D3DBase.h b/Source/Core/VideoBackends/D3D12/D3DBase.h index 21cd9650ea..7f321121f9 100644 --- a/Source/Core/VideoBackends/D3D12/D3DBase.h +++ b/Source/Core/VideoBackends/D3D12/D3DBase.h @@ -44,17 +44,21 @@ class D3DCommandListManager; class D3DDescriptorHeapManager; class D3DTexture2D; +enum GRAPHICS_ROOT_PARAMETER : u32 +{ + DESCRIPTOR_TABLE_PS_SRV, + DESCRIPTOR_TABLE_PS_SAMPLER, + DESCRIPTOR_TABLE_GS_CBV, + DESCRIPTOR_TABLE_VS_CBV, + DESCRIPTOR_TABLE_PS_CBVONE, + DESCRIPTOR_TABLE_PS_CBVTWO, + DESCRIPTOR_TABLE_PS_UAV, + NUM_GRAPHICS_ROOT_PARAMETERS +}; + namespace D3D { -#define DESCRIPTOR_TABLE_PS_SRV 0 -#define DESCRIPTOR_TABLE_PS_SAMPLER 1 -#define DESCRIPTOR_TABLE_GS_CBV 2 -#define DESCRIPTOR_TABLE_VS_CBV 3 -// #define DESCRIPTOR_TABLE_PS_UAV 4 -#define DESCRIPTOR_TABLE_PS_CBVONE 4 -#define DESCRIPTOR_TABLE_PS_CBVTWO 5 - HRESULT LoadDXGI(); HRESULT LoadD3D(); HRESULT LoadD3DCompiler(); diff --git a/Source/Core/VideoBackends/D3D12/FramebufferManager.cpp b/Source/Core/VideoBackends/D3D12/FramebufferManager.cpp index a28d1cf6ef..a15d55c66b 100644 --- a/Source/Core/VideoBackends/D3D12/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/D3D12/FramebufferManager.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "Core/HW/Memmap.h" +#include "VideoBackends/D3D12/BoundingBox.h" #include "VideoBackends/D3D12/D3DBase.h" #include "VideoBackends/D3D12/D3DCommandListManager.h" #include "VideoBackends/D3D12/D3DUtil.h" @@ -208,6 +209,8 @@ void FramebufferManager::RestoreEFBRenderTargets() D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12()); + + BBox::Bind(); } u32 FramebufferManager::ReadEFBColorAccessCopy(u32 x, u32 y) diff --git a/Source/Core/VideoBackends/D3D12/Render.cpp b/Source/Core/VideoBackends/D3D12/Render.cpp index 7d30f68061..c0fad9208a 100644 --- a/Source/Core/VideoBackends/D3D12/Render.cpp +++ b/Source/Core/VideoBackends/D3D12/Render.cpp @@ -735,6 +735,7 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height // Invalidate EFB access copies. Not strictly necessary, but this avoids having the buffers mapped when calling Present(). FramebufferManager::InvalidateEFBAccessCopies(); + BBox::Invalidate(); // Prepare to copy the XFBs to our backbuffer UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); diff --git a/Source/Core/VideoBackends/D3D12/VertexManager.cpp b/Source/Core/VideoBackends/D3D12/VertexManager.cpp index ce0dba472b..ba413316ac 100644 --- a/Source/Core/VideoBackends/D3D12/VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D12/VertexManager.cpp @@ -137,10 +137,7 @@ void VertexManager::vFlush(bool use_dst_alpha) ShaderCache::LoadAndSetActiveShaders(use_dst_alpha ? DSTALPHA_DUAL_SOURCE_BLEND : DSTALPHA_NONE, current_primitive_type); if (g_ActiveConfig.backend_info.bSupportsBBox && BoundingBox::active) - { - // D3D12TODO: Support GPU-side bounding box. - // D3D::context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, nullptr, nullptr, 2, 1, &BBox::GetUAV(), nullptr); - } + BBox::Invalidate(); u32 stride = VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(); diff --git a/Source/Core/VideoBackends/D3D12/main.cpp b/Source/Core/VideoBackends/D3D12/main.cpp index 8b045ba5b7..636723458a 100644 --- a/Source/Core/VideoBackends/D3D12/main.cpp +++ b/Source/Core/VideoBackends/D3D12/main.cpp @@ -116,7 +116,7 @@ void InitBackendInfo() g_Config.backend_info.bSupportsEarlyZ = shader_model_5_supported; // Requires full UAV functionality (only available in shader model 5) - g_Config.backend_info.bSupportsBBox = false; + g_Config.backend_info.bSupportsBBox = shader_model_5_supported; // Requires the instance attribute (only available in shader model 5) g_Config.backend_info.bSupportsGSInstancing = shader_model_5_supported;