EXPERIMENTAL: Metal backend (#441)
This is not a continuation of the Metal backend; this is simply bringing the branch up to date and merging it as-is behind an experiment. --------- Co-authored-by: Isaac Marovitz <isaacryu@icloud.com> Co-authored-by: Samuliak <samuliak77@gmail.com> Co-authored-by: SamoZ256 <96914946+SamoZ256@users.noreply.github.com> Co-authored-by: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Co-authored-by: riperiperi <rhy3756547@hotmail.com> Co-authored-by: Gabriel A <gab.dark.100@gmail.com>
This commit is contained in:
110
src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs
Normal file
110
src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal.State
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct StencilUid
|
||||
{
|
||||
public uint ReadMask;
|
||||
public uint WriteMask;
|
||||
public ushort Operations;
|
||||
|
||||
public MTLStencilOperation StencilFailureOperation
|
||||
{
|
||||
readonly get => (MTLStencilOperation)((Operations >> 0) & 0xF);
|
||||
set => Operations = (ushort)((Operations & 0xFFF0) | ((int)value << 0));
|
||||
}
|
||||
|
||||
public MTLStencilOperation DepthFailureOperation
|
||||
{
|
||||
readonly get => (MTLStencilOperation)((Operations >> 4) & 0xF);
|
||||
set => Operations = (ushort)((Operations & 0xFF0F) | ((int)value << 4));
|
||||
}
|
||||
|
||||
public MTLStencilOperation DepthStencilPassOperation
|
||||
{
|
||||
readonly get => (MTLStencilOperation)((Operations >> 8) & 0xF);
|
||||
set => Operations = (ushort)((Operations & 0xF0FF) | ((int)value << 8));
|
||||
}
|
||||
|
||||
public MTLCompareFunction StencilCompareFunction
|
||||
{
|
||||
readonly get => (MTLCompareFunction)((Operations >> 12) & 0xF);
|
||||
set => Operations = (ushort)((Operations & 0x0FFF) | ((int)value << 12));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 24)]
|
||||
internal struct DepthStencilUid : IEquatable<DepthStencilUid>
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public StencilUid FrontFace;
|
||||
|
||||
[FieldOffset(10)]
|
||||
public ushort DepthState;
|
||||
|
||||
[FieldOffset(12)]
|
||||
public StencilUid BackFace;
|
||||
|
||||
[FieldOffset(22)]
|
||||
private readonly ushort _padding;
|
||||
|
||||
// Quick access aliases
|
||||
#pragma warning disable IDE0044 // Add readonly modifier
|
||||
[FieldOffset(0)]
|
||||
private ulong _id0;
|
||||
[FieldOffset(8)]
|
||||
private ulong _id1;
|
||||
[FieldOffset(0)]
|
||||
private Vector128<byte> _id01;
|
||||
[FieldOffset(16)]
|
||||
private ulong _id2;
|
||||
#pragma warning restore IDE0044 // Add readonly modifier
|
||||
|
||||
public MTLCompareFunction DepthCompareFunction
|
||||
{
|
||||
readonly get => (MTLCompareFunction)((DepthState >> 0) & 0xF);
|
||||
set => DepthState = (ushort)((DepthState & 0xFFF0) | ((int)value << 0));
|
||||
}
|
||||
|
||||
public bool StencilTestEnabled
|
||||
{
|
||||
readonly get => ((DepthState >> 4) & 0x1) != 0;
|
||||
set => DepthState = (ushort)((DepthState & 0xFFEF) | ((value ? 1 : 0) << 4));
|
||||
}
|
||||
|
||||
public bool DepthWriteEnabled
|
||||
{
|
||||
readonly get => ((DepthState >> 15) & 0x1) != 0;
|
||||
set => DepthState = (ushort)((DepthState & 0x7FFF) | ((value ? 1 : 0) << 15));
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object obj)
|
||||
{
|
||||
return obj is DepthStencilUid other && EqualsRef(ref other);
|
||||
}
|
||||
|
||||
public readonly bool EqualsRef(ref DepthStencilUid other)
|
||||
{
|
||||
return _id01.Equals(other._id01) && _id2 == other._id2;
|
||||
}
|
||||
|
||||
public readonly bool Equals(DepthStencilUid other)
|
||||
{
|
||||
return EqualsRef(ref other);
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
ulong hash64 = _id0 * 23 ^
|
||||
_id1 * 23 ^
|
||||
_id2 * 23;
|
||||
|
||||
return (int)hash64 ^ ((int)(hash64 >> 32) * 17);
|
||||
}
|
||||
}
|
||||
}
|
||||
341
src/Ryujinx.Graphics.Metal/State/PipelineState.cs
Normal file
341
src/Ryujinx.Graphics.Metal/State/PipelineState.cs
Normal file
@@ -0,0 +1,341 @@
|
||||
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 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);
|
||||
}
|
||||
*/
|
||||
|
||||
public MTLLogicOperation LogicOp
|
||||
{
|
||||
readonly get => (MTLLogicOperation)((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);
|
||||
}
|
||||
|
||||
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 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 MTLRenderPipelineDescriptor 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;
|
||||
}
|
||||
}
|
||||
|
||||
renderPipelineDescriptor.LogicOperationEnabled = LogicOpEnable;
|
||||
renderPipelineDescriptor.LogicOperation = LogicOp;
|
||||
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 renderPipelineDescriptor;
|
||||
}
|
||||
|
||||
public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program)
|
||||
{
|
||||
if (program.TryGetGraphicsPipeline(ref Internal, out var pipelineState))
|
||||
{
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
using var descriptor = CreateRenderDescriptor(program);
|
||||
|
||||
var error = new NSError(IntPtr.Zero);
|
||||
pipelineState = device.NewRenderPipelineState(descriptor, 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 MTLComputePipelineDescriptor CreateComputeDescriptor(Program program)
|
||||
{
|
||||
ComputeSize localSize = program.ComputeLocalSize;
|
||||
|
||||
uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z);
|
||||
|
||||
if (maxThreads == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension.");
|
||||
}
|
||||
|
||||
var descriptor = new MTLComputePipelineDescriptor
|
||||
{
|
||||
ComputeFunction = program.ComputeFunction,
|
||||
MaxTotalThreadsPerThreadgroup = maxThreads,
|
||||
ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true,
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program)
|
||||
{
|
||||
if (program.TryGetComputePipeline(out var pipelineState))
|
||||
{
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program);
|
||||
|
||||
var error = new NSError(IntPtr.Zero);
|
||||
pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, 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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
208
src/Ryujinx.Graphics.Metal/State/PipelineUid.cs
Normal file
208
src/Ryujinx.Graphics.Metal/State/PipelineUid.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
struct VertexInputAttributeUid
|
||||
{
|
||||
public ulong Id0;
|
||||
|
||||
public ulong Offset
|
||||
{
|
||||
readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF);
|
||||
set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
||||
}
|
||||
|
||||
public MTLVertexFormat Format
|
||||
{
|
||||
readonly get => (MTLVertexFormat)((Id0 >> 32) & 0xFFFF);
|
||||
set => Id0 = (Id0 & 0xFFFF0000FFFFFFFF) | ((ulong)value << 32);
|
||||
}
|
||||
|
||||
public ulong BufferIndex
|
||||
{
|
||||
readonly get => ((Id0 >> 48) & 0xFFFF);
|
||||
set => Id0 = (Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48);
|
||||
}
|
||||
}
|
||||
|
||||
struct VertexInputLayoutUid
|
||||
{
|
||||
public ulong Id0;
|
||||
|
||||
public uint Stride
|
||||
{
|
||||
readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF);
|
||||
set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
|
||||
}
|
||||
|
||||
public uint StepRate
|
||||
{
|
||||
readonly get => (uint)((Id0 >> 32) & 0x1FFFFFFF);
|
||||
set => Id0 = (Id0 & 0xE0000000FFFFFFFF) | ((ulong)value << 32);
|
||||
}
|
||||
|
||||
public MTLVertexStepFunction StepFunction
|
||||
{
|
||||
readonly get => (MTLVertexStepFunction)((Id0 >> 61) & 0x7);
|
||||
set => Id0 = (Id0 & 0x1FFFFFFFFFFFFFFF) | ((ulong)value << 61);
|
||||
}
|
||||
}
|
||||
|
||||
struct ColorBlendStateUid
|
||||
{
|
||||
public ulong Id0;
|
||||
|
||||
public MTLPixelFormat PixelFormat
|
||||
{
|
||||
readonly get => (MTLPixelFormat)((Id0 >> 0) & 0xFFFF);
|
||||
set => Id0 = (Id0 & 0xFFFFFFFFFFFF0000) | ((ulong)value << 0);
|
||||
}
|
||||
|
||||
public MTLBlendFactor SourceRGBBlendFactor
|
||||
{
|
||||
readonly get => (MTLBlendFactor)((Id0 >> 16) & 0xFF);
|
||||
set => Id0 = (Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16);
|
||||
}
|
||||
|
||||
public MTLBlendFactor DestinationRGBBlendFactor
|
||||
{
|
||||
readonly get => (MTLBlendFactor)((Id0 >> 24) & 0xFF);
|
||||
set => Id0 = (Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24);
|
||||
}
|
||||
|
||||
public MTLBlendOperation RgbBlendOperation
|
||||
{
|
||||
readonly get => (MTLBlendOperation)((Id0 >> 32) & 0xF);
|
||||
set => Id0 = (Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32);
|
||||
}
|
||||
|
||||
public MTLBlendOperation AlphaBlendOperation
|
||||
{
|
||||
readonly get => (MTLBlendOperation)((Id0 >> 36) & 0xF);
|
||||
set => Id0 = (Id0 & 0xFFFFFF0FFFFFFFFF) | ((ulong)value << 36);
|
||||
}
|
||||
|
||||
public MTLBlendFactor SourceAlphaBlendFactor
|
||||
{
|
||||
readonly get => (MTLBlendFactor)((Id0 >> 40) & 0xFF);
|
||||
set => Id0 = (Id0 & 0xFFFF00FFFFFFFFFF) | ((ulong)value << 40);
|
||||
}
|
||||
|
||||
public MTLBlendFactor DestinationAlphaBlendFactor
|
||||
{
|
||||
readonly get => (MTLBlendFactor)((Id0 >> 48) & 0xFF);
|
||||
set => Id0 = (Id0 & 0xFF00FFFFFFFFFFFF) | ((ulong)value << 48);
|
||||
}
|
||||
|
||||
public MTLColorWriteMask WriteMask
|
||||
{
|
||||
readonly get => (MTLColorWriteMask)((Id0 >> 56) & 0xF);
|
||||
set => Id0 = (Id0 & 0xF0FFFFFFFFFFFFFF) | ((ulong)value << 56);
|
||||
}
|
||||
|
||||
public bool Enable
|
||||
{
|
||||
readonly get => ((Id0 >> 63) & 0x1) != 0UL;
|
||||
set => Id0 = (Id0 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
|
||||
}
|
||||
|
||||
public void Swap(ColorBlendStateUid uid)
|
||||
{
|
||||
var format = PixelFormat;
|
||||
|
||||
this = uid;
|
||||
PixelFormat = format;
|
||||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
struct PipelineUid : IRefEquatable<PipelineUid>
|
||||
{
|
||||
public ulong Id0;
|
||||
public ulong Id1;
|
||||
|
||||
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 8) & 0xFF);
|
||||
private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 16) & 0xFF);
|
||||
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id0 >> 24) & 0xFF);
|
||||
|
||||
public Array32<VertexInputAttributeUid> VertexAttributes;
|
||||
public Array33<VertexInputLayoutUid> VertexBindings;
|
||||
public Array8<ColorBlendStateUid> ColorBlendState;
|
||||
public uint AttachmentIntegerFormatMask;
|
||||
public bool LogicOpsAllowed;
|
||||
|
||||
public void ResetColorState()
|
||||
{
|
||||
ColorBlendState = new();
|
||||
|
||||
for (int i = 0; i < ColorBlendState.Length; i++)
|
||||
{
|
||||
ColorBlendState[i].WriteMask = MTLColorWriteMask.All;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object obj)
|
||||
{
|
||||
return obj is PipelineUid other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(ref PipelineUid other)
|
||||
{
|
||||
if (!Unsafe.As<ulong, Vector128<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SequenceEqual<VertexInputAttributeUid>(VertexAttributes.AsSpan(), other.VertexAttributes.AsSpan(), VertexAttributeDescriptionsCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SequenceEqual<VertexInputLayoutUid>(VertexBindings.AsSpan(), other.VertexBindings.AsSpan(), VertexBindingDescriptionsCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SequenceEqual<ColorBlendStateUid>(ColorBlendState.AsSpan(), other.ColorBlendState.AsSpan(), ColorBlendAttachmentStateCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SequenceEqual<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> y, uint count) where T : unmanaged
|
||||
{
|
||||
return MemoryMarshal.Cast<T, byte>(x[..(int)count]).SequenceEqual(MemoryMarshal.Cast<T, byte>(y[..(int)count]));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
ulong hash64 = Id0 * 23 ^
|
||||
Id1 * 23;
|
||||
|
||||
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
|
||||
{
|
||||
hash64 ^= VertexAttributes[i].Id0 * 23;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++)
|
||||
{
|
||||
hash64 ^= VertexBindings[i].Id0 * 23;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++)
|
||||
{
|
||||
hash64 ^= ColorBlendState[i].Id0 * 23;
|
||||
}
|
||||
|
||||
return (int)hash64 ^ ((int)(hash64 >> 32) * 17);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user