Skip to content
Permalink
Browse files
Merge pull request #10754 from tellowkrinkle/Metal
VideoBackends: Add Metal backend
  • Loading branch information
JMC47 committed Jul 23, 2022
2 parents df399b0 + 6559c6b commit 89c4fde
Show file tree
Hide file tree
Showing 58 changed files with 4,316 additions and 53 deletions.
@@ -631,6 +631,10 @@ if(ENABLE_VULKAN)
target_link_libraries(core PUBLIC videovulkan)
endif()

if(APPLE)
target_link_libraries(core PUBLIC videometal)
endif()

if(USE_MGBA)
target_sources(core PRIVATE
HW/GBACore.cpp
@@ -8,6 +8,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_subdirectory(D3D12)
endif()

if(APPLE)
add_subdirectory(Metal)
endif()

if(ENABLE_VULKAN)
add_subdirectory(Vulkan)
endif()
@@ -111,6 +111,7 @@ void VideoBackend::FillBackendInfo()
g_Config.backend_info.bSupportsLodBiasInSampler = true;
g_Config.backend_info.bSupportsLogicOp = D3D::SupportsLogicOp(g_Config.iAdapter);
g_Config.backend_info.bSupportsSettingObjectNames = true;
g_Config.backend_info.bSupportsPartialMultisampleResolve = true;

g_Config.backend_info.Adapters = D3DCommon::GetAdapterNames();
g_Config.backend_info.AAModes = D3D::GetAAModes(g_Config.iAdapter);
@@ -150,8 +150,8 @@ void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
D3D::context->DrawIndexed(num_indices, base_index, base_vertex);
}

void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
u32 groups_z)
void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z)
{
D3D::stateman->SetComputeShader(static_cast<const DXShader*>(shader)->GetD3DComputeShader());
D3D::stateman->SyncComputeBindings();
@@ -57,8 +57,8 @@ class Renderer : public ::Renderer
float far_depth) override;
void Draw(u32 base_vertex, u32 num_vertices) override;
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
u32 groups_z) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override;
void SetFullscreen(bool enable_fullscreen) override;
@@ -365,8 +365,8 @@ void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
g_dx_context->GetCommandList()->DrawIndexedInstanced(num_indices, 1, base_index, base_vertex, 0);
}

void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
u32 groups_z)
void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z)
{
SetRootSignatures();
SetDescriptorHeaps();
@@ -69,8 +69,8 @@ class Renderer final : public ::Renderer
float far_depth) override;
void Draw(u32 base_vertex, u32 num_vertices) override;
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
u32 groups_z) override;
void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
void BindBackbuffer(const ClearColor& clear_color = {}) override;
void PresentBackbuffer() override;

@@ -86,6 +86,7 @@ void VideoBackend::FillBackendInfo()
g_Config.backend_info.bSupportsTextureQueryLevels = true;
g_Config.backend_info.bSupportsLodBiasInSampler = true;
g_Config.backend_info.bSupportsSettingObjectNames = true;
g_Config.backend_info.bSupportsPartialMultisampleResolve = true;

// We can only check texture support once we have a device.
if (g_dx_context)
@@ -0,0 +1,40 @@
add_library(videometal
MRCHelpers.h
MTLBoundingBox.mm
MTLBoundingBox.h
MTLMain.mm
MTLObjectCache.h
MTLObjectCache.mm
MTLPerfQuery.mm
MTLPerfQuery.h
MTLPipeline.mm
MTLPipeline.h
MTLRenderer.mm
MTLRenderer.h
MTLShader.mm
MTLShader.h
MTLStateTracker.mm
MTLStateTracker.h
MTLTexture.mm
MTLTexture.h
MTLUtil.mm
MTLUtil.h
MTLVertexFormat.mm
MTLVertexFormat.h
MTLVertexManager.mm
MTLVertexManager.h
VideoBackend.h
)

find_library(METAL_LIBRARY Metal)
find_library(QUARTZCORE_LIBRARY QuartzCore)

target_link_libraries(videometal
PUBLIC
common
videocommon
PRIVATE
spirv_cross
${METAL_LIBRARY}
${QUARTZCORE_LIBRARY}
)
@@ -0,0 +1,81 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

// clang-format off
#ifndef __OBJC__
#error This header is for use with Objective-C++ only.
#endif
#if __has_feature(objc_arc)
#error This file is for manual reference counting! Compile without -fobjc-arc
#endif
// clang-format on

#pragma once

#include <cstddef>
#include <utility>

/// Managed Obj-C pointer
template <typename T>
class MRCOwned
{
T ptr;
MRCOwned(T ptr) : ptr(ptr) {}

public:
MRCOwned() : ptr(nullptr) {}
MRCOwned(std::nullptr_t) : ptr(nullptr) {}
MRCOwned(MRCOwned&& other) : ptr(other.ptr) { other.ptr = nullptr; }
MRCOwned(const MRCOwned& other) : ptr(other.ptr) { [ptr retain]; }
~MRCOwned()
{
if (ptr)
[ptr release];
}
operator T() const { return ptr; }
MRCOwned& operator=(const MRCOwned& other)
{
[other.ptr retain];
if (ptr)
[ptr release];
ptr = other.ptr;
return *this;
}
MRCOwned& operator=(MRCOwned&& other)
{
std::swap(ptr, other.ptr);
return *this;
}
void Reset()
{
[ptr release];
ptr = nullptr;
}
T Disown()
{
T tmp = ptr;
ptr = nullptr;
return tmp;
}
T Get() const { return ptr; }
static MRCOwned Transfer(T ptr) { return MRCOwned(ptr); }
static MRCOwned Retain(T ptr)
{
[ptr retain];
return MRCOwned(ptr);
}
};

/// Take ownership of an Obj-C pointer (equivalent to __bridge_transfer)
template <typename T>
static inline MRCOwned<T> MRCTransfer(T ptr)
{
return MRCOwned<T>::Transfer(ptr);
}

/// Retain an Obj-C pointer (equivalent to __bridge)
template <typename T>
static inline MRCOwned<T> MRCRetain(T ptr)
{
return MRCOwned<T>::Retain(ptr);
}
@@ -0,0 +1,30 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "VideoCommon/BoundingBox.h"

#include <Metal/Metal.h>

#include "VideoBackends/Metal/MRCHelpers.h"

namespace Metal
{
class BoundingBox final : public ::BoundingBox
{
public:
~BoundingBox() override;

bool Initialize() override;

protected:
std::vector<BBoxType> Read(u32 index, u32 length) override;
void Write(u32 index, const std::vector<BBoxType>& values) override;

private:
BBoxType* m_cpu_buffer_ptr;
MRCOwned<id<MTLFence>> m_download_fence;
MRCOwned<id<MTLFence>> m_upload_fence;
MRCOwned<id<MTLBuffer>> m_cpu_buffer;
MRCOwned<id<MTLBuffer>> m_gpu_buffer;
};
} // namespace Metal
@@ -0,0 +1,72 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "VideoBackends/Metal/MTLBoundingBox.h"

#include "VideoBackends/Metal/MTLObjectCache.h"
#include "VideoBackends/Metal/MTLStateTracker.h"

static constexpr size_t BUFFER_SIZE = sizeof(BBoxType) * NUM_BBOX_VALUES;

Metal::BoundingBox::~BoundingBox()
{
if (g_state_tracker)
g_state_tracker->SetBBoxBuffer(nullptr, nullptr, nullptr);
}

bool Metal::BoundingBox::Initialize()
{
const MTLResourceOptions gpu_options =
MTLResourceStorageModeShared | MTLResourceHazardTrackingModeUntracked;
const id<MTLDevice> dev = g_device;
m_upload_fence = MRCTransfer([dev newFence]);
[m_upload_fence setLabel:@"BBox Upload Fence"];
m_download_fence = MRCTransfer([dev newFence]);
[m_download_fence setLabel:@"BBox Download Fence"];
m_gpu_buffer = MRCTransfer([dev newBufferWithLength:BUFFER_SIZE options:gpu_options]);
[m_gpu_buffer setLabel:@"BBox Buffer"];
m_cpu_buffer_ptr = static_cast<BBoxType*>([m_gpu_buffer contents]);
g_state_tracker->SetBBoxBuffer(m_gpu_buffer, m_upload_fence, m_download_fence);
return true;
}

std::vector<BBoxType> Metal::BoundingBox::Read(u32 index, u32 length)
{
@autoreleasepool
{
g_state_tracker->EndRenderPass();
g_state_tracker->FlushEncoders();
g_state_tracker->WaitForFlushedEncoders();
return std::vector<BBoxType>(m_cpu_buffer_ptr + index, m_cpu_buffer_ptr + index + length);
}
}

void Metal::BoundingBox::Write(u32 index, const std::vector<BBoxType>& values)
{
const u32 size = values.size() * sizeof(BBoxType);
if (!g_state_tracker->HasUnflushedData() && !g_state_tracker->GPUBusy())
{
// We can just write directly to the buffer!
memcpy(m_cpu_buffer_ptr + index, values.data(), size);
}
else
{
@autoreleasepool
{
StateTracker::Map map = g_state_tracker->Allocate(StateTracker::UploadBuffer::Other, size,
StateTracker::AlignMask::Other);
memcpy(map.cpu_buffer, values.data(), size);
g_state_tracker->EndRenderPass();
id<MTLBlitCommandEncoder> upload = [g_state_tracker->GetRenderCmdBuf() blitCommandEncoder];
[upload setLabel:@"BBox Upload"];
[upload waitForFence:m_download_fence];
[upload copyFromBuffer:map.gpu_buffer
sourceOffset:map.gpu_offset
toBuffer:m_gpu_buffer
destinationOffset:index * sizeof(BBoxType)
size:size];
[upload updateFence:m_upload_fence];
[upload endEncoding];
}
}
}

0 comments on commit 89c4fde

Please sign in to comment.