Break everything :D
This commit is contained in:
parent
de86f20b94
commit
738227519d
15 changed files with 885 additions and 542 deletions
50
src/Ryujinx.Graphics.Metal/EncoderState.cs
Normal file
50
src/Ryujinx.Graphics.Metal/EncoderState.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using SharpMetal.Metal;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
public struct EncoderState
|
||||||
|
{
|
||||||
|
public MTLFunction? VertexFunction = null;
|
||||||
|
public MTLFunction? FragmentFunction = null;
|
||||||
|
|
||||||
|
public Dictionary<ulong, MTLTexture> FragmentTextures = new();
|
||||||
|
public Dictionary<ulong, MTLSamplerState> FragmentSamplers = new();
|
||||||
|
|
||||||
|
public Dictionary<ulong, MTLTexture> VertexTextures = new();
|
||||||
|
public Dictionary<ulong, MTLSamplerState> VertexSamplers = new();
|
||||||
|
|
||||||
|
public List<BufferInfo> VertexBuffers = [];
|
||||||
|
public List<BufferInfo> UniformBuffers = [];
|
||||||
|
public List<BufferInfo> StorageBuffers = [];
|
||||||
|
|
||||||
|
public MTLBuffer IndexBuffer = default;
|
||||||
|
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
||||||
|
public ulong IndexBufferOffset = 0;
|
||||||
|
|
||||||
|
public MTLDepthStencilState? DepthStencilState = null;
|
||||||
|
|
||||||
|
public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always;
|
||||||
|
public bool DepthWriteEnabled = false;
|
||||||
|
|
||||||
|
public MTLStencilDescriptor BackFaceStencil = new();
|
||||||
|
public MTLStencilDescriptor FrontFaceStencil = new();
|
||||||
|
|
||||||
|
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
|
||||||
|
public MTLCullMode CullMode = MTLCullMode.None;
|
||||||
|
public MTLWinding Winding = MTLWinding.Clockwise;
|
||||||
|
|
||||||
|
public MTLViewport[] Viewports = [];
|
||||||
|
public MTLScissorRect[] Scissors = [];
|
||||||
|
|
||||||
|
// Changes to attachments take recreation!
|
||||||
|
public MTLTexture DepthStencil = default;
|
||||||
|
public MTLTexture[] RenderTargets = [];
|
||||||
|
public MTLVertexDescriptor VertexDescriptor = new();
|
||||||
|
|
||||||
|
public EncoderState() { }
|
||||||
|
}
|
||||||
|
}
|
558
src/Ryujinx.Graphics.Metal/EncoderStateManager.cs
Normal file
558
src/Ryujinx.Graphics.Metal/EncoderStateManager.cs
Normal file
|
@ -0,0 +1,558 @@
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using SharpMetal.Foundation;
|
||||||
|
using SharpMetal.Metal;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
struct EncoderStateManager
|
||||||
|
{
|
||||||
|
private readonly MTLDevice _device;
|
||||||
|
private Pipeline _pipeline;
|
||||||
|
|
||||||
|
private EncoderState _currentState = new();
|
||||||
|
private EncoderState _backState = new();
|
||||||
|
|
||||||
|
// Public accessors
|
||||||
|
public MTLBuffer IndexBuffer => _currentState.IndexBuffer;
|
||||||
|
public MTLIndexType IndexType => _currentState.IndexType;
|
||||||
|
public ulong IndexBufferOffset => _currentState.IndexBufferOffset;
|
||||||
|
public PrimitiveTopology Topology => _currentState.Topology;
|
||||||
|
|
||||||
|
public EncoderStateManager(MTLDevice device, Pipeline pipeline)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_pipeline = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SwapStates()
|
||||||
|
{
|
||||||
|
(_currentState, _backState) = (_backState, _currentState);
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render)
|
||||||
|
{
|
||||||
|
_pipeline.EndCurrentPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MTLRenderCommandEncoder CreateRenderCommandEncoder()
|
||||||
|
{
|
||||||
|
// Initialise Pass & State
|
||||||
|
|
||||||
|
var renderPassDescriptor = new MTLRenderPassDescriptor();
|
||||||
|
var renderPipelineDescriptor = new MTLRenderPipelineDescriptor();
|
||||||
|
|
||||||
|
const int MaxColorAttachments = 8;
|
||||||
|
for (int i = 0; i < MaxColorAttachments; i++)
|
||||||
|
{
|
||||||
|
if (_currentState.RenderTargets[i] != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i);
|
||||||
|
passAttachment.Texture = _currentState.RenderTargets[i];
|
||||||
|
passAttachment.LoadAction = MTLLoadAction.Load;
|
||||||
|
|
||||||
|
var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i);
|
||||||
|
pipelineAttachment.SetBlendingEnabled(true);
|
||||||
|
pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat;
|
||||||
|
pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha;
|
||||||
|
pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
|
||||||
|
pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha;
|
||||||
|
pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var depthAttachment = renderPassDescriptor.DepthAttachment;
|
||||||
|
depthAttachment.Texture = _currentState.DepthStencil;
|
||||||
|
depthAttachment.LoadAction = MTLLoadAction.Load;
|
||||||
|
|
||||||
|
// var stencilAttachment = renderPassDescriptor.StencilAttachment;
|
||||||
|
// stencilAttachment.Texture =
|
||||||
|
// stencilAttachment.LoadAction = MTLLoadAction.Load;
|
||||||
|
|
||||||
|
renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat;
|
||||||
|
// renderPipelineDescriptor.StencilAttachmentPixelFormat =
|
||||||
|
|
||||||
|
renderPipelineDescriptor.VertexDescriptor = _currentState.VertexDescriptor;
|
||||||
|
|
||||||
|
if (_currentState.VertexFunction != null)
|
||||||
|
{
|
||||||
|
renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new (IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentState.FragmentFunction != null)
|
||||||
|
{
|
||||||
|
renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var error = new NSError(IntPtr.Zero);
|
||||||
|
var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error);
|
||||||
|
if (error != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise Encoder
|
||||||
|
|
||||||
|
var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor);
|
||||||
|
|
||||||
|
renderCommandEncoder.SetRenderPipelineState(pipelineState);
|
||||||
|
|
||||||
|
SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState);
|
||||||
|
SetScissors(renderCommandEncoder, _currentState.Scissors);
|
||||||
|
SetViewports(renderCommandEncoder, _currentState.Viewports);
|
||||||
|
SetBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
||||||
|
SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true);
|
||||||
|
SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true);
|
||||||
|
SetCullMode(renderCommandEncoder, _currentState.CullMode);
|
||||||
|
SetFrontFace(renderCommandEncoder, _currentState.Winding);
|
||||||
|
SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers);
|
||||||
|
SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers);
|
||||||
|
|
||||||
|
return renderCommandEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateIndexBuffer(BufferRange buffer, IndexType type)
|
||||||
|
{
|
||||||
|
if (buffer.Handle != BufferHandle.Null)
|
||||||
|
{
|
||||||
|
_currentState.IndexType = type.Convert();
|
||||||
|
_currentState.IndexBufferOffset = (ulong)buffer.Offset;
|
||||||
|
var handle = buffer.Handle;
|
||||||
|
_currentState.IndexBuffer = new(Unsafe.As<BufferHandle, IntPtr>(ref handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePrimitiveTopology(PrimitiveTopology topology)
|
||||||
|
{
|
||||||
|
_currentState.Topology = topology;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProgram(IProgram program)
|
||||||
|
{
|
||||||
|
Program prg = (Program)program;
|
||||||
|
|
||||||
|
if (prg.VertexFunction == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentState.VertexFunction = prg.VertexFunction;
|
||||||
|
_currentState.FragmentFunction = prg.FragmentFunction;
|
||||||
|
|
||||||
|
// Requires recreating pipeline
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render)
|
||||||
|
{
|
||||||
|
_pipeline.EndCurrentPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
|
{
|
||||||
|
_currentState.RenderTargets = new MTLTexture[colors.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.Length; i++)
|
||||||
|
{
|
||||||
|
if (colors[i] is not Texture tex)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentState.RenderTargets[i] = tex.MTLTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthStencil is Texture depthTexture)
|
||||||
|
{
|
||||||
|
_currentState.DepthStencil = depthTexture.MTLTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires recreating pipeline
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render)
|
||||||
|
{
|
||||||
|
_pipeline.EndCurrentPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < vertexAttribs.Length; i++)
|
||||||
|
{
|
||||||
|
if (!vertexAttribs[i].IsZero)
|
||||||
|
{
|
||||||
|
// TODO: Format should not be hardcoded
|
||||||
|
var attrib = _currentState.VertexDescriptor.Attributes.Object((ulong)i);
|
||||||
|
attrib.Format = MTLVertexFormat.Float4;
|
||||||
|
attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex;
|
||||||
|
attrib.Offset = (ulong)vertexAttribs[i].Offset;
|
||||||
|
|
||||||
|
var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex);
|
||||||
|
layout.Stride = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires recreating pipeline
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render)
|
||||||
|
{
|
||||||
|
_pipeline.EndCurrentPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateStencilState(StencilTestDescriptor stencilTest)
|
||||||
|
{
|
||||||
|
_currentState.BackFaceStencil = new MTLStencilDescriptor
|
||||||
|
{
|
||||||
|
StencilFailureOperation = stencilTest.BackSFail.Convert(),
|
||||||
|
DepthFailureOperation = stencilTest.BackDpFail.Convert(),
|
||||||
|
DepthStencilPassOperation = stencilTest.BackDpPass.Convert(),
|
||||||
|
StencilCompareFunction = stencilTest.BackFunc.Convert(),
|
||||||
|
ReadMask = (uint)stencilTest.BackFuncMask,
|
||||||
|
WriteMask = (uint)stencilTest.BackMask
|
||||||
|
};
|
||||||
|
|
||||||
|
_currentState.FrontFaceStencil = new MTLStencilDescriptor
|
||||||
|
{
|
||||||
|
StencilFailureOperation = stencilTest.FrontSFail.Convert(),
|
||||||
|
DepthFailureOperation = stencilTest.FrontDpFail.Convert(),
|
||||||
|
DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(),
|
||||||
|
StencilCompareFunction = stencilTest.FrontFunc.Convert(),
|
||||||
|
ReadMask = (uint)stencilTest.FrontFuncMask,
|
||||||
|
WriteMask = (uint)stencilTest.FrontMask
|
||||||
|
};
|
||||||
|
|
||||||
|
_currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor
|
||||||
|
{
|
||||||
|
DepthCompareFunction = _currentState.DepthCompareFunction,
|
||||||
|
DepthWriteEnabled = _currentState.DepthWriteEnabled,
|
||||||
|
BackFaceStencil = stencilTest.TestEnable ? _currentState.BackFaceStencil : new MTLStencilDescriptor(IntPtr.Zero),
|
||||||
|
FrontFaceStencil = stencilTest.TestEnable ? _currentState.FrontFaceStencil : new MTLStencilDescriptor(IntPtr.Zero)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateDepthState(DepthTestDescriptor depthTest)
|
||||||
|
{
|
||||||
|
_currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always;
|
||||||
|
_currentState.DepthWriteEnabled = depthTest.WriteEnable;
|
||||||
|
|
||||||
|
_currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor
|
||||||
|
{
|
||||||
|
DepthCompareFunction = _currentState.DepthCompareFunction,
|
||||||
|
DepthWriteEnabled = _currentState.DepthWriteEnabled,
|
||||||
|
BackFaceStencil = _currentState.BackFaceStencil,
|
||||||
|
FrontFaceStencil = _currentState.FrontFaceStencil
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateScissors(ReadOnlySpan<Rectangle<int>> regions)
|
||||||
|
{
|
||||||
|
int maxScissors = Math.Min(regions.Length, _currentState.Viewports.Length);
|
||||||
|
|
||||||
|
if (maxScissors == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentState.Scissors = new MTLScissorRect[maxScissors];
|
||||||
|
|
||||||
|
for (int i = 0; i < maxScissors; i++)
|
||||||
|
{
|
||||||
|
var region = regions[i];
|
||||||
|
|
||||||
|
_currentState.Scissors[i] = new MTLScissorRect
|
||||||
|
{
|
||||||
|
height = Math.Clamp((ulong)region.Height, 0, (ulong)_currentState.Viewports[i].height),
|
||||||
|
width = Math.Clamp((ulong)region.Width, 0, (ulong)_currentState.Viewports[i].width),
|
||||||
|
x = (ulong)region.X,
|
||||||
|
y = (ulong)region.Y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetScissors(renderCommandEncoder, _currentState.Scissors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateViewports(ReadOnlySpan<Viewport> viewports)
|
||||||
|
{
|
||||||
|
static float Clamp(float value)
|
||||||
|
{
|
||||||
|
return Math.Clamp(value, 0f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentState.Viewports = new MTLViewport[viewports.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < viewports.Length; i++)
|
||||||
|
{
|
||||||
|
var viewport = viewports[i];
|
||||||
|
_currentState.Viewports[i] = new MTLViewport
|
||||||
|
{
|
||||||
|
originX = viewport.Region.X,
|
||||||
|
originY = viewport.Region.Y,
|
||||||
|
width = viewport.Region.Width,
|
||||||
|
height = viewport.Region.Height,
|
||||||
|
znear = Clamp(viewport.DepthNear),
|
||||||
|
zfar = Clamp(viewport.DepthFar)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetViewports(renderCommandEncoder, _currentState.Viewports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
|
||||||
|
{
|
||||||
|
_currentState.VertexBuffers = [];
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexBuffers.Length; i++)
|
||||||
|
{
|
||||||
|
if (vertexBuffers[i].Stride != 0)
|
||||||
|
{
|
||||||
|
var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)i);
|
||||||
|
layout.Stride = (ulong)vertexBuffers[i].Stride;
|
||||||
|
|
||||||
|
_currentState.VertexBuffers.Add(new BufferInfo
|
||||||
|
{
|
||||||
|
Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(),
|
||||||
|
Offset = vertexBuffers[i].Buffer.Offset,
|
||||||
|
Index = i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
|
{
|
||||||
|
_currentState.UniformBuffers = [];
|
||||||
|
|
||||||
|
foreach (BufferAssignment buffer in buffers)
|
||||||
|
{
|
||||||
|
if (buffer.Range.Size != 0)
|
||||||
|
{
|
||||||
|
_currentState.UniformBuffers.Add(new BufferInfo
|
||||||
|
{
|
||||||
|
Handle = buffer.Range.Handle.ToIntPtr(),
|
||||||
|
Offset = buffer.Range.Offset,
|
||||||
|
Index = buffer.Binding
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
|
{
|
||||||
|
_currentState.StorageBuffers = [];
|
||||||
|
|
||||||
|
foreach (BufferAssignment buffer in buffers)
|
||||||
|
{
|
||||||
|
if (buffer.Range.Size != 0)
|
||||||
|
{
|
||||||
|
// TODO: DONT offset the binding by 15
|
||||||
|
_currentState.StorageBuffers.Add(new BufferInfo
|
||||||
|
{
|
||||||
|
Handle = buffer.Range.Handle.ToIntPtr(),
|
||||||
|
Offset = buffer.Range.Offset,
|
||||||
|
Index = buffer.Binding + 15
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateCullMode(bool enable, Face face)
|
||||||
|
{
|
||||||
|
_currentState.CullMode = enable ? face.Convert() : MTLCullMode.None;
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetCullMode(renderCommandEncoder, _currentState.CullMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateFrontFace(FrontFace frontFace)
|
||||||
|
{
|
||||||
|
_currentState.Winding = frontFace.Convert();
|
||||||
|
|
||||||
|
// Inline Update
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetFrontFace(renderCommandEncoder, _currentState.Winding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlineable
|
||||||
|
public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler)
|
||||||
|
{
|
||||||
|
switch (stage)
|
||||||
|
{
|
||||||
|
case ShaderStage.Fragment:
|
||||||
|
_currentState.FragmentTextures[binding] = texture;
|
||||||
|
_currentState.FragmentSamplers[binding] = sampler;
|
||||||
|
break;
|
||||||
|
case ShaderStage.Vertex:
|
||||||
|
_currentState.VertexTextures[binding] = texture;
|
||||||
|
_currentState.VertexSamplers[binding] = sampler;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null)
|
||||||
|
{
|
||||||
|
var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value);
|
||||||
|
SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers);
|
||||||
|
SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthStencilState? depthStencilState)
|
||||||
|
{
|
||||||
|
if (depthStencilState != null)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetDepthStencilState(depthStencilState.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static void SetScissors(MTLRenderCommandEncoder renderCommandEncoder, MTLScissorRect[] scissors)
|
||||||
|
{
|
||||||
|
if (scissors.Length > 0)
|
||||||
|
{
|
||||||
|
fixed (MTLScissorRect* pMtlScissors = scissors)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)scissors.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static void SetViewports(MTLRenderCommandEncoder renderCommandEncoder, MTLViewport[] viewports)
|
||||||
|
{
|
||||||
|
if (viewports.Length > 0)
|
||||||
|
{
|
||||||
|
fixed (MTLViewport* pMtlViewports = viewports)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List<BufferInfo> buffers, bool fragment = false)
|
||||||
|
{
|
||||||
|
foreach (var buffer in buffers)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetVertexBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index);
|
||||||
|
|
||||||
|
if (fragment)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder, MTLCullMode cullMode)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetCullMode(cullMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder, MTLWinding winding)
|
||||||
|
{
|
||||||
|
renderCommandEncoder.SetFrontFacingWinding(winding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary<ulong, MTLTexture> textures, Dictionary<ulong, MTLSamplerState> samplers)
|
||||||
|
{
|
||||||
|
foreach (var texture in textures)
|
||||||
|
{
|
||||||
|
switch (stage)
|
||||||
|
{
|
||||||
|
case ShaderStage.Vertex:
|
||||||
|
renderCommandEncoder.SetVertexTexture(texture.Value, texture.Key);
|
||||||
|
break;
|
||||||
|
case ShaderStage.Fragment:
|
||||||
|
renderCommandEncoder.SetFragmentTexture(texture.Value, texture.Key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var sampler in samplers)
|
||||||
|
{
|
||||||
|
switch (stage)
|
||||||
|
{
|
||||||
|
case ShaderStage.Vertex:
|
||||||
|
renderCommandEncoder.SetVertexSamplerState(sampler.Value, sampler.Key);
|
||||||
|
break;
|
||||||
|
case ShaderStage.Fragment:
|
||||||
|
renderCommandEncoder.SetFragmentSamplerState(sampler.Value, sampler.Key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
206
src/Ryujinx.Graphics.Metal/HelperShader.cs
Normal file
206
src/Ryujinx.Graphics.Metal/HelperShader.cs
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using SharpMetal.Metal;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
{
|
||||||
|
enum ComponentType
|
||||||
|
{
|
||||||
|
Float,
|
||||||
|
SignedInteger,
|
||||||
|
UnsignedInteger,
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
class HelperShader : IDisposable
|
||||||
|
{
|
||||||
|
private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders";
|
||||||
|
private MTLDevice _device;
|
||||||
|
private Pipeline _pipeline;
|
||||||
|
|
||||||
|
private readonly IProgram _programColorBlit;
|
||||||
|
private readonly IProgram _programColorClearF;
|
||||||
|
private readonly IProgram _programColorClearSI;
|
||||||
|
private readonly IProgram _programColorClearUI;
|
||||||
|
private readonly IProgram _programDepthStencilClear;
|
||||||
|
|
||||||
|
public HelperShader(MTLDevice device, Pipeline pipeline)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_pipeline = pipeline;
|
||||||
|
|
||||||
|
var blitSource = ReadMsl("Blit.metal");
|
||||||
|
_programColorBlit = new Program(
|
||||||
|
[
|
||||||
|
new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
|
new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||||
|
], device);
|
||||||
|
|
||||||
|
// var colorClearFSource = ReadMsl("ColorClearF.metal");
|
||||||
|
// _programColorClearF = new Program(
|
||||||
|
// [
|
||||||
|
// new ShaderSource(colorClearFSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
|
// new ShaderSource(colorClearFSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||||
|
// ], device);
|
||||||
|
//
|
||||||
|
// var colorClearSiSource = ReadMsl("ColorClearSI.metal");
|
||||||
|
// _programColorClearSI = new Program(
|
||||||
|
// [
|
||||||
|
// new ShaderSource(colorClearSiSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
|
// new ShaderSource(colorClearSiSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||||
|
// ], device);
|
||||||
|
//
|
||||||
|
// var colorClearUiSource = ReadMsl("ColorClearUI.metal");
|
||||||
|
// _programColorClearUI = new Program(
|
||||||
|
// [
|
||||||
|
// new ShaderSource(colorClearUiSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
|
// new ShaderSource(colorClearUiSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||||
|
// ], device);
|
||||||
|
//
|
||||||
|
// var depthStencilClearSource = ReadMsl("DepthStencilClear.metal");
|
||||||
|
// _programDepthStencilClear = new Program(
|
||||||
|
// [
|
||||||
|
// new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
|
// new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||||
|
// ], device);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReadMsl(string fileName)
|
||||||
|
{
|
||||||
|
return EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BlitColor(
|
||||||
|
ITexture source,
|
||||||
|
ITexture destination)
|
||||||
|
{
|
||||||
|
var sampler = _device.NewSamplerState(new MTLSamplerDescriptor
|
||||||
|
{
|
||||||
|
MinFilter = MTLSamplerMinMagFilter.Nearest,
|
||||||
|
MagFilter = MTLSamplerMinMagFilter.Nearest,
|
||||||
|
MipFilter = MTLSamplerMipFilter.NotMipmapped
|
||||||
|
});
|
||||||
|
|
||||||
|
_pipeline.SetProgram(_programColorBlit);
|
||||||
|
_pipeline.SetRenderTargets([destination], null);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler));
|
||||||
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles);
|
||||||
|
_pipeline.Draw(6, 1, 0, 0);
|
||||||
|
_pipeline.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearColor(
|
||||||
|
Texture dst,
|
||||||
|
uint componentMask,
|
||||||
|
int dstWidth,
|
||||||
|
int dstHeight,
|
||||||
|
ComponentType type,
|
||||||
|
Rectangle<int> scissor)
|
||||||
|
{
|
||||||
|
Span<Viewport> viewports = stackalloc Viewport[1];
|
||||||
|
|
||||||
|
viewports[0] = new Viewport(
|
||||||
|
new Rectangle<float>(0, 0, dstWidth, dstHeight),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
IProgram program;
|
||||||
|
|
||||||
|
if (type == ComponentType.SignedInteger)
|
||||||
|
{
|
||||||
|
program = _programColorClearSI;
|
||||||
|
}
|
||||||
|
else if (type == ComponentType.UnsignedInteger)
|
||||||
|
{
|
||||||
|
program = _programColorClearUI;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
program = _programColorClearF;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pipeline.SetProgram(program);
|
||||||
|
// _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
|
||||||
|
_pipeline.SetRenderTargetColorMasks([componentMask]);
|
||||||
|
_pipeline.SetViewports(viewports);
|
||||||
|
_pipeline.SetScissors([scissor]);
|
||||||
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
_pipeline.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearDepthStencil(
|
||||||
|
Texture dst,
|
||||||
|
float depthValue,
|
||||||
|
bool depthMask,
|
||||||
|
int stencilValue,
|
||||||
|
int stencilMask,
|
||||||
|
int dstWidth,
|
||||||
|
int dstHeight,
|
||||||
|
Rectangle<int> scissor)
|
||||||
|
{
|
||||||
|
Span<Viewport> viewports = stackalloc Viewport[1];
|
||||||
|
|
||||||
|
viewports[0] = new Viewport(
|
||||||
|
new Rectangle<float>(0, 0, dstWidth, dstHeight),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
_pipeline.SetProgram(_programDepthStencilClear);
|
||||||
|
// _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
|
||||||
|
_pipeline.SetViewports(viewports);
|
||||||
|
_pipeline.SetScissors([scissor]);
|
||||||
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
|
||||||
|
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask));
|
||||||
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
_pipeline.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StencilTestDescriptor CreateStencilTestDescriptor(
|
||||||
|
bool enabled,
|
||||||
|
int refValue = 0,
|
||||||
|
int compareMask = 0xff,
|
||||||
|
int writeMask = 0xff)
|
||||||
|
{
|
||||||
|
return new StencilTestDescriptor(
|
||||||
|
enabled,
|
||||||
|
CompareOp.Always,
|
||||||
|
StencilOp.Replace,
|
||||||
|
StencilOp.Replace,
|
||||||
|
StencilOp.Replace,
|
||||||
|
refValue,
|
||||||
|
compareMask,
|
||||||
|
writeMask,
|
||||||
|
CompareOp.Always,
|
||||||
|
StencilOp.Replace,
|
||||||
|
StencilOp.Replace,
|
||||||
|
StencilOp.Replace,
|
||||||
|
refValue,
|
||||||
|
compareMask,
|
||||||
|
writeMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_programColorBlit.Dispose();
|
||||||
|
_programColorClearF.Dispose();
|
||||||
|
_programColorClearSI.Dispose();
|
||||||
|
_programColorClearUI.Dispose();
|
||||||
|
_programDepthStencilClear.Dispose();
|
||||||
|
_pipeline.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using SharpMetal.Foundation;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
public class HelperShaders
|
|
||||||
{
|
|
||||||
private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/HelperShadersSource.metal";
|
|
||||||
|
|
||||||
public HelperShader BlitShader;
|
|
||||||
|
|
||||||
public HelperShaders(MTLDevice device)
|
|
||||||
{
|
|
||||||
var error = new NSError(IntPtr.Zero);
|
|
||||||
|
|
||||||
var shaderSource = EmbeddedResources.ReadAllText(ShadersSourcePath);
|
|
||||||
var library = device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref error);
|
|
||||||
if (error != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
BlitShader = new HelperShader(library, "vertexBlit", "fragmentBlit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
public readonly struct HelperShader
|
|
||||||
{
|
|
||||||
public readonly MTLFunction VertexFunction;
|
|
||||||
public readonly MTLFunction FragmentFunction;
|
|
||||||
|
|
||||||
public HelperShader(MTLLibrary library, string vertex, string fragment)
|
|
||||||
{
|
|
||||||
VertexFunction = library.NewFunction(StringHelper.NSString(vertex));
|
|
||||||
FragmentFunction = library.NewFunction(StringHelper.NSString(fragment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ using SharpMetal.Foundation;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
using SharpMetal.QuartzCore;
|
using SharpMetal.QuartzCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
@ -24,37 +23,27 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
private readonly MTLDevice _device;
|
private readonly MTLDevice _device;
|
||||||
private readonly MTLCommandQueue _commandQueue;
|
private readonly MTLCommandQueue _commandQueue;
|
||||||
private readonly HelperShaders _helperShaders;
|
private readonly HelperShader _helperShader;
|
||||||
|
|
||||||
private MTLCommandBuffer _commandBuffer;
|
private MTLCommandBuffer _commandBuffer;
|
||||||
|
public MTLCommandBuffer CommandBuffer => _commandBuffer;
|
||||||
|
|
||||||
private MTLCommandEncoder? _currentEncoder;
|
private MTLCommandEncoder? _currentEncoder;
|
||||||
|
public MTLCommandEncoder? CurrentEncoder => _currentEncoder;
|
||||||
|
|
||||||
private EncoderType _currentEncoderType = EncoderType.None;
|
private EncoderType _currentEncoderType = EncoderType.None;
|
||||||
private MTLTexture[] _renderTargets = [];
|
public EncoderType CurrentEncoderType => _currentEncoderType;
|
||||||
private MTLTexture _depthTarget;
|
|
||||||
|
|
||||||
private RenderEncoderState _renderEncoderState;
|
private EncoderStateManager _encoderStateManager;
|
||||||
private readonly MTLVertexDescriptor _vertexDescriptor = new();
|
|
||||||
private List<BufferInfo> _vertexBuffers = [];
|
|
||||||
private List<BufferInfo> _uniformBuffers = [];
|
|
||||||
private List<BufferInfo> _storageBuffers = [];
|
|
||||||
|
|
||||||
private MTLBuffer _indexBuffer;
|
|
||||||
private MTLIndexType _indexType;
|
|
||||||
private ulong _indexBufferOffset;
|
|
||||||
private MTLClearColor _clearColor;
|
|
||||||
|
|
||||||
public Pipeline(MTLDevice device, MTLCommandQueue commandQueue)
|
public Pipeline(MTLDevice device, MTLCommandQueue commandQueue)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
_commandQueue = commandQueue;
|
_commandQueue = commandQueue;
|
||||||
_helperShaders = new HelperShaders(_device);
|
_helperShader = new HelperShader(_device, this);
|
||||||
|
|
||||||
_renderEncoderState = new RenderEncoderState(
|
|
||||||
_helperShaders.BlitShader.VertexFunction,
|
|
||||||
_helperShaders.BlitShader.FragmentFunction,
|
|
||||||
_device);
|
|
||||||
|
|
||||||
_commandBuffer = _commandQueue.CommandBuffer();
|
_commandBuffer = _commandQueue.CommandBuffer();
|
||||||
|
_encoderStateManager = new EncoderStateManager(_device, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MTLRenderCommandEncoder GetOrCreateRenderEncoder()
|
public MTLRenderCommandEncoder GetOrCreateRenderEncoder()
|
||||||
|
@ -126,28 +115,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
EndCurrentPass();
|
EndCurrentPass();
|
||||||
|
|
||||||
var descriptor = new MTLRenderPassDescriptor();
|
var renderCommandEncoder = _encoderStateManager.CreateRenderCommandEncoder();
|
||||||
for (int i = 0; i < _renderTargets.Length; i++)
|
|
||||||
{
|
|
||||||
if (_renderTargets[i] != null)
|
|
||||||
{
|
|
||||||
var attachment = descriptor.ColorAttachments.Object((ulong)i);
|
|
||||||
attachment.Texture = _renderTargets[i];
|
|
||||||
attachment.LoadAction = MTLLoadAction.Load;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var depthAttachment = descriptor.DepthAttachment;
|
|
||||||
depthAttachment.Texture = _depthTarget;
|
|
||||||
depthAttachment.LoadAction = MTLLoadAction.Load;
|
|
||||||
|
|
||||||
var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor);
|
|
||||||
_renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor);
|
|
||||||
|
|
||||||
RebindBuffers(renderCommandEncoder);
|
|
||||||
|
|
||||||
_currentEncoder = renderCommandEncoder;
|
_currentEncoder = renderCommandEncoder;
|
||||||
_currentEncoderType = EncoderType.Render;
|
_currentEncoderType = EncoderType.Render;
|
||||||
|
|
||||||
return renderCommandEncoder;
|
return renderCommandEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,34 +156,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
EndCurrentPass();
|
EndCurrentPass();
|
||||||
|
|
||||||
var descriptor = new MTLRenderPassDescriptor();
|
_encoderStateManager.SwapStates();
|
||||||
var colorAttachment = descriptor.ColorAttachments.Object(0);
|
|
||||||
|
|
||||||
colorAttachment.Texture = drawable.Texture;
|
// _helperShader.BlitColor(tex, drawable.Texture);
|
||||||
colorAttachment.LoadAction = MTLLoadAction.Clear;
|
|
||||||
colorAttachment.ClearColor = _clearColor;
|
|
||||||
|
|
||||||
descriptor.ColorAttachments.SetObject(colorAttachment, 0);
|
|
||||||
|
|
||||||
var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor);
|
|
||||||
_renderEncoderState = new RenderEncoderState(
|
|
||||||
_helperShaders.BlitShader.VertexFunction,
|
|
||||||
_helperShaders.BlitShader.FragmentFunction,
|
|
||||||
_device);
|
|
||||||
_renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor);
|
|
||||||
|
|
||||||
var sampler = _device.NewSamplerState(new MTLSamplerDescriptor
|
|
||||||
{
|
|
||||||
MinFilter = MTLSamplerMinMagFilter.Nearest,
|
|
||||||
MagFilter = MTLSamplerMinMagFilter.Nearest,
|
|
||||||
MipFilter = MTLSamplerMipFilter.NotMipmapped
|
|
||||||
});
|
|
||||||
|
|
||||||
renderCommandEncoder.SetFragmentTexture(tex.MTLTexture, 0);
|
|
||||||
renderCommandEncoder.SetFragmentSamplerState(sampler, 0);
|
|
||||||
|
|
||||||
renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6);
|
|
||||||
renderCommandEncoder.EndEncoding();
|
|
||||||
|
|
||||||
_commandBuffer.PresentDrawable(drawable);
|
_commandBuffer.PresentDrawable(drawable);
|
||||||
_commandBuffer.Commit();
|
_commandBuffer.Commit();
|
||||||
|
@ -219,31 +166,16 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_commandBuffer = _commandQueue.CommandBuffer();
|
_commandBuffer = _commandQueue.CommandBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Finish()
|
||||||
|
{
|
||||||
|
_encoderStateManager.SwapStates();
|
||||||
|
}
|
||||||
|
|
||||||
public void Barrier()
|
public void Barrier()
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RebindBuffers(MTLRenderCommandEncoder renderCommandEncoder)
|
|
||||||
{
|
|
||||||
foreach (var vertexBuffer in _vertexBuffers)
|
|
||||||
{
|
|
||||||
renderCommandEncoder.SetVertexBuffer(new MTLBuffer(vertexBuffer.Handle), (ulong)vertexBuffer.Offset, (ulong)vertexBuffer.Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var uniformBuffer in _uniformBuffers)
|
|
||||||
{
|
|
||||||
renderCommandEncoder.SetVertexBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index);
|
|
||||||
renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var storageBuffer in _storageBuffers)
|
|
||||||
{
|
|
||||||
renderCommandEncoder.SetVertexBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index);
|
|
||||||
renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
|
public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
|
||||||
{
|
{
|
||||||
var blitCommandEncoder = GetOrCreateBlitEncoder();
|
var blitCommandEncoder = GetOrCreateBlitEncoder();
|
||||||
|
@ -262,11 +194,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
||||||
{
|
{
|
||||||
_clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha };
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue,
|
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||||
int stencilMask)
|
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||||
}
|
}
|
||||||
|
@ -301,9 +232,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
||||||
|
|
||||||
// TODO: Support topology re-indexing to provide support for TriangleFans
|
// TODO: Support topology re-indexing to provide support for TriangleFans
|
||||||
var primitiveType = _renderEncoderState.Topology.Convert();
|
var primitiveType = _encoderStateManager.Topology.Convert();
|
||||||
|
|
||||||
renderCommandEncoder.DrawPrimitives(primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance);
|
renderCommandEncoder.DrawPrimitives(
|
||||||
|
primitiveType,
|
||||||
|
(ulong)firstVertex,
|
||||||
|
(ulong)vertexCount,
|
||||||
|
(ulong)instanceCount,
|
||||||
|
(ulong)firstInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
||||||
|
@ -311,9 +247,17 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
||||||
|
|
||||||
// TODO: Support topology re-indexing to provide support for TriangleFans
|
// TODO: Support topology re-indexing to provide support for TriangleFans
|
||||||
var primitiveType = _renderEncoderState.Topology.Convert();
|
var primitiveType = _encoderStateManager.Topology.Convert();
|
||||||
|
|
||||||
renderCommandEncoder.DrawIndexedPrimitives(primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance);
|
renderCommandEncoder.DrawIndexedPrimitives(
|
||||||
|
primitiveType,
|
||||||
|
(ulong)indexCount,
|
||||||
|
_encoderStateManager.IndexType,
|
||||||
|
_encoderStateManager.IndexBuffer,
|
||||||
|
_encoderStateManager.IndexBufferOffset,
|
||||||
|
(ulong)instanceCount,
|
||||||
|
firstVertex,
|
||||||
|
(ulong)firstInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawIndexedIndirect(BufferRange indirectBuffer)
|
public void DrawIndexedIndirect(BufferRange indirectBuffer)
|
||||||
|
@ -383,49 +327,22 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void SetDepthTest(DepthTestDescriptor depthTest)
|
public void SetDepthTest(DepthTestDescriptor depthTest)
|
||||||
{
|
{
|
||||||
var depthStencilState = _renderEncoderState.UpdateDepthState(
|
_encoderStateManager.UpdateDepthState(depthTest);
|
||||||
depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always,
|
|
||||||
depthTest.WriteEnable);
|
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFaceCulling(bool enable, Face face)
|
public void SetFaceCulling(bool enable, Face face)
|
||||||
{
|
{
|
||||||
var cullMode = enable ? face.Convert() : MTLCullMode.None;
|
_encoderStateManager.UpdateCullMode(enable, face);
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
new MTLRenderCommandEncoder(_currentEncoder.Value).SetCullMode(cullMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderEncoderState.CullMode = cullMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFrontFace(FrontFace frontFace)
|
public void SetFrontFace(FrontFace frontFace)
|
||||||
{
|
{
|
||||||
var winding = frontFace.Convert();
|
_encoderStateManager.UpdateFrontFace(frontFace);
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
new MTLRenderCommandEncoder(_currentEncoder.Value).SetFrontFacingWinding(winding);
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderEncoderState.Winding = winding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
||||||
{
|
{
|
||||||
if (buffer.Handle != BufferHandle.Null)
|
_encoderStateManager.UpdateIndexBuffer(buffer, type);
|
||||||
{
|
|
||||||
_indexType = type.Convert();
|
|
||||||
_indexBufferOffset = (ulong)buffer.Offset;
|
|
||||||
var handle = buffer.Handle;
|
|
||||||
_indexBuffer = new(Unsafe.As<BufferHandle, IntPtr>(ref handle));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
||||||
|
@ -482,23 +399,12 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
_renderEncoderState.Topology = topology;
|
_encoderStateManager.UpdatePrimitiveTopology(topology);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetProgram(IProgram program)
|
public void SetProgram(IProgram program)
|
||||||
{
|
{
|
||||||
Program prg = (Program)program;
|
_encoderStateManager.UpdateProgram(program);
|
||||||
|
|
||||||
if (prg.VertexFunction == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderEncoderState = new RenderEncoderState(
|
|
||||||
prg.VertexFunction,
|
|
||||||
prg.FragmentFunction,
|
|
||||||
_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRasterizerDiscard(bool discard)
|
public void SetRasterizerDiscard(bool discard)
|
||||||
|
@ -513,118 +419,27 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
_renderTargets = new MTLTexture[colors.Length];
|
_encoderStateManager.UpdateRenderTargets(colors, depthStencil);
|
||||||
|
|
||||||
for (int i = 0; i < colors.Length; i++)
|
|
||||||
{
|
|
||||||
if (colors[i] is not Texture tex)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tex.MTLTexture != null)
|
|
||||||
{
|
|
||||||
_renderTargets[i] = tex.MTLTexture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depthStencil is Texture depthTexture)
|
|
||||||
{
|
|
||||||
_depthTarget = depthTexture.MTLTexture;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recreate Render Command Encoder
|
|
||||||
BeginRenderPass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
|
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
|
||||||
{
|
{
|
||||||
int maxScissors = Math.Min(regions.Length, _renderEncoderState.Viewports.Length);
|
_encoderStateManager.UpdateScissors(regions);
|
||||||
|
|
||||||
if (maxScissors == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mtlScissorRects = new MTLScissorRect[maxScissors];
|
|
||||||
|
|
||||||
for (int i = 0; i < maxScissors; i++)
|
|
||||||
{
|
|
||||||
var region = regions[i];
|
|
||||||
|
|
||||||
mtlScissorRects[i] = new MTLScissorRect
|
|
||||||
{
|
|
||||||
height = Math.Clamp((ulong)region.Height, 0, (ulong)_renderEncoderState.Viewports[i].height),
|
|
||||||
width = Math.Clamp((ulong)region.Width, 0, (ulong)_renderEncoderState.Viewports[i].width),
|
|
||||||
x = (ulong)region.X,
|
|
||||||
y = (ulong)region.Y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderEncoderState.UpdateScissors(mtlScissorRects);
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects)
|
|
||||||
{
|
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStencilTest(StencilTestDescriptor stencilTest)
|
public void SetStencilTest(StencilTestDescriptor stencilTest)
|
||||||
{
|
{
|
||||||
var backFace = new MTLStencilDescriptor
|
_encoderStateManager.UpdateStencilState(stencilTest);
|
||||||
{
|
}
|
||||||
StencilFailureOperation = stencilTest.BackSFail.Convert(),
|
|
||||||
DepthFailureOperation = stencilTest.BackDpFail.Convert(),
|
|
||||||
DepthStencilPassOperation = stencilTest.BackDpPass.Convert(),
|
|
||||||
StencilCompareFunction = stencilTest.BackFunc.Convert(),
|
|
||||||
ReadMask = (uint)stencilTest.BackFuncMask,
|
|
||||||
WriteMask = (uint)stencilTest.BackMask
|
|
||||||
};
|
|
||||||
|
|
||||||
var frontFace = new MTLStencilDescriptor
|
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
StencilFailureOperation = stencilTest.FrontSFail.Convert(),
|
_encoderStateManager.UpdateUniformBuffers(buffers);
|
||||||
DepthFailureOperation = stencilTest.FrontDpFail.Convert(),
|
|
||||||
DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(),
|
|
||||||
StencilCompareFunction = stencilTest.FrontFunc.Convert(),
|
|
||||||
ReadMask = (uint)stencilTest.FrontFuncMask,
|
|
||||||
WriteMask = (uint)stencilTest.FrontMask
|
|
||||||
};
|
|
||||||
|
|
||||||
var depthStencilState = _renderEncoderState.UpdateStencilState(backFace, frontFace);
|
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_storageBuffers = [];
|
_encoderStateManager.UpdateStorageBuffers(buffers);
|
||||||
|
|
||||||
foreach (BufferAssignment buffer in buffers)
|
|
||||||
{
|
|
||||||
if (buffer.Range.Size != 0)
|
|
||||||
{
|
|
||||||
// Offset the binding by 15
|
|
||||||
_storageBuffers.Add(new BufferInfo
|
|
||||||
{
|
|
||||||
Handle = buffer.Range.Handle.ToIntPtr(),
|
|
||||||
Offset = buffer.Range.Offset,
|
|
||||||
Index = buffer.Binding + 15
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
RebindBuffers(renderCommandEncoder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
|
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
|
||||||
|
@ -633,27 +448,18 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
if (sampler is Sampler samp)
|
if (sampler is Sampler samp)
|
||||||
{
|
{
|
||||||
MTLRenderCommandEncoder renderCommandEncoder;
|
|
||||||
MTLComputeCommandEncoder computeCommandEncoder;
|
|
||||||
|
|
||||||
var mtlTexture = tex.MTLTexture;
|
var mtlTexture = tex.MTLTexture;
|
||||||
var mtlSampler = samp.GetSampler();
|
var mtlSampler = samp.GetSampler();
|
||||||
var index = (ulong)binding;
|
var index = (ulong)binding;
|
||||||
|
|
||||||
switch (stage)
|
switch (stage)
|
||||||
{
|
{
|
||||||
case ShaderStage.Fragment:
|
|
||||||
renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
renderCommandEncoder.SetFragmentTexture(mtlTexture, index);
|
|
||||||
renderCommandEncoder.SetFragmentSamplerState(mtlSampler, index);
|
|
||||||
break;
|
|
||||||
case ShaderStage.Vertex:
|
case ShaderStage.Vertex:
|
||||||
renderCommandEncoder = GetOrCreateRenderEncoder();
|
case ShaderStage.Fragment:
|
||||||
renderCommandEncoder.SetVertexTexture(mtlTexture, index);
|
_encoderStateManager.UpdateTextureAndSampler(stage, index, mtlTexture, mtlSampler);
|
||||||
renderCommandEncoder.SetVertexSamplerState(mtlSampler, index);
|
|
||||||
break;
|
break;
|
||||||
case ShaderStage.Compute:
|
case ShaderStage.Compute:
|
||||||
computeCommandEncoder = GetOrCreateComputeEncoder();
|
var computeCommandEncoder = GetOrCreateComputeEncoder();
|
||||||
computeCommandEncoder.SetTexture(mtlTexture, index);
|
computeCommandEncoder.SetTexture(mtlTexture, index);
|
||||||
computeCommandEncoder.SetSamplerState(mtlSampler, index);
|
computeCommandEncoder.SetSamplerState(mtlSampler, index);
|
||||||
break;
|
break;
|
||||||
|
@ -669,30 +475,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
|
||||||
{
|
|
||||||
_uniformBuffers = [];
|
|
||||||
|
|
||||||
foreach (BufferAssignment buffer in buffers)
|
|
||||||
{
|
|
||||||
if (buffer.Range.Size != 0)
|
|
||||||
{
|
|
||||||
_uniformBuffers.Add(new BufferInfo
|
|
||||||
{
|
|
||||||
Handle = buffer.Range.Handle.ToIntPtr(),
|
|
||||||
Offset = buffer.Range.Offset,
|
|
||||||
Index = buffer.Binding
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
RebindBuffers(renderCommandEncoder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetUserClipDistance(int index, bool enableClip)
|
public void SetUserClipDistance(int index, bool enableClip)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||||
|
@ -700,81 +482,17 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < vertexAttribs.Length; i++)
|
_encoderStateManager.UpdateVertexAttribs(vertexAttribs);
|
||||||
{
|
|
||||||
if (!vertexAttribs[i].IsZero)
|
|
||||||
{
|
|
||||||
// TODO: Format should not be hardcoded
|
|
||||||
var attrib = _vertexDescriptor.Attributes.Object((ulong)i);
|
|
||||||
attrib.Format = MTLVertexFormat.Float4;
|
|
||||||
attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex;
|
|
||||||
attrib.Offset = (ulong)vertexAttribs[i].Offset;
|
|
||||||
|
|
||||||
var layout = _vertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex);
|
|
||||||
layout.Stride = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
|
public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
|
||||||
{
|
{
|
||||||
_vertexBuffers = [];
|
_encoderStateManager.UpdateVertexBuffers(vertexBuffers);
|
||||||
|
|
||||||
for (int i = 0; i < vertexBuffers.Length; i++)
|
|
||||||
{
|
|
||||||
if (vertexBuffers[i].Stride != 0)
|
|
||||||
{
|
|
||||||
var layout = _vertexDescriptor.Layouts.Object((ulong)i);
|
|
||||||
layout.Stride = (ulong)vertexBuffers[i].Stride;
|
|
||||||
|
|
||||||
_vertexBuffers.Add(new BufferInfo
|
|
||||||
{
|
|
||||||
Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(),
|
|
||||||
Offset = vertexBuffers[i].Buffer.Offset,
|
|
||||||
Index = i
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
RebindBuffers(renderCommandEncoder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SetViewports(ReadOnlySpan<Viewport> viewports)
|
public void SetViewports(ReadOnlySpan<Viewport> viewports)
|
||||||
{
|
{
|
||||||
static float Clamp(float value)
|
_encoderStateManager.UpdateViewports(viewports);
|
||||||
{
|
|
||||||
return Math.Clamp(value, 0f, 1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
var mtlViewports = new MTLViewport[viewports.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < viewports.Length; i++)
|
|
||||||
{
|
|
||||||
var viewport = viewports[i];
|
|
||||||
mtlViewports[i] = new MTLViewport
|
|
||||||
{
|
|
||||||
originX = viewport.Region.X,
|
|
||||||
originY = viewport.Region.Y,
|
|
||||||
width = viewport.Region.Width,
|
|
||||||
height = viewport.Region.Height,
|
|
||||||
znear = Clamp(viewport.DepthNear),
|
|
||||||
zfar = Clamp(viewport.DepthFar)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderEncoderState.UpdateViewport(mtlViewports);
|
|
||||||
if (_currentEncoderType == EncoderType.Render)
|
|
||||||
{
|
|
||||||
fixed (MTLViewport* pMtlViewports = mtlViewports)
|
|
||||||
{
|
|
||||||
var renderCommandEncoder = GetOrCreateRenderEncoder();
|
|
||||||
renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TextureBarrier()
|
public void TextureBarrier()
|
||||||
|
|
|
@ -28,8 +28,6 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}");
|
Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}");
|
||||||
_status = ProgramLinkStatus.Failure;
|
_status = ProgramLinkStatus.Failure;
|
||||||
//Console.WriteLine($"SHADER {index}: {shader.Code}");
|
|
||||||
//throw new NotImplementedException();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using SharpMetal.Foundation;
|
|
||||||
using SharpMetal.Metal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
struct RenderEncoderState
|
|
||||||
{
|
|
||||||
private readonly MTLDevice _device;
|
|
||||||
private readonly MTLFunction? _vertexFunction = null;
|
|
||||||
private readonly MTLFunction? _fragmentFunction = null;
|
|
||||||
private MTLDepthStencilState? _depthStencilState = null;
|
|
||||||
|
|
||||||
private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always;
|
|
||||||
private bool _depthWriteEnabled = false;
|
|
||||||
|
|
||||||
private MTLStencilDescriptor _backFaceStencil = new();
|
|
||||||
private MTLStencilDescriptor _frontFaceStencil = new();
|
|
||||||
|
|
||||||
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
|
|
||||||
public MTLCullMode CullMode = MTLCullMode.None;
|
|
||||||
public MTLWinding Winding = MTLWinding.Clockwise;
|
|
||||||
|
|
||||||
private MTLViewport[] _viewports = [];
|
|
||||||
private MTLScissorRect[] _scissors = [];
|
|
||||||
public readonly MTLViewport[] Viewports => _viewports;
|
|
||||||
|
|
||||||
public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device)
|
|
||||||
{
|
|
||||||
_vertexFunction = vertexFunction;
|
|
||||||
_fragmentFunction = fragmentFunction;
|
|
||||||
_device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLRenderPassDescriptor descriptor, MTLVertexDescriptor vertexDescriptor)
|
|
||||||
{
|
|
||||||
var renderPipelineDescriptor = new MTLRenderPipelineDescriptor
|
|
||||||
{
|
|
||||||
VertexDescriptor = vertexDescriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_vertexFunction != null)
|
|
||||||
{
|
|
||||||
renderPipelineDescriptor.VertexFunction = _vertexFunction.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_fragmentFunction != null)
|
|
||||||
{
|
|
||||||
renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int MaxColorAttachments = 8;
|
|
||||||
for (int i = 0; i < MaxColorAttachments; i++)
|
|
||||||
{
|
|
||||||
var renderAttachment = descriptor.ColorAttachments.Object((ulong)i);
|
|
||||||
if (renderAttachment.Texture != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
var attachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i);
|
|
||||||
attachment.SetBlendingEnabled(true);
|
|
||||||
attachment.PixelFormat = renderAttachment.Texture.PixelFormat;
|
|
||||||
attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha;
|
|
||||||
attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
|
|
||||||
attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha;
|
|
||||||
attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPipelineDescriptor.DepthAttachmentPixelFormat = descriptor.DepthAttachment.Texture.PixelFormat;
|
|
||||||
|
|
||||||
var error = new NSError(IntPtr.Zero);
|
|
||||||
var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error);
|
|
||||||
if (error != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
renderCommandEncoder.SetRenderPipelineState(pipelineState);
|
|
||||||
renderCommandEncoder.SetCullMode(CullMode);
|
|
||||||
renderCommandEncoder.SetFrontFacingWinding(Winding);
|
|
||||||
|
|
||||||
if (_depthStencilState != null)
|
|
||||||
{
|
|
||||||
renderCommandEncoder.SetDepthStencilState(_depthStencilState.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_viewports.Length > 0)
|
|
||||||
{
|
|
||||||
fixed (MTLViewport* pMtlViewports = _viewports)
|
|
||||||
{
|
|
||||||
renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_viewports.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_scissors.Length > 0)
|
|
||||||
{
|
|
||||||
fixed (MTLScissorRect* pMtlScissors = _scissors)
|
|
||||||
{
|
|
||||||
renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_scissors.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace)
|
|
||||||
{
|
|
||||||
_backFaceStencil = backFace;
|
|
||||||
_frontFaceStencil = frontFace;
|
|
||||||
|
|
||||||
_depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor
|
|
||||||
{
|
|
||||||
DepthCompareFunction = _depthCompareFunction,
|
|
||||||
DepthWriteEnabled = _depthWriteEnabled,
|
|
||||||
BackFaceStencil = _backFaceStencil,
|
|
||||||
FrontFaceStencil = _frontFaceStencil
|
|
||||||
});
|
|
||||||
|
|
||||||
return _depthStencilState.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MTLDepthStencilState UpdateDepthState(MTLCompareFunction depthCompareFunction, bool depthWriteEnabled)
|
|
||||||
{
|
|
||||||
_depthCompareFunction = depthCompareFunction;
|
|
||||||
_depthWriteEnabled = depthWriteEnabled;
|
|
||||||
|
|
||||||
var state = _device.NewDepthStencilState(new MTLDepthStencilDescriptor
|
|
||||||
{
|
|
||||||
DepthCompareFunction = _depthCompareFunction,
|
|
||||||
DepthWriteEnabled = _depthWriteEnabled,
|
|
||||||
BackFaceStencil = _backFaceStencil,
|
|
||||||
FrontFaceStencil = _frontFaceStencil
|
|
||||||
});
|
|
||||||
|
|
||||||
_depthStencilState = state;
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateScissors(MTLScissorRect[] scissors)
|
|
||||||
{
|
|
||||||
_scissors = scissors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateViewport(MTLViewport[] viewports)
|
|
||||||
{
|
|
||||||
_viewports = viewports;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="HelperShadersSource.metal" />
|
<EmbeddedResource Include="Shaders\Blit.metal" />
|
||||||
|
<EmbeddedResource Include="Shaders\ColorClearF.metal" />
|
||||||
|
<EmbeddedResource Include="Shaders\ColorClearSI.metal" />
|
||||||
|
<EmbeddedResource Include="Shaders\ColorClearUI.metal" />
|
||||||
|
<EmbeddedResource Include="Shaders\DepthStencilClear.metal" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -33,6 +33,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_mtlSamplerState = samplerState;
|
_mtlSamplerState = samplerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Sampler(MTLSamplerState samplerState)
|
||||||
|
{
|
||||||
|
_mtlSamplerState = samplerState;
|
||||||
|
}
|
||||||
|
|
||||||
public MTLSamplerState GetSampler()
|
public MTLSamplerState GetSampler()
|
||||||
{
|
{
|
||||||
return _mtlSamplerState;
|
return _mtlSamplerState;
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct CopyVertexOut {
|
||||||
float2 uv;
|
float2 uv;
|
||||||
};
|
};
|
||||||
|
|
||||||
vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) {
|
vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) {
|
||||||
float2 position = quadVertices[vid];
|
float2 position = quadVertices[vid];
|
||||||
|
|
||||||
CopyVertexOut out;
|
CopyVertexOut out;
|
||||||
|
@ -32,7 +32,7 @@ vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float4 fragmentBlit(CopyVertexOut in [[stage_in]],
|
fragment float4 fragmentMain(CopyVertexOut in [[stage_in]],
|
||||||
texture2d<float, access::sample> texture [[texture(0)]],
|
texture2d<float, access::sample> texture [[texture(0)]],
|
||||||
sampler sampler [[sampler(0)]]) {
|
sampler sampler [[sampler(0)]]) {
|
||||||
return texture.sample(sampler, in.uv);
|
return texture.sample(sampler, in.uv);
|
0
src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal
Normal file
0
src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal
Normal file
0
src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal
Normal file
0
src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal
Normal file
0
src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal
Normal file
0
src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal
Normal file
|
@ -1,5 +1,4 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using SharpMetal.Foundation;
|
using SharpMetal.Foundation;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
|
|
Loading…
Reference in a new issue