State and cache optimization (#27)
* WIP pipeline/depth state cache rework * Fix some issues * Fix some more default values * Reduce allocations for state changes * fix helpershader stuff * explanation comment * fix depth bias
This commit is contained in:
parent
9d26aa8d06
commit
e02df72323
14 changed files with 1142 additions and 565 deletions
|
@ -1,36 +0,0 @@
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using SharpMetal.Foundation;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class ComputePipelineCache : StateCache<MTLComputePipelineState, MTLFunction, MTLFunction>
|
|
||||||
{
|
|
||||||
private readonly MTLDevice _device;
|
|
||||||
|
|
||||||
public ComputePipelineCache(MTLDevice device)
|
|
||||||
{
|
|
||||||
_device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MTLFunction GetHash(MTLFunction function)
|
|
||||||
{
|
|
||||||
return function;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MTLComputePipelineState CreateValue(MTLFunction function)
|
|
||||||
{
|
|
||||||
var error = new NSError(IntPtr.Zero);
|
|
||||||
var pipelineState = _device.NewComputePipelineState(function, ref error);
|
|
||||||
if (error != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return pipelineState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +1,11 @@
|
||||||
|
using Ryujinx.Graphics.Metal.State;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
struct DepthStencilHash
|
class DepthStencilCache : StateCache<MTLDepthStencilState, DepthStencilUid, DepthStencilUid>
|
||||||
{
|
|
||||||
public struct StencilHash
|
|
||||||
{
|
|
||||||
public MTLStencilOperation StencilFailureOperation;
|
|
||||||
public MTLStencilOperation DepthFailureOperation;
|
|
||||||
public MTLStencilOperation DepthStencilPassOperation;
|
|
||||||
public MTLCompareFunction StencilCompareFunction;
|
|
||||||
public uint ReadMask;
|
|
||||||
public uint WriteMask;
|
|
||||||
}
|
|
||||||
public StencilHash FrontFace;
|
|
||||||
public StencilHash BackFace;
|
|
||||||
public MTLCompareFunction DepthCompareFunction;
|
|
||||||
public bool DepthWriteEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class DepthStencilCache : StateCache<MTLDepthStencilState, MTLDepthStencilDescriptor, DepthStencilHash>
|
|
||||||
{
|
{
|
||||||
private readonly MTLDevice _device;
|
private readonly MTLDevice _device;
|
||||||
|
|
||||||
|
@ -31,41 +14,55 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_device = device;
|
_device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DepthStencilHash GetHash(MTLDepthStencilDescriptor descriptor)
|
protected override DepthStencilUid GetHash(DepthStencilUid descriptor)
|
||||||
{
|
{
|
||||||
var hash = new DepthStencilHash
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor)
|
||||||
{
|
{
|
||||||
// Front face
|
// Create descriptors
|
||||||
FrontFace = new DepthStencilHash.StencilHash
|
|
||||||
|
ref StencilUid frontUid = ref descriptor.FrontFace;
|
||||||
|
|
||||||
|
using var frontFaceStencil = new MTLStencilDescriptor
|
||||||
{
|
{
|
||||||
StencilFailureOperation = descriptor.FrontFaceStencil.StencilFailureOperation,
|
StencilFailureOperation = frontUid.StencilFailureOperation,
|
||||||
DepthFailureOperation = descriptor.FrontFaceStencil.DepthFailureOperation,
|
DepthFailureOperation = frontUid.DepthFailureOperation,
|
||||||
DepthStencilPassOperation = descriptor.FrontFaceStencil.DepthStencilPassOperation,
|
DepthStencilPassOperation = frontUid.DepthStencilPassOperation,
|
||||||
StencilCompareFunction = descriptor.FrontFaceStencil.StencilCompareFunction,
|
StencilCompareFunction = frontUid.StencilCompareFunction,
|
||||||
ReadMask = descriptor.FrontFaceStencil.ReadMask,
|
ReadMask = frontUid.ReadMask,
|
||||||
WriteMask = descriptor.FrontFaceStencil.WriteMask
|
WriteMask = frontUid.WriteMask
|
||||||
},
|
};
|
||||||
// Back face
|
|
||||||
BackFace = new DepthStencilHash.StencilHash
|
ref StencilUid backUid = ref descriptor.BackFace;
|
||||||
|
|
||||||
|
using var backFaceStencil = new MTLStencilDescriptor
|
||||||
|
{
|
||||||
|
StencilFailureOperation = backUid.StencilFailureOperation,
|
||||||
|
DepthFailureOperation = backUid.DepthFailureOperation,
|
||||||
|
DepthStencilPassOperation = backUid.DepthStencilPassOperation,
|
||||||
|
StencilCompareFunction = backUid.StencilCompareFunction,
|
||||||
|
ReadMask = backUid.ReadMask,
|
||||||
|
WriteMask = backUid.WriteMask
|
||||||
|
};
|
||||||
|
|
||||||
|
var mtlDescriptor = new MTLDepthStencilDescriptor
|
||||||
{
|
{
|
||||||
StencilFailureOperation = descriptor.BackFaceStencil.StencilFailureOperation,
|
|
||||||
DepthFailureOperation = descriptor.BackFaceStencil.DepthFailureOperation,
|
|
||||||
DepthStencilPassOperation = descriptor.BackFaceStencil.DepthStencilPassOperation,
|
|
||||||
StencilCompareFunction = descriptor.BackFaceStencil.StencilCompareFunction,
|
|
||||||
ReadMask = descriptor.BackFaceStencil.ReadMask,
|
|
||||||
WriteMask = descriptor.BackFaceStencil.WriteMask
|
|
||||||
},
|
|
||||||
// Depth
|
|
||||||
DepthCompareFunction = descriptor.DepthCompareFunction,
|
DepthCompareFunction = descriptor.DepthCompareFunction,
|
||||||
DepthWriteEnabled = descriptor.DepthWriteEnabled
|
DepthWriteEnabled = descriptor.DepthWriteEnabled
|
||||||
};
|
};
|
||||||
|
|
||||||
return hash;
|
if (descriptor.StencilTestEnabled)
|
||||||
|
{
|
||||||
|
mtlDescriptor.BackFaceStencil = backFaceStencil;
|
||||||
|
mtlDescriptor.FrontFaceStencil = frontFaceStencil;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override MTLDepthStencilState CreateValue(MTLDepthStencilDescriptor descriptor)
|
using (mtlDescriptor)
|
||||||
{
|
{
|
||||||
return _device.NewDepthStencilState(descriptor);
|
return _device.NewDepthStencilState(mtlDescriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Metal.State;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -48,12 +50,29 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
struct PredrawState
|
||||||
struct EncoderState
|
|
||||||
{
|
{
|
||||||
public MTLFunction? VertexFunction = null;
|
public MTLCullMode CullMode;
|
||||||
public MTLFunction? FragmentFunction = null;
|
public DepthStencilUid DepthStencilUid;
|
||||||
public MTLFunction? ComputeFunction = null;
|
public PrimitiveTopology Topology;
|
||||||
|
public MTLViewport[] Viewports;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RenderTargetCopy
|
||||||
|
{
|
||||||
|
public MTLScissorRect[] Scissors;
|
||||||
|
public Texture DepthStencil;
|
||||||
|
public Texture[] RenderTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
class EncoderState
|
||||||
|
{
|
||||||
|
public Program RenderProgram = null;
|
||||||
|
public Program ComputeProgram = null;
|
||||||
|
|
||||||
|
public PipelineState Pipeline;
|
||||||
|
public DepthStencilUid DepthStencilUid;
|
||||||
|
|
||||||
public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage];
|
public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage];
|
||||||
public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
||||||
|
@ -71,21 +90,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
||||||
public ulong IndexBufferOffset = 0;
|
public ulong IndexBufferOffset = 0;
|
||||||
|
|
||||||
public MTLDepthStencilState? DepthStencilState = null;
|
|
||||||
|
|
||||||
public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip;
|
public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip;
|
||||||
public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always;
|
|
||||||
public bool DepthWriteEnabled = false;
|
|
||||||
|
|
||||||
public float DepthBias;
|
public float DepthBias;
|
||||||
public float SlopeScale;
|
public float SlopeScale;
|
||||||
public float Clamp;
|
public float Clamp;
|
||||||
|
|
||||||
public MTLStencilDescriptor BackFaceStencil = new();
|
|
||||||
public MTLStencilDescriptor FrontFaceStencil = new();
|
|
||||||
public int BackRefValue = 0;
|
public int BackRefValue = 0;
|
||||||
public int FrontRefValue = 0;
|
public int FrontRefValue = 0;
|
||||||
public bool StencilTestEnabled = false;
|
|
||||||
|
|
||||||
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
|
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
|
||||||
public MTLCullMode CullMode = MTLCullMode.None;
|
public MTLCullMode CullMode = MTLCullMode.None;
|
||||||
|
@ -102,8 +114,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public ITexture[] PreMaskRenderTargets;
|
public ITexture[] PreMaskRenderTargets;
|
||||||
public bool FramebufferUsingColorWriteMask;
|
public bool FramebufferUsingColorWriteMask;
|
||||||
|
|
||||||
public MTLColorWriteMask[] RenderTargetMasks = Enumerable.Repeat(MTLColorWriteMask.All, Constants.MaxColorAttachments).ToArray();
|
public Array8<ColorBlendStateUid> StoredBlend;
|
||||||
public BlendDescriptor?[] BlendDescriptors = new BlendDescriptor?[Constants.MaxColorAttachments];
|
|
||||||
public ColorF BlendColor = new();
|
public ColorF BlendColor = new();
|
||||||
|
|
||||||
public VertexBufferDescriptor[] VertexBuffers = [];
|
public VertexBufferDescriptor[] VertexBuffers = [];
|
||||||
|
@ -115,25 +126,52 @@ namespace Ryujinx.Graphics.Metal
|
||||||
// Only to be used for present
|
// Only to be used for present
|
||||||
public bool ClearLoadAction = false;
|
public bool ClearLoadAction = false;
|
||||||
|
|
||||||
public EncoderState() { }
|
public EncoderState()
|
||||||
|
|
||||||
public readonly EncoderState Clone()
|
|
||||||
{
|
{
|
||||||
// Certain state (like viewport and scissor) doesn't need to be cloned, as it is always reacreated when assigned to
|
Pipeline.Initialize();
|
||||||
EncoderState clone = this;
|
DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always;
|
||||||
clone.FragmentTextures = (TextureBase[])FragmentTextures.Clone();
|
}
|
||||||
clone.FragmentSamplers = (MTLSamplerState[])FragmentSamplers.Clone();
|
|
||||||
clone.VertexTextures = (TextureBase[])VertexTextures.Clone();
|
|
||||||
clone.VertexSamplers = (MTLSamplerState[])VertexSamplers.Clone();
|
|
||||||
clone.ComputeTextures = (TextureBase[])ComputeTextures.Clone();
|
|
||||||
clone.ComputeSamplers = (MTLSamplerState[])ComputeSamplers.Clone();
|
|
||||||
clone.BlendDescriptors = (BlendDescriptor?[])BlendDescriptors.Clone();
|
|
||||||
clone.VertexBuffers = (VertexBufferDescriptor[])VertexBuffers.Clone();
|
|
||||||
clone.VertexAttribs = (VertexAttribDescriptor[])VertexAttribs.Clone();
|
|
||||||
clone.UniformBuffers = (BufferRef[])UniformBuffers.Clone();
|
|
||||||
clone.StorageBuffers = (BufferRef[])StorageBuffers.Clone();
|
|
||||||
|
|
||||||
return clone;
|
public RenderTargetCopy InheritForClear(EncoderState other, bool depth, int singleIndex = -1)
|
||||||
|
{
|
||||||
|
// Inherit render target related information without causing a render encoder split.
|
||||||
|
|
||||||
|
var oldState = new RenderTargetCopy
|
||||||
|
{
|
||||||
|
Scissors = other.Scissors,
|
||||||
|
RenderTargets = other.RenderTargets,
|
||||||
|
DepthStencil = other.DepthStencil
|
||||||
|
};
|
||||||
|
|
||||||
|
Scissors = other.Scissors;
|
||||||
|
RenderTargets = other.RenderTargets;
|
||||||
|
DepthStencil = other.DepthStencil;
|
||||||
|
|
||||||
|
Pipeline.ColorBlendAttachmentStateCount = other.Pipeline.ColorBlendAttachmentStateCount;
|
||||||
|
Pipeline.Internal.ColorBlendState = other.Pipeline.Internal.ColorBlendState;
|
||||||
|
Pipeline.DepthStencilFormat = other.Pipeline.DepthStencilFormat;
|
||||||
|
|
||||||
|
ref var blendStates = ref Pipeline.Internal.ColorBlendState;
|
||||||
|
|
||||||
|
// Mask out irrelevant attachments.
|
||||||
|
for (int i = 0; i < blendStates.Length; i++)
|
||||||
|
{
|
||||||
|
if (depth || (singleIndex != -1 && singleIndex != i))
|
||||||
|
{
|
||||||
|
blendStates[i].WriteMask = MTLColorWriteMask.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Restore(RenderTargetCopy copy)
|
||||||
|
{
|
||||||
|
Scissors = copy.Scissors;
|
||||||
|
RenderTargets = copy.RenderTargets;
|
||||||
|
DepthStencil = copy.DepthStencil;
|
||||||
|
|
||||||
|
Pipeline.Internal.ResetColorState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Metal.State;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment;
|
using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment;
|
||||||
|
@ -17,12 +18,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
private readonly Pipeline _pipeline;
|
private readonly Pipeline _pipeline;
|
||||||
private readonly BufferManager _bufferManager;
|
private readonly BufferManager _bufferManager;
|
||||||
|
|
||||||
private readonly RenderPipelineCache _renderPipelineCache;
|
|
||||||
private readonly ComputePipelineCache _computePipelineCache;
|
|
||||||
private readonly DepthStencilCache _depthStencilCache;
|
private readonly DepthStencilCache _depthStencilCache;
|
||||||
|
|
||||||
private EncoderState _currentState = new();
|
private readonly EncoderState _mainState = new();
|
||||||
private readonly Stack<EncoderState> _backStates = [];
|
private EncoderState _currentState;
|
||||||
|
|
||||||
public readonly Auto<DisposableBuffer> IndexBuffer => _currentState.IndexBuffer;
|
public readonly Auto<DisposableBuffer> IndexBuffer => _currentState.IndexBuffer;
|
||||||
public readonly MTLIndexType IndexType => _currentState.IndexType;
|
public readonly MTLIndexType IndexType => _currentState.IndexType;
|
||||||
|
@ -41,9 +40,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline = pipeline;
|
_pipeline = pipeline;
|
||||||
_bufferManager = bufferManager;
|
_bufferManager = bufferManager;
|
||||||
|
|
||||||
_renderPipelineCache = new(device);
|
|
||||||
_computePipelineCache = new(device);
|
|
||||||
_depthStencilCache = new(device);
|
_depthStencilCache = new(device);
|
||||||
|
_currentState = _mainState;
|
||||||
|
|
||||||
// Zero buffer
|
// Zero buffer
|
||||||
byte[] zeros = new byte[ZeroBufferSize];
|
byte[] zeros = new byte[ZeroBufferSize];
|
||||||
|
@ -56,39 +54,38 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// State
|
// State
|
||||||
_currentState.FrontFaceStencil.Dispose();
|
|
||||||
_currentState.BackFaceStencil.Dispose();
|
|
||||||
|
|
||||||
_renderPipelineCache.Dispose();
|
|
||||||
_computePipelineCache.Dispose();
|
|
||||||
_depthStencilCache.Dispose();
|
_depthStencilCache.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveState()
|
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All)
|
||||||
{
|
{
|
||||||
_backStates.Push(_currentState);
|
_currentState = state ?? _mainState;
|
||||||
_currentState = _currentState.Clone();
|
|
||||||
|
_currentState.Dirty |= flags;
|
||||||
|
|
||||||
|
return _mainState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveAndResetState()
|
public PredrawState SavePredrawState()
|
||||||
{
|
{
|
||||||
_backStates.Push(_currentState);
|
return new PredrawState
|
||||||
_currentState = new();
|
{
|
||||||
|
CullMode = _currentState.CullMode,
|
||||||
|
DepthStencilUid = _currentState.DepthStencilUid,
|
||||||
|
Topology = _currentState.Topology,
|
||||||
|
Viewports = _currentState.Viewports.ToArray(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RestoreState()
|
public void RestorePredrawState(PredrawState state)
|
||||||
{
|
{
|
||||||
if (_backStates.Count > 0)
|
_currentState.CullMode = state.CullMode;
|
||||||
{
|
_currentState.DepthStencilUid = state.DepthStencilUid;
|
||||||
_currentState = _backStates.Pop();
|
_currentState.Topology = state.Topology;
|
||||||
|
_currentState.Viewports = state.Viewports;
|
||||||
|
|
||||||
// Mark the other state as dirty
|
_currentState.Dirty |= DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports;
|
||||||
_currentState.Dirty |= DirtyFlags.All;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Gpu, "No state to restore");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetClearLoadAction(bool clear)
|
public void SetClearLoadAction(bool clear)
|
||||||
|
@ -267,81 +264,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder)
|
private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||||
{
|
{
|
||||||
var renderPipelineDescriptor = new MTLRenderPipelineDescriptor();
|
MTLRenderPipelineState pipelineState = _currentState.Pipeline.CreateRenderPipeline(_device, _currentState.RenderProgram);
|
||||||
|
|
||||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
|
||||||
{
|
|
||||||
if (_currentState.RenderTargets[i] != null)
|
|
||||||
{
|
|
||||||
var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i);
|
|
||||||
pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].GetHandle().PixelFormat;
|
|
||||||
pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha;
|
|
||||||
pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
|
|
||||||
pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha;
|
|
||||||
pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
|
|
||||||
pipelineAttachment.WriteMask = _currentState.RenderTargetMasks[i];
|
|
||||||
|
|
||||||
if (_currentState.BlendDescriptors[i] != null)
|
|
||||||
{
|
|
||||||
var blendDescriptor = _currentState.BlendDescriptors[i].Value;
|
|
||||||
pipelineAttachment.SetBlendingEnabled(blendDescriptor.Enable);
|
|
||||||
pipelineAttachment.AlphaBlendOperation = blendDescriptor.AlphaOp.Convert();
|
|
||||||
pipelineAttachment.RgbBlendOperation = blendDescriptor.ColorOp.Convert();
|
|
||||||
pipelineAttachment.SourceAlphaBlendFactor = blendDescriptor.AlphaSrcFactor.Convert();
|
|
||||||
pipelineAttachment.DestinationAlphaBlendFactor = blendDescriptor.AlphaDstFactor.Convert();
|
|
||||||
pipelineAttachment.SourceRGBBlendFactor = blendDescriptor.ColorSrcFactor.Convert();
|
|
||||||
pipelineAttachment.DestinationRGBBlendFactor = blendDescriptor.ColorDstFactor.Convert();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentState.DepthStencil != null)
|
|
||||||
{
|
|
||||||
switch (_currentState.DepthStencil.GetHandle().PixelFormat)
|
|
||||||
{
|
|
||||||
// Depth Only Attachment
|
|
||||||
case MTLPixelFormat.Depth16Unorm:
|
|
||||||
case MTLPixelFormat.Depth32Float:
|
|
||||||
renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Stencil Only Attachment
|
|
||||||
case MTLPixelFormat.Stencil8:
|
|
||||||
renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Combined Attachment
|
|
||||||
case MTLPixelFormat.Depth24UnormStencil8:
|
|
||||||
case MTLPixelFormat.Depth32FloatStencil8:
|
|
||||||
renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat;
|
|
||||||
renderPipelineDescriptor.StencilAttachmentPixelFormat = _currentState.DepthStencil.GetHandle().PixelFormat;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.GetHandle().PixelFormat}!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var vertexDescriptor = BuildVertexDescriptor(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
|
||||||
renderPipelineDescriptor.VertexDescriptor = vertexDescriptor;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_currentState.VertexFunction != null)
|
|
||||||
{
|
|
||||||
renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentState.FragmentFunction != null)
|
|
||||||
{
|
|
||||||
renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pipelineState = _renderPipelineCache.GetOrCreate(renderPipelineDescriptor);
|
|
||||||
|
|
||||||
renderCommandEncoder.SetRenderPipelineState(pipelineState);
|
renderCommandEncoder.SetRenderPipelineState(pipelineState);
|
||||||
|
|
||||||
|
@ -351,22 +274,15 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.BlendColor.Blue,
|
_currentState.BlendColor.Blue,
|
||||||
_currentState.BlendColor.Alpha);
|
_currentState.BlendColor.Alpha);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Cleanup
|
|
||||||
renderPipelineDescriptor.Dispose();
|
|
||||||
vertexDescriptor.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder)
|
private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder)
|
||||||
{
|
{
|
||||||
if (_currentState.ComputeFunction == null)
|
if (_currentState.ComputeProgram == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pipelineState = _computePipelineCache.GetOrCreate(_currentState.ComputeFunction.Value);
|
var pipelineState = PipelineState.CreateComputePipeline(_device, _currentState.ComputeProgram);
|
||||||
|
|
||||||
computeCommandEncoder.SetComputePipelineState(pipelineState);
|
computeCommandEncoder.SetComputePipelineState(pipelineState);
|
||||||
}
|
}
|
||||||
|
@ -414,14 +330,13 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
if (prg.VertexFunction != IntPtr.Zero)
|
if (prg.VertexFunction != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
_currentState.VertexFunction = prg.VertexFunction;
|
_currentState.RenderProgram = prg;
|
||||||
_currentState.FragmentFunction = prg.FragmentFunction;
|
|
||||||
|
|
||||||
_currentState.Dirty |= DirtyFlags.RenderPipeline;
|
_currentState.Dirty |= DirtyFlags.RenderPipeline;
|
||||||
}
|
}
|
||||||
if (prg.ComputeFunction != IntPtr.Zero)
|
else if (prg.ComputeFunction != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
_currentState.ComputeFunction = prg.ComputeFunction;
|
_currentState.ComputeProgram = prg;
|
||||||
|
|
||||||
_currentState.Dirty |= DirtyFlags.ComputePipeline;
|
_currentState.Dirty |= DirtyFlags.ComputePipeline;
|
||||||
}
|
}
|
||||||
|
@ -435,7 +350,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void UpdateRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
public void UpdateRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
||||||
{
|
{
|
||||||
_currentState.RenderTargetMasks = new MTLColorWriteMask[Constants.MaxColorAttachments];
|
ref var blendState = ref _currentState.Pipeline.Internal.ColorBlendState;
|
||||||
|
|
||||||
for (int i = 0; i < componentMask.Length; i++)
|
for (int i = 0; i < componentMask.Length; i++)
|
||||||
{
|
{
|
||||||
|
@ -451,7 +366,25 @@ namespace Ryujinx.Graphics.Metal
|
||||||
mask |= blue ? MTLColorWriteMask.Blue : 0;
|
mask |= blue ? MTLColorWriteMask.Blue : 0;
|
||||||
mask |= alpha ? MTLColorWriteMask.Alpha : 0;
|
mask |= alpha ? MTLColorWriteMask.Alpha : 0;
|
||||||
|
|
||||||
_currentState.RenderTargetMasks[i] = mask;
|
ref ColorBlendStateUid mtlBlend = ref blendState[i];
|
||||||
|
|
||||||
|
// When color write mask is 0, remove all blend state to help the pipeline cache.
|
||||||
|
// Restore it when the mask becomes non-zero.
|
||||||
|
if (mtlBlend.WriteMask != mask)
|
||||||
|
{
|
||||||
|
if (mask == 0)
|
||||||
|
{
|
||||||
|
_currentState.StoredBlend[i] = mtlBlend;
|
||||||
|
|
||||||
|
mtlBlend = new ColorBlendStateUid();
|
||||||
|
}
|
||||||
|
else if (mtlBlend.WriteMask == 0)
|
||||||
|
{
|
||||||
|
mtlBlend = _currentState.StoredBlend[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blendState[i].WriteMask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentState.FramebufferUsingColorWriteMask)
|
if (_currentState.FramebufferUsingColorWriteMask)
|
||||||
|
@ -478,6 +411,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
// Look for textures that are masked out.
|
// Look for textures that are masked out.
|
||||||
|
|
||||||
|
ref PipelineState pipeline = ref _currentState.Pipeline;
|
||||||
|
ref var blendState = ref pipeline.Internal.ColorBlendState;
|
||||||
|
|
||||||
|
pipeline.ColorBlendAttachmentStateCount = (uint)colors.Length;
|
||||||
|
|
||||||
for (int i = 0; i < colors.Length; i++)
|
for (int i = 0; i < colors.Length; i++)
|
||||||
{
|
{
|
||||||
if (colors[i] == null)
|
if (colors[i] == null)
|
||||||
|
@ -485,7 +423,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var mtlMask = ref _currentState.RenderTargetMasks[i];
|
var mtlMask = blendState[i].WriteMask;
|
||||||
|
|
||||||
for (int j = 0; j < i; j++)
|
for (int j = 0; j < i; j++)
|
||||||
{
|
{
|
||||||
|
@ -495,7 +433,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
// Prefer the binding with no write mask.
|
// Prefer the binding with no write mask.
|
||||||
|
|
||||||
ref var mtlMask2 = ref _currentState.RenderTargetMasks[j];
|
var mtlMask2 = blendState[j].WriteMask;
|
||||||
|
|
||||||
if (mtlMask == 0)
|
if (mtlMask == 0)
|
||||||
{
|
{
|
||||||
|
@ -517,18 +455,23 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
if (colors[i] is not Texture tex)
|
if (colors[i] is not Texture tex)
|
||||||
{
|
{
|
||||||
|
blendState[i].PixelFormat = MTLPixelFormat.Invalid;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blendState[i].PixelFormat = tex.GetHandle().PixelFormat; // TODO: cache this
|
||||||
_currentState.RenderTargets[i] = tex;
|
_currentState.RenderTargets[i] = tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depthStencil is Texture depthTexture)
|
if (depthStencil is Texture depthTexture)
|
||||||
{
|
{
|
||||||
|
pipeline.DepthStencilFormat = depthTexture.GetHandle().PixelFormat; // TODO: cache this
|
||||||
_currentState.DepthStencil = depthTexture;
|
_currentState.DepthStencil = depthTexture;
|
||||||
}
|
}
|
||||||
else if (depthStencil == null)
|
else if (depthStencil == null)
|
||||||
{
|
{
|
||||||
|
pipeline.DepthStencilFormat = MTLPixelFormat.Invalid;
|
||||||
_currentState.DepthStencil = null;
|
_currentState.DepthStencil = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,13 +498,32 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
_currentState.VertexAttribs = vertexAttribs.ToArray();
|
_currentState.VertexAttribs = vertexAttribs.ToArray();
|
||||||
|
|
||||||
|
// Update the buffers on the pipeline
|
||||||
|
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
||||||
|
|
||||||
// Mark dirty
|
// Mark dirty
|
||||||
_currentState.Dirty |= DirtyFlags.RenderPipeline;
|
_currentState.Dirty |= DirtyFlags.RenderPipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateBlendDescriptors(int index, BlendDescriptor blend)
|
public void UpdateBlendDescriptors(int index, BlendDescriptor blend)
|
||||||
{
|
{
|
||||||
_currentState.BlendDescriptors[index] = blend;
|
ref var blendState = ref _currentState.Pipeline.Internal.ColorBlendState[index];
|
||||||
|
|
||||||
|
blendState.Enable = blend.Enable;
|
||||||
|
blendState.AlphaBlendOperation = blend.AlphaOp.Convert();
|
||||||
|
blendState.RgbBlendOperation = blend.ColorOp.Convert();
|
||||||
|
blendState.SourceAlphaBlendFactor = blend.AlphaSrcFactor.Convert();
|
||||||
|
blendState.DestinationAlphaBlendFactor = blend.AlphaDstFactor.Convert();
|
||||||
|
blendState.SourceRGBBlendFactor = blend.ColorSrcFactor.Convert();
|
||||||
|
blendState.DestinationRGBBlendFactor = blend.ColorDstFactor.Convert();
|
||||||
|
|
||||||
|
if (blendState.WriteMask == 0)
|
||||||
|
{
|
||||||
|
_currentState.StoredBlend[index] = blendState;
|
||||||
|
|
||||||
|
blendState = new ColorBlendStateUid();
|
||||||
|
}
|
||||||
|
|
||||||
_currentState.BlendColor = blend.BlendConstant;
|
_currentState.BlendColor = blend.BlendConstant;
|
||||||
|
|
||||||
// Mark dirty
|
// Mark dirty
|
||||||
|
@ -571,7 +533,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
// Inlineable
|
// Inlineable
|
||||||
public void UpdateStencilState(StencilTestDescriptor stencilTest)
|
public void UpdateStencilState(StencilTestDescriptor stencilTest)
|
||||||
{
|
{
|
||||||
_currentState.FrontFaceStencil = new MTLStencilDescriptor
|
ref DepthStencilUid uid = ref _currentState.DepthStencilUid;
|
||||||
|
|
||||||
|
uid.FrontFace = new StencilUid
|
||||||
{
|
{
|
||||||
StencilFailureOperation = stencilTest.FrontSFail.Convert(),
|
StencilFailureOperation = stencilTest.FrontSFail.Convert(),
|
||||||
DepthFailureOperation = stencilTest.FrontDpFail.Convert(),
|
DepthFailureOperation = stencilTest.FrontDpFail.Convert(),
|
||||||
|
@ -581,7 +545,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
WriteMask = (uint)stencilTest.FrontMask
|
WriteMask = (uint)stencilTest.FrontMask
|
||||||
};
|
};
|
||||||
|
|
||||||
_currentState.BackFaceStencil = new MTLStencilDescriptor
|
uid.BackFace = new StencilUid
|
||||||
{
|
{
|
||||||
StencilFailureOperation = stencilTest.BackSFail.Convert(),
|
StencilFailureOperation = stencilTest.BackSFail.Convert(),
|
||||||
DepthFailureOperation = stencilTest.BackDpFail.Convert(),
|
DepthFailureOperation = stencilTest.BackDpFail.Convert(),
|
||||||
|
@ -591,55 +555,23 @@ namespace Ryujinx.Graphics.Metal
|
||||||
WriteMask = (uint)stencilTest.BackMask
|
WriteMask = (uint)stencilTest.BackMask
|
||||||
};
|
};
|
||||||
|
|
||||||
_currentState.StencilTestEnabled = stencilTest.TestEnable;
|
uid.StencilTestEnabled = stencilTest.TestEnable;
|
||||||
|
|
||||||
var descriptor = new MTLDepthStencilDescriptor
|
|
||||||
{
|
|
||||||
DepthCompareFunction = _currentState.DepthCompareFunction,
|
|
||||||
DepthWriteEnabled = _currentState.DepthWriteEnabled
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_currentState.StencilTestEnabled)
|
|
||||||
{
|
|
||||||
descriptor.BackFaceStencil = _currentState.BackFaceStencil;
|
|
||||||
descriptor.FrontFaceStencil = _currentState.FrontFaceStencil;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentState.DepthStencilState = _device.NewDepthStencilState(descriptor);
|
|
||||||
|
|
||||||
UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef);
|
UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef);
|
||||||
|
|
||||||
// Mark dirty
|
// Mark dirty
|
||||||
_currentState.Dirty |= DirtyFlags.DepthStencil;
|
_currentState.Dirty |= DirtyFlags.DepthStencil;
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
descriptor.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateDepthState(DepthTestDescriptor depthTest)
|
public void UpdateDepthState(DepthTestDescriptor depthTest)
|
||||||
{
|
{
|
||||||
_currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always;
|
ref DepthStencilUid uid = ref _currentState.DepthStencilUid;
|
||||||
_currentState.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable;
|
|
||||||
|
|
||||||
var descriptor = new MTLDepthStencilDescriptor
|
uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always;
|
||||||
{
|
uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable;
|
||||||
DepthCompareFunction = _currentState.DepthCompareFunction,
|
|
||||||
DepthWriteEnabled = _currentState.DepthWriteEnabled
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_currentState.StencilTestEnabled)
|
|
||||||
{
|
|
||||||
descriptor.BackFaceStencil = _currentState.BackFaceStencil;
|
|
||||||
descriptor.FrontFaceStencil = _currentState.FrontFaceStencil;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentState.DepthStencilState = _device.NewDepthStencilState(descriptor);
|
|
||||||
|
|
||||||
// Mark dirty
|
// Mark dirty
|
||||||
_currentState.Dirty |= DirtyFlags.DepthStencil;
|
_currentState.Dirty |= DirtyFlags.DepthStencil;
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
descriptor.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inlineable
|
// Inlineable
|
||||||
|
@ -751,6 +683,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
_currentState.VertexBuffers = vertexBuffers.ToArray();
|
_currentState.VertexBuffers = vertexBuffers.ToArray();
|
||||||
|
|
||||||
|
// Update the buffers on the pipeline
|
||||||
|
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
||||||
|
|
||||||
// Inline update
|
// Inline update
|
||||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
{
|
{
|
||||||
|
@ -925,10 +860,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)
|
private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||||
{
|
{
|
||||||
if (_currentState.DepthStencilState != null)
|
MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid);
|
||||||
{
|
|
||||||
renderCommandEncoder.SetDepthStencilState(_currentState.DepthStencilState.Value);
|
renderCommandEncoder.SetDepthStencilState(state);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder)
|
private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder)
|
||||||
|
@ -973,16 +907,17 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly MTLVertexDescriptor BuildVertexDescriptor(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors)
|
private void UpdatePipelineVertexState(VertexBufferDescriptor[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors)
|
||||||
{
|
{
|
||||||
var vertexDescriptor = new MTLVertexDescriptor();
|
ref PipelineState pipeline = ref _currentState.Pipeline;
|
||||||
uint indexMask = 0;
|
uint indexMask = 0;
|
||||||
|
|
||||||
for (int i = 0; i < attribDescriptors.Length; i++)
|
for (int i = 0; i < attribDescriptors.Length; i++)
|
||||||
{
|
{
|
||||||
|
ref var attrib = ref pipeline.Internal.VertexAttributes[i];
|
||||||
|
|
||||||
if (attribDescriptors[i].IsZero)
|
if (attribDescriptors[i].IsZero)
|
||||||
{
|
{
|
||||||
var attrib = vertexDescriptor.Attributes.Object((ulong)i);
|
|
||||||
attrib.Format = attribDescriptors[i].Format.Convert();
|
attrib.Format = attribDescriptors[i].Format.Convert();
|
||||||
indexMask |= 1u << (int)Constants.ZeroBufferIndex;
|
indexMask |= 1u << (int)Constants.ZeroBufferIndex;
|
||||||
attrib.BufferIndex = Constants.ZeroBufferIndex;
|
attrib.BufferIndex = Constants.ZeroBufferIndex;
|
||||||
|
@ -990,7 +925,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var attrib = vertexDescriptor.Attributes.Object((ulong)i);
|
|
||||||
attrib.Format = attribDescriptors[i].Format.Convert();
|
attrib.Format = attribDescriptors[i].Format.Convert();
|
||||||
indexMask |= 1u << attribDescriptors[i].BufferIndex;
|
indexMask |= 1u << attribDescriptors[i].BufferIndex;
|
||||||
attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex;
|
attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex;
|
||||||
|
@ -1000,11 +934,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
for (int i = 0; i < bufferDescriptors.Length; i++)
|
for (int i = 0; i < bufferDescriptors.Length; i++)
|
||||||
{
|
{
|
||||||
var layout = vertexDescriptor.Layouts.Object((ulong)i);
|
ref var layout = ref pipeline.Internal.VertexBindings[i];
|
||||||
|
|
||||||
if ((indexMask & (1u << i)) != 0)
|
if ((indexMask & (1u << i)) != 0)
|
||||||
{
|
{
|
||||||
layout.Stride = (ulong)bufferDescriptors[i].Stride;
|
layout.Stride = (uint)bufferDescriptors[i].Stride;
|
||||||
|
|
||||||
if (layout.Stride == 0)
|
if (layout.Stride == 0)
|
||||||
{
|
{
|
||||||
|
@ -1017,7 +951,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
if (bufferDescriptors[i].Divisor > 0)
|
if (bufferDescriptors[i].Divisor > 0)
|
||||||
{
|
{
|
||||||
layout.StepFunction = MTLVertexStepFunction.PerInstance;
|
layout.StepFunction = MTLVertexStepFunction.PerInstance;
|
||||||
layout.StepRate = (ulong)bufferDescriptors[i].Divisor;
|
layout.StepRate = (uint)bufferDescriptors[i].Divisor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1028,20 +962,21 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
layout.Stride = 0;
|
layout = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero buffer
|
// Zero buffer
|
||||||
if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0)
|
if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0)
|
||||||
{
|
{
|
||||||
var layout = vertexDescriptor.Layouts.Object(Constants.ZeroBufferIndex);
|
ref var layout = ref pipeline.Internal.VertexBindings[(int)Constants.ZeroBufferIndex];
|
||||||
layout.Stride = 1;
|
layout.Stride = 1;
|
||||||
layout.StepFunction = MTLVertexStepFunction.Constant;
|
layout.StepFunction = MTLVertexStepFunction.Constant;
|
||||||
layout.StepRate = 0;
|
layout.StepRate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vertexDescriptor;
|
pipeline.VertexAttributeDescriptionsCount = (uint)attribDescriptors.Length;
|
||||||
|
pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out?
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors)
|
private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors)
|
||||||
|
|
143
src/Ryujinx.Graphics.Metal/HashTableSlim.cs
Normal file
143
src/Ryujinx.Graphics.Metal/HashTableSlim.cs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
{
|
||||||
|
interface IRefEquatable<T>
|
||||||
|
{
|
||||||
|
bool Equals(ref T other);
|
||||||
|
}
|
||||||
|
|
||||||
|
class HashTableSlim<TKey, TValue> where TKey : IRefEquatable<TKey>
|
||||||
|
{
|
||||||
|
private const int TotalBuckets = 16; // Must be power of 2
|
||||||
|
private const int TotalBucketsMask = TotalBuckets - 1;
|
||||||
|
|
||||||
|
private struct Entry
|
||||||
|
{
|
||||||
|
public int Hash;
|
||||||
|
public TKey Key;
|
||||||
|
public TValue Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Bucket
|
||||||
|
{
|
||||||
|
public int Length;
|
||||||
|
public Entry[] Entries;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly Span<Entry> AsSpan()
|
||||||
|
{
|
||||||
|
return Entries == null ? Span<Entry>.Empty : Entries.AsSpan(0, Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Bucket[] _hashTable = new Bucket[TotalBuckets];
|
||||||
|
|
||||||
|
public IEnumerable<TKey> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (Bucket bucket in _hashTable)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bucket.Length; i++)
|
||||||
|
{
|
||||||
|
yield return bucket.Entries[i].Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<TValue> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (Bucket bucket in _hashTable)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bucket.Length; i++)
|
||||||
|
{
|
||||||
|
yield return bucket.Entries[i].Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(ref TKey key, TValue value)
|
||||||
|
{
|
||||||
|
var entry = new Entry
|
||||||
|
{
|
||||||
|
Hash = key.GetHashCode(),
|
||||||
|
Key = key,
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
|
||||||
|
int hashCode = key.GetHashCode();
|
||||||
|
int bucketIndex = hashCode & TotalBucketsMask;
|
||||||
|
|
||||||
|
ref var bucket = ref _hashTable[bucketIndex];
|
||||||
|
if (bucket.Entries != null)
|
||||||
|
{
|
||||||
|
int index = bucket.Length;
|
||||||
|
|
||||||
|
if (index >= bucket.Entries.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref bucket.Entries, index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket.Entries[index] = entry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bucket.Entries = new[]
|
||||||
|
{
|
||||||
|
entry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket.Length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(ref TKey key)
|
||||||
|
{
|
||||||
|
int hashCode = key.GetHashCode();
|
||||||
|
|
||||||
|
ref var bucket = ref _hashTable[hashCode & TotalBucketsMask];
|
||||||
|
var entries = bucket.AsSpan();
|
||||||
|
for (int i = 0; i < entries.Length; i++)
|
||||||
|
{
|
||||||
|
ref var entry = ref entries[i];
|
||||||
|
|
||||||
|
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
||||||
|
{
|
||||||
|
entries[(i + 1)..].CopyTo(entries[i..]);
|
||||||
|
bucket.Length--;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(ref TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
int hashCode = key.GetHashCode();
|
||||||
|
|
||||||
|
var entries = _hashTable[hashCode & TotalBucketsMask].AsSpan();
|
||||||
|
for (int i = 0; i < entries.Length; i++)
|
||||||
|
{
|
||||||
|
ref var entry = ref entries[i];
|
||||||
|
|
||||||
|
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
||||||
|
{
|
||||||
|
value = entry.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
private readonly IProgram _programDepthStencilClear;
|
private readonly IProgram _programDepthStencilClear;
|
||||||
private readonly IProgram _programStrideChange;
|
private readonly IProgram _programStrideChange;
|
||||||
|
|
||||||
|
private readonly EncoderState _helperShaderState = new();
|
||||||
|
|
||||||
public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
|
public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
|
@ -80,8 +82,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
bool linearFilter,
|
bool linearFilter,
|
||||||
bool clear = false)
|
bool clear = false)
|
||||||
{
|
{
|
||||||
// Save current state
|
_pipeline.SwapState(_helperShaderState);
|
||||||
_pipeline.SaveAndResetState();
|
|
||||||
|
|
||||||
const int RegionBufferSize = 16;
|
const int RegionBufferSize = 16;
|
||||||
|
|
||||||
|
@ -141,8 +142,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
if (clear)
|
||||||
|
{
|
||||||
|
_pipeline.SetClearLoadAction(false);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.RestoreState();
|
_pipeline.SwapState(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void DrawTexture(
|
public unsafe void DrawTexture(
|
||||||
|
@ -152,7 +159,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
Extents2DF dstRegion)
|
Extents2DF dstRegion)
|
||||||
{
|
{
|
||||||
// Save current state
|
// Save current state
|
||||||
_pipeline.SaveState();
|
var state = _pipeline.SavePredrawState();
|
||||||
|
|
||||||
|
_pipeline.SetFaceCulling(false, Face.Front);
|
||||||
|
_pipeline.SetStencilTest(new StencilTestDescriptor());
|
||||||
|
_pipeline.SetDepthTest(new DepthTestDescriptor());
|
||||||
|
|
||||||
const int RegionBufferSize = 16;
|
const int RegionBufferSize = 16;
|
||||||
|
|
||||||
|
@ -204,7 +215,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_renderer.BufferManager.Delete(bufferHandle);
|
_renderer.BufferManager.Delete(bufferHandle);
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.RestoreState();
|
_pipeline.RestorePredrawState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
|
public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
|
||||||
|
@ -229,7 +240,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
const int ParamsBufferSize = 16;
|
const int ParamsBufferSize = 16;
|
||||||
|
|
||||||
// Save current state
|
// Save current state
|
||||||
_pipeline.SaveAndResetState();
|
_pipeline.SwapState(_helperShaderState);
|
||||||
|
|
||||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||||
|
|
||||||
|
@ -252,7 +263,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1);
|
_pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1);
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.RestoreState();
|
_pipeline.SwapState(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void ClearColor(
|
public unsafe void ClearColor(
|
||||||
|
@ -262,8 +273,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
int dstWidth,
|
int dstWidth,
|
||||||
int dstHeight)
|
int dstHeight)
|
||||||
{
|
{
|
||||||
|
// Keep original scissor
|
||||||
|
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||||
|
|
||||||
// Save current state
|
// Save current state
|
||||||
_pipeline.SaveState();
|
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags);
|
||||||
|
|
||||||
|
// Inherit some state without fully recreating render pipeline.
|
||||||
|
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index);
|
||||||
|
|
||||||
const int ClearColorBufferSize = 16;
|
const int ClearColorBufferSize = 16;
|
||||||
|
|
||||||
|
@ -286,7 +303,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
_pipeline.SetProgram(_programsColorClear[index]);
|
_pipeline.SetProgram(_programsColorClear[index]);
|
||||||
_pipeline.SetBlendState(index, new BlendDescriptor(false, new ColorF(0f, 0f, 0f, 1f), BlendOp.Add, BlendFactor.One, BlendFactor.Zero, BlendOp.Add, BlendFactor.One, BlendFactor.Zero));
|
_pipeline.SetBlendState(index, new BlendDescriptor());
|
||||||
_pipeline.SetFaceCulling(false, Face.Front);
|
_pipeline.SetFaceCulling(false, Face.Front);
|
||||||
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||||
_pipeline.SetRenderTargetColorMasks([componentMask]);
|
_pipeline.SetRenderTargetColorMasks([componentMask]);
|
||||||
|
@ -295,7 +312,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.RestoreState();
|
_pipeline.SwapState(null, clearFlags);
|
||||||
|
|
||||||
|
_helperShaderState.Restore(save);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void ClearDepthStencil(
|
public unsafe void ClearDepthStencil(
|
||||||
|
@ -306,8 +325,15 @@ namespace Ryujinx.Graphics.Metal
|
||||||
int dstWidth,
|
int dstWidth,
|
||||||
int dstHeight)
|
int dstHeight)
|
||||||
{
|
{
|
||||||
|
// Keep original scissor
|
||||||
|
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||||
|
var helperScissors = _helperShaderState.Scissors;
|
||||||
|
|
||||||
// Save current state
|
// Save current state
|
||||||
_pipeline.SaveState();
|
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags);
|
||||||
|
|
||||||
|
// Inherit some state without fully recreating render pipeline.
|
||||||
|
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true);
|
||||||
|
|
||||||
const int ClearDepthBufferSize = 16;
|
const int ClearDepthBufferSize = 16;
|
||||||
|
|
||||||
|
@ -334,8 +360,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask));
|
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask));
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||||
|
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
|
||||||
|
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
_pipeline.RestoreState();
|
_pipeline.SwapState(null, clearFlags);
|
||||||
|
|
||||||
|
_helperShaderState.Restore(save);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StencilTestDescriptor CreateStencilTestDescriptor(
|
private static StencilTestDescriptor CreateStencilTestDescriptor(
|
||||||
|
|
|
@ -57,19 +57,19 @@ namespace Ryujinx.Graphics.Metal
|
||||||
TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveState()
|
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All)
|
||||||
{
|
{
|
||||||
_encoderStateManager.SaveState();
|
return _encoderStateManager.SwapState(state, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveAndResetState()
|
public PredrawState SavePredrawState()
|
||||||
{
|
{
|
||||||
_encoderStateManager.SaveAndResetState();
|
return _encoderStateManager.SavePredrawState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RestoreState()
|
public void RestorePredrawState(PredrawState state)
|
||||||
{
|
{
|
||||||
_encoderStateManager.RestoreState();
|
_encoderStateManager.RestorePredrawState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetClearLoadAction(bool clear)
|
public void SetClearLoadAction(bool clear)
|
||||||
|
@ -240,8 +240,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void FlushCommandsImpl()
|
public void FlushCommandsImpl()
|
||||||
{
|
{
|
||||||
SaveState();
|
|
||||||
|
|
||||||
EndCurrentPass();
|
EndCurrentPass();
|
||||||
|
|
||||||
_byteWeight = 0;
|
_byteWeight = 0;
|
||||||
|
@ -254,8 +252,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||||
_renderer.RegisterFlush();
|
_renderer.RegisterFlush();
|
||||||
|
|
||||||
RestoreState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BlitColor(
|
public void BlitColor(
|
||||||
|
@ -510,9 +506,16 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
|
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
|
||||||
|
{
|
||||||
|
if (enables == 0)
|
||||||
|
{
|
||||||
|
_encoderStateManager.UpdateDepthBias(0, 0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
|
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDepthClamp(bool clamp)
|
public void SetDepthClamp(bool clamp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public MTLFunction FragmentFunction;
|
public MTLFunction FragmentFunction;
|
||||||
public MTLFunction ComputeFunction;
|
public MTLFunction ComputeFunction;
|
||||||
|
|
||||||
|
private HashTableSlim<PipelineUid, MTLRenderPipelineState> _graphicsPipelineCache;
|
||||||
|
private MTLComputePipelineState? _computePipelineCache;
|
||||||
|
private bool _firstBackgroundUse;
|
||||||
|
|
||||||
public Program(ShaderSource[] shaders, MTLDevice device)
|
public Program(ShaderSource[] shaders, MTLDevice device)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < shaders.Length; index++)
|
for (int index = 0; index < shaders.Length; index++)
|
||||||
|
@ -62,8 +66,64 @@ namespace Ryujinx.Graphics.Metal
|
||||||
return ""u8.ToArray();
|
return ""u8.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline)
|
||||||
|
{
|
||||||
|
(_graphicsPipelineCache ??= new()).Add(ref key, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddComputePipeline(MTLComputePipelineState pipeline)
|
||||||
|
{
|
||||||
|
_computePipelineCache = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGraphicsPipeline(ref PipelineUid key, out MTLRenderPipelineState pipeline)
|
||||||
|
{
|
||||||
|
if (_graphicsPipelineCache == null)
|
||||||
|
{
|
||||||
|
pipeline = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline))
|
||||||
|
{
|
||||||
|
if (_firstBackgroundUse)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?");
|
||||||
|
_firstBackgroundUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_firstBackgroundUse = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetComputePipeline(out MTLComputePipelineState pipeline)
|
||||||
|
{
|
||||||
|
if (_computePipelineCache.HasValue)
|
||||||
|
{
|
||||||
|
pipeline = _computePipelineCache.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (_graphicsPipelineCache != null)
|
||||||
|
{
|
||||||
|
foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values)
|
||||||
|
{
|
||||||
|
pipeline.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_computePipelineCache?.Dispose();
|
||||||
|
|
||||||
VertexFunction.Dispose();
|
VertexFunction.Dispose();
|
||||||
FragmentFunction.Dispose();
|
FragmentFunction.Dispose();
|
||||||
ComputeFunction.Dispose();
|
ComputeFunction.Dispose();
|
||||||
|
|
|
@ -1,248 +0,0 @@
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using SharpMetal.Foundation;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
struct RenderPipelineHash
|
|
||||||
{
|
|
||||||
public MTLFunction VertexFunction;
|
|
||||||
public MTLFunction FragmentFunction;
|
|
||||||
public struct ColorAttachmentHash
|
|
||||||
{
|
|
||||||
public MTLPixelFormat PixelFormat;
|
|
||||||
public bool BlendingEnabled;
|
|
||||||
public MTLBlendOperation RgbBlendOperation;
|
|
||||||
public MTLBlendOperation AlphaBlendOperation;
|
|
||||||
public MTLBlendFactor SourceRGBBlendFactor;
|
|
||||||
public MTLBlendFactor DestinationRGBBlendFactor;
|
|
||||||
public MTLBlendFactor SourceAlphaBlendFactor;
|
|
||||||
public MTLBlendFactor DestinationAlphaBlendFactor;
|
|
||||||
public MTLColorWriteMask WriteMask;
|
|
||||||
}
|
|
||||||
[System.Runtime.CompilerServices.InlineArray(Constants.MaxColorAttachments)]
|
|
||||||
public struct ColorAttachmentHashArray
|
|
||||||
{
|
|
||||||
public ColorAttachmentHash data;
|
|
||||||
}
|
|
||||||
public ColorAttachmentHashArray ColorAttachments;
|
|
||||||
public struct DepthStencilAttachmentHash
|
|
||||||
{
|
|
||||||
public MTLPixelFormat DepthPixelFormat;
|
|
||||||
public MTLPixelFormat StencilPixelFormat;
|
|
||||||
}
|
|
||||||
public DepthStencilAttachmentHash DepthStencilAttachment;
|
|
||||||
public struct VertexDescriptorHash
|
|
||||||
{
|
|
||||||
public struct AttributeHash
|
|
||||||
{
|
|
||||||
public MTLVertexFormat Format;
|
|
||||||
public ulong Offset;
|
|
||||||
public ulong BufferIndex;
|
|
||||||
}
|
|
||||||
[System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexAttributes)]
|
|
||||||
public struct AttributeHashArray
|
|
||||||
{
|
|
||||||
public AttributeHash data;
|
|
||||||
}
|
|
||||||
public AttributeHashArray Attributes;
|
|
||||||
public struct LayoutHash
|
|
||||||
{
|
|
||||||
public ulong Stride;
|
|
||||||
public MTLVertexStepFunction StepFunction;
|
|
||||||
public ulong StepRate;
|
|
||||||
}
|
|
||||||
[System.Runtime.CompilerServices.InlineArray(Constants.MaxVertexLayouts)]
|
|
||||||
public struct LayoutHashArray
|
|
||||||
{
|
|
||||||
public LayoutHash data;
|
|
||||||
}
|
|
||||||
public LayoutHashArray Layouts;
|
|
||||||
}
|
|
||||||
public VertexDescriptorHash VertexDescriptor;
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is not RenderPipelineHash other)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VertexFunction != other.VertexFunction)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (FragmentFunction != other.FragmentFunction)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (DepthStencilAttachment.DepthPixelFormat != other.DepthStencilAttachment.DepthPixelFormat)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (DepthStencilAttachment.StencilPixelFormat != other.DepthStencilAttachment.StencilPixelFormat)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
|
||||||
{
|
|
||||||
if (ColorAttachments[i].PixelFormat != other.ColorAttachments[i].PixelFormat)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].BlendingEnabled != other.ColorAttachments[i].BlendingEnabled)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].RgbBlendOperation != other.ColorAttachments[i].RgbBlendOperation)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].AlphaBlendOperation != other.ColorAttachments[i].AlphaBlendOperation)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].SourceRGBBlendFactor != other.ColorAttachments[i].SourceRGBBlendFactor)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].DestinationRGBBlendFactor != other.ColorAttachments[i].DestinationRGBBlendFactor)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].SourceAlphaBlendFactor != other.ColorAttachments[i].SourceAlphaBlendFactor)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].DestinationAlphaBlendFactor != other.ColorAttachments[i].DestinationAlphaBlendFactor)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ColorAttachments[i].WriteMask != other.ColorAttachments[i].WriteMask)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < Constants.MaxVertexAttributes; i++)
|
|
||||||
{
|
|
||||||
if (VertexDescriptor.Attributes[i].Format != other.VertexDescriptor.Attributes[i].Format)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VertexDescriptor.Attributes[i].Offset != other.VertexDescriptor.Attributes[i].Offset)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VertexDescriptor.Attributes[i].BufferIndex != other.VertexDescriptor.Attributes[i].BufferIndex)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < Constants.MaxVertexLayouts; i++)
|
|
||||||
{
|
|
||||||
if (VertexDescriptor.Layouts[i].Stride != other.VertexDescriptor.Layouts[i].Stride)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VertexDescriptor.Layouts[i].StepFunction != other.VertexDescriptor.Layouts[i].StepFunction)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VertexDescriptor.Layouts[i].StepRate != other.VertexDescriptor.Layouts[i].StepRate)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
class RenderPipelineCache : StateCache<MTLRenderPipelineState, MTLRenderPipelineDescriptor, RenderPipelineHash>
|
|
||||||
{
|
|
||||||
private readonly MTLDevice _device;
|
|
||||||
|
|
||||||
public RenderPipelineCache(MTLDevice device)
|
|
||||||
{
|
|
||||||
_device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override RenderPipelineHash GetHash(MTLRenderPipelineDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hash = new RenderPipelineHash
|
|
||||||
{
|
|
||||||
// Functions
|
|
||||||
VertexFunction = descriptor.VertexFunction,
|
|
||||||
FragmentFunction = descriptor.FragmentFunction,
|
|
||||||
DepthStencilAttachment = new RenderPipelineHash.DepthStencilAttachmentHash
|
|
||||||
{
|
|
||||||
DepthPixelFormat = descriptor.DepthAttachmentPixelFormat,
|
|
||||||
StencilPixelFormat = descriptor.StencilAttachmentPixelFormat
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Color Attachments
|
|
||||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
|
||||||
{
|
|
||||||
var attachment = descriptor.ColorAttachments.Object((ulong)i);
|
|
||||||
hash.ColorAttachments[i] = new RenderPipelineHash.ColorAttachmentHash
|
|
||||||
{
|
|
||||||
PixelFormat = attachment.PixelFormat,
|
|
||||||
BlendingEnabled = attachment.BlendingEnabled,
|
|
||||||
RgbBlendOperation = attachment.RgbBlendOperation,
|
|
||||||
AlphaBlendOperation = attachment.AlphaBlendOperation,
|
|
||||||
SourceRGBBlendFactor = attachment.SourceRGBBlendFactor,
|
|
||||||
DestinationRGBBlendFactor = attachment.DestinationRGBBlendFactor,
|
|
||||||
SourceAlphaBlendFactor = attachment.SourceAlphaBlendFactor,
|
|
||||||
DestinationAlphaBlendFactor = attachment.DestinationAlphaBlendFactor,
|
|
||||||
WriteMask = attachment.WriteMask
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertex descriptor
|
|
||||||
hash.VertexDescriptor = new RenderPipelineHash.VertexDescriptorHash();
|
|
||||||
|
|
||||||
// Attributes
|
|
||||||
for (int i = 0; i < Constants.MaxVertexAttributes; i++)
|
|
||||||
{
|
|
||||||
var attribute = descriptor.VertexDescriptor.Attributes.Object((ulong)i);
|
|
||||||
hash.VertexDescriptor.Attributes[i] = new RenderPipelineHash.VertexDescriptorHash.AttributeHash
|
|
||||||
{
|
|
||||||
Format = attribute.Format,
|
|
||||||
Offset = attribute.Offset,
|
|
||||||
BufferIndex = attribute.BufferIndex
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layouts
|
|
||||||
for (int i = 0; i < Constants.MaxVertexLayouts; i++)
|
|
||||||
{
|
|
||||||
var layout = descriptor.VertexDescriptor.Layouts.Object((ulong)i);
|
|
||||||
hash.VertexDescriptor.Layouts[i] = new RenderPipelineHash.VertexDescriptorHash.LayoutHash
|
|
||||||
{
|
|
||||||
Stride = layout.Stride,
|
|
||||||
StepFunction = layout.StepFunction,
|
|
||||||
StepRate = layout.StepRate
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MTLRenderPipelineState CreateValue(MTLRenderPipelineDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var error = new NSError(IntPtr.Zero);
|
|
||||||
var pipelineState = _device.NewRenderPipelineState(descriptor, ref error);
|
|
||||||
if (error != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return pipelineState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
110
src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs
Normal file
110
src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
using SharpMetal.Metal;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal.State
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct StencilUid
|
||||||
|
{
|
||||||
|
public uint ReadMask;
|
||||||
|
public uint WriteMask;
|
||||||
|
public ushort Operations;
|
||||||
|
|
||||||
|
public MTLStencilOperation StencilFailureOperation
|
||||||
|
{
|
||||||
|
readonly get => (MTLStencilOperation)((Operations >> 0) & 0xF);
|
||||||
|
set => Operations = (ushort)((Operations & 0xFFF0) | ((int)value << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLStencilOperation DepthFailureOperation
|
||||||
|
{
|
||||||
|
readonly get => (MTLStencilOperation)((Operations >> 4) & 0xF);
|
||||||
|
set => Operations = (ushort)((Operations & 0xFF0F) | ((int)value << 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLStencilOperation DepthStencilPassOperation
|
||||||
|
{
|
||||||
|
readonly get => (MTLStencilOperation)((Operations >> 8) & 0xF);
|
||||||
|
set => Operations = (ushort)((Operations & 0xF0FF) | ((int)value << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLCompareFunction StencilCompareFunction
|
||||||
|
{
|
||||||
|
readonly get => (MTLCompareFunction)((Operations >> 12) & 0xF);
|
||||||
|
set => Operations = (ushort)((Operations & 0x0FFF) | ((int)value << 12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 24)]
|
||||||
|
internal struct DepthStencilUid : IEquatable<DepthStencilUid>
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public StencilUid FrontFace;
|
||||||
|
|
||||||
|
[FieldOffset(10)]
|
||||||
|
public ushort DepthState;
|
||||||
|
|
||||||
|
[FieldOffset(12)]
|
||||||
|
public StencilUid BackFace;
|
||||||
|
|
||||||
|
[FieldOffset(22)]
|
||||||
|
private readonly ushort _padding;
|
||||||
|
|
||||||
|
// Quick access aliases
|
||||||
|
#pragma warning disable IDE0044 // Add readonly modifier
|
||||||
|
[FieldOffset(0)]
|
||||||
|
private ulong _id0;
|
||||||
|
[FieldOffset(8)]
|
||||||
|
private ulong _id1;
|
||||||
|
[FieldOffset(0)]
|
||||||
|
private Vector128<byte> _id01;
|
||||||
|
[FieldOffset(16)]
|
||||||
|
private ulong _id2;
|
||||||
|
#pragma warning restore IDE0044 // Add readonly modifier
|
||||||
|
|
||||||
|
public MTLCompareFunction DepthCompareFunction
|
||||||
|
{
|
||||||
|
readonly get => (MTLCompareFunction)((DepthState >> 0) & 0xF);
|
||||||
|
set => DepthState = (ushort)((DepthState & 0xFFF0) | ((int)value << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StencilTestEnabled
|
||||||
|
{
|
||||||
|
readonly get => ((DepthState >> 4) & 0x1) != 0;
|
||||||
|
set => DepthState = (ushort)((DepthState & 0xFFEF) | ((value ? 1 : 0) << 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DepthWriteEnabled
|
||||||
|
{
|
||||||
|
readonly get => ((DepthState >> 15) & 0x1) != 0;
|
||||||
|
set => DepthState = (ushort)((DepthState & 0x7FFF) | ((value ? 1 : 0) << 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is DepthStencilUid other && EqualsRef(ref other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool EqualsRef(ref DepthStencilUid other)
|
||||||
|
{
|
||||||
|
return _id01.Equals(other._id01) && _id2 == other._id2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Equals(DepthStencilUid other)
|
||||||
|
{
|
||||||
|
return EqualsRef(ref other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override int GetHashCode()
|
||||||
|
{
|
||||||
|
ulong hash64 = _id0 * 23 ^
|
||||||
|
_id1 * 23 ^
|
||||||
|
_id2 * 23;
|
||||||
|
|
||||||
|
return (int)hash64 ^ ((int)(hash64 >> 32) * 17);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
338
src/Ryujinx.Graphics.Metal/State/PipelineState.cs
Normal file
338
src/Ryujinx.Graphics.Metal/State/PipelineState.cs
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using SharpMetal.Foundation;
|
||||||
|
using SharpMetal.Metal;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
struct PipelineState
|
||||||
|
{
|
||||||
|
public PipelineUid Internal;
|
||||||
|
|
||||||
|
public uint StagesCount
|
||||||
|
{
|
||||||
|
readonly get => (byte)((Internal.Id0 >> 0) & 0xFF);
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint VertexAttributeDescriptionsCount
|
||||||
|
{
|
||||||
|
readonly get => (byte)((Internal.Id0 >> 8) & 0xFF);
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint VertexBindingDescriptionsCount
|
||||||
|
{
|
||||||
|
readonly get => (byte)((Internal.Id0 >> 16) & 0xFF);
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint ColorBlendAttachmentStateCount
|
||||||
|
{
|
||||||
|
readonly get => (byte)((Internal.Id0 >> 24) & 0xFF);
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can be an input to a pipeline, but not sure what the situation for that is.
|
||||||
|
public PrimitiveTopology Topology
|
||||||
|
{
|
||||||
|
readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF);
|
||||||
|
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Reserved for when API is available.
|
||||||
|
public int LogicOp
|
||||||
|
{
|
||||||
|
readonly get => (int)((Internal.Id0 >> 32) & 0xF);
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
//?
|
||||||
|
public bool PrimitiveRestartEnable
|
||||||
|
{
|
||||||
|
readonly get => ((Internal.Id0 >> 36) & 0x1) != 0UL;
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFEFFFFFFFFF) | ((value ? 1UL : 0UL) << 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RasterizerDiscardEnable
|
||||||
|
{
|
||||||
|
readonly get => ((Internal.Id0 >> 37) & 0x1) != 0UL;
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFDFFFFFFFFF) | ((value ? 1UL : 0UL) << 37);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserved for when API is available.
|
||||||
|
public bool LogicOpEnable
|
||||||
|
{
|
||||||
|
readonly get => ((Internal.Id0 >> 38) & 0x1) != 0UL;
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFBFFFFFFFFF) | ((value ? 1UL : 0UL) << 38);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AlphaToCoverageEnable
|
||||||
|
{
|
||||||
|
readonly get => ((Internal.Id0 >> 40) & 0x1) != 0UL;
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFEFFFFFFFFFF) | ((value ? 1UL : 0UL) << 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AlphaToOneEnable
|
||||||
|
{
|
||||||
|
readonly get => ((Internal.Id0 >> 41) & 0x1) != 0UL;
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFDFFFFFFFFFF) | ((value ? 1UL : 0UL) << 41);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLPixelFormat DepthStencilFormat
|
||||||
|
{
|
||||||
|
readonly get => (MTLPixelFormat)(Internal.Id0 >> 48);
|
||||||
|
set => Internal.Id0 = (Internal.Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure how to appropriately use this, but it does need to be passed for tess.
|
||||||
|
public uint PatchControlPoints
|
||||||
|
{
|
||||||
|
readonly get => (uint)((Internal.Id1 >> 0) & 0xFFFFFFFF);
|
||||||
|
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint SamplesCount
|
||||||
|
{
|
||||||
|
readonly get => (uint)((Internal.Id1 >> 32) & 0xFFFFFFFF);
|
||||||
|
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)value << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advanced blend not supported
|
||||||
|
|
||||||
|
private struct RenderPipelineDescriptorResult : IDisposable
|
||||||
|
{
|
||||||
|
public MTLRenderPipelineDescriptor Pipeline;
|
||||||
|
private MTLVertexDescriptor _vertex;
|
||||||
|
|
||||||
|
public RenderPipelineDescriptorResult(MTLRenderPipelineDescriptor pipeline, MTLVertexDescriptor vertex)
|
||||||
|
{
|
||||||
|
Pipeline = pipeline;
|
||||||
|
_vertex = vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Pipeline.Dispose();
|
||||||
|
_vertex.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly void BuildColorAttachment(MTLRenderPipelineColorAttachmentDescriptor descriptor, ColorBlendStateUid blendState)
|
||||||
|
{
|
||||||
|
descriptor.PixelFormat = blendState.PixelFormat;
|
||||||
|
descriptor.SetBlendingEnabled(blendState.Enable);
|
||||||
|
descriptor.AlphaBlendOperation = blendState.AlphaBlendOperation;
|
||||||
|
descriptor.RgbBlendOperation = blendState.RgbBlendOperation;
|
||||||
|
descriptor.SourceAlphaBlendFactor = blendState.SourceAlphaBlendFactor;
|
||||||
|
descriptor.DestinationAlphaBlendFactor = blendState.DestinationAlphaBlendFactor;
|
||||||
|
descriptor.SourceRGBBlendFactor = blendState.SourceRGBBlendFactor;
|
||||||
|
descriptor.DestinationRGBBlendFactor = blendState.DestinationRGBBlendFactor;
|
||||||
|
descriptor.WriteMask = blendState.WriteMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly MTLVertexDescriptor BuildVertexDescriptor()
|
||||||
|
{
|
||||||
|
var vertexDescriptor = new MTLVertexDescriptor();
|
||||||
|
|
||||||
|
for (int i = 0; i < VertexAttributeDescriptionsCount; i++)
|
||||||
|
{
|
||||||
|
VertexInputAttributeUid uid = Internal.VertexAttributes[i];
|
||||||
|
|
||||||
|
var attrib = vertexDescriptor.Attributes.Object((ulong)i);
|
||||||
|
attrib.Format = uid.Format;
|
||||||
|
attrib.Offset = uid.Offset;
|
||||||
|
attrib.BufferIndex = uid.BufferIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < VertexBindingDescriptionsCount; i++)
|
||||||
|
{
|
||||||
|
VertexInputLayoutUid uid = Internal.VertexBindings[i];
|
||||||
|
|
||||||
|
var layout = vertexDescriptor.Layouts.Object((ulong)i);
|
||||||
|
|
||||||
|
layout.StepFunction = uid.StepFunction;
|
||||||
|
layout.StepRate = uid.StepRate;
|
||||||
|
layout.Stride = uid.Stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertexDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderPipelineDescriptorResult CreateRenderDescriptor(Program program)
|
||||||
|
{
|
||||||
|
var renderPipelineDescriptor = new MTLRenderPipelineDescriptor();
|
||||||
|
|
||||||
|
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
||||||
|
{
|
||||||
|
var blendState = Internal.ColorBlendState[i];
|
||||||
|
|
||||||
|
if (blendState.PixelFormat != MTLPixelFormat.Invalid)
|
||||||
|
{
|
||||||
|
var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i);
|
||||||
|
|
||||||
|
BuildColorAttachment(pipelineAttachment, blendState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MTLPixelFormat dsFormat = DepthStencilFormat;
|
||||||
|
if (dsFormat != MTLPixelFormat.Invalid)
|
||||||
|
{
|
||||||
|
switch (dsFormat)
|
||||||
|
{
|
||||||
|
// Depth Only Attachment
|
||||||
|
case MTLPixelFormat.Depth16Unorm:
|
||||||
|
case MTLPixelFormat.Depth32Float:
|
||||||
|
renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Stencil Only Attachment
|
||||||
|
case MTLPixelFormat.Stencil8:
|
||||||
|
renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Combined Attachment
|
||||||
|
case MTLPixelFormat.Depth24UnormStencil8:
|
||||||
|
case MTLPixelFormat.Depth32FloatStencil8:
|
||||||
|
renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat;
|
||||||
|
renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {dsFormat}!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: enable when sharpmetal fixes the bindings
|
||||||
|
renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable;
|
||||||
|
renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable;
|
||||||
|
renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable;
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount);
|
||||||
|
|
||||||
|
var vertexDescriptor = BuildVertexDescriptor();
|
||||||
|
renderPipelineDescriptor.VertexDescriptor = vertexDescriptor;
|
||||||
|
|
||||||
|
renderPipelineDescriptor.VertexFunction = program.VertexFunction;
|
||||||
|
|
||||||
|
if (program.FragmentFunction.NativePtr != 0)
|
||||||
|
{
|
||||||
|
renderPipelineDescriptor.FragmentFunction = program.FragmentFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RenderPipelineDescriptorResult(renderPipelineDescriptor, vertexDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program)
|
||||||
|
{
|
||||||
|
if (program.TryGetGraphicsPipeline(ref Internal, out var pipelineState))
|
||||||
|
{
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
using RenderPipelineDescriptorResult descriptors = CreateRenderDescriptor(program);
|
||||||
|
|
||||||
|
var error = new NSError(IntPtr.Zero);
|
||||||
|
pipelineState = device.NewRenderPipelineState(descriptors.Pipeline, ref error);
|
||||||
|
if (error != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
program.AddGraphicsPipeline(ref Internal, pipelineState);
|
||||||
|
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program)
|
||||||
|
{
|
||||||
|
if (program.TryGetComputePipeline(out var pipelineState))
|
||||||
|
{
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
var error = new NSError(IntPtr.Zero);
|
||||||
|
pipelineState = device.NewComputePipelineState(program.ComputeFunction, ref error);
|
||||||
|
if (error != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
program.AddComputePipeline(pipelineState);
|
||||||
|
|
||||||
|
return pipelineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
SamplesCount = 1;
|
||||||
|
|
||||||
|
Internal.ResetColorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO, this is from vulkan.
|
||||||
|
|
||||||
|
private void UpdateVertexAttributeDescriptions(VulkanRenderer gd)
|
||||||
|
{
|
||||||
|
// Vertex attributes exceeding the stride are invalid.
|
||||||
|
// In metal, they cause glitches with the vertex shader fetching incorrect values.
|
||||||
|
// To work around this, we reduce the format to something that doesn't exceed the stride if possible.
|
||||||
|
// The assumption is that the exceeding components are not actually accessed on the shader.
|
||||||
|
|
||||||
|
for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
|
||||||
|
{
|
||||||
|
var attribute = Internal.VertexAttributeDescriptions[index];
|
||||||
|
int vbIndex = GetVertexBufferIndex(attribute.Binding);
|
||||||
|
|
||||||
|
if (vbIndex >= 0)
|
||||||
|
{
|
||||||
|
ref var vb = ref Internal.VertexBindingDescriptions[vbIndex];
|
||||||
|
|
||||||
|
Format format = attribute.Format;
|
||||||
|
|
||||||
|
while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride)
|
||||||
|
{
|
||||||
|
Format newFormat = FormatTable.DropLastComponent(format);
|
||||||
|
|
||||||
|
if (newFormat == format)
|
||||||
|
{
|
||||||
|
// That case means we failed to find a format that fits within the stride,
|
||||||
|
// so just restore the original format and give up.
|
||||||
|
format = attribute.Format;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = newFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format))
|
||||||
|
{
|
||||||
|
attribute.Format = format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vertexAttributeDescriptions2[index] = attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetVertexBufferIndex(uint binding)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < VertexBindingDescriptionsCount; index++)
|
||||||
|
{
|
||||||
|
if (Internal.VertexBindingDescriptions[index].Binding == binding)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
200
src/Ryujinx.Graphics.Metal/State/PipelineUid.cs
Normal file
200
src/Ryujinx.Graphics.Metal/State/PipelineUid.cs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using SharpMetal.Metal;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
{
|
||||||
|
struct VertexInputAttributeUid
|
||||||
|
{
|
||||||
|
public ulong Id0;
|
||||||
|
|
||||||
|
public ulong Offset
|
||||||
|
{
|
||||||
|
readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLVertexFormat Format
|
||||||
|
{
|
||||||
|
readonly get => (MTLVertexFormat)((Id0 >> 32) & 0xFFFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFF0000FFFFFFFF) | ((ulong)value << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong BufferIndex
|
||||||
|
{
|
||||||
|
readonly get => ((Id0 >> 48) & 0xFFFF);
|
||||||
|
set => Id0 = (Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertexInputLayoutUid
|
||||||
|
{
|
||||||
|
public ulong Id0;
|
||||||
|
|
||||||
|
public uint Stride
|
||||||
|
{
|
||||||
|
readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint StepRate
|
||||||
|
{
|
||||||
|
readonly get => (uint)((Id0 >> 32) & 0x1FFFFFFF);
|
||||||
|
set => Id0 = (Id0 & 0xE0000000FFFFFFFF) | ((ulong)value << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLVertexStepFunction StepFunction
|
||||||
|
{
|
||||||
|
readonly get => (MTLVertexStepFunction)((Id0 >> 61) & 0x7);
|
||||||
|
set => Id0 = (Id0 & 0x1FFFFFFFFFFFFFFF) | ((ulong)value << 61);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ColorBlendStateUid
|
||||||
|
{
|
||||||
|
public ulong Id0;
|
||||||
|
|
||||||
|
public MTLPixelFormat PixelFormat
|
||||||
|
{
|
||||||
|
readonly get => (MTLPixelFormat)((Id0 >> 0) & 0xFFFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFFFFFFFF0000) | ((ulong)value << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLBlendFactor SourceRGBBlendFactor
|
||||||
|
{
|
||||||
|
readonly get => (MTLBlendFactor)((Id0 >> 16) & 0xFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLBlendFactor DestinationRGBBlendFactor
|
||||||
|
{
|
||||||
|
readonly get => (MTLBlendFactor)((Id0 >> 24) & 0xFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLBlendOperation RgbBlendOperation
|
||||||
|
{
|
||||||
|
readonly get => (MTLBlendOperation)((Id0 >> 32) & 0xF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLBlendOperation AlphaBlendOperation
|
||||||
|
{
|
||||||
|
readonly get => (MTLBlendOperation)((Id0 >> 36) & 0xF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFFFF0FFFFFFFFF) | ((ulong)value << 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLBlendFactor SourceAlphaBlendFactor
|
||||||
|
{
|
||||||
|
readonly get => (MTLBlendFactor)((Id0 >> 40) & 0xFF);
|
||||||
|
set => Id0 = (Id0 & 0xFFFF00FFFFFFFFFF) | ((ulong)value << 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLBlendFactor DestinationAlphaBlendFactor
|
||||||
|
{
|
||||||
|
readonly get => (MTLBlendFactor)((Id0 >> 48) & 0xFF);
|
||||||
|
set => Id0 = (Id0 & 0xFF00FFFFFFFFFFFF) | ((ulong)value << 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLColorWriteMask WriteMask
|
||||||
|
{
|
||||||
|
readonly get => (MTLColorWriteMask)((Id0 >> 56) & 0xF);
|
||||||
|
set => Id0 = (Id0 & 0xF0FFFFFFFFFFFFFF) | ((ulong)value << 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Enable
|
||||||
|
{
|
||||||
|
readonly get => ((Id0 >> 63) & 0x1) != 0UL;
|
||||||
|
set => Id0 = (Id0 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
struct PipelineUid : IRefEquatable<PipelineUid>
|
||||||
|
{
|
||||||
|
public ulong Id0;
|
||||||
|
public ulong Id1;
|
||||||
|
|
||||||
|
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 8) & 0xFF);
|
||||||
|
private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 16) & 0xFF);
|
||||||
|
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id0 >> 24) & 0xFF);
|
||||||
|
|
||||||
|
public Array32<VertexInputAttributeUid> VertexAttributes;
|
||||||
|
public Array33<VertexInputLayoutUid> VertexBindings;
|
||||||
|
public Array8<ColorBlendStateUid> ColorBlendState;
|
||||||
|
public uint AttachmentIntegerFormatMask;
|
||||||
|
public bool LogicOpsAllowed;
|
||||||
|
|
||||||
|
public void ResetColorState()
|
||||||
|
{
|
||||||
|
ColorBlendState = new();
|
||||||
|
|
||||||
|
for (int i = 0; i < ColorBlendState.Length; i++)
|
||||||
|
{
|
||||||
|
ColorBlendState[i].WriteMask = MTLColorWriteMask.All;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is PipelineUid other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ref PipelineUid other)
|
||||||
|
{
|
||||||
|
if (!Unsafe.As<ulong, Vector128<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id0)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SequenceEqual<VertexInputAttributeUid>(VertexAttributes.AsSpan(), other.VertexAttributes.AsSpan(), VertexAttributeDescriptionsCount))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SequenceEqual<VertexInputLayoutUid>(VertexBindings.AsSpan(), other.VertexBindings.AsSpan(), VertexBindingDescriptionsCount))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SequenceEqual<ColorBlendStateUid>(ColorBlendState.AsSpan(), other.ColorBlendState.AsSpan(), ColorBlendAttachmentStateCount))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SequenceEqual<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> y, uint count) where T : unmanaged
|
||||||
|
{
|
||||||
|
return MemoryMarshal.Cast<T, byte>(x[..(int)count]).SequenceEqual(MemoryMarshal.Cast<T, byte>(y[..(int)count]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
ulong hash64 = Id0 * 23 ^
|
||||||
|
Id1 * 23;
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
|
||||||
|
{
|
||||||
|
hash64 ^= VertexAttributes[i].Id0 * 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++)
|
||||||
|
{
|
||||||
|
hash64 ^= VertexBindings[i].Id0 * 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++)
|
||||||
|
{
|
||||||
|
hash64 ^= ColorBlendState[i].Id0 * 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)hash64 ^ ((int)(hash64 >> 32) * 17);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info)
|
public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info)
|
||||||
{
|
{
|
||||||
|
MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format);
|
||||||
|
|
||||||
var descriptor = new MTLTextureDescriptor
|
var descriptor = new MTLTextureDescriptor
|
||||||
{
|
{
|
||||||
PixelFormat = FormatTable.GetFormat(Info.Format),
|
PixelFormat = pixelFormat,
|
||||||
Usage = MTLTextureUsage.Unknown,
|
Usage = MTLTextureUsage.Unknown,
|
||||||
SampleCount = (ulong)Info.Samples,
|
SampleCount = (ulong)Info.Samples,
|
||||||
TextureType = Info.Target.Convert(),
|
TextureType = Info.Target.Convert(),
|
||||||
|
@ -35,6 +37,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat);
|
descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat);
|
||||||
|
|
||||||
_mtlTexture = _device.NewTexture(descriptor);
|
_mtlTexture = _device.NewTexture(descriptor);
|
||||||
|
MtlFormat = pixelFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info)
|
public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info)
|
||||||
|
@ -51,6 +54,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var swizzle = GetSwizzle(info, pixelFormat);
|
var swizzle = GetSwizzle(info, pixelFormat);
|
||||||
|
|
||||||
_mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle);
|
_mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle);
|
||||||
|
MtlFormat = pixelFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat)
|
private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat)
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public int Width => Info.Width;
|
public int Width => Info.Width;
|
||||||
public int Height => Info.Height;
|
public int Height => Info.Height;
|
||||||
public int Depth => Info.Depth;
|
public int Depth => Info.Depth;
|
||||||
|
public MTLPixelFormat MtlFormat { get; protected set; }
|
||||||
|
|
||||||
public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info)
|
public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue