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
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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue