Shader cache support

This commit is contained in:
Isaac Marovitz 2024-07-22 13:40:10 +01:00 committed by Isaac Marovitz
parent eb2dae561f
commit b44167d12a
3 changed files with 73 additions and 32 deletions

View file

@ -38,7 +38,7 @@
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview14" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview18" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />

View file

@ -324,6 +324,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
if (context.Capabilities.Api == TargetApi.Metal)
{
loadHostCache = false;
}
int programIndex = 0;
DataEntry entry = new();
@ -630,8 +635,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return;
}
if (context.Capabilities.Api != TargetApi.Metal)
{
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
}
}
/// <summary>
/// Clears all content from the guest cache files.

View file

@ -6,6 +6,7 @@ using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
@ -13,7 +14,11 @@ namespace Ryujinx.Graphics.Metal
[SupportedOSPlatform("macos")]
class Program : IProgram
{
private readonly ProgramLinkStatus _status;
private ProgramLinkStatus _status;
private ShaderSource[] _shaders;
private GCHandle[] _handles;
private int _successCount;
public MTLFunction VertexFunction;
public MTLFunction FragmentFunction;
public MTLFunction ComputeFunction;
@ -33,45 +38,65 @@ namespace Ryujinx.Graphics.Metal
public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default)
{
ComputeLocalSize = computeLocalSize;
_shaders = shaders;
_handles = new GCHandle[_shaders.Length];
for (int index = 0; index < shaders.Length; index++)
_status = ProgramLinkStatus.Incomplete;
for (int i = 0; i < _shaders.Length; i++)
{
ShaderSource shader = shaders[index];
ShaderSource shader = _shaders[i];
var compileOptions = new MTLCompileOptions { PreserveInvariance = true };
var index = i;
var libraryError = new NSError(IntPtr.Zero);
var shaderLibrary = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, ref libraryError);
if (libraryError != IntPtr.Zero)
{
Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code);
Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}");
_status = ProgramLinkStatus.Failure;
return;
}
switch (shaders[index].Stage)
{
case ShaderStage.Compute:
ComputeFunction = shaderLibrary.NewFunction(StringHelper.NSString("kernelMain"));
break;
case ShaderStage.Vertex:
VertexFunction = shaderLibrary.NewFunction(StringHelper.NSString("vertexMain"));
break;
case ShaderStage.Fragment:
FragmentFunction = shaderLibrary.NewFunction(StringHelper.NSString("fragmentMain"));
break;
default:
Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shaders[index].Stage}!");
break;
}
_handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index));
}
ClearSegments = BuildClearSegments(resourceLayout.Sets);
(BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages);
}
public void CompilationResultHandler(MTLLibrary library, NSError error, int index)
{
var shader = _shaders[index];
if (_handles[index].IsAllocated)
{
_handles[index].Free();
}
if (error != IntPtr.Zero)
{
Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code);
Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(error.LocalizedDescription)}");
_status = ProgramLinkStatus.Failure;
return;
}
switch (_shaders[index].Stage)
{
case ShaderStage.Compute:
ComputeFunction = library.NewFunction(StringHelper.NSString("kernelMain"));
break;
case ShaderStage.Vertex:
VertexFunction = library.NewFunction(StringHelper.NSString("vertexMain"));
break;
case ShaderStage.Fragment:
FragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain"));
break;
default:
Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {_shaders[index].Stage}!");
break;
}
_successCount++;
if (_successCount >= _shaders.Length && _status != ProgramLinkStatus.Failure)
{
_status = ProgramLinkStatus.Success;
}
}
private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
{
@ -218,12 +243,20 @@ namespace Ryujinx.Graphics.Metal
public ProgramLinkStatus CheckProgramLink(bool blocking)
{
if (blocking)
{
while (_status == ProgramLinkStatus.Incomplete)
{ }
return _status;
}
return _status;
}
public byte[] GetBinary()
{
return ""u8.ToArray();
return [];
}
public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline)