Merge pull request #10754 from tellowkrinkle/Metal

VideoBackends: Add Metal backend
This commit is contained in:
JMC47
2022-07-23 01:57:35 -04:00
committed by GitHub
58 changed files with 4318 additions and 55 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()
@@ -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);
+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;
@@ -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];
}
}
}
+166
View File
@@ -0,0 +1,166 @@
// 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);
auto adapters = Util::GetAdapterList();
Util::PopulateBackendInfoAdapters(&g_Config, adapters);
if (!adapters.empty())
{
// Use the selected adapter, or the first to fill features.
size_t index = static_cast<size_t>(g_Config.iAdapter);
if (index >= adapters.size())
index = 0;
Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]);
}
}
}
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,106 @@
// 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"
struct AbstractPipelineConfig;
class AbstractPipeline;
namespace Metal
{
class Shader;
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)
{
if (__builtin_expect(!m_samplers[sel.value], false))
m_samplers[sel.value] = CreateSampler(sel);
return m_samplers[sel.value];
}
id<MTLSamplerState> GetSampler(SamplerState state) { return GetSampler(SamplerSelector(state)); }
void ReloadSamplers();
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config);
void ShaderDestroyed(const Shader* shader);
private:
class Internal;
std::unique_ptr<Internal> m_internal;
MRCOwned<id<MTLSamplerState>> CreateSampler(SamplerSelector sel);
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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <condition_variable>
#include <mutex>
#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;
void FlushResults() override;
bool IsFlushed() const override;
/// Notify PerfQuery of a new pending encoder
/// One call to ReturnResults should be made for every call to IncCount
void IncCount() { m_query_count.fetch_add(1, std::memory_order_relaxed); }
/// May be called from any thread
void ReturnResults(const u64* data, const PerfQueryGroup* groups, size_t count, u32 query_id);
private:
u32 m_current_query = 0;
std::mutex m_results_mtx;
std::condition_variable m_cv;
};
} // namespace Metal
@@ -0,0 +1,90 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/Metal/MTLPerfQuery.h"
#include "VideoBackends/Metal/MTLStateTracker.h"
void Metal::PerfQuery::EnableQuery(PerfQueryGroup type)
{
if (type == PQG_ZCOMP_ZCOMPLOC || type == PQG_ZCOMP)
g_state_tracker->EnablePerfQuery(type, m_current_query);
}
void Metal::PerfQuery::DisableQuery(PerfQueryGroup type)
{
if (type == PQG_ZCOMP_ZCOMPLOC || type == PQG_ZCOMP)
g_state_tracker->DisablePerfQuery();
}
void Metal::PerfQuery::ResetQuery()
{
std::lock_guard<std::mutex> lock(m_results_mtx);
m_current_query++;
for (std::atomic<u32>& result : m_results)
result.store(0, std::memory_order_relaxed);
}
u32 Metal::PerfQuery::GetQueryResult(PerfQueryType type)
{
u32 result = 0;
if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC)
{
result = m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed);
}
else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT)
{
result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed);
}
else if (type == PQ_BLEND_INPUT)
{
result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed) +
m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed);
}
else if (type == PQ_EFB_COPY_CLOCKS)
{
result = m_results[PQG_EFB_COPY_CLOCKS].load(std::memory_order_relaxed);
}
return result;
}
void Metal::PerfQuery::FlushResults()
{
if (IsFlushed())
return;
// There's a possibility that some active performance queries are unflushed
g_state_tracker->FlushEncoders();
std::unique_lock<std::mutex> lock(m_results_mtx);
while (!IsFlushed())
m_cv.wait(lock);
}
bool Metal::PerfQuery::IsFlushed() const
{
return m_query_count.load(std::memory_order_acquire) == 0;
}
void Metal::PerfQuery::ReturnResults(const u64* data, const PerfQueryGroup* groups, size_t count,
u32 query_id)
{
{
std::lock_guard<std::mutex> lock(m_results_mtx);
if (m_current_query == query_id)
{
for (size_t i = 0; i < count; ++i)
{
u64 native_res_result = data[i] * (EFB_WIDTH * EFB_HEIGHT) /
(g_renderer->GetTargetWidth() * g_renderer->GetTargetHeight());
native_res_result /= g_ActiveConfig.iMultisamples;
m_results[groups[i]].fetch_add(native_res_result, std::memory_order_relaxed);
}
}
m_query_count.fetch_sub(1, std::memory_order_release);
}
m_cv.notify_one();
}
@@ -0,0 +1,71 @@
// 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
{
struct PipelineReflection
{
u32 textures = 0;
u32 samplers = 0;
u32 vertex_buffers = 0;
u32 fragment_buffers = 0;
PipelineReflection() = default;
explicit PipelineReflection(MTLRenderPipelineReflection* reflection);
};
class Pipeline final : public AbstractPipeline
{
public:
explicit Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
const PipelineReflection& 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_reflection.textures; }
u32 GetSamplers() const { return m_reflection.samplers; }
u32 GetVertexBuffers() const { return m_reflection.vertex_buffers; }
u32 GetFragmentBuffers() const { return m_reflection.fragment_buffers; }
bool UsesVertexBuffer(u32 index) const { return m_reflection.vertex_buffers & (1 << index); }
bool UsesFragmentBuffer(u32 index) const { return m_reflection.fragment_buffers & (1 << index); }
private:
MRCOwned<id<MTLRenderPipelineState>> m_pipeline;
MTLPrimitiveType m_prim;
MTLCullMode m_cull;
DepthStencilSelector m_depth_stencil;
AbstractPipelineUsage m_usage;
PipelineReflection m_reflection;
};
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,70 @@
// 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::PipelineReflection::PipelineReflection(MTLRenderPipelineReflection* reflection)
{
GetArguments([reflection vertexArguments], nullptr, nullptr, &vertex_buffers);
GetArguments([reflection fragmentArguments], &textures, &samplers, &fragment_buffers);
}
Metal::Pipeline::Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
const PipelineReflection& 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), m_reflection(reflection)
{
}
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,90 @@
// 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 = {};
void CheckForSurfaceChange();
void CheckForSurfaceResize();
void SetupSurface();
};
} // namespace Metal

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