summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorTellowKrinkle <tellowkrinkle@gmail.com>2022-06-13 02:24:33 -0500
committerTellowKrinkle <tellowkrinkle@gmail.com>2022-07-21 20:44:19 -0500
commit5742ccf8dedd382b17138cf0a40fa1102c66283c (patch)
tree398f626e0c562ed61f0ecc4f36eba15321d9bcda /Source/Core
parente32213d031f30e82d2d19ce26ef3a0262c061bd8 (diff)
VideoBackends:Metal: Cache pipelines
Metal pipelines hold less stuff than Dolphin pipelines, so duplicates will appear
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/VideoBackends/Metal/CMakeLists.txt3
-rw-r--r--Source/Core/VideoBackends/Metal/MTLObjectCache.h9
-rw-r--r--Source/Core/VideoBackends/Metal/MTLObjectCache.mm326
-rw-r--r--Source/Core/VideoBackends/Metal/MTLPipeline.h31
-rw-r--r--Source/Core/VideoBackends/Metal/MTLPipeline.mm12
-rw-r--r--Source/Core/VideoBackends/Metal/MTLRenderer.h1
-rw-r--r--Source/Core/VideoBackends/Metal/MTLRenderer.mm211
-rw-r--r--Source/Core/VideoBackends/Metal/MTLShader.h8
-rw-r--r--Source/Core/VideoBackends/Metal/MTLShader.mm19
-rw-r--r--Source/Core/VideoBackends/Metal/MTLVertexFormat.h23
-rw-r--r--Source/Core/VideoBackends/Metal/MTLVertexFormat.mm143
11 files changed, 555 insertions, 231 deletions
diff --git a/Source/Core/VideoBackends/Metal/CMakeLists.txt b/Source/Core/VideoBackends/Metal/CMakeLists.txt
index a3becc1d2e..744cb2f755 100644
--- a/Source/Core/VideoBackends/Metal/CMakeLists.txt
+++ b/Source/Core/VideoBackends/Metal/CMakeLists.txt
@@ -11,6 +11,7 @@ add_library(videometal
MTLPipeline.h
MTLRenderer.mm
MTLRenderer.h
+ MTLShader.mm
MTLShader.h
MTLStateTracker.mm
MTLStateTracker.h
@@ -18,6 +19,8 @@ add_library(videometal
MTLTexture.h
MTLUtil.mm
MTLUtil.h
+ MTLVertexFormat.mm
+ MTLVertexFormat.h
MTLVertexManager.mm
MTLVertexManager.h
VideoBackend.h
diff --git a/Source/Core/VideoBackends/Metal/MTLObjectCache.h b/Source/Core/VideoBackends/Metal/MTLObjectCache.h
index 2f2bcd48dc..86668e614d 100644
--- a/Source/Core/VideoBackends/Metal/MTLObjectCache.h
+++ b/Source/Core/VideoBackends/Metal/MTLObjectCache.h
@@ -10,8 +10,12 @@
#include "VideoCommon/RenderState.h"
+struct AbstractPipelineConfig;
+class AbstractPipeline;
+
namespace Metal
{
+class Shader;
extern MRCOwned<id<MTLDevice>> g_device;
extern MRCOwned<id<MTLCommandQueue>> g_queue;
@@ -87,7 +91,12 @@ public:
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];
diff --git a/Source/Core/VideoBackends/Metal/MTLObjectCache.mm b/Source/Core/VideoBackends/Metal/MTLObjectCache.mm
index afee912016..e9b836ce1c 100644
--- a/Source/Core/VideoBackends/Metal/MTLObjectCache.mm
+++ b/Source/Core/VideoBackends/Metal/MTLObjectCache.mm
@@ -3,6 +3,20 @@
#include "VideoBackends/Metal/MTLObjectCache.h"
+#include <map>
+#include <mutex>
+#include <optional>
+
+#include "Common/Assert.h"
+#include "Common/MsgHandler.h"
+
+#include "VideoBackends/Metal/MTLPipeline.h"
+#include "VideoBackends/Metal/MTLUtil.h"
+#include "VideoBackends/Metal/MTLVertexFormat.h"
+
+#include "VideoCommon/AbstractPipeline.h"
+#include "VideoCommon/NativeVertexFormat.h"
+#include "VideoCommon/VertexShaderGen.h"
#include "VideoCommon/VideoConfig.h"
MRCOwned<id<MTLDevice>> Metal::g_device;
@@ -14,6 +28,7 @@ static void SetupDepthStencil(
Metal::ObjectCache::ObjectCache()
{
+ m_internal = std::make_unique<Internal>();
SetupDepthStencil(m_dss);
}
@@ -170,3 +185,314 @@ void Metal::ObjectCache::ReloadSamplers()
for (auto& sampler : m_samplers)
sampler = nullptr;
}
+
+// MARK: Pipelines
+
+static MTLPrimitiveTopologyClass GetClass(PrimitiveType prim)
+{
+ switch (prim)
+ {
+ case PrimitiveType::Points:
+ return MTLPrimitiveTopologyClassPoint;
+ case PrimitiveType::Lines:
+ return MTLPrimitiveTopologyClassLine;
+ case PrimitiveType::Triangles:
+ case PrimitiveType::TriangleStrip:
+ return MTLPrimitiveTopologyClassTriangle;
+ }
+}
+
+static MTLPrimitiveType Convert(PrimitiveType prim)
+{
+ // clang-format off
+ switch (prim)
+ {
+ case PrimitiveType::Points: return MTLPrimitiveTypePoint;
+ case PrimitiveType::Lines: return MTLPrimitiveTypeLine;
+ case PrimitiveType::Triangles: return MTLPrimitiveTypeTriangle;
+ case PrimitiveType::TriangleStrip: return MTLPrimitiveTypeTriangleStrip;
+ }
+ // clang-format on
+}
+
+static MTLCullMode Convert(CullMode cull)
+{
+ switch (cull)
+ {
+ case CullMode::None:
+ case CullMode::All: // Handled by disabling rasterization
+ return MTLCullModeNone;
+ case CullMode::Front:
+ return MTLCullModeFront;
+ case CullMode::Back:
+ return MTLCullModeBack;
+ }
+}
+
+static MTLBlendFactor Convert(DstBlendFactor factor, bool usedualsrc)
+{
+ // clang-format off
+ switch (factor)
+ {
+ case DstBlendFactor::Zero: return MTLBlendFactorZero;
+ case DstBlendFactor::One: return MTLBlendFactorOne;
+ case DstBlendFactor::SrcClr: return MTLBlendFactorSourceColor;
+ case DstBlendFactor::InvSrcClr: return MTLBlendFactorOneMinusSourceColor;
+ case DstBlendFactor::SrcAlpha: return usedualsrc ? MTLBlendFactorSource1Alpha
+ : MTLBlendFactorSourceAlpha;
+ case DstBlendFactor::InvSrcAlpha: return usedualsrc ? MTLBlendFactorOneMinusSource1Alpha
+ : MTLBlendFactorOneMinusSourceAlpha;
+ case DstBlendFactor::DstAlpha: return MTLBlendFactorDestinationAlpha;
+ case DstBlendFactor::InvDstAlpha: return MTLBlendFactorOneMinusDestinationAlpha;
+ }
+ // clang-format on
+}
+
+static MTLBlendFactor Convert(SrcBlendFactor factor, bool usedualsrc)
+{
+ // clang-format off
+ switch (factor)
+ {
+ case SrcBlendFactor::Zero: return MTLBlendFactorZero;
+ case SrcBlendFactor::One: return MTLBlendFactorOne;
+ case SrcBlendFactor::DstClr: return MTLBlendFactorDestinationColor;
+ case SrcBlendFactor::InvDstClr: return MTLBlendFactorOneMinusDestinationColor;
+ case SrcBlendFactor::SrcAlpha: return usedualsrc ? MTLBlendFactorSource1Alpha
+ : MTLBlendFactorSourceAlpha;
+ case SrcBlendFactor::InvSrcAlpha: return usedualsrc ? MTLBlendFactorOneMinusSource1Alpha
+ : MTLBlendFactorOneMinusSourceAlpha;
+ case SrcBlendFactor::DstAlpha: return MTLBlendFactorDestinationAlpha;
+ case SrcBlendFactor::InvDstAlpha: return MTLBlendFactorOneMinusDestinationAlpha;
+ }
+ // clang-format on
+}
+
+class Metal::ObjectCache::Internal
+{
+public:
+ using StoredPipeline = std::pair<MRCOwned<id<MTLRenderPipelineState>>, PipelineReflection>;
+ /// Holds only the things that are actually used in a Metal pipeline
+ struct PipelineID
+ {
+ struct VertexAttribute
+ {
+ // Just hold the things that might differ while using the same shader
+ // (Really only a thing for ubershaders)
+ u8 offset : 6;
+ u8 components : 2;
+ VertexAttribute() = default;
+ explicit VertexAttribute(AttributeFormat format)
+ : offset(format.offset), components(format.components - 1)
+ {
+ if (!format.enable)
+ offset = 0x3F; // Set it to something unlikely
+ }
+ };
+ template <size_t N>
+ static void CopyAll(std::array<VertexAttribute, N>& output, const AttributeFormat (&input)[N])
+ {
+ for (size_t i = 0; i < N; ++i)
+ output[i] = VertexAttribute(input[i]);
+ }
+ PipelineID(const AbstractPipelineConfig& cfg)
+ {
+ memset(this, 0, sizeof(*this));
+ if (const NativeVertexFormat* v = cfg.vertex_format)
+ {
+ const PortableVertexDeclaration& decl = v->GetVertexDeclaration();
+ v_stride = v->GetVertexStride();
+ v_position = VertexAttribute(decl.position);
+ CopyAll(v_normals, decl.normals);
+ CopyAll(v_colors, decl.colors);
+ CopyAll(v_texcoords, decl.texcoords);
+ v_posmtx = VertexAttribute(decl.posmtx);
+ }
+ vertex_shader = static_cast<const Shader*>(cfg.vertex_shader);
+ fragment_shader = static_cast<const Shader*>(cfg.pixel_shader);
+ framebuffer.color_texture_format = cfg.framebuffer_state.color_texture_format.Value();
+ framebuffer.depth_texture_format = cfg.framebuffer_state.depth_texture_format.Value();
+ blend.colorupdate = cfg.blending_state.colorupdate.Value();
+ blend.alphaupdate = cfg.blending_state.alphaupdate.Value();
+ if (cfg.blending_state.blendenable)
+ {
+ // clang-format off
+ blend.blendenable = true;
+ blend.usedualsrc = cfg.blending_state.usedualsrc.Value();
+ blend.srcfactor = cfg.blending_state.srcfactor.Value();
+ blend.dstfactor = cfg.blending_state.dstfactor.Value();
+ blend.srcfactoralpha = cfg.blending_state.srcfactoralpha.Value();
+ blend.dstfactoralpha = cfg.blending_state.dstfactoralpha.Value();
+ blend.subtract = cfg.blending_state.subtract.Value();
+ blend.subtractAlpha = cfg.blending_state.subtractAlpha.Value();
+ // clang-format on
+ }
+ // Throw extras in bits we don't otherwise use
+ if (cfg.rasterization_state.cullmode == CullMode::All)
+ blend.hex |= 1 << 29;
+ if (cfg.rasterization_state.primitive == PrimitiveType::Points)
+ blend.hex |= 1 << 30;
+ else if (cfg.rasterization_state.primitive == PrimitiveType::Lines)
+ blend.hex |= 1 << 31;
+ }
+ PipelineID() { memset(this, 0, sizeof(*this)); }
+ PipelineID(const PipelineID& other) { memcpy(this, &other, sizeof(*this)); }
+ PipelineID& operator=(const PipelineID& other)
+ {
+ memcpy(this, &other, sizeof(*this));
+ return *this;
+ }
+ bool operator<(const PipelineID& other) const
+ {
+ return memcmp(this, &other, sizeof(*this)) < 0;
+ }
+ bool operator==(const PipelineID& other) const
+ {
+ return memcmp(this, &other, sizeof(*this)) == 0;
+ }
+
+ u8 v_stride;
+ VertexAttribute v_position;
+ std::array<VertexAttribute, 3> v_normals;
+ std::array<VertexAttribute, 2> v_colors;
+ std::array<VertexAttribute, 8> v_texcoords;
+ VertexAttribute v_posmtx;
+ const Shader* vertex_shader;
+ const Shader* fragment_shader;
+ BlendingState blend;
+ FramebufferState framebuffer;
+ };
+
+ std::mutex m_mtx;
+ std::condition_variable m_cv;
+ std::map<PipelineID, StoredPipeline> m_pipelines;
+ std::map<const Shader*, std::vector<PipelineID>> m_shaders;
+ std::array<u32, 3> m_pipeline_counter;
+
+ StoredPipeline CreatePipeline(const AbstractPipelineConfig& config)
+ {
+ @autoreleasepool
+ {
+ ASSERT(!config.geometry_shader);
+ auto desc = MRCTransfer([MTLRenderPipelineDescriptor new]);
+ [desc setVertexFunction:static_cast<const Shader*>(config.vertex_shader)->GetShader()];
+ [desc setFragmentFunction:static_cast<const Shader*>(config.pixel_shader)->GetShader()];
+ if (config.usage == AbstractPipelineUsage::GX)
+ {
+ if ([[[desc vertexFunction] label] containsString:@"Uber"])
+ [desc
+ setLabel:[NSString stringWithFormat:@"GX Uber Pipeline %d", m_pipeline_counter[0]++]];
+ else
+ [desc setLabel:[NSString stringWithFormat:@"GX Pipeline %d", m_pipeline_counter[1]++]];
+ }
+ else
+ {
+ [desc setLabel:[NSString stringWithFormat:@"Utility Pipeline %d", m_pipeline_counter[2]++]];
+ }
+ if (config.vertex_format)
+ [desc setVertexDescriptor:static_cast<const VertexFormat*>(config.vertex_format)->Get()];
+ RasterizationState rs = config.rasterization_state;
+ [desc setInputPrimitiveTopology:GetClass(rs.primitive)];
+ if (rs.cullmode == CullMode::All)
+ [desc setRasterizationEnabled:NO];
+ MTLRenderPipelineColorAttachmentDescriptor* color0 =
+ [[desc colorAttachments] objectAtIndexedSubscript:0];
+ BlendingState bs = config.blending_state;
+ MTLColorWriteMask mask = MTLColorWriteMaskNone;
+ if (bs.colorupdate)
+ mask |= MTLColorWriteMaskRed | MTLColorWriteMaskGreen | MTLColorWriteMaskBlue;
+ if (bs.alphaupdate)
+ mask |= MTLColorWriteMaskAlpha;
+ [color0 setWriteMask:mask];
+ if (bs.blendenable)
+ {
+ // clang-format off
+ [color0 setBlendingEnabled:YES];
+ [color0 setSourceRGBBlendFactor: Convert(bs.srcfactor, bs.usedualsrc)];
+ [color0 setSourceAlphaBlendFactor: Convert(bs.srcfactoralpha, bs.usedualsrc)];
+ [color0 setDestinationRGBBlendFactor: Convert(bs.dstfactor, bs.usedualsrc)];
+ [color0 setDestinationAlphaBlendFactor:Convert(bs.dstfactoralpha, bs.usedualsrc)];
+ [color0 setRgbBlendOperation: bs.subtract ? MTLBlendOperationReverseSubtract : MTLBlendOperationAdd];
+ [color0 setAlphaBlendOperation:bs.subtractAlpha ? MTLBlendOperationReverseSubtract : MTLBlendOperationAdd];
+ // clang-format on
+ }
+ FramebufferState fs = config.framebuffer_state;
+ [color0 setPixelFormat:Util::FromAbstract(fs.color_texture_format)];
+ [desc setDepthAttachmentPixelFormat:Util::FromAbstract(fs.depth_texture_format)];
+ if (Util::HasStencil(fs.depth_texture_format))
+ [desc setStencilAttachmentPixelFormat:Util::FromAbstract(fs.depth_texture_format)];
+ NSError* err = nullptr;
+ MTLRenderPipelineReflection* reflection = nullptr;
+ id<MTLRenderPipelineState> pipe =
+ [g_device newRenderPipelineStateWithDescriptor:desc
+ options:MTLPipelineOptionArgumentInfo
+ reflection:&reflection
+ error:&err];
+ if (err)
+ {
+ PanicAlertFmt("Failed to compile pipeline for {} and {}: {}",
+ [[[desc vertexFunction] label] UTF8String],
+ [[[desc fragmentFunction] label] UTF8String],
+ [[err localizedDescription] UTF8String]);
+ return std::make_pair(nullptr, PipelineReflection());
+ }
+
+ return std::make_pair(MRCTransfer(pipe), PipelineReflection(reflection));
+ }
+ }
+
+ StoredPipeline GetOrCreatePipeline(const AbstractPipelineConfig& config)
+ {
+ std::unique_lock<std::mutex> lock(m_mtx);
+ PipelineID pid(config);
+ auto it = m_pipelines.find(pid);
+ if (it != m_pipelines.end())
+ {
+ while (!it->second.first && !it->second.second.textures)
+ m_cv.wait(lock); // Wait for whoever's already compiling this
+ return it->second;
+ }
+ // Reserve the spot now, so other threads know we're making it
+ it = m_pipelines.insert({pid, {nullptr, PipelineReflection()}}).first;
+ lock.unlock();
+ StoredPipeline pipe = CreatePipeline(config);
+ lock.lock();
+ if (pipe.first)
+ it->second = pipe;
+ else
+ it->second.second.textures = 1; // Abuse this as a "failed to create pipeline" flag
+ m_shaders[pid.vertex_shader].push_back(pid);
+ m_shaders[pid.fragment_shader].push_back(pid);
+ lock.unlock();
+ m_cv.notify_all(); // Wake up anyone who might be waiting
+ return pipe;
+ }
+
+ void ShaderDestroyed(const Shader* shader)
+ {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ auto it = m_shaders.find(shader);
+ if (it == m_shaders.end())
+ return;
+ // It's unlikely, but if a shader is destroyed, a new one could be made with the same address
+ // (Also, we know it won't be used anymore, so there's no reason to keep these around)
+ for (const PipelineID& pid : it->second)
+ m_pipelines.erase(pid);
+ m_shaders.erase(it);
+ }
+};
+
+std::unique_ptr<AbstractPipeline>
+Metal::ObjectCache::CreatePipeline(const AbstractPipelineConfig& config)
+{
+ Internal::StoredPipeline pipeline = m_internal->GetOrCreatePipeline(config);
+ if (!pipeline.first)
+ return nullptr;
+ return std::make_unique<Pipeline>(
+ std::move(pipeline.first), pipeline.second, Convert(config.rasterization_state.primitive),
+ Convert(config.rasterization_state.cullmode), config.depth_state, config.usage);
+}
+
+void Metal::ObjectCache::ShaderDestroyed(const Shader* shader)
+{
+ m_internal->ShaderDestroyed(shader);
+}
diff --git a/Source/Core/VideoBackends/Metal/MTLPipeline.h b/Source/Core/VideoBackends/Metal/MTLPipeline.h
index d5a642aedd..aa4fadbdbf 100644
--- a/Source/Core/VideoBackends/Metal/MTLPipeline.h
+++ b/Source/Core/VideoBackends/Metal/MTLPipeline.h
@@ -14,24 +14,34 @@
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,
- MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim,
- MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage);
+ 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_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); }
+ 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;
@@ -39,10 +49,7 @@ private:
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;
+ PipelineReflection m_reflection;
};
class ComputePipeline : public Shader
diff --git a/Source/Core/VideoBackends/Metal/MTLPipeline.mm b/Source/Core/VideoBackends/Metal/MTLPipeline.mm
index 405ea76973..5afaa477a1 100644
--- a/Source/Core/VideoBackends/Metal/MTLPipeline.mm
+++ b/Source/Core/VideoBackends/Metal/MTLPipeline.mm
@@ -47,14 +47,18 @@ static void GetArguments(NSArray<MTLArgument*>* arguments, u32* textures, u32* s
}
}
+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,
- MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim,
+ 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_usage(usage), m_reflection(reflection)
{
- 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,
diff --git a/Source/Core/VideoBackends/Metal/MTLRenderer.h b/Source/Core/VideoBackends/Metal/MTLRenderer.h
index 6208e3f57e..f8b97dc0d3 100644
--- a/Source/Core/VideoBackends/Metal/MTLRenderer.h
+++ b/Source/Core/VideoBackends/Metal/MTLRenderer.h
@@ -82,7 +82,6 @@ private:
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();
diff --git a/Source/Core/VideoBackends/Metal/MTLRenderer.mm b/Source/Core/VideoBackends/Metal/MTLRenderer.mm
index b552b11722..e8e033c8aa 100644
--- a/Source/Core/VideoBackends/Metal/MTLRenderer.mm
+++ b/Source/Core/VideoBackends/Metal/MTLRenderer.mm
@@ -9,11 +9,10 @@
#include "VideoBackends/Metal/MTLStateTracker.h"
#include "VideoBackends/Metal/MTLTexture.h"
#include "VideoBackends/Metal/MTLUtil.h"
+#include "VideoBackends/Metal/MTLVertexFormat.h"
#include "VideoBackends/Metal/MTLVertexManager.h"
#include "VideoCommon/FramebufferManager.h"
-#include "VideoCommon/NativeVertexFormat.h"
-#include "VideoCommon/VertexShaderGen.h"
#include "VideoCommon/VideoBackendBase.h"
Metal::Renderer::Renderer(MRCOwned<CAMetalLayer*> layer, int width, int height, float layer_scale)
@@ -110,66 +109,6 @@ Metal::Renderer::CreateFramebuffer(AbstractTexture* color_attachment,
// MARK: Pipeline Creation
-namespace Metal
-{
-class VertexFormat : public NativeVertexFormat
-{
-public:
- VertexFormat(const PortableVertexDeclaration& vtx_decl)
- : NativeVertexFormat(vtx_decl), m_desc(MRCTransfer([MTLVertexDescriptor new]))
- {
- [[[m_desc layouts] objectAtIndexedSubscript:0] setStride:vtx_decl.stride];
- SetAttribute(SHADER_POSITION_ATTRIB, vtx_decl.position);
- SetAttributes(SHADER_NORMAL_ATTRIB, vtx_decl.normals);
- SetAttributes(SHADER_COLOR0_ATTRIB, vtx_decl.colors);
- SetAttributes(SHADER_TEXTURE0_ATTRIB, vtx_decl.texcoords);
- SetAttribute(SHADER_POSMTX_ATTRIB, vtx_decl.posmtx);
- }
-
- MTLVertexDescriptor* Get() const { return m_desc; }
-
-private:
- template <size_t N>
- void SetAttributes(u32 attribute, const AttributeFormat (&format)[N])
- {
- for (size_t i = 0; i < N; i++)
- SetAttribute(attribute + i, format[i]);
- }
- void SetAttribute(u32 attribute, const AttributeFormat& format)
- {
- if (!format.enable)
- return;
- MTLVertexAttributeDescriptor* desc = [[m_desc attributes] objectAtIndexedSubscript:attribute];
- [desc setFormat:ConvertFormat(format.type, format.components, format.integer)];
- [desc setOffset:format.offset];
- [desc setBufferIndex:0];
- }
-
- static MTLVertexFormat ConvertFormat(ComponentFormat format, int count, bool int_format)
- {
- static constexpr MTLVertexFormat formats[2][5][4] = {
- [false] = {
- [static_cast<int>(ComponentFormat::UByte)] = { MTLVertexFormatUCharNormalized, MTLVertexFormatUChar2Normalized, MTLVertexFormatUChar3Normalized, MTLVertexFormatUChar4Normalized },
- [static_cast<int>(ComponentFormat::Byte)] = { MTLVertexFormatCharNormalized, MTLVertexFormatChar2Normalized, MTLVertexFormatChar3Normalized, MTLVertexFormatChar4Normalized },
- [static_cast<int>(ComponentFormat::UShort)] = { MTLVertexFormatUShortNormalized, MTLVertexFormatUShort2Normalized, MTLVertexFormatUShort3Normalized, MTLVertexFormatUShort4Normalized },
- [static_cast<int>(ComponentFormat::Short)] = { MTLVertexFormatShortNormalized, MTLVertexFormatShort2Normalized, MTLVertexFormatShort3Normalized, MTLVertexFormatShort4Normalized },
- [static_cast<int>(ComponentFormat::Float)] = { MTLVertexFormatFloat, MTLVertexFormatFloat2, MTLVertexFormatFloat3, MTLVertexFormatFloat4 },
- },
- [true] = {
- [static_cast<int>(ComponentFormat::UByte)] = { MTLVertexFormatUChar, MTLVertexFormatUChar2, MTLVertexFormatUChar3, MTLVertexFormatUChar4 },
- [static_cast<int>(ComponentFormat::Byte)] = { MTLVertexFormatChar, MTLVertexFormatChar2, MTLVertexFormatChar3, MTLVertexFormatChar4 },
- [static_cast<int>(ComponentFormat::UShort)] = { MTLVertexFormatUShort, MTLVertexFormatUShort2, MTLVertexFormatUShort3, MTLVertexFormatUShort4 },
- [static_cast<int>(ComponentFormat::Short)] = { MTLVertexFormatShort, MTLVertexFormatShort2, MTLVertexFormatShort3, MTLVertexFormatShort4 },
- [static_cast<int>(ComponentFormat::Float)] = { MTLVertexFormatFloat, MTLVertexFormatFloat2, MTLVertexFormatFloat3, MTLVertexFormatFloat4 },
- },
- };
- return formats[int_format][static_cast<int>(format)][count - 1];
- }
-
- MRCOwned<MTLVertexDescriptor*> m_desc;
-};
-} // namespace Metal
-
std::unique_ptr<AbstractShader> Metal::Renderer::CreateShaderFromSource(ShaderStage stage,
std::string_view source,
std::string_view name)
@@ -311,157 +250,11 @@ Metal::Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_d
}
}
-static MTLPrimitiveTopologyClass GetClass(PrimitiveType prim)
-{
- switch (prim)
- {
- case PrimitiveType::Points:
- return MTLPrimitiveTopologyClassPoint;
- case PrimitiveType::Lines:
- return MTLPrimitiveTopologyClassLine;
- case PrimitiveType::Triangles:
- case PrimitiveType::TriangleStrip:
- return MTLPrimitiveTopologyClassTriangle;
- }
-}
-
-static MTLPrimitiveType Convert(PrimitiveType prim)
-{
- switch (prim)
- {
- case PrimitiveType::Points: return MTLPrimitiveTypePoint;
- case PrimitiveType::Lines: return MTLPrimitiveTypeLine;
- case PrimitiveType::Triangles: return MTLPrimitiveTypeTriangle;
- case PrimitiveType::TriangleStrip: return MTLPrimitiveTypeTriangleStrip;
- }
-}
-
-static MTLCullMode Convert(CullMode cull)
-{
- switch (cull)
- {
- case CullMode::None:
- case CullMode::All: // Handled by disabling rasterization
- return MTLCullModeNone;
- case CullMode::Front:
- return MTLCullModeFront;
- case CullMode::Back:
- return MTLCullModeBack;
- }
-}
-
-static MTLBlendFactor Convert(DstBlendFactor factor, bool src1)
-{
- static constexpr MTLBlendFactor factors[2][8] = {
- [false] = {
- [static_cast<int>(DstBlendFactor::Zero)] = MTLBlendFactorZero,
- [static_cast<int>(DstBlendFactor::One)] = MTLBlendFactorOne,
- [static_cast<int>(DstBlendFactor::SrcClr)] = MTLBlendFactorSourceColor,
- [static_cast<int>(DstBlendFactor::InvSrcClr)] = MTLBlendFactorOneMinusSourceColor,
- [static_cast<int>(DstBlendFactor::SrcAlpha)] = MTLBlendFactorSourceAlpha,
- [static_cast<int>(DstBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSourceAlpha,
- [static_cast<int>(DstBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha,
- [static_cast<int>(DstBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha,
- },
- [true] = {
- [static_cast<int>(DstBlendFactor::Zero)] = MTLBlendFactorZero,
- [static_cast<int>(DstBlendFactor::One)] = MTLBlendFactorOne,
- [static_cast<int>(DstBlendFactor::SrcClr)] = MTLBlendFactorSourceColor,
- [static_cast<int>(DstBlendFactor::InvSrcClr)] = MTLBlendFactorOneMinusSource1Color,
- [static_cast<int>(DstBlendFactor::SrcAlpha)] = MTLBlendFactorSource1Alpha,
- [static_cast<int>(DstBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSource1Alpha,
- [static_cast<int>(DstBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha,
- [static_cast<int>(DstBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha,
- },
- };
- return factors[src1][static_cast<int>(factor)];
-}
-
-static MTLBlendFactor Convert(SrcBlendFactor factor, bool src1)
-{
- static constexpr MTLBlendFactor factors[2][8] = {
- [false] = {
- [static_cast<int>(SrcBlendFactor::Zero)] = MTLBlendFactorZero,
- [static_cast<int>(SrcBlendFactor::One)] = MTLBlendFactorOne,
- [static_cast<int>(SrcBlendFactor::DstClr)] = MTLBlendFactorDestinationColor,
- [static_cast<int>(SrcBlendFactor::InvDstClr)] = MTLBlendFactorOneMinusDestinationColor,
- [static_cast<int>(SrcBlendFactor::SrcAlpha)] = MTLBlendFactorSourceAlpha,
- [static_cast<int>(SrcBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSourceAlpha,
- [static_cast<int>(SrcBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha,
- [static_cast<int>(SrcBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha,
- },
- [true] = {
- [static_cast<int>(SrcBlendFactor::Zero)] = MTLBlendFactorZero,
- [static_cast<int>(SrcBlendFactor::One)] = MTLBlendFactorOne,
- [static_cast<int>(SrcBlendFactor::DstClr)] = MTLBlendFactorDestinationColor,
- [static_cast<int>(SrcBlendFactor::InvDstClr)] = MTLBlendFactorOneMinusDestinationColor,
- [static_cast<int>(SrcBlendFactor::SrcAlpha)] = MTLBlendFactorSource1Alpha,
- [static_cast<int>(SrcBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSource1Alpha,
- [static_cast<int>(SrcBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha,
- [static_cast<int>(SrcBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha,
- },
- };
- return factors[src1][static_cast<int>(factor)];
-}
-
std::unique_ptr<AbstractPipeline>
Metal::Renderer::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data,
size_t cache_data_length)
{
- @autoreleasepool
- {
- assert(!config.geometry_shader);
- auto desc = MRCTransfer([MTLRenderPipelineDescriptor new]);
- [desc setLabel:[NSString stringWithFormat:@"Pipeline %d", m_pipeline_counter++]];
- [desc setVertexFunction:static_cast<const Shader*>(config.vertex_shader)->GetShader()];
- [desc setFragmentFunction:static_cast<const Shader*>(config.pixel_shader)->GetShader()];
- if (config.vertex_format)
- [desc setVertexDescriptor:static_cast<const VertexFormat*>(config.vertex_format)->Get()];
- RasterizationState rs = config.rasterization_state;
- [desc setInputPrimitiveTopology:GetClass(rs.primitive)];
- if (rs.cullmode == CullMode::All)
- [desc setRasterizationEnabled:NO];
- MTLRenderPipelineColorAttachmentDescriptor* color0 = [desc colorAttachments][0];
- BlendingState bs = config.blending_state;
- MTLColorWriteMask mask = MTLColorWriteMaskNone;
- if (bs.colorupdate)
- mask |= MTLColorWriteMaskRed | MTLColorWriteMaskGreen | MTLColorWriteMaskBlue;
- if (bs.alphaupdate)
- mask |= MTLColorWriteMaskAlpha;
- [color0 setWriteMask:mask];
- if (bs.blendenable)
- {
- [color0 setBlendingEnabled:YES];
- [color0 setSourceRGBBlendFactor: Convert(bs.srcfactor, bs.usedualsrc)];
- [color0 setSourceAlphaBlendFactor: Convert(bs.srcfactoralpha, bs.usedualsrc)];
- [color0 setDestinationRGBBlendFactor: Convert(bs.dstfactor, bs.usedualsrc)];
- [color0 setDestinationAlphaBlendFactor:Convert(bs.dstfactoralpha, bs.usedualsrc)];
- [color0 setRgbBlendOperation: bs.subtract ? MTLBlendOperationReverseSubtract : MTLBlendOperationAdd];
- [color0 setAlphaBlendOperation:bs.subtractAlpha ? MTLBlendOperationReverseSubtract : MTLBlendOperationAdd];
- }
- FramebufferState fs = config.framebuffer_state;
- [color0 setPixelFormat:Util::FromAbstract(fs.color_texture_format)];
- [desc setDepthAttachmentPixelFormat:Util::FromAbstract(fs.depth_texture_format)];
- if (Util::HasStencil(fs.depth_texture_format))
- [desc setStencilAttachmentPixelFormat:Util::FromAbstract(fs.depth_texture_format)];
- NSError* err = nullptr;
- MTLRenderPipelineReflection* reflection = nullptr;
- id<MTLRenderPipelineState> pipe =
- [g_device newRenderPipelineStateWithDescriptor:desc
- options:MTLPipelineOptionArgumentInfo
- reflection:&reflection
- error:&err];
- if (err)
- {
- PanicAlertFmt("Failed to compile pipeline for {} and {}: {}",
- [[[desc vertexFunction] label] UTF8String],
- [[[desc fragmentFunction] label] UTF8String],
- [[err localizedDescription] UTF8String]);
- return nullptr;
- }
- return std::make_unique<Pipeline>(MRCTransfer(pipe), reflection, Convert(rs.primitive),
- Convert(rs.cullmode), config.depth_state, config.usage);
- }
+ return g_object_cache->CreatePipeline(config);
}
void Metal::Renderer::Flush()
diff --git a/Source/Core/VideoBackends/Metal/MTLShader.h b/Source/Core/VideoBackends/Metal/MTLShader.h
index 053d84c2b9..018346bb6d 100644
--- a/Source/Core/VideoBackends/Metal/MTLShader.h
+++ b/Source/Core/VideoBackends/Metal/MTLShader.h
@@ -15,13 +15,11 @@ 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))
- {
- }
+ explicit Shader(ShaderStage stage, std::string msl, MRCOwned<id<MTLFunction>> shader);
+ ~Shader();
id<MTLFunction> GetShader() const { return m_shader; }
- BinaryData GetBinary() const override { return BinaryData(m_msl.begin(), m_msl.end()); }
+ BinaryData GetBinary() const override;
private:
std::string m_msl;
diff --git a/Source/Core/VideoBackends/Metal/MTLShader.mm b/Source/Core/VideoBackends/Metal/MTLShader.mm
new file mode 100644
index 0000000000..a1afcb160b
--- /dev/null
+++ b/Source/Core/VideoBackends/Metal/MTLShader.mm
@@ -0,0 +1,19 @@
+
+#include "VideoBackends/Metal/MTLShader.h"
+
+#include "VideoBackends/Metal/MTLObjectCache.h"
+
+Metal::Shader::Shader(ShaderStage stage, std::string msl, MRCOwned<id<MTLFunction>> shader)
+ : AbstractShader(stage), m_msl(std::move(msl)), m_shader(std::move(shader))
+{
+}
+
+Metal::Shader::~Shader()
+{
+ g_object_cache->ShaderDestroyed(this);
+}
+
+AbstractShader::BinaryData Metal::Shader::GetBinary() const
+{
+ return BinaryData(m_msl.begin(), m_msl.end());
+}
diff --git a/Source/Core/VideoBackends/Metal/MTLVertexFormat.h b/Source/Core/VideoBackends/Metal/MTLVertexFormat.h
new file mode 100644
index 0000000000..e4babc057b
--- /dev/null
+++ b/Source/Core/VideoBackends/Metal/MTLVertexFormat.h
@@ -0,0 +1,23 @@
+// 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/NativeVertexFormat.h"
+
+namespace Metal
+{
+class VertexFormat : public NativeVertexFormat
+{
+public:
+ VertexFormat(const PortableVertexDeclaration& vtx_decl);
+
+ MTLVertexDescriptor* Get() const { return m_desc; }
+
+ MRCOwned<MTLVertexDescriptor*> m_desc;
+};
+} // namespace Metal
diff --git a/Source/Core/VideoBackends/Metal/MTLVertexFormat.mm b/Source/Core/VideoBackends/Metal/MTLVertexFormat.mm
new file mode 100644
index 0000000000..6453c5189a
--- /dev/null
+++ b/Source/Core/VideoBackends/Metal/MTLVertexFormat.mm
@@ -0,0 +1,143 @@
+// Copyright 2022 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "VideoBackends/Metal/MTLVertexFormat.h"
+
+#include "VideoCommon/VertexShaderGen.h"
+
+static MTLVertexFormat ConvertFormat(ComponentFormat format, int count, bool int_format)
+{
+ // clang-format off
+ if (int_format)
+ {
+ switch (format)
+ {
+ case ComponentFormat::UByte:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatUChar;
+ case 2: return MTLVertexFormatUChar2;
+ case 3: return MTLVertexFormatUChar3;
+ case 4: return MTLVertexFormatUChar4;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::Byte:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatChar;
+ case 2: return MTLVertexFormatChar2;
+ case 3: return MTLVertexFormatChar3;
+ case 4: return MTLVertexFormatChar4;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::UShort:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatUShort;
+ case 2: return MTLVertexFormatUShort2;
+ case 3: return MTLVertexFormatUShort3;
+ case 4: return MTLVertexFormatUShort4;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::Short:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatShort;
+ case 2: return MTLVertexFormatShort2;
+ case 3: return MTLVertexFormatShort3;
+ case 4: return MTLVertexFormatShort4;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::Float:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatFloat;
+ case 2: return MTLVertexFormatFloat2;
+ case 3: return MTLVertexFormatFloat3;
+ case 4: return MTLVertexFormatFloat4;
+ default: return MTLVertexFormatInvalid;
+ }
+ }
+ }
+ else
+ {
+ switch (format)
+ {
+ case ComponentFormat::UByte:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatUCharNormalized;
+ case 2: return MTLVertexFormatUChar2Normalized;
+ case 3: return MTLVertexFormatUChar3Normalized;
+ case 4: return MTLVertexFormatUChar4Normalized;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::Byte:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatCharNormalized;
+ case 2: return MTLVertexFormatChar2Normalized;
+ case 3: return MTLVertexFormatChar3Normalized;
+ case 4: return MTLVertexFormatChar4Normalized;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::UShort:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatUShortNormalized;
+ case 2: return MTLVertexFormatUShort2Normalized;
+ case 3: return MTLVertexFormatUShort3Normalized;
+ case 4: return MTLVertexFormatUShort4Normalized;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::Short:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatShortNormalized;
+ case 2: return MTLVertexFormatShort2Normalized;
+ case 3: return MTLVertexFormatShort3Normalized;
+ case 4: return MTLVertexFormatShort4Normalized;
+ default: return MTLVertexFormatInvalid;
+ }
+ case ComponentFormat::Float:
+ switch (count)
+ {
+ case 1: return MTLVertexFormatFloat;
+ case 2: return MTLVertexFormatFloat2;
+ case 3: return MTLVertexFormatFloat3;
+ case 4: return MTLVertexFormatFloat4;
+ default: return MTLVertexFormatInvalid;
+ }
+ }
+ }
+ // clang-format on
+}
+
+static void SetAttribute(MTLVertexDescriptor* desc, u32 attribute, const AttributeFormat& format)
+{
+ if (!format.enable)
+ return;
+ MTLVertexAttributeDescriptor* attr_desc = [[desc attributes] objectAtIndexedSubscript:attribute];
+ [attr_desc setFormat:ConvertFormat(format.type, format.components, format.integer)];
+ [attr_desc setOffset:format.offset];
+ [attr_desc setBufferIndex:0];
+}
+
+template <size_t N>
+static void SetAttributes(MTLVertexDescriptor* desc, u32 attribute,
+ const AttributeFormat (&format)[N])
+{
+ for (size_t i = 0; i < N; ++i)
+ SetAttribute(desc, attribute + i, format[i]);
+}
+
+Metal::VertexFormat::VertexFormat(const PortableVertexDeclaration& vtx_decl)
+ : NativeVertexFormat(vtx_decl), m_desc(MRCTransfer([MTLVertexDescriptor new]))
+{
+ [[[m_desc layouts] objectAtIndexedSubscript:0] setStride:vtx_decl.stride];
+ SetAttribute(m_desc, SHADER_POSITION_ATTRIB, vtx_decl.position);
+ SetAttributes(m_desc, SHADER_NORMAL_ATTRIB, vtx_decl.normals);
+ SetAttributes(m_desc, SHADER_COLOR0_ATTRIB, vtx_decl.colors);
+ SetAttributes(m_desc, SHADER_TEXTURE0_ATTRIB, vtx_decl.texcoords);
+ SetAttribute(m_desc, SHADER_POSMTX_ATTRIB, vtx_decl.posmtx);
+}