Ryujinx/src/Ryujinx.Graphics.Metal/HelperShader.cs

193 lines
6.9 KiB
C#
Raw Normal View History

2024-05-18 18:54:55 -04:00
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using SharpMetal.Foundation;
2024-05-18 18:54:55 -04:00
using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
2024-05-18 18:54:55 -04:00
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class HelperShader : IDisposable
{
private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders";
2024-05-18 20:40:37 -04:00
private readonly Pipeline _pipeline;
2024-05-18 18:54:55 -04:00
private MTLDevice _device;
private readonly IProgram _programColorBlit;
private readonly List<IProgram> _programsColorClear = new();
2024-05-18 18:54:55 -04:00
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 colorClearSource = ReadMsl("ColorClear.metal");
for (int i = 0; i < Constants.MaxColorAttachments; i++)
{
var crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString());
_programsColorClear.Add(new Program(
[
new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl),
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
], device));
}
2024-05-22 20:26:54 -04:00
var depthStencilClearSource = ReadMsl("DepthStencilClear.metal");
_programDepthStencilClear = new Program(
[
new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl)
], device);
2024-05-18 18:54:55 -04:00
}
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
});
// Save current state
_pipeline.SaveState();
2024-05-18 18:54:55 -04:00
_pipeline.SetProgram(_programColorBlit);
2024-05-24 12:37:31 -04:00
// Viewport and scissor needs to be set before render pass begin so as not to bind the old ones
//_pipeline.SetViewports([]);
//_pipeline.SetScissors([]);
2024-05-18 18:54:55 -04:00
_pipeline.SetRenderTargets([destination], null);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler));
_pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles);
_pipeline.Draw(6, 1, 0, 0);
// Restore previous state
_pipeline.RestoreState();
2024-05-18 18:54:55 -04:00
}
public unsafe void ClearColor(
int index,
ReadOnlySpan<float> clearColor)
2024-05-18 18:54:55 -04:00
{
const int ClearColorBufferSize = 16;
2024-05-18 18:54:55 -04:00
var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged);
var span = new Span<float>(buffer.Contents.ToPointer(), ClearColorBufferSize);
clearColor.CopyTo(span);
2024-05-18 18:54:55 -04:00
buffer.DidModifyRange(new NSRange
2024-05-18 18:54:55 -04:00
{
location = 0,
length = ClearColorBufferSize
});
2024-05-18 18:54:55 -04:00
var handle = buffer.NativePtr;
var range = new BufferRange(Unsafe.As<IntPtr, BufferHandle>(ref handle), 0, ClearColorBufferSize);
// Save current state
_pipeline.SaveState();
_pipeline.SetUniformBuffers([new BufferAssignment(0, range)]);
_pipeline.SetProgram(_programsColorClear[index]);
// _pipeline.SetRenderTargetColorMasks([componentMask]);
2024-05-18 18:54:55 -04:00
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0);
// Restore previous state
_pipeline.RestoreState();
2024-05-18 18:54:55 -04:00
}
2024-05-22 20:26:54 -04:00
public unsafe void ClearDepthStencil(
ReadOnlySpan<float> depthValue,
2024-05-18 18:54:55 -04:00
bool depthMask,
int stencilValue,
2024-05-22 20:26:54 -04:00
int stencilMask)
2024-05-18 18:54:55 -04:00
{
2024-05-22 20:26:54 -04:00
const int ClearColorBufferSize = 16;
var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged);
var span = new Span<float>(buffer.Contents.ToPointer(), ClearColorBufferSize);
depthValue.CopyTo(span);
buffer.DidModifyRange(new NSRange
{
location = 0,
length = ClearColorBufferSize
});
2024-05-18 18:54:55 -04:00
2024-05-22 20:26:54 -04:00
var handle = buffer.NativePtr;
var range = new BufferRange(Unsafe.As<IntPtr, BufferHandle>(ref handle), 0, ClearColorBufferSize);
// Save current state
_pipeline.SaveState();
2024-05-22 20:26:54 -04:00
_pipeline.SetUniformBuffers([new BufferAssignment(0, range)]);
2024-05-18 18:54:55 -04:00
_pipeline.SetProgram(_programDepthStencilClear);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
2024-05-22 20:26:54 -04:00
// _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask));
2024-05-18 18:54:55 -04:00
_pipeline.Draw(4, 1, 0, 0);
// Restore previous state
_pipeline.RestoreState();
2024-05-18 18:54:55 -04:00
}
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();
foreach (var programColorClear in _programsColorClear)
{
programColorClear.Dispose();
}
2024-05-18 18:54:55 -04:00
_programDepthStencilClear.Dispose();
_pipeline.Dispose();
}
}
}