Skip to content

Commit

Permalink
Add support for D3D12 video backend
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Mar 4, 2019
1 parent 2be032b commit 45a3443
Show file tree
Hide file tree
Showing 34 changed files with 5,496 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Source/Core/DolphinQt/DolphinQt.vcxproj
Expand Up @@ -472,6 +472,9 @@
<ProjectReference Include="..\..\..\Externals\imgui\imgui.vcxproj">
<Project>{4c3b2264-ea73-4a7b-9cfe-65b0fd635ebb}</Project>
</ProjectReference>
<ProjectReference Include="..\VideoBackends\D3D12\D3D12.vcxproj">
<Project>{570215b7-e32f-4438-95ae-c8d955f9fca3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
Expand Down
181 changes: 181 additions & 0 deletions Source/Core/VideoBackends/D3D12/BoundingBox.cpp
@@ -0,0 +1,181 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "VideoBackends/D3D12/BoundingBox.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/D3D12/DXContext.h"
#include "VideoBackends/D3D12/Renderer.h"
#include "VideoBackends/D3D12/StreamBuffer.h"

// TODO: Use write 32-bit value to buffer in command list where available to skip stream buffer.
namespace DX12
{
BoundingBox::BoundingBox() = default;

BoundingBox::~BoundingBox()
{
if (m_gpu_descriptor)
g_context->GetDescriptorHeapManager()->Free(m_gpu_descriptor);
}

std::unique_ptr<BoundingBox> BoundingBox::Create()
{
auto bbox = std::unique_ptr<BoundingBox>(new BoundingBox());
if (!bbox->CreateBuffers())
return nullptr;

return bbox;
}

bool BoundingBox::CreateBuffers()
{
static constexpr D3D12_HEAP_PROPERTIES gpu_heap_properties = {
D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1};
static constexpr D3D12_HEAP_PROPERTIES cpu_heap_properties = {
D3D12_HEAP_TYPE_READBACK, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1};
D3D12_RESOURCE_DESC buffer_desc = {D3D12_RESOURCE_DIMENSION_BUFFER,
0,
BUFFER_SIZE,
1,
1,
1,
DXGI_FORMAT_UNKNOWN,
{1, 0},
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS};

HRESULT hr = g_context->GetDevice()->CreateCommittedResource(
&gpu_heap_properties, D3D12_HEAP_FLAG_NONE, &buffer_desc,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&m_gpu_buffer));
CHECK(SUCCEEDED(hr), "Creating bounding box GPU buffer failed");
if (FAILED(hr) || !g_context->GetDescriptorHeapManager()->Allocate(&m_gpu_descriptor))
return false;

D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {DXGI_FORMAT_R32_SINT, D3D12_UAV_DIMENSION_BUFFER};
uav_desc.Buffer.NumElements = NUM_VALUES;
g_context->GetDevice()->CreateUnorderedAccessView(m_gpu_buffer.Get(), nullptr, &uav_desc,
m_gpu_descriptor.cpu_handle);
g_context->GetDevice()->CreateUnorderedAccessView(m_gpu_buffer.Get(), nullptr, &uav_desc,
m_gpu_descriptor.cpu_handle_shadow);

buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
hr = g_context->GetDevice()->CreateCommittedResource(&cpu_heap_properties, D3D12_HEAP_FLAG_NONE,
&buffer_desc, D3D12_RESOURCE_STATE_COPY_DEST,
nullptr, IID_PPV_ARGS(&m_readback_buffer));
CHECK(SUCCEEDED(hr), "Creating bounding box CPU buffer failed");
if (FAILED(hr))
return false;

m_stream_buffer = StreamBuffer::Create(STREAM_BUFFER_SIZE);
if (!m_stream_buffer)
return false;

// Both the CPU and GPU buffer's contents is unknown, so force a flush the first time.
m_values.fill(0);
m_dirty.fill(true);
m_valid = true;
return true;
}

void BoundingBox::Readback()
{
// Copy from GPU->CPU buffer, and wait for the GPU to finish the copy.
D3D::ResourceBarrier(g_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
g_context->GetCommandList()->CopyBufferRegion(m_readback_buffer.Get(), 0, m_gpu_buffer.Get(), 0,
BUFFER_SIZE);
D3D::ResourceBarrier(g_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
Renderer::GetInstance()->ExecuteCommandList(true);

// Read back to cached values.
static constexpr D3D12_RANGE read_range = {0, BUFFER_SIZE};
void* mapped_pointer;
HRESULT hr = m_readback_buffer->Map(0, &read_range, &mapped_pointer);
CHECK(SUCCEEDED(hr), "Map bounding box CPU buffer");
if (FAILED(hr))
return;

static constexpr D3D12_RANGE write_range = {0, 0};
std::array<s32, NUM_VALUES> new_values;
std::memcpy(new_values.data(), mapped_pointer, BUFFER_SIZE);
m_readback_buffer->Unmap(0, &write_range);

// Preserve dirty values, that way we don't need to sync.
for (u32 i = 0; i < NUM_VALUES; i++)
{
if (!m_dirty[i])
m_values[i] = new_values[i];
}
m_valid = true;
}

s32 BoundingBox::Get(size_t index)
{
if (!m_valid)
Readback();

return m_values[index];
}

void BoundingBox::Set(size_t index, s32 value)
{
m_values[index] = value;
m_dirty[index] = true;
}

void BoundingBox::Invalidate()
{
m_dirty.fill(false);
m_valid = false;
}

void BoundingBox::Flush()
{
// Reserve space for all values, even we only copy some.
if (!m_stream_buffer->ReserveMemory(BUFFER_SIZE, BUFFER_SIZE))
{
WARN_LOG(VIDEO, "Executing command list while waiting for space in the bbox stream buffer");
if (!m_stream_buffer->ReserveMemory(BUFFER_SIZE, BUFFER_SIZE))
{
PanicAlert("Failed to reserve bbox stream buffer memory");
return;
}
}
const u32 src_buffer_offset = m_stream_buffer->GetCurrentOffset();
std::memcpy(m_stream_buffer->GetCurrentHostPointer(), m_values.data(), BUFFER_SIZE);
m_stream_buffer->CommitMemory(BUFFER_SIZE);

D3D::ResourceBarrier(g_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);

// Try to batch updates together, but don't overwrite non-dirty values.
// It's likely all of them will be written anyway.
for (u32 start = 0; start < NUM_VALUES;)
{
if (!m_dirty[start])
{
start++;
continue;
}

u32 end = start + 1;
for (; end < NUM_VALUES; end++)
{
if (!m_dirty[end])
break;
}

const u32 count = end - start;
g_context->GetCommandList()->CopyBufferRegion(
m_gpu_buffer.Get(), 0, m_stream_buffer->GetBuffer(), src_buffer_offset, BUFFER_SIZE);

std::fill_n(&m_dirty[start], count, false);
start = end;
}

D3D::ResourceBarrier(g_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
}; // namespace DX12
50 changes: 50 additions & 0 deletions Source/Core/VideoBackends/D3D12/BoundingBox.h
@@ -0,0 +1,50 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once
#include <memory>
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/DescriptorHeapManager.h"

namespace DX12
{
class StreamBuffer;
class BoundingBox
{
public:
~BoundingBox();

static std::unique_ptr<BoundingBox> Create();

const DescriptorHandle& GetGPUDescriptor() const { return m_gpu_descriptor; }

s32 Get(size_t index);
void Set(size_t index, s32 value);

void Invalidate();
void Flush();

private:
static const u32 NUM_VALUES = 4;
static const u32 BUFFER_SIZE = sizeof(u32) * NUM_VALUES;
static const u32 MAX_UPDATES_PER_FRAME = 128;
static const u32 STREAM_BUFFER_SIZE = BUFFER_SIZE * MAX_UPDATES_PER_FRAME;

BoundingBox();

bool CreateBuffers();
void Readback();

// Three buffers: One on the GPU for read/write, one on the CPU for reading back, and a third for
// streaming changes. We use a stream buffer so we don't need to block when two bbox updates are
// within a single command buffer.
ComPtr<ID3D12Resource> m_gpu_buffer;
ComPtr<ID3D12Resource> m_readback_buffer;
std::unique_ptr<StreamBuffer> m_stream_buffer;
DescriptorHandle m_gpu_descriptor;
std::array<s32, NUM_VALUES> m_values = {};
std::array<bool, NUM_VALUES> m_dirty = {};
bool m_valid = true;
};
}; // namespace DX12
90 changes: 90 additions & 0 deletions Source/Core/VideoBackends/D3D12/D3D12.vcxproj
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{570215B7-E32F-4438-95AE-C8D955F9FCA3}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\VSProps\Base.props" />
<Import Project="..\..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles />
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles />
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="BoundingBox.cpp" />
<ClCompile Include="D3DBase.cpp" />
<ClCompile Include="DXContext.cpp" />
<ClCompile Include="DescriptorHeapManager.cpp" />
<ClCompile Include="DXPipeline.cpp" />
<ClCompile Include="DXShader.cpp" />
<ClCompile Include="StreamBuffer.cpp" />
<ClCompile Include="DXTexture.cpp" />
<ClCompile Include="VideoBackend.cpp" />
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="Renderer.cpp" />
<ClCompile Include="DXVertexFormat.cpp" />
<ClCompile Include="SwapChain.cpp" />
<ClCompile Include="VertexManager.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BoundingBox.h" />
<ClInclude Include="D3DBase.h" />
<ClInclude Include="DXContext.h" />
<ClInclude Include="DescriptorHeapManager.h" />
<ClInclude Include="DXPipeline.h" />
<ClInclude Include="DXShader.h" />
<ClInclude Include="StreamBuffer.h" />
<ClInclude Include="DXTexture.h" />
<ClInclude Include="PerfQuery.h" />
<ClInclude Include="Renderer.h" />
<ClInclude Include="DXVertexFormat.h" />
<ClInclude Include="SwapChain.h" />
<ClInclude Include="VertexManager.h" />
<ClInclude Include="VideoBackend.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
35 changes: 35 additions & 0 deletions Source/Core/VideoBackends/D3D12/D3D12.vcxproj.filters
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="VideoBackend.cpp" />
<ClCompile Include="D3DBase.cpp" />
<ClCompile Include="DescriptorHeapManager.cpp" />
<ClCompile Include="DXContext.cpp" />
<ClCompile Include="DXPipeline.cpp" />
<ClCompile Include="DXShader.cpp" />
<ClCompile Include="DXTexture.cpp" />
<ClCompile Include="DXVertexFormat.cpp" />
<ClCompile Include="StreamBuffer.cpp" />
<ClCompile Include="SwapChain.cpp" />
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="Renderer.cpp" />
<ClCompile Include="VertexManager.cpp" />
<ClCompile Include="BoundingBox.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="VideoBackend.h" />
<ClInclude Include="SwapChain.h" />
<ClInclude Include="D3DBase.h" />
<ClInclude Include="DescriptorHeapManager.h" />
<ClInclude Include="DXContext.h" />
<ClInclude Include="DXPipeline.h" />
<ClInclude Include="DXShader.h" />
<ClInclude Include="DXTexture.h" />
<ClInclude Include="DXVertexFormat.h" />
<ClInclude Include="StreamBuffer.h" />
<ClInclude Include="VertexManager.h" />
<ClInclude Include="BoundingBox.h" />
<ClInclude Include="PerfQuery.h" />
<ClInclude Include="Renderer.h" />
</ItemGroup>
</Project>

0 comments on commit 45a3443

Please sign in to comment.