mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-03-16 05:50:18 +00:00
292 lines
11 KiB
C#
292 lines
11 KiB
C#
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.Collections.ObjectModel;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace Ryujinx.Graphics.Metal
|
|
{
|
|
[SupportedOSPlatform("macos")]
|
|
class Program : IProgram
|
|
{
|
|
private readonly ProgramLinkStatus _status;
|
|
public MTLFunction VertexFunction;
|
|
public MTLFunction FragmentFunction;
|
|
public MTLFunction ComputeFunction;
|
|
public ComputeSize ComputeLocalSize { get; }
|
|
|
|
private HashTableSlim<PipelineUid, MTLRenderPipelineState> _graphicsPipelineCache;
|
|
private MTLComputePipelineState? _computePipelineCache;
|
|
private bool _firstBackgroundUse;
|
|
|
|
public ResourceBindingSegment[][] ClearSegments { get; }
|
|
public ResourceBindingSegment[][] BindingSegments { get; }
|
|
// Argument buffer sizes for Vertex or Compute stages
|
|
public int[] ArgumentBufferSizes { get; }
|
|
// Argument buffer sizes for Fragment stage
|
|
public int[] FragArgumentBufferSizes { get; }
|
|
|
|
public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default)
|
|
{
|
|
ComputeLocalSize = computeLocalSize;
|
|
|
|
for (int index = 0; index < shaders.Length; index++)
|
|
{
|
|
ShaderSource shader = shaders[index];
|
|
|
|
var compileOptions = new MTLCompileOptions { PreserveInvariance = true };
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
ClearSegments = BuildClearSegments(resourceLayout.Sets);
|
|
(BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages);
|
|
|
|
_status = ProgramLinkStatus.Success;
|
|
}
|
|
|
|
private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
|
|
{
|
|
ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
|
|
|
|
for (int setIndex = 0; setIndex < sets.Count; setIndex++)
|
|
{
|
|
List<ResourceBindingSegment> currentSegments = new();
|
|
|
|
ResourceDescriptor currentDescriptor = default;
|
|
int currentCount = 0;
|
|
|
|
for (int index = 0; index < sets[setIndex].Descriptors.Count; index++)
|
|
{
|
|
ResourceDescriptor descriptor = sets[setIndex].Descriptors[index];
|
|
|
|
if (currentDescriptor.Binding + currentCount != descriptor.Binding ||
|
|
currentDescriptor.Type != descriptor.Type ||
|
|
currentDescriptor.Stages != descriptor.Stages ||
|
|
currentDescriptor.Count > 1 ||
|
|
descriptor.Count > 1)
|
|
{
|
|
if (currentCount != 0)
|
|
{
|
|
currentSegments.Add(new ResourceBindingSegment(
|
|
currentDescriptor.Binding,
|
|
currentCount,
|
|
currentDescriptor.Type,
|
|
currentDescriptor.Stages,
|
|
currentDescriptor.Count > 1));
|
|
}
|
|
|
|
currentDescriptor = descriptor;
|
|
currentCount = descriptor.Count;
|
|
}
|
|
else
|
|
{
|
|
currentCount += descriptor.Count;
|
|
}
|
|
}
|
|
|
|
if (currentCount != 0)
|
|
{
|
|
currentSegments.Add(new ResourceBindingSegment(
|
|
currentDescriptor.Binding,
|
|
currentCount,
|
|
currentDescriptor.Type,
|
|
currentDescriptor.Stages,
|
|
currentDescriptor.Count > 1));
|
|
}
|
|
|
|
segments[setIndex] = currentSegments.ToArray();
|
|
}
|
|
|
|
return segments;
|
|
}
|
|
|
|
private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
|
{
|
|
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
|
int[] argBufferSizes = new int[setUsages.Count];
|
|
int[] fragArgBufferSizes = new int[setUsages.Count];
|
|
|
|
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
|
{
|
|
List<ResourceBindingSegment> currentSegments = new();
|
|
|
|
ResourceUsage currentUsage = default;
|
|
int currentCount = 0;
|
|
|
|
for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
|
|
{
|
|
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
|
|
|
if (currentUsage.Binding + currentCount != usage.Binding ||
|
|
currentUsage.Type != usage.Type ||
|
|
currentUsage.Stages != usage.Stages ||
|
|
currentUsage.ArrayLength > 1 ||
|
|
usage.ArrayLength > 1)
|
|
{
|
|
if (currentCount != 0)
|
|
{
|
|
currentSegments.Add(new ResourceBindingSegment(
|
|
currentUsage.Binding,
|
|
currentCount,
|
|
currentUsage.Type,
|
|
currentUsage.Stages,
|
|
currentUsage.ArrayLength > 1));
|
|
|
|
var size = currentCount * ResourcePointerSize(currentUsage.Type);
|
|
if (currentUsage.Stages.HasFlag(ResourceStages.Fragment))
|
|
{
|
|
fragArgBufferSizes[setIndex] += size;
|
|
}
|
|
|
|
if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) ||
|
|
currentUsage.Stages.HasFlag(ResourceStages.Compute))
|
|
{
|
|
argBufferSizes[setIndex] += size;
|
|
}
|
|
}
|
|
|
|
currentUsage = usage;
|
|
currentCount = usage.ArrayLength;
|
|
}
|
|
else
|
|
{
|
|
currentCount++;
|
|
}
|
|
}
|
|
|
|
if (currentCount != 0)
|
|
{
|
|
currentSegments.Add(new ResourceBindingSegment(
|
|
currentUsage.Binding,
|
|
currentCount,
|
|
currentUsage.Type,
|
|
currentUsage.Stages,
|
|
currentUsage.ArrayLength > 1));
|
|
|
|
var size = currentCount * ResourcePointerSize(currentUsage.Type);
|
|
if (currentUsage.Stages.HasFlag(ResourceStages.Fragment))
|
|
{
|
|
fragArgBufferSizes[setIndex] += size;
|
|
}
|
|
|
|
if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) ||
|
|
currentUsage.Stages.HasFlag(ResourceStages.Compute))
|
|
{
|
|
argBufferSizes[setIndex] += size;
|
|
}
|
|
}
|
|
|
|
segments[setIndex] = currentSegments.ToArray();
|
|
}
|
|
|
|
return (segments, argBufferSizes, fragArgBufferSizes);
|
|
}
|
|
|
|
private static int ResourcePointerSize(ResourceType type)
|
|
{
|
|
return (type == ResourceType.TextureAndSampler ? 2 : 1);
|
|
}
|
|
|
|
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
|
{
|
|
return _status;
|
|
}
|
|
|
|
public byte[] GetBinary()
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
}
|