VideoBackends:Metal: Cache pipelines
Metal pipelines hold less stuff than Dolphin pipelines, so duplicates will appear
This commit is contained in:
parent
e32213d031
commit
5742ccf8de
|
@ -11,6 +11,7 @@ add_library(videometal
|
||||||
MTLPipeline.h
|
MTLPipeline.h
|
||||||
MTLRenderer.mm
|
MTLRenderer.mm
|
||||||
MTLRenderer.h
|
MTLRenderer.h
|
||||||
|
MTLShader.mm
|
||||||
MTLShader.h
|
MTLShader.h
|
||||||
MTLStateTracker.mm
|
MTLStateTracker.mm
|
||||||
MTLStateTracker.h
|
MTLStateTracker.h
|
||||||
|
@ -18,6 +19,8 @@ add_library(videometal
|
||||||
MTLTexture.h
|
MTLTexture.h
|
||||||
MTLUtil.mm
|
MTLUtil.mm
|
||||||
MTLUtil.h
|
MTLUtil.h
|
||||||
|
MTLVertexFormat.mm
|
||||||
|
MTLVertexFormat.h
|
||||||
MTLVertexManager.mm
|
MTLVertexManager.mm
|
||||||
MTLVertexManager.h
|
MTLVertexManager.h
|
||||||
VideoBackend.h
|
VideoBackend.h
|
||||||
|
|
|
@ -10,8 +10,12 @@
|
||||||
|
|
||||||
#include "VideoCommon/RenderState.h"
|
#include "VideoCommon/RenderState.h"
|
||||||
|
|
||||||
|
struct AbstractPipelineConfig;
|
||||||
|
class AbstractPipeline;
|
||||||
|
|
||||||
namespace Metal
|
namespace Metal
|
||||||
{
|
{
|
||||||
|
class Shader;
|
||||||
extern MRCOwned<id<MTLDevice>> g_device;
|
extern MRCOwned<id<MTLDevice>> g_device;
|
||||||
extern MRCOwned<id<MTLCommandQueue>> g_queue;
|
extern MRCOwned<id<MTLCommandQueue>> g_queue;
|
||||||
|
|
||||||
|
@ -87,7 +91,12 @@ public:
|
||||||
|
|
||||||
void ReloadSamplers();
|
void ReloadSamplers();
|
||||||
|
|
||||||
|
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config);
|
||||||
|
void ShaderDestroyed(const Shader* shader);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class Internal;
|
||||||
|
std::unique_ptr<Internal> m_internal;
|
||||||
MRCOwned<id<MTLSamplerState>> CreateSampler(SamplerSelector sel);
|
MRCOwned<id<MTLSamplerState>> CreateSampler(SamplerSelector sel);
|
||||||
MRCOwned<id<MTLDepthStencilState>> m_dss[DepthStencilSelector::N_VALUES];
|
MRCOwned<id<MTLDepthStencilState>> m_dss[DepthStencilSelector::N_VALUES];
|
||||||
MRCOwned<id<MTLSamplerState>> m_samplers[SamplerSelector::N_VALUES];
|
MRCOwned<id<MTLSamplerState>> m_samplers[SamplerSelector::N_VALUES];
|
||||||
|
|
|
@ -3,6 +3,20 @@
|
||||||
|
|
||||||
#include "VideoBackends/Metal/MTLObjectCache.h"
|
#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"
|
#include "VideoCommon/VideoConfig.h"
|
||||||
|
|
||||||
MRCOwned<id<MTLDevice>> Metal::g_device;
|
MRCOwned<id<MTLDevice>> Metal::g_device;
|
||||||
|
@ -14,6 +28,7 @@ static void SetupDepthStencil(
|
||||||
|
|
||||||
Metal::ObjectCache::ObjectCache()
|
Metal::ObjectCache::ObjectCache()
|
||||||
{
|
{
|
||||||
|
m_internal = std::make_unique<Internal>();
|
||||||
SetupDepthStencil(m_dss);
|
SetupDepthStencil(m_dss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,3 +185,314 @@ void Metal::ObjectCache::ReloadSamplers()
|
||||||
for (auto& sampler : m_samplers)
|
for (auto& sampler : m_samplers)
|
||||||
sampler = nullptr;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -14,24 +14,34 @@
|
||||||
|
|
||||||
namespace Metal
|
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
|
class Pipeline final : public AbstractPipeline
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
|
explicit Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
|
||||||
MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim,
|
const PipelineReflection& reflection, MTLPrimitiveType prim, MTLCullMode cull,
|
||||||
MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage);
|
DepthState depth, AbstractPipelineUsage usage);
|
||||||
|
|
||||||
id<MTLRenderPipelineState> Get() const { return m_pipeline; }
|
id<MTLRenderPipelineState> Get() const { return m_pipeline; }
|
||||||
MTLPrimitiveType Prim() const { return m_prim; }
|
MTLPrimitiveType Prim() const { return m_prim; }
|
||||||
MTLCullMode Cull() const { return m_cull; }
|
MTLCullMode Cull() const { return m_cull; }
|
||||||
DepthStencilSelector DepthStencil() const { return m_depth_stencil; }
|
DepthStencilSelector DepthStencil() const { return m_depth_stencil; }
|
||||||
AbstractPipelineUsage Usage() const { return m_usage; }
|
AbstractPipelineUsage Usage() const { return m_usage; }
|
||||||
u32 GetTextures() const { return m_textures; }
|
u32 GetTextures() const { return m_reflection.textures; }
|
||||||
u32 GetSamplers() const { return m_samplers; }
|
u32 GetSamplers() const { return m_reflection.samplers; }
|
||||||
u32 GetVertexBuffers() const { return m_vertex_buffers; }
|
u32 GetVertexBuffers() const { return m_reflection.vertex_buffers; }
|
||||||
u32 GetFragmentBuffers() const { return m_fragment_buffers; }
|
u32 GetFragmentBuffers() const { return m_reflection.fragment_buffers; }
|
||||||
bool UsesVertexBuffer(u32 index) const { return m_vertex_buffers & (1 << index); }
|
bool UsesVertexBuffer(u32 index) const { return m_reflection.vertex_buffers & (1 << index); }
|
||||||
bool UsesFragmentBuffer(u32 index) const { return m_fragment_buffers & (1 << index); }
|
bool UsesFragmentBuffer(u32 index) const { return m_reflection.fragment_buffers & (1 << index); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MRCOwned<id<MTLRenderPipelineState>> m_pipeline;
|
MRCOwned<id<MTLRenderPipelineState>> m_pipeline;
|
||||||
|
@ -39,10 +49,7 @@ private:
|
||||||
MTLCullMode m_cull;
|
MTLCullMode m_cull;
|
||||||
DepthStencilSelector m_depth_stencil;
|
DepthStencilSelector m_depth_stencil;
|
||||||
AbstractPipelineUsage m_usage;
|
AbstractPipelineUsage m_usage;
|
||||||
u32 m_textures = 0;
|
PipelineReflection m_reflection;
|
||||||
u32 m_samplers = 0;
|
|
||||||
u32 m_vertex_buffers = 0;
|
|
||||||
u32 m_fragment_buffers = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ComputePipeline : public Shader
|
class ComputePipeline : public Shader
|
||||||
|
|
|
@ -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,
|
Metal::Pipeline::Pipeline(MRCOwned<id<MTLRenderPipelineState>> pipeline,
|
||||||
MTLRenderPipelineReflection* reflection, MTLPrimitiveType prim,
|
const PipelineReflection& reflection, MTLPrimitiveType prim,
|
||||||
MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage)
|
MTLCullMode cull, DepthState depth, AbstractPipelineUsage usage)
|
||||||
: m_pipeline(std::move(pipeline)), m_prim(prim), m_cull(cull), m_depth_stencil(depth),
|
: 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,
|
Metal::ComputePipeline::ComputePipeline(ShaderStage stage, MTLComputePipelineReflection* reflection,
|
||||||
|
|
|
@ -82,7 +82,6 @@ private:
|
||||||
u32 m_texture_counter = 0;
|
u32 m_texture_counter = 0;
|
||||||
u32 m_staging_texture_counter = 0;
|
u32 m_staging_texture_counter = 0;
|
||||||
std::array<u32, 4> m_shader_counter = {};
|
std::array<u32, 4> m_shader_counter = {};
|
||||||
u32 m_pipeline_counter = 0;
|
|
||||||
|
|
||||||
void CheckForSurfaceChange();
|
void CheckForSurfaceChange();
|
||||||
void CheckForSurfaceResize();
|
void CheckForSurfaceResize();
|
||||||
|
|
|
@ -9,11 +9,10 @@
|
||||||
#include "VideoBackends/Metal/MTLStateTracker.h"
|
#include "VideoBackends/Metal/MTLStateTracker.h"
|
||||||
#include "VideoBackends/Metal/MTLTexture.h"
|
#include "VideoBackends/Metal/MTLTexture.h"
|
||||||
#include "VideoBackends/Metal/MTLUtil.h"
|
#include "VideoBackends/Metal/MTLUtil.h"
|
||||||
|
#include "VideoBackends/Metal/MTLVertexFormat.h"
|
||||||
#include "VideoBackends/Metal/MTLVertexManager.h"
|
#include "VideoBackends/Metal/MTLVertexManager.h"
|
||||||
|
|
||||||
#include "VideoCommon/FramebufferManager.h"
|
#include "VideoCommon/FramebufferManager.h"
|
||||||
#include "VideoCommon/NativeVertexFormat.h"
|
|
||||||
#include "VideoCommon/VertexShaderGen.h"
|
|
||||||
#include "VideoCommon/VideoBackendBase.h"
|
#include "VideoCommon/VideoBackendBase.h"
|
||||||
|
|
||||||
Metal::Renderer::Renderer(MRCOwned<CAMetalLayer*> layer, int width, int height, float layer_scale)
|
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
|
// 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::unique_ptr<AbstractShader> Metal::Renderer::CreateShaderFromSource(ShaderStage stage,
|
||||||
std::string_view source,
|
std::string_view source,
|
||||||
std::string_view name)
|
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>
|
std::unique_ptr<AbstractPipeline>
|
||||||
Metal::Renderer::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data,
|
Metal::Renderer::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data,
|
||||||
size_t cache_data_length)
|
size_t cache_data_length)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
return g_object_cache->CreatePipeline(config);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Metal::Renderer::Flush()
|
void Metal::Renderer::Flush()
|
||||||
|
|
|
@ -15,13 +15,11 @@ namespace Metal
|
||||||
class Shader : public AbstractShader
|
class Shader : public AbstractShader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Shader(ShaderStage stage, std::string msl, MRCOwned<id<MTLFunction>> shader)
|
explicit Shader(ShaderStage stage, std::string msl, MRCOwned<id<MTLFunction>> shader);
|
||||||
: AbstractShader(stage), m_msl(std::move(msl)), m_shader(std::move(shader))
|
~Shader();
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
id<MTLFunction> GetShader() const { return m_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:
|
private:
|
||||||
std::string m_msl;
|
std::string m_msl;
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue