Ryujinx/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
riperiperi eb1ce41b00
GPU: Migrate buffers on GPU project, pre-emptively flush device local mappings (#6794)
* GPU: Migrate buffers on GPU project, pre-emptively flush device local mappings

Essentially retreading #4540, but it's on the GPU project now instead of the backend. This allows us to have a lot more control + knowledge of where the buffer backing has been changed and allows us to pre-emptively flush pages to host memory for quicker readback. It will allow us to do other stuff in the future, but we'll get there when we get there.

Performance greatly improved in Hyrule Warriors: Age of Calamity. Performance notably improved in TOTK (average). Performance for BOTW restored to how it was before #4911, perhaps a bit better.

- Rewrites a bunch of buffer migration stuff. Might want to tighten up how dispose stuff works.
- Fixed an issue where the copy for texture pre-flush would happen _after_ the syncpoint.

TODO: remove a page from pre-flush if it isn't flushed after a certain number of copies.

* Add copy deactivation

* Fix dependent virtual buffers

* Remove logging

* Fix format issues (maybe)

* Vulkan: Remove backing swap

* Add explicit memory access types for most buffers

* Fix typo

* Add device local force expiry, change buffer inheritance behaviour

* General cleanup, OGL fix

* BufferPreFlush comments

* BufferBackingState comments

* Add an extra precaution to BufferMigration

This is very unlikely, but it's important to cover loose ends like this.

* Address some feedback

* Docs
2024-05-19 16:53:37 -03:00

453 lines
22 KiB
C#

using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using BlendFactor = Silk.NET.Vulkan.BlendFactor;
using BlendOp = Silk.NET.Vulkan.BlendOp;
using CompareOp = Silk.NET.Vulkan.CompareOp;
using Format = Ryujinx.Graphics.GAL.Format;
using FrontFace = Silk.NET.Vulkan.FrontFace;
using IndexType = Silk.NET.Vulkan.IndexType;
using PrimitiveTopology = Silk.NET.Vulkan.PrimitiveTopology;
using StencilOp = Silk.NET.Vulkan.StencilOp;
namespace Ryujinx.Graphics.Vulkan
{
static class EnumConversion
{
public static ShaderStageFlags Convert(this ShaderStage stage)
{
return stage switch
{
ShaderStage.Vertex => ShaderStageFlags.VertexBit,
ShaderStage.Geometry => ShaderStageFlags.GeometryBit,
ShaderStage.TessellationControl => ShaderStageFlags.TessellationControlBit,
ShaderStage.TessellationEvaluation => ShaderStageFlags.TessellationEvaluationBit,
ShaderStage.Fragment => ShaderStageFlags.FragmentBit,
ShaderStage.Compute => ShaderStageFlags.ComputeBit,
_ => LogInvalidAndReturn(stage, nameof(ShaderStage), (ShaderStageFlags)0),
};
}
public static PipelineStageFlags ConvertToPipelineStageFlags(this ShaderStage stage)
{
return stage switch
{
ShaderStage.Vertex => PipelineStageFlags.VertexShaderBit,
ShaderStage.Geometry => PipelineStageFlags.GeometryShaderBit,
ShaderStage.TessellationControl => PipelineStageFlags.TessellationControlShaderBit,
ShaderStage.TessellationEvaluation => PipelineStageFlags.TessellationEvaluationShaderBit,
ShaderStage.Fragment => PipelineStageFlags.FragmentShaderBit,
ShaderStage.Compute => PipelineStageFlags.ComputeShaderBit,
_ => LogInvalidAndReturn(stage, nameof(ShaderStage), (PipelineStageFlags)0),
};
}
public static ShaderStageFlags Convert(this ResourceStages stages)
{
ShaderStageFlags stageFlags = stages.HasFlag(ResourceStages.Compute)
? ShaderStageFlags.ComputeBit
: ShaderStageFlags.None;
if (stages.HasFlag(ResourceStages.Vertex))
{
stageFlags |= ShaderStageFlags.VertexBit;
}
if (stages.HasFlag(ResourceStages.TessellationControl))
{
stageFlags |= ShaderStageFlags.TessellationControlBit;
}
if (stages.HasFlag(ResourceStages.TessellationEvaluation))
{
stageFlags |= ShaderStageFlags.TessellationEvaluationBit;
}
if (stages.HasFlag(ResourceStages.Geometry))
{
stageFlags |= ShaderStageFlags.GeometryBit;
}
if (stages.HasFlag(ResourceStages.Fragment))
{
stageFlags |= ShaderStageFlags.FragmentBit;
}
return stageFlags;
}
public static DescriptorType Convert(this ResourceType type)
{
return type switch
{
ResourceType.UniformBuffer => DescriptorType.UniformBuffer,
ResourceType.StorageBuffer => DescriptorType.StorageBuffer,
ResourceType.Texture => DescriptorType.SampledImage,
ResourceType.Sampler => DescriptorType.Sampler,
ResourceType.TextureAndSampler => DescriptorType.CombinedImageSampler,
ResourceType.Image => DescriptorType.StorageImage,
ResourceType.BufferTexture => DescriptorType.UniformTexelBuffer,
ResourceType.BufferImage => DescriptorType.StorageTexelBuffer,
_ => throw new ArgumentException($"Invalid resource type \"{type}\"."),
};
}
public static SamplerAddressMode Convert(this AddressMode mode)
{
return mode switch
{
AddressMode.Clamp => SamplerAddressMode.ClampToEdge, // TODO: Should be clamp.
AddressMode.Repeat => SamplerAddressMode.Repeat,
AddressMode.MirrorClamp => SamplerAddressMode.ClampToEdge, // TODO: Should be mirror clamp.
AddressMode.MirrorClampToEdge => SamplerAddressMode.MirrorClampToEdgeKhr,
AddressMode.MirrorClampToBorder => SamplerAddressMode.ClampToBorder, // TODO: Should be mirror clamp to border.
AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
_ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge), // TODO: Should be clamp.
};
}
public static BlendFactor Convert(this GAL.BlendFactor factor)
{
return factor switch
{
GAL.BlendFactor.Zero or GAL.BlendFactor.ZeroGl => BlendFactor.Zero,
GAL.BlendFactor.One or GAL.BlendFactor.OneGl => BlendFactor.One,
GAL.BlendFactor.SrcColor or GAL.BlendFactor.SrcColorGl => BlendFactor.SrcColor,
GAL.BlendFactor.OneMinusSrcColor or GAL.BlendFactor.OneMinusSrcColorGl => BlendFactor.OneMinusSrcColor,
GAL.BlendFactor.SrcAlpha or GAL.BlendFactor.SrcAlphaGl => BlendFactor.SrcAlpha,
GAL.BlendFactor.OneMinusSrcAlpha or GAL.BlendFactor.OneMinusSrcAlphaGl => BlendFactor.OneMinusSrcAlpha,
GAL.BlendFactor.DstAlpha or GAL.BlendFactor.DstAlphaGl => BlendFactor.DstAlpha,
GAL.BlendFactor.OneMinusDstAlpha or GAL.BlendFactor.OneMinusDstAlphaGl => BlendFactor.OneMinusDstAlpha,
GAL.BlendFactor.DstColor or GAL.BlendFactor.DstColorGl => BlendFactor.DstColor,
GAL.BlendFactor.OneMinusDstColor or GAL.BlendFactor.OneMinusDstColorGl => BlendFactor.OneMinusDstColor,
GAL.BlendFactor.SrcAlphaSaturate or GAL.BlendFactor.SrcAlphaSaturateGl => BlendFactor.SrcAlphaSaturate,
GAL.BlendFactor.Src1Color or GAL.BlendFactor.Src1ColorGl => BlendFactor.Src1Color,
GAL.BlendFactor.OneMinusSrc1Color or GAL.BlendFactor.OneMinusSrc1ColorGl => BlendFactor.OneMinusSrc1Color,
GAL.BlendFactor.Src1Alpha or GAL.BlendFactor.Src1AlphaGl => BlendFactor.Src1Alpha,
GAL.BlendFactor.OneMinusSrc1Alpha or GAL.BlendFactor.OneMinusSrc1AlphaGl => BlendFactor.OneMinusSrc1Alpha,
GAL.BlendFactor.ConstantColor => BlendFactor.ConstantColor,
GAL.BlendFactor.OneMinusConstantColor => BlendFactor.OneMinusConstantColor,
GAL.BlendFactor.ConstantAlpha => BlendFactor.ConstantAlpha,
GAL.BlendFactor.OneMinusConstantAlpha => BlendFactor.OneMinusConstantAlpha,
_ => LogInvalidAndReturn(factor, nameof(GAL.BlendFactor), BlendFactor.Zero),
};
}
public static BlendOp Convert(this AdvancedBlendOp op)
{
return op switch
{
AdvancedBlendOp.Zero => BlendOp.ZeroExt,
AdvancedBlendOp.Src => BlendOp.SrcExt,
AdvancedBlendOp.Dst => BlendOp.DstExt,
AdvancedBlendOp.SrcOver => BlendOp.SrcOverExt,
AdvancedBlendOp.DstOver => BlendOp.DstOverExt,
AdvancedBlendOp.SrcIn => BlendOp.SrcInExt,
AdvancedBlendOp.DstIn => BlendOp.DstInExt,
AdvancedBlendOp.SrcOut => BlendOp.SrcOutExt,
AdvancedBlendOp.DstOut => BlendOp.DstOutExt,
AdvancedBlendOp.SrcAtop => BlendOp.SrcAtopExt,
AdvancedBlendOp.DstAtop => BlendOp.DstAtopExt,
AdvancedBlendOp.Xor => BlendOp.XorExt,
AdvancedBlendOp.Plus => BlendOp.PlusExt,
AdvancedBlendOp.PlusClamped => BlendOp.PlusClampedExt,
AdvancedBlendOp.PlusClampedAlpha => BlendOp.PlusClampedAlphaExt,
AdvancedBlendOp.PlusDarker => BlendOp.PlusDarkerExt,
AdvancedBlendOp.Multiply => BlendOp.MultiplyExt,
AdvancedBlendOp.Screen => BlendOp.ScreenExt,
AdvancedBlendOp.Overlay => BlendOp.OverlayExt,
AdvancedBlendOp.Darken => BlendOp.DarkenExt,
AdvancedBlendOp.Lighten => BlendOp.LightenExt,
AdvancedBlendOp.ColorDodge => BlendOp.ColordodgeExt,
AdvancedBlendOp.ColorBurn => BlendOp.ColorburnExt,
AdvancedBlendOp.HardLight => BlendOp.HardlightExt,
AdvancedBlendOp.SoftLight => BlendOp.SoftlightExt,
AdvancedBlendOp.Difference => BlendOp.DifferenceExt,
AdvancedBlendOp.Minus => BlendOp.MinusExt,
AdvancedBlendOp.MinusClamped => BlendOp.MinusClampedExt,
AdvancedBlendOp.Exclusion => BlendOp.ExclusionExt,
AdvancedBlendOp.Contrast => BlendOp.ContrastExt,
AdvancedBlendOp.Invert => BlendOp.InvertExt,
AdvancedBlendOp.InvertRGB => BlendOp.InvertRgbExt,
AdvancedBlendOp.InvertOvg => BlendOp.InvertOvgExt,
AdvancedBlendOp.LinearDodge => BlendOp.LineardodgeExt,
AdvancedBlendOp.LinearBurn => BlendOp.LinearburnExt,
AdvancedBlendOp.VividLight => BlendOp.VividlightExt,
AdvancedBlendOp.LinearLight => BlendOp.LinearlightExt,
AdvancedBlendOp.PinLight => BlendOp.PinlightExt,
AdvancedBlendOp.HardMix => BlendOp.HardmixExt,
AdvancedBlendOp.Red => BlendOp.RedExt,
AdvancedBlendOp.Green => BlendOp.GreenExt,
AdvancedBlendOp.Blue => BlendOp.BlueExt,
AdvancedBlendOp.HslHue => BlendOp.HslHueExt,
AdvancedBlendOp.HslSaturation => BlendOp.HslSaturationExt,
AdvancedBlendOp.HslColor => BlendOp.HslColorExt,
AdvancedBlendOp.HslLuminosity => BlendOp.HslLuminosityExt,
_ => LogInvalidAndReturn(op, nameof(AdvancedBlendOp), BlendOp.Add),
};
}
public static BlendOp Convert(this GAL.BlendOp op)
{
return op switch
{
GAL.BlendOp.Add or GAL.BlendOp.AddGl => BlendOp.Add,
GAL.BlendOp.Subtract or GAL.BlendOp.SubtractGl => BlendOp.Subtract,
GAL.BlendOp.ReverseSubtract or GAL.BlendOp.ReverseSubtractGl => BlendOp.ReverseSubtract,
GAL.BlendOp.Minimum or GAL.BlendOp.MinimumGl => BlendOp.Min,
GAL.BlendOp.Maximum or GAL.BlendOp.MaximumGl => BlendOp.Max,
_ => LogInvalidAndReturn(op, nameof(GAL.BlendOp), BlendOp.Add),
};
}
public static BlendOverlapEXT Convert(this AdvancedBlendOverlap overlap)
{
return overlap switch
{
AdvancedBlendOverlap.Uncorrelated => BlendOverlapEXT.UncorrelatedExt,
AdvancedBlendOverlap.Disjoint => BlendOverlapEXT.DisjointExt,
AdvancedBlendOverlap.Conjoint => BlendOverlapEXT.ConjointExt,
_ => LogInvalidAndReturn(overlap, nameof(AdvancedBlendOverlap), BlendOverlapEXT.UncorrelatedExt),
};
}
public static CompareOp Convert(this GAL.CompareOp op)
{
return op switch
{
GAL.CompareOp.Never or GAL.CompareOp.NeverGl => CompareOp.Never,
GAL.CompareOp.Less or GAL.CompareOp.LessGl => CompareOp.Less,
GAL.CompareOp.Equal or GAL.CompareOp.EqualGl => CompareOp.Equal,
GAL.CompareOp.LessOrEqual or GAL.CompareOp.LessOrEqualGl => CompareOp.LessOrEqual,
GAL.CompareOp.Greater or GAL.CompareOp.GreaterGl => CompareOp.Greater,
GAL.CompareOp.NotEqual or GAL.CompareOp.NotEqualGl => CompareOp.NotEqual,
GAL.CompareOp.GreaterOrEqual or GAL.CompareOp.GreaterOrEqualGl => CompareOp.GreaterOrEqual,
GAL.CompareOp.Always or GAL.CompareOp.AlwaysGl => CompareOp.Always,
_ => LogInvalidAndReturn(op, nameof(GAL.CompareOp), CompareOp.Never),
};
}
public static CullModeFlags Convert(this Face face)
{
return face switch
{
Face.Back => CullModeFlags.BackBit,
Face.Front => CullModeFlags.FrontBit,
Face.FrontAndBack => CullModeFlags.FrontAndBack,
_ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit),
};
}
public static FrontFace Convert(this GAL.FrontFace frontFace)
{
// Flipped to account for origin differences.
return frontFace switch
{
GAL.FrontFace.Clockwise => FrontFace.CounterClockwise,
GAL.FrontFace.CounterClockwise => FrontFace.Clockwise,
_ => LogInvalidAndReturn(frontFace, nameof(GAL.FrontFace), FrontFace.Clockwise),
};
}
public static IndexType Convert(this GAL.IndexType type)
{
return type switch
{
GAL.IndexType.UByte => IndexType.Uint8Ext,
GAL.IndexType.UShort => IndexType.Uint16,
GAL.IndexType.UInt => IndexType.Uint32,
_ => LogInvalidAndReturn(type, nameof(GAL.IndexType), IndexType.Uint16),
};
}
public static Filter Convert(this MagFilter filter)
{
return filter switch
{
MagFilter.Nearest => Filter.Nearest,
MagFilter.Linear => Filter.Linear,
_ => LogInvalidAndReturn(filter, nameof(MagFilter), Filter.Nearest),
};
}
public static (Filter, SamplerMipmapMode) Convert(this MinFilter filter)
{
return filter switch
{
MinFilter.Nearest => (Filter.Nearest, SamplerMipmapMode.Nearest),
MinFilter.Linear => (Filter.Linear, SamplerMipmapMode.Nearest),
MinFilter.NearestMipmapNearest => (Filter.Nearest, SamplerMipmapMode.Nearest),
MinFilter.LinearMipmapNearest => (Filter.Linear, SamplerMipmapMode.Nearest),
MinFilter.NearestMipmapLinear => (Filter.Nearest, SamplerMipmapMode.Linear),
MinFilter.LinearMipmapLinear => (Filter.Linear, SamplerMipmapMode.Linear),
_ => LogInvalidAndReturn(filter, nameof(MinFilter), (Filter.Nearest, SamplerMipmapMode.Nearest)),
};
}
public static PrimitiveTopology Convert(this GAL.PrimitiveTopology topology)
{
return topology switch
{
GAL.PrimitiveTopology.Points => PrimitiveTopology.PointList,
GAL.PrimitiveTopology.Lines => PrimitiveTopology.LineList,
GAL.PrimitiveTopology.LineStrip => PrimitiveTopology.LineStrip,
GAL.PrimitiveTopology.Triangles => PrimitiveTopology.TriangleList,
GAL.PrimitiveTopology.TriangleStrip => PrimitiveTopology.TriangleStrip,
GAL.PrimitiveTopology.TriangleFan => PrimitiveTopology.TriangleFan,
GAL.PrimitiveTopology.LinesAdjacency => PrimitiveTopology.LineListWithAdjacency,
GAL.PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.LineStripWithAdjacency,
GAL.PrimitiveTopology.TrianglesAdjacency => PrimitiveTopology.TriangleListWithAdjacency,
GAL.PrimitiveTopology.TriangleStripAdjacency => PrimitiveTopology.TriangleStripWithAdjacency,
GAL.PrimitiveTopology.Patches => PrimitiveTopology.PatchList,
GAL.PrimitiveTopology.Polygon => PrimitiveTopology.TriangleFan,
GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), PrimitiveTopology.TriangleList),
};
}
public static StencilOp Convert(this GAL.StencilOp op)
{
return op switch
{
GAL.StencilOp.Keep or GAL.StencilOp.KeepGl => StencilOp.Keep,
GAL.StencilOp.Zero or GAL.StencilOp.ZeroGl => StencilOp.Zero,
GAL.StencilOp.Replace or GAL.StencilOp.ReplaceGl => StencilOp.Replace,
GAL.StencilOp.IncrementAndClamp or GAL.StencilOp.IncrementAndClampGl => StencilOp.IncrementAndClamp,
GAL.StencilOp.DecrementAndClamp or GAL.StencilOp.DecrementAndClampGl => StencilOp.DecrementAndClamp,
GAL.StencilOp.Invert or GAL.StencilOp.InvertGl => StencilOp.Invert,
GAL.StencilOp.IncrementAndWrap or GAL.StencilOp.IncrementAndWrapGl => StencilOp.IncrementAndWrap,
GAL.StencilOp.DecrementAndWrap or GAL.StencilOp.DecrementAndWrapGl => StencilOp.DecrementAndWrap,
_ => LogInvalidAndReturn(op, nameof(GAL.StencilOp), StencilOp.Keep),
};
}
public static ComponentSwizzle Convert(this SwizzleComponent swizzleComponent)
{
return swizzleComponent switch
{
SwizzleComponent.Zero => ComponentSwizzle.Zero,
SwizzleComponent.One => ComponentSwizzle.One,
SwizzleComponent.Red => ComponentSwizzle.R,
SwizzleComponent.Green => ComponentSwizzle.G,
SwizzleComponent.Blue => ComponentSwizzle.B,
SwizzleComponent.Alpha => ComponentSwizzle.A,
_ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), ComponentSwizzle.Zero),
};
}
public static ImageType Convert(this Target target)
{
return target switch
{
Target.Texture1D or
Target.Texture1DArray or
Target.TextureBuffer => ImageType.Type1D,
Target.Texture2D or
Target.Texture2DArray or
Target.Texture2DMultisample or
Target.Cubemap or
Target.CubemapArray => ImageType.Type2D,
Target.Texture3D => ImageType.Type3D,
_ => LogInvalidAndReturn(target, nameof(Target), ImageType.Type2D),
};
}
public static ImageViewType ConvertView(this Target target)
{
return target switch
{
Target.Texture1D => ImageViewType.Type1D,
Target.Texture2D or Target.Texture2DMultisample => ImageViewType.Type2D,
Target.Texture3D => ImageViewType.Type3D,
Target.Texture1DArray => ImageViewType.Type1DArray,
Target.Texture2DArray => ImageViewType.Type2DArray,
Target.Cubemap => ImageViewType.TypeCube,
Target.CubemapArray => ImageViewType.TypeCubeArray,
_ => LogInvalidAndReturn(target, nameof(Target), ImageViewType.Type2D),
};
}
public static ImageAspectFlags ConvertAspectFlags(this Format format)
{
return format switch
{
Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
Format.S8Uint => ImageAspectFlags.StencilBit,
Format.D24UnormS8Uint or
Format.D32FloatS8Uint or
Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit,
_ => ImageAspectFlags.ColorBit,
};
}
public static ImageAspectFlags ConvertAspectFlags(this Format format, DepthStencilMode depthStencilMode)
{
return format switch
{
Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
Format.S8Uint => ImageAspectFlags.StencilBit,
Format.D24UnormS8Uint or
Format.D32FloatS8Uint or
Format.S8UintD24Unorm => depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.StencilBit : ImageAspectFlags.DepthBit,
_ => ImageAspectFlags.ColorBit,
};
}
public static LogicOp Convert(this LogicalOp op)
{
return op switch
{
LogicalOp.Clear => LogicOp.Clear,
LogicalOp.And => LogicOp.And,
LogicalOp.AndReverse => LogicOp.AndReverse,
LogicalOp.Copy => LogicOp.Copy,
LogicalOp.AndInverted => LogicOp.AndInverted,
LogicalOp.Noop => LogicOp.NoOp,
LogicalOp.Xor => LogicOp.Xor,
LogicalOp.Or => LogicOp.Or,
LogicalOp.Nor => LogicOp.Nor,
LogicalOp.Equiv => LogicOp.Equivalent,
LogicalOp.Invert => LogicOp.Invert,
LogicalOp.OrReverse => LogicOp.OrReverse,
LogicalOp.CopyInverted => LogicOp.CopyInverted,
LogicalOp.OrInverted => LogicOp.OrInverted,
LogicalOp.Nand => LogicOp.Nand,
LogicalOp.Set => LogicOp.Set,
_ => LogInvalidAndReturn(op, nameof(LogicalOp), LogicOp.Copy),
};
}
public static BufferAllocationType Convert(this BufferAccess access)
{
BufferAccess memType = access & BufferAccess.MemoryTypeMask;
if (memType == BufferAccess.HostMemory || access.HasFlag(BufferAccess.Stream))
{
return BufferAllocationType.HostMapped;
}
else if (memType == BufferAccess.DeviceMemory)
{
return BufferAllocationType.DeviceLocal;
}
else if (memType == BufferAccess.DeviceMemoryMapped)
{
return BufferAllocationType.DeviceLocalMapped;
}
return BufferAllocationType.Auto;
}
private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default)
{
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}.");
return defaultValue;
}
}
}