Skip to content

Commit

Permalink
Implement D3D12 backend
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Apr 1, 2019
1 parent 6eb126b commit 113bd60
Show file tree
Hide file tree
Showing 38 changed files with 5,132 additions and 0 deletions.
1 change: 1 addition & 0 deletions Source/Core/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ if(WIN32)
)
target_link_libraries(core PUBLIC
videod3d
videod3d12
setupapi.lib
iphlpapi.lib
)
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/DolphinQt/DolphinQt.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,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
1 change: 1 addition & 0 deletions Source/Core/VideoBackends/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ add_subdirectory(Vulkan)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_subdirectory(D3DCommon)
add_subdirectory(D3D)
add_subdirectory(D3D12)
endif()

183 changes: 183 additions & 0 deletions Source/Core/VideoBackends/D3D12/BoundingBox.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// 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"

namespace DX12
{
BoundingBox::BoundingBox() = default;

BoundingBox::~BoundingBox()
{
if (m_gpu_descriptor)
g_dx_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};
static constexpr D3D12_HEAP_PROPERTIES cpu_heap_properties = {D3D12_HEAP_TYPE_READBACK};
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_dx_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_dx_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_dx_context->GetDevice()->CreateUnorderedAccessView(m_gpu_buffer.Get(), nullptr, &uav_desc,
m_gpu_descriptor.cpu_handle);

buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
hr = g_dx_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;

if (!m_upload_buffer.AllocateBuffer(STREAM_BUFFER_SIZE))
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.
ResourceBarrier(g_dx_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
g_dx_context->GetCommandList()->CopyBufferRegion(m_readback_buffer.Get(), 0, m_gpu_buffer.Get(),
0, BUFFER_SIZE);
ResourceBarrier(g_dx_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()
{
bool in_copy_state = false;
for (u32 start = 0; start < NUM_VALUES;)
{
if (!m_dirty[start])
{
start++;
continue;
}

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

m_dirty[end] = false;
}

const u32 copy_size = (end - start) * sizeof(ValueType);
if (!m_upload_buffer.ReserveMemory(copy_size, sizeof(ValueType)))
{
WARN_LOG(VIDEO, "Executing command list while waiting for space in bbox stream buffer");
Renderer::GetInstance()->ExecuteCommandList(false);
if (!m_upload_buffer.ReserveMemory(copy_size, sizeof(ValueType)))
{
PanicAlert("Failed to allocate bbox stream buffer space");
return;
}
}

const u32 upload_buffer_offset = m_upload_buffer.GetCurrentOffset();
std::memcpy(m_upload_buffer.GetCurrentHostPointer(), &m_values[start], copy_size);
m_upload_buffer.CommitMemory(copy_size);

if (!in_copy_state)
{
ResourceBarrier(g_dx_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
in_copy_state = true;
}

g_dx_context->GetCommandList()->CopyBufferRegion(m_gpu_buffer.Get(), start * sizeof(ValueType),
m_upload_buffer.GetBuffer(),
upload_buffer_offset, copy_size);
start = end;
}

if (in_copy_state)
{
ResourceBarrier(g_dx_context->GetCommandList(), m_gpu_buffer.Get(),
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
}
}; // namespace DX12
49 changes: 49 additions & 0 deletions Source/Core/VideoBackends/D3D12/BoundingBox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

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

namespace DX12
{
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:
using ValueType = s32;
static const u32 NUM_VALUES = 4;
static const u32 BUFFER_SIZE = sizeof(ValueType) * 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: GPU for read/write, CPU for reading back, and CPU for staging changes.
ComPtr<ID3D12Resource> m_gpu_buffer;
ComPtr<ID3D12Resource> m_readback_buffer;
StreamBuffer m_upload_buffer;
DescriptorHandle m_gpu_descriptor;
std::array<ValueType, NUM_VALUES> m_values = {};
std::array<bool, NUM_VALUES> m_dirty = {};
bool m_valid = true;
};
}; // namespace DX12
37 changes: 37 additions & 0 deletions Source/Core/VideoBackends/D3D12/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
add_library(videod3d12
BoundingBox.cpp
BoundingBox.h
DescriptorAllocator.cpp
DescriptorAllocator.h
DescriptorHeapManager.cpp
DescriptorHeapManager.h
DXContext.cpp
DXContext.h
DXPipeline.cpp
DXPipeline.h
DXShader.cpp
DXShader.h
DXTexture.cpp
DXTexture.h
DXVertexFormat.cpp
DXVertexFormat.h
PerfQuery.cpp
PerfQuery.h
Renderer.cpp
Renderer.h
StreamBuffer.cpp
StreamBuffer.h
SwapChain.cpp
SwapChain.h
VertexManager.cpp
VertexManager.h
VideoBackend.cpp
VideoBackend.h
)

target_link_libraries(videod3d12
PUBLIC
common
videocommon
videod3dcommon
)
32 changes: 32 additions & 0 deletions Source/Core/VideoBackends/D3D12/Common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once

#include <d3d12.h>
#include <wrl/client.h>

#include "Common/MsgHandler.h"
#include "VideoBackends/D3DCommon/Common.h"

#define CHECK(cond, Message, ...) \
if (!(cond)) \
{ \
PanicAlert(__FUNCTION__ " failed in %s at line %d: " Message, __FILE__, __LINE__, \
__VA_ARGS__); \
}

namespace DX12
{
using Microsoft::WRL::ComPtr;

static void ResourceBarrier(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource,
D3D12_RESOURCE_STATES from_state, D3D12_RESOURCE_STATES to_state)
{
const D3D12_RESOURCE_BARRIER barrier = {
D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
D3D12_RESOURCE_BARRIER_FLAG_NONE,
{{resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, from_state, to_state}}};
cmdlist->ResourceBarrier(1, &barrier);
}
} // namespace DX12
Loading

0 comments on commit 113bd60

Please sign in to comment.