VideoBackends: Add Metal renderer

This commit is contained in:
TellowKrinkle
2022-07-21 20:44:19 -05:00
parent b0b5faa793
commit 716c0980d7
42 changed files with 3714 additions and 47 deletions
+4
View File
@@ -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
+4
View File
@@ -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()
+2 -2
View File
@@ -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();
+2 -2
View File
@@ -57,8 +57,8 @@ public:
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 @@ public:
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;
@@ -0,0 +1,37 @@
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.h
MTLStateTracker.mm
MTLStateTracker.h
MTLTexture.mm
MTLTexture.h
MTLUtil.mm
MTLUtil.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,97 @@
// 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_storage_mode =
g_features.unified_memory ? MTLResourceStorageModeShared : MTLResourceStorageModePrivate;
const MTLResourceOptions gpu_options = gpu_storage_mode | 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]);
if (g_features.unified_memory)
{
[m_gpu_buffer setLabel:@"BBox Buffer"];
m_cpu_buffer_ptr = static_cast<BBoxType*>([m_gpu_buffer contents]);
}
else
{
m_cpu_buffer = MRCTransfer([dev newBufferWithLength:BUFFER_SIZE
options:MTLResourceStorageModeShared]);
m_cpu_buffer_ptr = static_cast<BBoxType*>([m_cpu_buffer contents]);
[m_gpu_buffer setLabel:@"BBox GPU Buffer"];
[m_cpu_buffer setLabel:@"BBox CPU Buffer"];
}
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();
if (!g_features.unified_memory)
{
id<MTLBlitCommandEncoder> download = [g_state_tracker->GetRenderCmdBuf() blitCommandEncoder];
[download setLabel:@"BBox Download"];
[download waitForFence:m_download_fence];
[download copyFromBuffer:m_gpu_buffer
sourceOffset:0
toBuffer:m_cpu_buffer
destinationOffset:0
size:BUFFER_SIZE];
[download endEncoding];
}
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_features.unified_memory && !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];
}
}
}
+157
View File
@@ -0,0 +1,157 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/VideoBackend.h"
#include <AppKit/AppKit.h>
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include "Common/Common.h"
#include "Common/MsgHandler.h"
#include "VideoBackends/Metal/MTLObjectCache.h"
#include "VideoBackends/Metal/MTLPerfQuery.h"
#include "VideoBackends/Metal/MTLRenderer.h"
#include "VideoBackends/Metal/MTLStateTracker.h"
#include "VideoBackends/Metal/MTLUtil.h"
#include "VideoBackends/Metal/MTLVertexManager.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
std::string Metal::VideoBackend::GetName() const
{
return NAME;
}
std::string Metal::VideoBackend::GetDisplayName() const
{
// i18n: Apple's Metal graphics API (https://developer.apple.com/metal/)
return _trans("Metal");
}
std::optional<std::string> Metal::VideoBackend::GetWarningMessage() const
{
if (Util::GetAdapterList().empty())
{
return _trans("No Metal-compatible GPUs were found. "
"Use the OpenGL backend or upgrade your computer/GPU");
}
return std::nullopt;
}
static bool WindowSystemTypeSupportsMetal(WindowSystemType type)
{
switch (type)
{
case WindowSystemType::MacOS:
return true;
default:
return false;
}
}
bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi)
{
@autoreleasepool
{
if (!WindowSystemTypeSupportsMetal(wsi.type) || !wsi.render_surface)
{
PanicAlertFmt("Bad WindowSystemInfo for Metal renderer.");
return false;
}
auto devs = Util::GetAdapterList();
if (devs.empty())
{
PanicAlertFmt("No Metal GPUs detected.");
return false;
}
Util::PopulateBackendInfo(&g_Config);
Util::PopulateBackendInfoAdapters(&g_Config, devs);
// Since we haven't called InitializeShared yet, iAdapter may be out of range,
// so we have to check it ourselves.
size_t selected_adapter_index = static_cast<size_t>(g_Config.iAdapter);
if (selected_adapter_index >= devs.size())
{
WARN_LOG_FMT(VIDEO, "Metal adapter index out of range, selecting default adapter.");
selected_adapter_index = 0;
}
MRCOwned<id<MTLDevice>> adapter = std::move(devs[selected_adapter_index]);
Util::PopulateBackendInfoFeatures(&g_Config, adapter);
// With the backend information populated, we can now initialize videocommon.
InitializeShared();
MRCOwned<CAMetalLayer*> layer = MRCRetain(static_cast<CAMetalLayer*>(wsi.render_surface));
[layer setDevice:adapter];
if (Util::ToAbstract([layer pixelFormat]) == AbstractTextureFormat::Undefined)
[layer setPixelFormat:MTLPixelFormatBGRA8Unorm];
CGSize size = [layer bounds].size;
float scale = [layer contentsScale];
ObjectCache::Initialize(std::move(adapter));
g_state_tracker = std::make_unique<StateTracker>();
g_renderer = std::make_unique<Renderer>(std::move(layer), size.width * scale,
size.height * scale, scale);
g_vertex_manager = std::make_unique<VertexManager>();
g_perf_query = std::make_unique<PerfQuery>();
g_framebuffer_manager = std::make_unique<FramebufferManager>();
g_texture_cache = std::make_unique<TextureCacheBase>();
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() ||
!g_renderer->Initialize() || !g_framebuffer_manager->Initialize() ||
!g_texture_cache->Initialize())
{
PanicAlertFmt("Failed to initialize renderer classes");
Shutdown();
return false;
}
g_shader_cache->InitializeShaderCache();
return true;
}
}
void Metal::VideoBackend::Shutdown()
{
g_shader_cache->Shutdown();
g_renderer->Shutdown();
g_shader_cache.reset();
g_texture_cache.reset();
g_framebuffer_manager.reset();
g_perf_query.reset();
g_vertex_manager.reset();
g_renderer.reset();
g_state_tracker.reset();
ObjectCache::Shutdown();
ShutdownShared();
}
void Metal::VideoBackend::InitBackendInfo()
{
@autoreleasepool
{
Util::PopulateBackendInfo(&g_Config);
Util::PopulateBackendInfoAdapters(&g_Config, Util::GetAdapterList());
}
}
void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
{
if (wsi.type != WindowSystemType::MacOS)
return;
NSView* view = static_cast<NSView*>(wsi.render_surface);
CAMetalLayer* layer = [CAMetalLayer layer];
[view setWantsLayer:YES];
[view setLayer:layer];
wsi.render_surface = layer;
}
@@ -0,0 +1,91 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Metal/Metal.h>
#include <memory>
#include "VideoBackends/Metal/MRCHelpers.h"
#include "VideoCommon/RenderState.h"
namespace Metal
{
extern MRCOwned<id<MTLDevice>> g_device;
extern MRCOwned<id<MTLCommandQueue>> g_queue;
struct DepthStencilSelector
{
u8 value;
DepthStencilSelector() : value(0) {}
DepthStencilSelector(bool update_enable, enum CompareMode cmp)
: value(update_enable | (static_cast<u32>(cmp) << 1))
{
}
DepthStencilSelector(DepthState state)
: DepthStencilSelector(state.testenable ? state.updateenable : false,
state.testenable ? state.func : CompareMode::Always)
{
}
bool UpdateEnable() const { return value & 1; }
enum CompareMode CompareMode() const { return static_cast<enum CompareMode>(value >> 1); }
bool operator==(const DepthStencilSelector& other) { return value == other.value; }
bool operator!=(const DepthStencilSelector& other) { return !(*this == other); }
static constexpr size_t N_VALUES = 1 << 4;
};
struct SamplerSelector
{
u8 value;
SamplerSelector() : value(0) {}
SamplerSelector(SamplerState state)
{
value = (static_cast<u32>(state.tm0.min_filter.Value()) << 0) |
(static_cast<u32>(state.tm0.mag_filter.Value()) << 1) |
(static_cast<u32>(state.tm0.mipmap_filter.Value()) << 2) |
(static_cast<u32>(state.tm0.anisotropic_filtering) << 3);
value |= (static_cast<u32>(state.tm0.wrap_u.Value()) +
3 * static_cast<u32>(state.tm0.wrap_v.Value()))
<< 4;
}
FilterMode MinFilter() const { return static_cast<FilterMode>(value & 1); }
FilterMode MagFilter() const { return static_cast<FilterMode>((value >> 1) & 1); }
FilterMode MipFilter() const { return static_cast<FilterMode>((value >> 2) & 1); }
WrapMode WrapU() const { return static_cast<WrapMode>((value >> 4) % 3); }
WrapMode WrapV() const { return static_cast<WrapMode>((value >> 4) / 3); }
bool AnisotropicFiltering() const { return ((value >> 3) & 1); }
bool operator==(const SamplerSelector& other) { return value == other.value; }
bool operator!=(const SamplerSelector& other) { return !(*this == other); }
static constexpr size_t N_VALUES = (1 << 4) * 9;
};
class ObjectCache
{
ObjectCache();
public:
~ObjectCache();
static void Initialize(MRCOwned<id<MTLDevice>> device);
static void Shutdown();
id<MTLDepthStencilState> GetDepthStencil(DepthStencilSelector sel) { return m_dss[sel.value]; }
id<MTLSamplerState> GetSampler(SamplerSelector sel) { return m_samplers[sel.value]; }
id<MTLSamplerState> GetSampler(SamplerState state) { return GetSampler(SamplerSelector(state)); }
void ReloadSamplers();
private:
MRCOwned<id<MTLDepthStencilState>> m_dss[DepthStencilSelector::N_VALUES];
MRCOwned<id<MTLSamplerState>> m_samplers[SamplerSelector::N_VALUES];
};
extern std::unique_ptr<ObjectCache> g_object_cache;
} // namespace Metal
@@ -0,0 +1,175 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/MTLObjectCache.h"
#include "VideoCommon/VideoConfig.h"
MRCOwned<id<MTLDevice>> Metal::g_device;
MRCOwned<id<MTLCommandQueue>> Metal::g_queue;
std::unique_ptr<Metal::ObjectCache> Metal::g_object_cache;
static void SetupDepthStencil(
MRCOwned<id<MTLDepthStencilState>> (&dss)[Metal::DepthStencilSelector::N_VALUES]);
static void SetupSamplers(MRCOwned<id<MTLSamplerState>> (&samplers)[Metal::SamplerSelector::N_VALUES]);
Metal::ObjectCache::ObjectCache()
{
SetupDepthStencil(m_dss);
SetupSamplers(m_samplers);
}
Metal::ObjectCache::~ObjectCache()
{
}
void Metal::ObjectCache::Initialize(MRCOwned<id<MTLDevice>> device)
{
g_device = std::move(device);
g_queue = MRCTransfer([g_device newCommandQueue]);
g_object_cache = std::unique_ptr<ObjectCache>(new ObjectCache);
}
void Metal::ObjectCache::Shutdown()
{
g_object_cache.reset();
g_queue = nullptr;
g_device = nullptr;
}
// MARK: Depth Stencil State
// clang-format off
static MTLCompareFunction Convert(CompareMode mode)
{
const bool invert_depth = !g_Config.backend_info.bSupportsReversedDepthRange;
switch (mode)
{
case CompareMode::Never: return MTLCompareFunctionNever;
case CompareMode::Less: return invert_depth ? MTLCompareFunctionGreater
: MTLCompareFunctionLess;
case CompareMode::Equal: return MTLCompareFunctionEqual;
case CompareMode::LEqual: return invert_depth ? MTLCompareFunctionGreaterEqual
: MTLCompareFunctionLessEqual;
case CompareMode::Greater: return invert_depth ? MTLCompareFunctionLess
: MTLCompareFunctionGreater;
case CompareMode::NEqual: return MTLCompareFunctionNotEqual;
case CompareMode::GEqual: return invert_depth ? MTLCompareFunctionLessEqual
: MTLCompareFunctionGreaterEqual;
case CompareMode::Always: return MTLCompareFunctionAlways;
}
}
static const char* to_string(MTLCompareFunction compare)
{
switch (compare)
{
case MTLCompareFunctionNever: return "Never";
case MTLCompareFunctionGreater: return "Greater";
case MTLCompareFunctionEqual: return "Equal";
case MTLCompareFunctionGreaterEqual: return "GEqual";
case MTLCompareFunctionLess: return "Less";
case MTLCompareFunctionNotEqual: return "NEqual";
case MTLCompareFunctionLessEqual: return "LEqual";
case MTLCompareFunctionAlways: return "Always";
}
}
// clang-format on
static void SetupDepthStencil(
MRCOwned<id<MTLDepthStencilState>> (&dss)[Metal::DepthStencilSelector::N_VALUES])
{
auto desc = MRCTransfer([MTLDepthStencilDescriptor new]);
Metal::DepthStencilSelector sel;
for (size_t i = 0; i < std::size(dss); ++i)
{
sel.value = i;
MTLCompareFunction mcompare = Convert(sel.CompareMode());
[desc setDepthWriteEnabled:sel.UpdateEnable()];
[desc setDepthCompareFunction:mcompare];
[desc setLabel:[NSString stringWithFormat:@"DSS %s%s", to_string(mcompare),
sel.UpdateEnable() ? "+Write" : ""]];
dss[i] = MRCTransfer([Metal::g_device newDepthStencilStateWithDescriptor:desc]);
}
}
// MARK: Samplers
// clang-format off
static MTLSamplerMinMagFilter ConvertMinMag(FilterMode filter)
{
switch (filter)
{
case FilterMode::Linear: return MTLSamplerMinMagFilterLinear;
case FilterMode::Near: return MTLSamplerMinMagFilterNearest;
}
}
static MTLSamplerMipFilter ConvertMip(FilterMode filter)
{
switch (filter)
{
case FilterMode::Linear: return MTLSamplerMipFilterLinear;
case FilterMode::Near: return MTLSamplerMipFilterNearest;
}
}
static MTLSamplerAddressMode Convert(WrapMode wrap)
{
switch (wrap)
{
case WrapMode::Clamp: return MTLSamplerAddressModeClampToEdge;
case WrapMode::Mirror: return MTLSamplerAddressModeMirrorRepeat;
case WrapMode::Repeat: return MTLSamplerAddressModeRepeat;
}
}
static const char* to_string(FilterMode filter)
{
switch (filter)
{
case FilterMode::Linear: return "Ln";
case FilterMode::Near: return "Pt";
}
}
static const char* to_string(WrapMode wrap)
{
switch (wrap)
{
case WrapMode::Clamp: return "C";
case WrapMode::Mirror: return "M";
case WrapMode::Repeat: return "R";
}
}
// clang-format on
static void SetupSamplers(MRCOwned<id<MTLSamplerState>> (&samplers)[Metal::SamplerSelector::N_VALUES])
{
auto desc = MRCTransfer([MTLSamplerDescriptor new]);
Metal::SamplerSelector sel;
for (size_t i = 0; i < std::size(samplers); i++)
{
sel.value = i;
[desc setMinFilter:ConvertMinMag(sel.MinFilter())];
[desc setMagFilter:ConvertMinMag(sel.MagFilter())];
[desc setMipFilter:ConvertMip(sel.MipFilter())];
[desc setSAddressMode:Convert(sel.WrapU())];
[desc setTAddressMode:Convert(sel.WrapV())];
[desc setMaxAnisotropy:1 << (sel.AnisotropicFiltering() ? g_ActiveConfig.iMaxAnisotropy : 0)];
[desc setLabel:MRCTransfer([[NSString alloc]
initWithFormat:@"%s%s%s %s%s%s", to_string(sel.MinFilter()),
to_string(sel.MagFilter()), to_string(sel.MipFilter()),
to_string(sel.WrapU()), to_string(sel.WrapV()),
sel.AnisotropicFiltering() ? "(AF)" : ""])];
samplers[i] = MRCTransfer([Metal::g_device newSamplerStateWithDescriptor:desc]);
}
}
void Metal::ObjectCache::ReloadSamplers()
{
SetupSamplers(m_samplers);
}
@@ -0,0 +1,20 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "VideoCommon/PerfQueryBase.h"
namespace Metal
{
class PerfQuery final : public PerfQueryBase
{
public:
void EnableQuery(PerfQueryGroup type) override {}
void DisableQuery(PerfQueryGroup type) override {}
void ResetQuery() override {}
u32 GetQueryResult(PerfQueryType type) override { return 0; }
void FlushResults() override {}
bool IsFlushed() const override { return true; }
};
} // namespace Metal
@@ -0,0 +1,4 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/MTLPerfQuery.h"
@@ -0,0 +1,64 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Metal/Metal.h>
#include "VideoBackends/Metal/MRCHelpers.h"
#include "VideoBackends/Metal/MTLObjectCache.h"
#include "VideoBackends/Metal/MTLShader.h"
#include "VideoCommon/AbstractPipeline.h"
#include "VideoCommon/AbstractShader.h"
namespace Metal
{
class Pipeline final : public AbstractPipeline
{
public:
explicit Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim,
MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage);
id<MTLRenderPipelineState> Get() const { return m_pipeline; }
MTLPrimitiveType Prim() const { return m_prim; }
MTLCullMode Cull() const { return m_cull; }
DepthStencilSelector DepthStencil() const { return m_depth_stencil; }
AbstractPipelineUsage Usage() const { return m_usage; }
u32 GetTextures() const { return m_textures; }
u32 GetSamplers() const { return m_samplers; }
u32 GetVertexBuffers() const { return m_vertex_buffers; }
u32 GetFragmentBuffers() const { return m_fragment_buffers; }
bool UsesVertexBuffer(u32 index) const { return m_vertex_buffers & (1 << index); }
bool UsesFragmentBuffer(u32 index) const { return m_fragment_buffers & (1 << index); }
private:
MRCOwned<id<MTLRenderPipelineState>> m_pipeline;
MTLPrimitiveType m_prim;
MTLCullMode m_cull;
DepthStencilSelector m_depth_stencil;
AbstractPipelineUsage m_usage;
u32 m_textures = 0;
u32 m_samplers = 0;
u32 m_vertex_buffers = 0;
u32 m_fragment_buffers = 0;
};
class ComputePipeline : public Shader
{
public:
explicit ComputePipeline(ShaderStage stage, MTLComputePipelineReflection* reflection,
std::string msl, MRCOwned<id<MTLFunction>> shader,
MRCOwned<id<MTLComputePipelineState>> pipeline);
id<MTLComputePipelineState> GetComputePipeline() const { return m_compute_pipeline; }
bool UsesTexture(u32 index) const { return m_textures & (1 << index); }
bool UsesBuffer(u32 index) const { return m_buffers & (1 << index); }
private:
MRCOwned<id<MTLComputePipelineState>> m_compute_pipeline;
u32 m_textures = 0;
u32 m_buffers = 0;
};
} // namespace Metal
@@ -0,0 +1,66 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/MTLPipeline.h"
#include "Common/MsgHandler.h"
static void MarkAsUsed(u32* list, u32 start, u32 length)
{
for (u32 i = start; i < start + length; ++i)
*list |= 1 << i;
}
static void GetArguments(NSArray<MTLArgument*>* arguments, u32* textures, u32* samplers,
u32* buffers)
{
for (MTLArgument* argument in arguments)
{
const u32 idx = [argument index];
const u32 length = [argument arrayLength];
if (idx + length > 32)
{
PanicAlertFmt("Making a MTLPipeline with high argument index {:d}..<{:d} for {:s}", //
idx, idx + length, [[argument name] UTF8String]);
continue;
}
switch ([argument type])
{
case MTLArgumentTypeTexture:
if (textures)
MarkAsUsed(textures, idx, length);
else
PanicAlertFmt("Vertex function wants a texture!");
break;
case MTLArgumentTypeSampler:
if (samplers)
MarkAsUsed(samplers, idx, length);
else
PanicAlertFmt("Vertex function wants a sampler!");
break;
case MTLArgumentTypeBuffer:
MarkAsUsed(buffers, idx, length);
break;
default:
break;
}
}
}
Metal::Pipeline::Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim,
MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage)
: m_pipeline(std::move(pipeline)), m_prim(prim), m_cull(cull), m_depth_stencil(depth),
m_usage(usage)
{
GetArguments([reflection vertexArguments], nullptr, nullptr, &m_vertex_buffers);
GetArguments([reflection fragmentArguments], &m_textures, &m_samplers, &m_fragment_buffers);
}
Metal::ComputePipeline::ComputePipeline(ShaderStage stage, MTLComputePipelineReflection* reflection,
std::string msl, MRCOwned<id<MTLFunction>> shader,
MRCOwned<id<MTLComputePipelineState>> pipeline)
: Shader(stage, std::move(msl), std::move(shader)), m_compute_pipeline(std::move(pipeline))
{
GetArguments([reflection arguments], &m_textures, nullptr, &m_buffers);
}
@@ -0,0 +1,91 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include "VideoCommon/RenderBase.h"
#include "VideoBackends/Metal/MRCHelpers.h"
namespace Metal
{
class Framebuffer;
class Texture;
class Renderer final : public ::Renderer
{
public:
Renderer(MRCOwned<CAMetalLayer*> layer, int width, int height, float layer_scale);
~Renderer() override;
bool IsHeadless() const override;
bool Initialize() override;
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config,
std::string_view name) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
std::unique_ptr<AbstractFramebuffer>
CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) override;
std::unique_ptr<AbstractShader> CreateShaderFromSource(ShaderStage stage, std::string_view source,
std::string_view name) override;
std::unique_ptr<AbstractShader> CreateShaderFromBinary(ShaderStage stage, const void* data,
size_t length,
std::string_view name) override;
std::unique_ptr<AbstractShader> CreateShaderFromMSL(ShaderStage stage, std::string msl,
std::string_view glsl, std::string_view name);
std::unique_ptr<NativeVertexFormat>
CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data = nullptr,
size_t cache_data_length = 0) override;
void Flush() override;
void WaitForGPUIdle() override;
void OnConfigChanged(u32 bits) override;
void ClearScreen(const MathUtil::Rectangle<int>& rc, bool color_enable, bool alpha_enable,
bool z_enable, u32 color, u32 z) override;
void SetPipeline(const AbstractPipeline* pipeline) override;
void SetFramebuffer(AbstractFramebuffer* framebuffer) override;
void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) override;
void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value = {},
float depth_value = 0.0f) override;
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
void SetTexture(u32 index, const AbstractTexture* texture) override;
void SetSamplerState(u32 index, const SamplerState& state) override;
void SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) override;
void UnbindTexture(const AbstractTexture* texture) override;
void SetViewport(float x, float y, float width, float height, float near_depth,
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 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;
protected:
std::unique_ptr<::BoundingBox> CreateBoundingBox() const override;
private:
MRCOwned<CAMetalLayer*> m_layer;
MRCOwned<id<CAMetalDrawable>> m_drawable;
std::unique_ptr<Texture> m_bb_texture;
std::unique_ptr<Framebuffer> m_backbuffer;
u32 m_texture_counter = 0;
u32 m_staging_texture_counter = 0;
std::array<u32, 4> m_shader_counter = {};
u32 m_pipeline_counter = 0;
void CheckForSurfaceChange();
void CheckForSurfaceResize();
void SetupSurface();
};
} // namespace Metal
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Metal/Metal.h>
#include "VideoBackends/Metal/MRCHelpers.h"
#include "VideoCommon/AbstractPipeline.h"
#include "VideoCommon/AbstractShader.h"
namespace Metal
{
class Shader : public AbstractShader
{
public:
explicit Shader(ShaderStage stage, std::string msl, MRCOwned<id<MTLFunction>> shader)
: AbstractShader(stage), m_msl(std::move(msl)), m_shader(std::move(shader))
{
}
id<MTLFunction> GetShader() const { return m_shader; }
BinaryData GetBinary() const override { return BinaryData(m_msl.begin(), m_msl.end()); }
private:
std::string m_msl;
MRCOwned<id<MTLFunction>> m_shader;
};
} // namespace Metal

Some files were not shown because too many files have changed in this diff Show More