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 System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
struct DepthStencilHash
|
||||
{
|
||||
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>
|
||||
class DepthStencilCache : StateCache<MTLDepthStencilState, DepthStencilUid, DepthStencilUid>
|
||||
{
|
||||
private readonly MTLDevice _device;
|
||||
|
||||
|
@ -31,41 +14,55 @@ namespace Ryujinx.Graphics.Metal
|
|||
_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)
|
||||
{
|
||||
// Create descriptors
|
||||
|
||||
ref StencilUid frontUid = ref descriptor.FrontFace;
|
||||
|
||||
using var frontFaceStencil = new MTLStencilDescriptor
|
||||
{
|
||||
StencilFailureOperation = frontUid.StencilFailureOperation,
|
||||
DepthFailureOperation = frontUid.DepthFailureOperation,
|
||||
DepthStencilPassOperation = frontUid.DepthStencilPassOperation,
|
||||
StencilCompareFunction = frontUid.StencilCompareFunction,
|
||||
ReadMask = frontUid.ReadMask,
|
||||
WriteMask = frontUid.WriteMask
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
// Front face
|
||||
FrontFace = new DepthStencilHash.StencilHash
|
||||
{
|
||||
StencilFailureOperation = descriptor.FrontFaceStencil.StencilFailureOperation,
|
||||
DepthFailureOperation = descriptor.FrontFaceStencil.DepthFailureOperation,
|
||||
DepthStencilPassOperation = descriptor.FrontFaceStencil.DepthStencilPassOperation,
|
||||
StencilCompareFunction = descriptor.FrontFaceStencil.StencilCompareFunction,
|
||||
ReadMask = descriptor.FrontFaceStencil.ReadMask,
|
||||
WriteMask = descriptor.FrontFaceStencil.WriteMask
|
||||
},
|
||||
// Back face
|
||||
BackFace = new DepthStencilHash.StencilHash
|
||||
{
|
||||
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,
|
||||
DepthWriteEnabled = descriptor.DepthWriteEnabled
|
||||
};
|
||||
|
||||
return hash;
|
||||
}
|
||||
if (descriptor.StencilTestEnabled)
|
||||
{
|
||||
mtlDescriptor.BackFaceStencil = backFaceStencil;
|
||||
mtlDescriptor.FrontFaceStencil = frontFaceStencil;
|
||||
}
|
||||
|
||||
protected override MTLDepthStencilState CreateValue(MTLDepthStencilDescriptor descriptor)
|
||||
{
|
||||
return _device.NewDepthStencilState(descriptor);
|
||||
using (mtlDescriptor)
|
||||
{
|
||||
return _device.NewDepthStencilState(mtlDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Metal.State;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
@ -48,12 +50,29 @@ namespace Ryujinx.Graphics.Metal
|
|||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
struct EncoderState
|
||||
struct PredrawState
|
||||
{
|
||||
public MTLFunction? VertexFunction = null;
|
||||
public MTLFunction? FragmentFunction = null;
|
||||
public MTLFunction? ComputeFunction = null;
|
||||
public MTLCullMode CullMode;
|
||||
public DepthStencilUid DepthStencilUid;
|
||||
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 MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
||||
|
@ -71,21 +90,14 @@ namespace Ryujinx.Graphics.Metal
|
|||
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
||||
public ulong IndexBufferOffset = 0;
|
||||
|
||||
public MTLDepthStencilState? DepthStencilState = null;
|
||||
|
||||
public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip;
|
||||
public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always;
|
||||
public bool DepthWriteEnabled = false;
|
||||
|
||||
public float DepthBias;
|
||||
public float SlopeScale;
|
||||
public float Clamp;
|
||||
|
||||
public MTLStencilDescriptor BackFaceStencil = new();
|
||||
public MTLStencilDescriptor FrontFaceStencil = new();
|
||||
public int BackRefValue = 0;
|
||||
public int FrontRefValue = 0;
|
||||
public bool StencilTestEnabled = false;
|
||||
|
||||
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
|
||||
public MTLCullMode CullMode = MTLCullMode.None;
|
||||
|
@ -102,8 +114,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
public ITexture[] PreMaskRenderTargets;
|
||||
public bool FramebufferUsingColorWriteMask;
|
||||
|
||||
public MTLColorWriteMask[] RenderTargetMasks = Enumerable.Repeat(MTLColorWriteMask.All, Constants.MaxColorAttachments).ToArray();
|
||||
public BlendDescriptor?[] BlendDescriptors = new BlendDescriptor?[Constants.MaxColorAttachments];
|
||||
public Array8<ColorBlendStateUid> StoredBlend;
|
||||
public ColorF BlendColor = new();
|
||||
|
||||
public VertexBufferDescriptor[] VertexBuffers = [];
|
||||
|
@ -115,25 +126,52 @@ namespace Ryujinx.Graphics.Metal
|
|||
// Only to be used for present
|
||||
public bool ClearLoadAction = false;
|
||||
|
||||
public EncoderState() { }
|
||||
|
||||
public readonly EncoderState Clone()
|
||||
public EncoderState()
|
||||
{
|
||||
// Certain state (like viewport and scissor) doesn't need to be cloned, as it is always reacreated when assigned to
|
||||
EncoderState clone = this;
|
||||
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();
|
||||
Pipeline.Initialize();
|
||||
DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always;
|
||||
}
|
||||
|
||||
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.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Metal.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment;
|
||||
|
@ -17,12 +18,10 @@ namespace Ryujinx.Graphics.Metal
|
|||
private readonly Pipeline _pipeline;
|
||||
private readonly BufferManager _bufferManager;
|
||||
|
||||
private readonly RenderPipelineCache _renderPipelineCache;
|
||||
private readonly ComputePipelineCache _computePipelineCache;
|
||||
private readonly DepthStencilCache _depthStencilCache;
|
||||
|
||||
private EncoderState _currentState = new();
|
||||
private readonly Stack<EncoderState> _backStates = [];
|
||||
private readonly EncoderState _mainState = new();
|
||||
private EncoderState _currentState;
|
||||
|
||||
public readonly Auto<DisposableBuffer> IndexBuffer => _currentState.IndexBuffer;
|
||||
public readonly MTLIndexType IndexType => _currentState.IndexType;
|
||||
|
@ -41,9 +40,8 @@ namespace Ryujinx.Graphics.Metal
|
|||
_pipeline = pipeline;
|
||||
_bufferManager = bufferManager;
|
||||
|
||||
_renderPipelineCache = new(device);
|
||||
_computePipelineCache = new(device);
|
||||
_depthStencilCache = new(device);
|
||||
_currentState = _mainState;
|
||||
|
||||
// Zero buffer
|
||||
byte[] zeros = new byte[ZeroBufferSize];
|
||||
|
@ -56,39 +54,38 @@ namespace Ryujinx.Graphics.Metal
|
|||
public void Dispose()
|
||||
{
|
||||
// State
|
||||
_currentState.FrontFaceStencil.Dispose();
|
||||
_currentState.BackFaceStencil.Dispose();
|
||||
|
||||
_renderPipelineCache.Dispose();
|
||||
_computePipelineCache.Dispose();
|
||||
_depthStencilCache.Dispose();
|
||||
}
|
||||
|
||||
public void SaveState()
|
||||
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All)
|
||||
{
|
||||
_backStates.Push(_currentState);
|
||||
_currentState = _currentState.Clone();
|
||||
_currentState = state ?? _mainState;
|
||||
|
||||
_currentState.Dirty |= flags;
|
||||
|
||||
return _mainState;
|
||||
}
|
||||
|
||||
public void SaveAndResetState()
|
||||
public PredrawState SavePredrawState()
|
||||
{
|
||||
_backStates.Push(_currentState);
|
||||
_currentState = new();
|
||||
return new PredrawState
|
||||
{
|
||||
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 = _backStates.Pop();
|
||||
_currentState.CullMode = state.CullMode;
|
||||
_currentState.DepthStencilUid = state.DepthStencilUid;
|
||||
_currentState.Topology = state.Topology;
|
||||
_currentState.Viewports = state.Viewports;
|
||||
|
||||
// Mark the other state as dirty
|
||||
_currentState.Dirty |= DirtyFlags.All;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Gpu, "No state to restore");
|
||||
}
|
||||
_currentState.Dirty |= DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports;
|
||||
}
|
||||
|
||||
public void SetClearLoadAction(bool clear)
|
||||
|
@ -267,106 +264,25 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
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];
|
||||
renderCommandEncoder.SetRenderPipelineState(pipelineState);
|
||||
|
||||
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.SetBlendColor(
|
||||
_currentState.BlendColor.Red,
|
||||
_currentState.BlendColor.Green,
|
||||
_currentState.BlendColor.Blue,
|
||||
_currentState.BlendColor.Alpha);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup
|
||||
renderPipelineDescriptor.Dispose();
|
||||
vertexDescriptor.Dispose();
|
||||
}
|
||||
renderCommandEncoder.SetBlendColor(
|
||||
_currentState.BlendColor.Red,
|
||||
_currentState.BlendColor.Green,
|
||||
_currentState.BlendColor.Blue,
|
||||
_currentState.BlendColor.Alpha);
|
||||
}
|
||||
|
||||
private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder)
|
||||
{
|
||||
if (_currentState.ComputeFunction == null)
|
||||
if (_currentState.ComputeProgram == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pipelineState = _computePipelineCache.GetOrCreate(_currentState.ComputeFunction.Value);
|
||||
var pipelineState = PipelineState.CreateComputePipeline(_device, _currentState.ComputeProgram);
|
||||
|
||||
computeCommandEncoder.SetComputePipelineState(pipelineState);
|
||||
}
|
||||
|
@ -414,14 +330,13 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
if (prg.VertexFunction != IntPtr.Zero)
|
||||
{
|
||||
_currentState.VertexFunction = prg.VertexFunction;
|
||||
_currentState.FragmentFunction = prg.FragmentFunction;
|
||||
_currentState.RenderProgram = prg;
|
||||
|
||||
_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;
|
||||
}
|
||||
|
@ -435,7 +350,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
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++)
|
||||
{
|
||||
|
@ -451,7 +366,25 @@ namespace Ryujinx.Graphics.Metal
|
|||
mask |= blue ? MTLColorWriteMask.Blue : 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)
|
||||
|
@ -478,6 +411,11 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
// 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++)
|
||||
{
|
||||
if (colors[i] == null)
|
||||
|
@ -485,7 +423,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
continue;
|
||||
}
|
||||
|
||||
ref var mtlMask = ref _currentState.RenderTargetMasks[i];
|
||||
var mtlMask = blendState[i].WriteMask;
|
||||
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
|
@ -495,7 +433,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
{
|
||||
// Prefer the binding with no write mask.
|
||||
|
||||
ref var mtlMask2 = ref _currentState.RenderTargetMasks[j];
|
||||
var mtlMask2 = blendState[j].WriteMask;
|
||||
|
||||
if (mtlMask == 0)
|
||||
{
|
||||
|
@ -517,18 +455,23 @@ namespace Ryujinx.Graphics.Metal
|
|||
{
|
||||
if (colors[i] is not Texture tex)
|
||||
{
|
||||
blendState[i].PixelFormat = MTLPixelFormat.Invalid;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
blendState[i].PixelFormat = tex.GetHandle().PixelFormat; // TODO: cache this
|
||||
_currentState.RenderTargets[i] = tex;
|
||||
}
|
||||
|
||||
if (depthStencil is Texture depthTexture)
|
||||
{
|
||||
pipeline.DepthStencilFormat = depthTexture.GetHandle().PixelFormat; // TODO: cache this
|
||||
_currentState.DepthStencil = depthTexture;
|
||||
}
|
||||
else if (depthStencil == null)
|
||||
{
|
||||
pipeline.DepthStencilFormat = MTLPixelFormat.Invalid;
|
||||
_currentState.DepthStencil = null;
|
||||
}
|
||||
|
||||
|
@ -555,13 +498,32 @@ namespace Ryujinx.Graphics.Metal
|
|||
{
|
||||
_currentState.VertexAttribs = vertexAttribs.ToArray();
|
||||
|
||||
// Update the buffers on the pipeline
|
||||
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
||||
|
||||
// Mark dirty
|
||||
_currentState.Dirty |= DirtyFlags.RenderPipeline;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Mark dirty
|
||||
|
@ -571,7 +533,9 @@ namespace Ryujinx.Graphics.Metal
|
|||
// Inlineable
|
||||
public void UpdateStencilState(StencilTestDescriptor stencilTest)
|
||||
{
|
||||
_currentState.FrontFaceStencil = new MTLStencilDescriptor
|
||||
ref DepthStencilUid uid = ref _currentState.DepthStencilUid;
|
||||
|
||||
uid.FrontFace = new StencilUid
|
||||
{
|
||||
StencilFailureOperation = stencilTest.FrontSFail.Convert(),
|
||||
DepthFailureOperation = stencilTest.FrontDpFail.Convert(),
|
||||
|
@ -581,7 +545,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
WriteMask = (uint)stencilTest.FrontMask
|
||||
};
|
||||
|
||||
_currentState.BackFaceStencil = new MTLStencilDescriptor
|
||||
uid.BackFace = new StencilUid
|
||||
{
|
||||
StencilFailureOperation = stencilTest.BackSFail.Convert(),
|
||||
DepthFailureOperation = stencilTest.BackDpFail.Convert(),
|
||||
|
@ -591,55 +555,23 @@ namespace Ryujinx.Graphics.Metal
|
|||
WriteMask = (uint)stencilTest.BackMask
|
||||
};
|
||||
|
||||
_currentState.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);
|
||||
uid.StencilTestEnabled = stencilTest.TestEnable;
|
||||
|
||||
UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef);
|
||||
|
||||
// Mark dirty
|
||||
_currentState.Dirty |= DirtyFlags.DepthStencil;
|
||||
|
||||
// Cleanup
|
||||
descriptor.Dispose();
|
||||
}
|
||||
|
||||
public void UpdateDepthState(DepthTestDescriptor depthTest)
|
||||
{
|
||||
_currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always;
|
||||
_currentState.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable;
|
||||
ref DepthStencilUid uid = ref _currentState.DepthStencilUid;
|
||||
|
||||
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);
|
||||
uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always;
|
||||
uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable;
|
||||
|
||||
// Mark dirty
|
||||
_currentState.Dirty |= DirtyFlags.DepthStencil;
|
||||
|
||||
// Cleanup
|
||||
descriptor.Dispose();
|
||||
}
|
||||
|
||||
// Inlineable
|
||||
|
@ -751,6 +683,9 @@ namespace Ryujinx.Graphics.Metal
|
|||
{
|
||||
_currentState.VertexBuffers = vertexBuffers.ToArray();
|
||||
|
||||
// Update the buffers on the pipeline
|
||||
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
|
||||
|
||||
// Inline update
|
||||
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||
{
|
||||
|
@ -925,10 +860,9 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||
{
|
||||
if (_currentState.DepthStencilState != null)
|
||||
{
|
||||
renderCommandEncoder.SetDepthStencilState(_currentState.DepthStencilState.Value);
|
||||
}
|
||||
MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid);
|
||||
|
||||
renderCommandEncoder.SetDepthStencilState(state);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (int i = 0; i < attribDescriptors.Length; i++)
|
||||
{
|
||||
ref var attrib = ref pipeline.Internal.VertexAttributes[i];
|
||||
|
||||
if (attribDescriptors[i].IsZero)
|
||||
{
|
||||
var attrib = vertexDescriptor.Attributes.Object((ulong)i);
|
||||
attrib.Format = attribDescriptors[i].Format.Convert();
|
||||
indexMask |= 1u << (int)Constants.ZeroBufferIndex;
|
||||
attrib.BufferIndex = Constants.ZeroBufferIndex;
|
||||
|
@ -990,7 +925,6 @@ namespace Ryujinx.Graphics.Metal
|
|||
}
|
||||
else
|
||||
{
|
||||
var attrib = vertexDescriptor.Attributes.Object((ulong)i);
|
||||
attrib.Format = attribDescriptors[i].Format.Convert();
|
||||
indexMask |= 1u << 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++)
|
||||
{
|
||||
var layout = vertexDescriptor.Layouts.Object((ulong)i);
|
||||
ref var layout = ref pipeline.Internal.VertexBindings[i];
|
||||
|
||||
if ((indexMask & (1u << i)) != 0)
|
||||
{
|
||||
layout.Stride = (ulong)bufferDescriptors[i].Stride;
|
||||
layout.Stride = (uint)bufferDescriptors[i].Stride;
|
||||
|
||||
if (layout.Stride == 0)
|
||||
{
|
||||
|
@ -1017,7 +951,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
if (bufferDescriptors[i].Divisor > 0)
|
||||
{
|
||||
layout.StepFunction = MTLVertexStepFunction.PerInstance;
|
||||
layout.StepRate = (ulong)bufferDescriptors[i].Divisor;
|
||||
layout.StepRate = (uint)bufferDescriptors[i].Divisor;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1028,20 +962,21 @@ namespace Ryujinx.Graphics.Metal
|
|||
}
|
||||
else
|
||||
{
|
||||
layout.Stride = 0;
|
||||
layout = new();
|
||||
}
|
||||
}
|
||||
|
||||
// Zero buffer
|
||||
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.StepFunction = MTLVertexStepFunction.Constant;
|
||||
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)
|
||||
|
|
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 _programStrideChange;
|
||||
|
||||
private readonly EncoderState _helperShaderState = new();
|
||||
|
||||
public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
|
||||
{
|
||||
_device = device;
|
||||
|
@ -80,8 +82,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
bool linearFilter,
|
||||
bool clear = false)
|
||||
{
|
||||
// Save current state
|
||||
_pipeline.SaveAndResetState();
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
|
@ -141,8 +142,14 @@ namespace Ryujinx.Graphics.Metal
|
|||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
|
||||
// Cleanup
|
||||
if (clear)
|
||||
{
|
||||
_pipeline.SetClearLoadAction(false);
|
||||
}
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.RestoreState();
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
public unsafe void DrawTexture(
|
||||
|
@ -152,7 +159,11 @@ namespace Ryujinx.Graphics.Metal
|
|||
Extents2DF dstRegion)
|
||||
{
|
||||
// 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;
|
||||
|
||||
|
@ -204,7 +215,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
_renderer.BufferManager.Delete(bufferHandle);
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.RestoreState();
|
||||
_pipeline.RestorePredrawState(state);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Save current state
|
||||
_pipeline.SaveAndResetState();
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
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);
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.RestoreState();
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
public unsafe void ClearColor(
|
||||
|
@ -262,8 +273,14 @@ namespace Ryujinx.Graphics.Metal
|
|||
int dstWidth,
|
||||
int dstHeight)
|
||||
{
|
||||
// Keep original scissor
|
||||
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -286,7 +303,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
1f);
|
||||
|
||||
_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.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||
_pipeline.SetRenderTargetColorMasks([componentMask]);
|
||||
|
@ -295,7 +312,9 @@ namespace Ryujinx.Graphics.Metal
|
|||
_pipeline.Draw(4, 1, 0, 0);
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.RestoreState();
|
||||
_pipeline.SwapState(null, clearFlags);
|
||||
|
||||
_helperShaderState.Restore(save);
|
||||
}
|
||||
|
||||
public unsafe void ClearDepthStencil(
|
||||
|
@ -306,8 +325,15 @@ namespace Ryujinx.Graphics.Metal
|
|||
int dstWidth,
|
||||
int dstHeight)
|
||||
{
|
||||
// Keep original scissor
|
||||
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||
var helperScissors = _helperShaderState.Scissors;
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -334,8 +360,14 @@ namespace Ryujinx.Graphics.Metal
|
|||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask));
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
|
||||
// Cleanup
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.RestoreState();
|
||||
_pipeline.SwapState(null, clearFlags);
|
||||
|
||||
_helperShaderState.Restore(save);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -240,8 +240,6 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
public void FlushCommandsImpl()
|
||||
{
|
||||
SaveState();
|
||||
|
||||
EndCurrentPass();
|
||||
|
||||
_byteWeight = 0;
|
||||
|
@ -254,8 +252,6 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||
_renderer.RegisterFlush();
|
||||
|
||||
RestoreState();
|
||||
}
|
||||
|
||||
public void BlitColor(
|
||||
|
@ -511,7 +507,14 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
|
||||
{
|
||||
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
|
||||
if (enables == 0)
|
||||
{
|
||||
_encoderStateManager.UpdateDepthBias(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDepthClamp(bool clamp)
|
||||
|
|
|
@ -16,6 +16,10 @@ namespace Ryujinx.Graphics.Metal
|
|||
public MTLFunction FragmentFunction;
|
||||
public MTLFunction ComputeFunction;
|
||||
|
||||
private HashTableSlim<PipelineUid, MTLRenderPipelineState> _graphicsPipelineCache;
|
||||
private MTLComputePipelineState? _computePipelineCache;
|
||||
private bool _firstBackgroundUse;
|
||||
|
||||
public Program(ShaderSource[] shaders, MTLDevice device)
|
||||
{
|
||||
for (int index = 0; index < shaders.Length; index++)
|
||||
|
@ -62,8 +66,64 @@ namespace Ryujinx.Graphics.Metal
|
|||
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()
|
||||
{
|
||||
if (_graphicsPipelineCache != null)
|
||||
{
|
||||
foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values)
|
||||
{
|
||||
pipeline.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_computePipelineCache?.Dispose();
|
||||
|
||||
VertexFunction.Dispose();
|
||||
FragmentFunction.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)
|
||||
{
|
||||
MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format);
|
||||
|
||||
var descriptor = new MTLTextureDescriptor
|
||||
{
|
||||
PixelFormat = FormatTable.GetFormat(Info.Format),
|
||||
PixelFormat = pixelFormat,
|
||||
Usage = MTLTextureUsage.Unknown,
|
||||
SampleCount = (ulong)Info.Samples,
|
||||
TextureType = Info.Target.Convert(),
|
||||
|
@ -35,6 +37,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat);
|
||||
|
||||
_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)
|
||||
|
@ -51,6 +54,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
var swizzle = GetSwizzle(info, pixelFormat);
|
||||
|
||||
_mtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle);
|
||||
MtlFormat = pixelFormat;
|
||||
}
|
||||
|
||||
private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat)
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
public int Width => Info.Width;
|
||||
public int Height => Info.Height;
|
||||
public int Depth => Info.Depth;
|
||||
public MTLPixelFormat MtlFormat { get; protected set; }
|
||||
|
||||
public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue