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> g_device; extern MRCOwned> g_queue; @@ -87,7 +91,12 @@ public: void ReloadSamplers(); + std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config); + void ShaderDestroyed(const Shader* shader); + private: + class Internal; + std::unique_ptr m_internal; MRCOwned> CreateSampler(SamplerSelector sel); MRCOwned> m_dss[DepthStencilSelector::N_VALUES]; MRCOwned> 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 +#include +#include + +#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> Metal::g_device; @@ -14,6 +28,7 @@ static void SetupDepthStencil( Metal::ObjectCache::ObjectCache() { + m_internal = std::make_unique(); 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>, 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 + static void CopyAll(std::array& 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(cfg.vertex_shader); + fragment_shader = static_cast(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 v_normals; + std::array v_colors; + std::array 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 m_pipelines; + std::map> m_shaders; + std::array m_pipeline_counter; + + StoredPipeline CreatePipeline(const AbstractPipelineConfig& config) + { + @autoreleasepool + { + ASSERT(!config.geometry_shader); + auto desc = MRCTransfer([MTLRenderPipelineDescriptor new]); + [desc setVertexFunction:static_cast(config.vertex_shader)->GetShader()]; + [desc setFragmentFunction:static_cast(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(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 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 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 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 +Metal::ObjectCache::CreatePipeline(const AbstractPipelineConfig& config) +{ + Internal::StoredPipeline pipeline = m_internal->GetOrCreatePipeline(config); + if (!pipeline.first) + return nullptr; + return std::make_unique( + 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> pipeline, - MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim, - MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage); + const PipelineReflection& reflection, MTLPrimitiveType prim, MTLCullMode cull, + DepthState depth, AbstractPipelineUsage usage); id 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> 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* 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> 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 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 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 - 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(ComponentFormat::UByte)] = { MTLVertexFormatUCharNormalized, MTLVertexFormatUChar2Normalized, MTLVertexFormatUChar3Normalized, MTLVertexFormatUChar4Normalized }, - [static_cast(ComponentFormat::Byte)] = { MTLVertexFormatCharNormalized, MTLVertexFormatChar2Normalized, MTLVertexFormatChar3Normalized, MTLVertexFormatChar4Normalized }, - [static_cast(ComponentFormat::UShort)] = { MTLVertexFormatUShortNormalized, MTLVertexFormatUShort2Normalized, MTLVertexFormatUShort3Normalized, MTLVertexFormatUShort4Normalized }, - [static_cast(ComponentFormat::Short)] = { MTLVertexFormatShortNormalized, MTLVertexFormatShort2Normalized, MTLVertexFormatShort3Normalized, MTLVertexFormatShort4Normalized }, - [static_cast(ComponentFormat::Float)] = { MTLVertexFormatFloat, MTLVertexFormatFloat2, MTLVertexFormatFloat3, MTLVertexFormatFloat4 }, - }, - [true] = { - [static_cast(ComponentFormat::UByte)] = { MTLVertexFormatUChar, MTLVertexFormatUChar2, MTLVertexFormatUChar3, MTLVertexFormatUChar4 }, - [static_cast(ComponentFormat::Byte)] = { MTLVertexFormatChar, MTLVertexFormatChar2, MTLVertexFormatChar3, MTLVertexFormatChar4 }, - [static_cast(ComponentFormat::UShort)] = { MTLVertexFormatUShort, MTLVertexFormatUShort2, MTLVertexFormatUShort3, MTLVertexFormatUShort4 }, - [static_cast(ComponentFormat::Short)] = { MTLVertexFormatShort, MTLVertexFormatShort2, MTLVertexFormatShort3, MTLVertexFormatShort4 }, - [static_cast(ComponentFormat::Float)] = { MTLVertexFormatFloat, MTLVertexFormatFloat2, MTLVertexFormatFloat3, MTLVertexFormatFloat4 }, - }, - }; - return formats[int_format][static_cast(format)][count - 1]; - } - - MRCOwned m_desc; -}; -} // namespace Metal - std::unique_ptr 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(DstBlendFactor::Zero)] = MTLBlendFactorZero, - [static_cast(DstBlendFactor::One)] = MTLBlendFactorOne, - [static_cast(DstBlendFactor::SrcClr)] = MTLBlendFactorSourceColor, - [static_cast(DstBlendFactor::InvSrcClr)] = MTLBlendFactorOneMinusSourceColor, - [static_cast(DstBlendFactor::SrcAlpha)] = MTLBlendFactorSourceAlpha, - [static_cast(DstBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSourceAlpha, - [static_cast(DstBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha, - [static_cast(DstBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha, - }, - [true] = { - [static_cast(DstBlendFactor::Zero)] = MTLBlendFactorZero, - [static_cast(DstBlendFactor::One)] = MTLBlendFactorOne, - [static_cast(DstBlendFactor::SrcClr)] = MTLBlendFactorSourceColor, - [static_cast(DstBlendFactor::InvSrcClr)] = MTLBlendFactorOneMinusSource1Color, - [static_cast(DstBlendFactor::SrcAlpha)] = MTLBlendFactorSource1Alpha, - [static_cast(DstBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSource1Alpha, - [static_cast(DstBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha, - [static_cast(DstBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha, - }, - }; - return factors[src1][static_cast(factor)]; -} - -static MTLBlendFactor Convert(SrcBlendFactor factor, bool src1) -{ - static constexpr MTLBlendFactor factors[2][8] = { - [false] = { - [static_cast(SrcBlendFactor::Zero)] = MTLBlendFactorZero, - [static_cast(SrcBlendFactor::One)] = MTLBlendFactorOne, - [static_cast(SrcBlendFactor::DstClr)] = MTLBlendFactorDestinationColor, - [static_cast(SrcBlendFactor::InvDstClr)] = MTLBlendFactorOneMinusDestinationColor, - [static_cast(SrcBlendFactor::SrcAlpha)] = MTLBlendFactorSourceAlpha, - [static_cast(SrcBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSourceAlpha, - [static_cast(SrcBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha, - [static_cast(SrcBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha, - }, - [true] = { - [static_cast(SrcBlendFactor::Zero)] = MTLBlendFactorZero, - [static_cast(SrcBlendFactor::One)] = MTLBlendFactorOne, - [static_cast(SrcBlendFactor::DstClr)] = MTLBlendFactorDestinationColor, - [static_cast(SrcBlendFactor::InvDstClr)] = MTLBlendFactorOneMinusDestinationColor, - [static_cast(SrcBlendFactor::SrcAlpha)] = MTLBlendFactorSource1Alpha, - [static_cast(SrcBlendFactor::InvSrcAlpha)] = MTLBlendFactorOneMinusSource1Alpha, - [static_cast(SrcBlendFactor::DstAlpha)] = MTLBlendFactorDestinationAlpha, - [static_cast(SrcBlendFactor::InvDstAlpha)] = MTLBlendFactorOneMinusDestinationAlpha, - }, - }; - return factors[src1][static_cast(factor)]; -} - std::unique_ptr 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(config.vertex_shader)->GetShader()]; - [desc setFragmentFunction:static_cast(config.pixel_shader)->GetShader()]; - if (config.vertex_format) - [desc setVertexDescriptor:static_cast(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 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(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> shader) - : AbstractShader(stage), m_msl(std::move(msl)), m_shader(std::move(shader)) - { - } + explicit Shader(ShaderStage stage, std::string msl, MRCOwned> shader); + ~Shader(); id 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> 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 + +#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 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 +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); +}