diff options
Diffstat (limited to 'src/Ryujinx.Graphics.GAL')
174 files changed, 6523 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.GAL/AddressMode.cs b/src/Ryujinx.Graphics.GAL/AddressMode.cs new file mode 100644 index 00000000..153925b1 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/AddressMode.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AddressMode + { + Repeat, + MirroredRepeat, + ClampToEdge, + ClampToBorder, + Clamp, + MirrorClampToEdge, + MirrorClampToBorder, + MirrorClamp + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs new file mode 100644 index 00000000..1f1f7c3f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct AdvancedBlendDescriptor + { + public AdvancedBlendOp Op { get; } + public AdvancedBlendOverlap Overlap { get; } + public bool SrcPreMultiplied { get; } + + public AdvancedBlendDescriptor(AdvancedBlendOp op, AdvancedBlendOverlap overlap, bool srcPreMultiplied) + { + Op = op; + Overlap = overlap; + SrcPreMultiplied = srcPreMultiplied; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs new file mode 100644 index 00000000..4140bf49 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/AdvancedBlendOp.cs @@ -0,0 +1,52 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AdvancedBlendOp + { + Zero, + Src, + Dst, + SrcOver, + DstOver, + SrcIn, + DstIn, + SrcOut, + DstOut, + SrcAtop, + DstAtop, + Xor, + Plus, + PlusClamped, + PlusClampedAlpha, + PlusDarker, + Multiply, + Screen, + Overlay, + Darken, + Lighten, + ColorDodge, + ColorBurn, + HardLight, + SoftLight, + Difference, + Minus, + MinusClamped, + Exclusion, + Contrast, + Invert, + InvertRGB, + InvertOvg, + LinearDodge, + LinearBurn, + VividLight, + LinearLight, + PinLight, + HardMix, + Red, + Green, + Blue, + HslHue, + HslSaturation, + HslColor, + HslLuminosity + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs b/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs new file mode 100644 index 00000000..d4feb2b3 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AdvancedBlendOverlap + { + Uncorrelated, + Disjoint, + Conjoint + } +} diff --git a/src/Ryujinx.Graphics.GAL/AntiAliasing.cs b/src/Ryujinx.Graphics.GAL/AntiAliasing.cs new file mode 100644 index 00000000..d4e5754d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/AntiAliasing.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum AntiAliasing + { + None, + Fxaa, + SmaaLow, + SmaaMedium, + SmaaHigh, + SmaaUltra + } +} diff --git a/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs b/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs new file mode 100644 index 00000000..83b8afe2 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BlendDescriptor.cs @@ -0,0 +1,35 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct BlendDescriptor + { + public bool Enable { get; } + + public ColorF BlendConstant { get; } + public BlendOp ColorOp { get; } + public BlendFactor ColorSrcFactor { get; } + public BlendFactor ColorDstFactor { get; } + public BlendOp AlphaOp { get; } + public BlendFactor AlphaSrcFactor { get; } + public BlendFactor AlphaDstFactor { get; } + + public BlendDescriptor( + bool enable, + ColorF blendConstant, + BlendOp colorOp, + BlendFactor colorSrcFactor, + BlendFactor colorDstFactor, + BlendOp alphaOp, + BlendFactor alphaSrcFactor, + BlendFactor alphaDstFactor) + { + Enable = enable; + BlendConstant = blendConstant; + ColorOp = colorOp; + ColorSrcFactor = colorSrcFactor; + ColorDstFactor = colorDstFactor; + AlphaOp = alphaOp; + AlphaSrcFactor = alphaSrcFactor; + AlphaDstFactor = alphaDstFactor; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/BlendFactor.cs b/src/Ryujinx.Graphics.GAL/BlendFactor.cs new file mode 100644 index 00000000..4149ad51 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BlendFactor.cs @@ -0,0 +1,62 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum BlendFactor + { + Zero = 1, + One, + SrcColor, + OneMinusSrcColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha, + DstColor, + OneMinusDstColor, + SrcAlphaSaturate, + Src1Color = 0x10, + OneMinusSrc1Color, + Src1Alpha, + OneMinusSrc1Alpha, + ConstantColor = 0xc001, + OneMinusConstantColor, + ConstantAlpha, + OneMinusConstantAlpha, + + ZeroGl = 0x4000, + OneGl = 0x4001, + SrcColorGl = 0x4300, + OneMinusSrcColorGl = 0x4301, + SrcAlphaGl = 0x4302, + OneMinusSrcAlphaGl = 0x4303, + DstAlphaGl = 0x4304, + OneMinusDstAlphaGl = 0x4305, + DstColorGl = 0x4306, + OneMinusDstColorGl = 0x4307, + SrcAlphaSaturateGl = 0x4308, + Src1ColorGl = 0xc900, + OneMinusSrc1ColorGl = 0xc901, + Src1AlphaGl = 0xc902, + OneMinusSrc1AlphaGl = 0xc903 + } + + public static class BlendFactorExtensions + { + public static bool IsDualSource(this BlendFactor factor) + { + switch (factor) + { + case BlendFactor.Src1Color: + case BlendFactor.Src1ColorGl: + case BlendFactor.Src1Alpha: + case BlendFactor.Src1AlphaGl: + case BlendFactor.OneMinusSrc1Color: + case BlendFactor.OneMinusSrc1ColorGl: + case BlendFactor.OneMinusSrc1Alpha: + case BlendFactor.OneMinusSrc1AlphaGl: + return true; + } + + return false; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/BlendOp.cs b/src/Ryujinx.Graphics.GAL/BlendOp.cs new file mode 100644 index 00000000..b4a5a930 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BlendOp.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum BlendOp + { + Add = 1, + Subtract, + ReverseSubtract, + Minimum, + Maximum, + + AddGl = 0x8006, + MinimumGl = 0x8007, + MaximumGl = 0x8008, + SubtractGl = 0x800a, + ReverseSubtractGl = 0x800b + } +} diff --git a/src/Ryujinx.Graphics.GAL/BufferAssignment.cs b/src/Ryujinx.Graphics.GAL/BufferAssignment.cs new file mode 100644 index 00000000..d803d90b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BufferAssignment.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct BufferAssignment + { + public readonly int Binding; + public readonly BufferRange Range; + + public BufferAssignment(int binding, BufferRange range) + { + Binding = binding; + Range = range; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/BufferHandle.cs b/src/Ryujinx.Graphics.GAL/BufferHandle.cs new file mode 100644 index 00000000..5ba50d19 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BufferHandle.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL +{ + [StructLayout(LayoutKind.Sequential, Size = 8)] + public readonly record struct BufferHandle + { + private readonly ulong _value; + + public static BufferHandle Null => new BufferHandle(0); + + private BufferHandle(ulong value) => _value = value; + } +} diff --git a/src/Ryujinx.Graphics.GAL/BufferRange.cs b/src/Ryujinx.Graphics.GAL/BufferRange.cs new file mode 100644 index 00000000..ad9ebee4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/BufferRange.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct BufferRange + { + private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0); + + public static BufferRange Empty => _empty; + + public BufferHandle Handle { get; } + + public int Offset { get; } + public int Size { get; } + + public BufferRange(BufferHandle handle, int offset, int size) + { + Handle = handle; + Offset = offset; + Size = size; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs new file mode 100644 index 00000000..a93d3846 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs @@ -0,0 +1,140 @@ +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.GAL +{ + public readonly struct Capabilities + { + public readonly TargetApi Api; + public readonly string VendorName; + + public readonly bool HasFrontFacingBug; + public readonly bool HasVectorIndexingBug; + public readonly bool NeedsFragmentOutputSpecialization; + public readonly bool ReduceShaderPrecision; + + public readonly bool SupportsAstcCompression; + public readonly bool SupportsBc123Compression; + public readonly bool SupportsBc45Compression; + public readonly bool SupportsBc67Compression; + public readonly bool SupportsEtc2Compression; + public readonly bool Supports3DTextureCompression; + public readonly bool SupportsBgraFormat; + public readonly bool SupportsR4G4Format; + public readonly bool SupportsR4G4B4A4Format; + public readonly bool SupportsSnormBufferTextureFormat; + public readonly bool Supports5BitComponentFormat; + public readonly bool SupportsBlendEquationAdvanced; + public readonly bool SupportsFragmentShaderInterlock; + public readonly bool SupportsFragmentShaderOrderingIntel; + public readonly bool SupportsGeometryShader; + public readonly bool SupportsGeometryShaderPassthrough; + public readonly bool SupportsImageLoadFormatted; + public readonly bool SupportsLayerVertexTessellation; + public readonly bool SupportsMismatchingViewFormat; + public readonly bool SupportsCubemapView; + public readonly bool SupportsNonConstantTextureOffset; + public readonly bool SupportsShaderBallot; + public readonly bool SupportsTextureShadowLod; + public readonly bool SupportsViewportIndexVertexTessellation; + public readonly bool SupportsViewportMask; + public readonly bool SupportsViewportSwizzle; + public readonly bool SupportsIndirectParameters; + + public readonly uint MaximumUniformBuffersPerStage; + public readonly uint MaximumStorageBuffersPerStage; + public readonly uint MaximumTexturesPerStage; + public readonly uint MaximumImagesPerStage; + + public readonly int MaximumComputeSharedMemorySize; + public readonly float MaximumSupportedAnisotropy; + public readonly int StorageBufferOffsetAlignment; + + public readonly int GatherBiasPrecision; + + public Capabilities( + TargetApi api, + string vendorName, + bool hasFrontFacingBug, + bool hasVectorIndexingBug, + bool needsFragmentOutputSpecialization, + bool reduceShaderPrecision, + bool supportsAstcCompression, + bool supportsBc123Compression, + bool supportsBc45Compression, + bool supportsBc67Compression, + bool supportsEtc2Compression, + bool supports3DTextureCompression, + bool supportsBgraFormat, + bool supportsR4G4Format, + bool supportsR4G4B4A4Format, + bool supportsSnormBufferTextureFormat, + bool supports5BitComponentFormat, + bool supportsBlendEquationAdvanced, + bool supportsFragmentShaderInterlock, + bool supportsFragmentShaderOrderingIntel, + bool supportsGeometryShader, + bool supportsGeometryShaderPassthrough, + bool supportsImageLoadFormatted, + bool supportsLayerVertexTessellation, + bool supportsMismatchingViewFormat, + bool supportsCubemapView, + bool supportsNonConstantTextureOffset, + bool supportsShaderBallot, + bool supportsTextureShadowLod, + bool supportsViewportIndexVertexTessellation, + bool supportsViewportMask, + bool supportsViewportSwizzle, + bool supportsIndirectParameters, + uint maximumUniformBuffersPerStage, + uint maximumStorageBuffersPerStage, + uint maximumTexturesPerStage, + uint maximumImagesPerStage, + int maximumComputeSharedMemorySize, + float maximumSupportedAnisotropy, + int storageBufferOffsetAlignment, + int gatherBiasPrecision) + { + Api = api; + VendorName = vendorName; + HasFrontFacingBug = hasFrontFacingBug; + HasVectorIndexingBug = hasVectorIndexingBug; + NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization; + ReduceShaderPrecision = reduceShaderPrecision; + SupportsAstcCompression = supportsAstcCompression; + SupportsBc123Compression = supportsBc123Compression; + SupportsBc45Compression = supportsBc45Compression; + SupportsBc67Compression = supportsBc67Compression; + SupportsEtc2Compression = supportsEtc2Compression; + Supports3DTextureCompression = supports3DTextureCompression; + SupportsBgraFormat = supportsBgraFormat; + SupportsR4G4Format = supportsR4G4Format; + SupportsR4G4B4A4Format = supportsR4G4B4A4Format; + SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; + Supports5BitComponentFormat = supports5BitComponentFormat; + SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced; + SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; + SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; + SupportsGeometryShader = supportsGeometryShader; + SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; + SupportsImageLoadFormatted = supportsImageLoadFormatted; + SupportsLayerVertexTessellation = supportsLayerVertexTessellation; + SupportsMismatchingViewFormat = supportsMismatchingViewFormat; + SupportsCubemapView = supportsCubemapView; + SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; + SupportsShaderBallot = supportsShaderBallot; + SupportsTextureShadowLod = supportsTextureShadowLod; + SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation; + SupportsViewportMask = supportsViewportMask; + SupportsViewportSwizzle = supportsViewportSwizzle; + SupportsIndirectParameters = supportsIndirectParameters; + MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage; + MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage; + MaximumTexturesPerStage = maximumTexturesPerStage; + MaximumImagesPerStage = maximumImagesPerStage; + MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; + MaximumSupportedAnisotropy = maximumSupportedAnisotropy; + StorageBufferOffsetAlignment = storageBufferOffsetAlignment; + GatherBiasPrecision = gatherBiasPrecision; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/ColorF.cs b/src/Ryujinx.Graphics.GAL/ColorF.cs new file mode 100644 index 00000000..235f4229 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ColorF.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly record struct ColorF(float Red, float Green, float Blue, float Alpha); +} diff --git a/src/Ryujinx.Graphics.GAL/CompareMode.cs b/src/Ryujinx.Graphics.GAL/CompareMode.cs new file mode 100644 index 00000000..7a64d9bb --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/CompareMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum CompareMode + { + None, + CompareRToTexture + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/CompareOp.cs b/src/Ryujinx.Graphics.GAL/CompareOp.cs new file mode 100644 index 00000000..358ed2b4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/CompareOp.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum CompareOp + { + Never = 1, + Less, + Equal, + LessOrEqual, + Greater, + NotEqual, + GreaterOrEqual, + Always, + + NeverGl = 0x200, + LessGl = 0x201, + EqualGl = 0x202, + LessOrEqualGl = 0x203, + GreaterGl = 0x204, + NotEqualGl = 0x205, + GreaterOrEqualGl = 0x206, + AlwaysGl = 0x207, + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/CounterType.cs b/src/Ryujinx.Graphics.GAL/CounterType.cs new file mode 100644 index 00000000..9d7b3b98 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/CounterType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum CounterType + { + SamplesPassed, + PrimitivesGenerated, + TransformFeedbackPrimitivesWritten + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/DepthMode.cs b/src/Ryujinx.Graphics.GAL/DepthMode.cs new file mode 100644 index 00000000..aafbb65a --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/DepthMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum DepthMode + { + MinusOneToOne, + ZeroToOne + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs b/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs new file mode 100644 index 00000000..e80d0d4b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/DepthStencilMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum DepthStencilMode + { + Depth, + Stencil + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs b/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs new file mode 100644 index 00000000..4c593392 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct DepthTestDescriptor + { + public bool TestEnable { get; } + public bool WriteEnable { get; } + + public CompareOp Func { get; } + + public DepthTestDescriptor( + bool testEnable, + bool writeEnable, + CompareOp func) + { + TestEnable = testEnable; + WriteEnable = writeEnable; + Func = func; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/DeviceInfo.cs b/src/Ryujinx.Graphics.GAL/DeviceInfo.cs new file mode 100644 index 00000000..eb4a016f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/DeviceInfo.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct DeviceInfo + { + public readonly string Id; + public readonly string Vendor; + public readonly string Name; + public readonly bool IsDiscrete; + + public DeviceInfo(string id, string vendor, string name, bool isDiscrete) + { + Id = id; + Vendor = vendor; + Name = name; + IsDiscrete = isDiscrete; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Extents2D.cs b/src/Ryujinx.Graphics.GAL/Extents2D.cs new file mode 100644 index 00000000..bac44f83 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Extents2D.cs @@ -0,0 +1,31 @@ +using Ryujinx.Common; + +namespace Ryujinx.Graphics.GAL +{ + public readonly struct Extents2D + { + public int X1 { get; } + public int Y1 { get; } + public int X2 { get; } + public int Y2 { get; } + + public Extents2D(int x1, int y1, int x2, int y2) + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + } + + public Extents2D Reduce(int level) + { + int div = 1 << level; + + return new Extents2D( + X1 >> level, + Y1 >> level, + BitUtils.DivRoundUp(X2, div), + BitUtils.DivRoundUp(Y2, div)); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Extents2DF.cs b/src/Ryujinx.Graphics.GAL/Extents2DF.cs new file mode 100644 index 00000000..43f0e25e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Extents2DF.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct Extents2DF + { + public float X1 { get; } + public float Y1 { get; } + public float X2 { get; } + public float Y2 { get; } + + public Extents2DF(float x1, float y1, float x2, float y2) + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Face.cs b/src/Ryujinx.Graphics.GAL/Face.cs new file mode 100644 index 00000000..0587641f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Face.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum Face + { + Front = 0x404, + Back = 0x405, + FrontAndBack = 0x408 + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs new file mode 100644 index 00000000..5e0274e5 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -0,0 +1,667 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum Format + { + R8Unorm, + R8Snorm, + R8Uint, + R8Sint, + R16Float, + R16Unorm, + R16Snorm, + R16Uint, + R16Sint, + R32Float, + R32Uint, + R32Sint, + R8G8Unorm, + R8G8Snorm, + R8G8Uint, + R8G8Sint, + R16G16Float, + R16G16Unorm, + R16G16Snorm, + R16G16Uint, + R16G16Sint, + R32G32Float, + R32G32Uint, + R32G32Sint, + R8G8B8Unorm, + R8G8B8Snorm, + R8G8B8Uint, + R8G8B8Sint, + R16G16B16Float, + R16G16B16Unorm, + R16G16B16Snorm, + R16G16B16Uint, + R16G16B16Sint, + R32G32B32Float, + R32G32B32Uint, + R32G32B32Sint, + R8G8B8A8Unorm, + R8G8B8A8Snorm, + R8G8B8A8Uint, + R8G8B8A8Sint, + R16G16B16A16Float, + R16G16B16A16Unorm, + R16G16B16A16Snorm, + R16G16B16A16Uint, + R16G16B16A16Sint, + R32G32B32A32Float, + R32G32B32A32Uint, + R32G32B32A32Sint, + S8Uint, + D16Unorm, + S8UintD24Unorm, + D32Float, + D24UnormS8Uint, + D32FloatS8Uint, + R8G8B8A8Srgb, + R4G4Unorm, + R4G4B4A4Unorm, + R5G5B5X1Unorm, + R5G5B5A1Unorm, + R5G6B5Unorm, + R10G10B10A2Unorm, + R10G10B10A2Uint, + R11G11B10Float, + R9G9B9E5Float, + Bc1RgbaUnorm, + Bc2Unorm, + Bc3Unorm, + Bc1RgbaSrgb, + Bc2Srgb, + Bc3Srgb, + Bc4Unorm, + Bc4Snorm, + Bc5Unorm, + Bc5Snorm, + Bc7Unorm, + Bc7Srgb, + Bc6HSfloat, + Bc6HUfloat, + Etc2RgbUnorm, + Etc2RgbaUnorm, + Etc2RgbPtaUnorm, + Etc2RgbSrgb, + Etc2RgbaSrgb, + Etc2RgbPtaSrgb, + R8Uscaled, + R8Sscaled, + R16Uscaled, + R16Sscaled, + R32Uscaled, + R32Sscaled, + R8G8Uscaled, + R8G8Sscaled, + R16G16Uscaled, + R16G16Sscaled, + R32G32Uscaled, + R32G32Sscaled, + R8G8B8Uscaled, + R8G8B8Sscaled, + R16G16B16Uscaled, + R16G16B16Sscaled, + R32G32B32Uscaled, + R32G32B32Sscaled, + R8G8B8A8Uscaled, + R8G8B8A8Sscaled, + R16G16B16A16Uscaled, + R16G16B16A16Sscaled, + R32G32B32A32Uscaled, + R32G32B32A32Sscaled, + R10G10B10A2Snorm, + R10G10B10A2Sint, + R10G10B10A2Uscaled, + R10G10B10A2Sscaled, + Astc4x4Unorm, + Astc5x4Unorm, + Astc5x5Unorm, + Astc6x5Unorm, + Astc6x6Unorm, + Astc8x5Unorm, + Astc8x6Unorm, + Astc8x8Unorm, + Astc10x5Unorm, + Astc10x6Unorm, + Astc10x8Unorm, + Astc10x10Unorm, + Astc12x10Unorm, + Astc12x12Unorm, + Astc4x4Srgb, + Astc5x4Srgb, + Astc5x5Srgb, + Astc6x5Srgb, + Astc6x6Srgb, + Astc8x5Srgb, + Astc8x6Srgb, + Astc8x8Srgb, + Astc10x5Srgb, + Astc10x6Srgb, + Astc10x8Srgb, + Astc10x10Srgb, + Astc12x10Srgb, + Astc12x12Srgb, + B5G6R5Unorm, + B5G5R5A1Unorm, + A1B5G5R5Unorm, + B8G8R8A8Unorm, + B8G8R8A8Srgb + } + + public static class FormatExtensions + { + /// <summary> + /// The largest scalar size for a buffer format. + /// </summary> + public const int MaxBufferFormatScalarSize = 4; + + /// <summary> + /// Gets the byte size for a single component of this format, or its packed size. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>Byte size for a single component, or packed size</returns> + public static int GetScalarSize(this Format format) + { + switch (format) + { + case Format.R8Unorm: + case Format.R8Snorm: + case Format.R8Uint: + case Format.R8Sint: + case Format.R8G8Unorm: + case Format.R8G8Snorm: + case Format.R8G8Uint: + case Format.R8G8Sint: + case Format.R8G8B8Unorm: + case Format.R8G8B8Snorm: + case Format.R8G8B8Uint: + case Format.R8G8B8Sint: + case Format.R8G8B8A8Unorm: + case Format.R8G8B8A8Snorm: + case Format.R8G8B8A8Uint: + case Format.R8G8B8A8Sint: + case Format.R8G8B8A8Srgb: + case Format.R4G4Unorm: + case Format.R8Uscaled: + case Format.R8Sscaled: + case Format.R8G8Uscaled: + case Format.R8G8Sscaled: + case Format.R8G8B8Uscaled: + case Format.R8G8B8Sscaled: + case Format.R8G8B8A8Uscaled: + case Format.R8G8B8A8Sscaled: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8A8Srgb: + return 1; + + case Format.R16Float: + case Format.R16Unorm: + case Format.R16Snorm: + case Format.R16Uint: + case Format.R16Sint: + case Format.R16G16Float: + case Format.R16G16Unorm: + case Format.R16G16Snorm: + case Format.R16G16Uint: + case Format.R16G16Sint: + case Format.R16G16B16Float: + case Format.R16G16B16Unorm: + case Format.R16G16B16Snorm: + case Format.R16G16B16Uint: + case Format.R16G16B16Sint: + case Format.R16G16B16A16Float: + case Format.R16G16B16A16Unorm: + case Format.R16G16B16A16Snorm: + case Format.R16G16B16A16Uint: + case Format.R16G16B16A16Sint: + case Format.R4G4B4A4Unorm: + case Format.R5G5B5X1Unorm: + case Format.R5G5B5A1Unorm: + case Format.R5G6B5Unorm: + case Format.R16Uscaled: + case Format.R16Sscaled: + case Format.R16G16Uscaled: + case Format.R16G16Sscaled: + case Format.R16G16B16Uscaled: + case Format.R16G16B16Sscaled: + case Format.R16G16B16A16Uscaled: + case Format.R16G16B16A16Sscaled: + case Format.B5G6R5Unorm: + case Format.B5G5R5A1Unorm: + case Format.A1B5G5R5Unorm: + return 2; + + case Format.R32Float: + case Format.R32Uint: + case Format.R32Sint: + case Format.R32G32Float: + case Format.R32G32Uint: + case Format.R32G32Sint: + case Format.R32G32B32Float: + case Format.R32G32B32Uint: + case Format.R32G32B32Sint: + case Format.R32G32B32A32Float: + case Format.R32G32B32A32Uint: + case Format.R32G32B32A32Sint: + case Format.R10G10B10A2Unorm: + case Format.R10G10B10A2Uint: + case Format.R11G11B10Float: + case Format.R9G9B9E5Float: + case Format.R32Uscaled: + case Format.R32Sscaled: + case Format.R32G32Uscaled: + case Format.R32G32Sscaled: + case Format.R32G32B32Uscaled: + case Format.R32G32B32Sscaled: + case Format.R32G32B32A32Uscaled: + case Format.R32G32B32A32Sscaled: + case Format.R10G10B10A2Snorm: + case Format.R10G10B10A2Sint: + case Format.R10G10B10A2Uscaled: + case Format.R10G10B10A2Sscaled: + return 4; + + case Format.S8Uint: + return 1; + case Format.D16Unorm: + return 2; + case Format.S8UintD24Unorm: + case Format.D32Float: + case Format.D24UnormS8Uint: + return 4; + case Format.D32FloatS8Uint: + return 8; + + case Format.Bc1RgbaUnorm: + case Format.Bc1RgbaSrgb: + return 8; + + case Format.Bc2Unorm: + case Format.Bc3Unorm: + case Format.Bc2Srgb: + case Format.Bc3Srgb: + case Format.Bc4Unorm: + case Format.Bc4Snorm: + case Format.Bc5Unorm: + case Format.Bc5Snorm: + case Format.Bc7Unorm: + case Format.Bc7Srgb: + case Format.Bc6HSfloat: + case Format.Bc6HUfloat: + return 16; + + case Format.Etc2RgbUnorm: + case Format.Etc2RgbPtaUnorm: + case Format.Etc2RgbSrgb: + case Format.Etc2RgbPtaSrgb: + return 8; + + case Format.Etc2RgbaUnorm: + case Format.Etc2RgbaSrgb: + return 16; + + case Format.Astc4x4Unorm: + case Format.Astc5x4Unorm: + case Format.Astc5x5Unorm: + case Format.Astc6x5Unorm: + case Format.Astc6x6Unorm: + case Format.Astc8x5Unorm: + case Format.Astc8x6Unorm: + case Format.Astc8x8Unorm: + case Format.Astc10x5Unorm: + case Format.Astc10x6Unorm: + case Format.Astc10x8Unorm: + case Format.Astc10x10Unorm: + case Format.Astc12x10Unorm: + case Format.Astc12x12Unorm: + case Format.Astc4x4Srgb: + case Format.Astc5x4Srgb: + case Format.Astc5x5Srgb: + case Format.Astc6x5Srgb: + case Format.Astc6x6Srgb: + case Format.Astc8x5Srgb: + case Format.Astc8x6Srgb: + case Format.Astc8x8Srgb: + case Format.Astc10x5Srgb: + case Format.Astc10x6Srgb: + case Format.Astc10x8Srgb: + case Format.Astc10x10Srgb: + case Format.Astc12x10Srgb: + case Format.Astc12x12Srgb: + return 16; + } + + return 1; + } + + /// <summary> + /// Checks if the texture format is valid to use as image format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture can be used as image, false otherwise</returns> + public static bool IsImageCompatible(this Format format) + { + switch (format) + { + case Format.R8Unorm: + case Format.R8Snorm: + case Format.R8Uint: + case Format.R8Sint: + case Format.R16Float: + case Format.R16Unorm: + case Format.R16Snorm: + case Format.R16Uint: + case Format.R16Sint: + case Format.R32Float: + case Format.R32Uint: + case Format.R32Sint: + case Format.R8G8Unorm: + case Format.R8G8Snorm: + case Format.R8G8Uint: + case Format.R8G8Sint: + case Format.R16G16Float: + case Format.R16G16Unorm: + case Format.R16G16Snorm: + case Format.R16G16Uint: + case Format.R16G16Sint: + case Format.R32G32Float: + case Format.R32G32Uint: + case Format.R32G32Sint: + case Format.R8G8B8A8Unorm: + case Format.R8G8B8A8Snorm: + case Format.R8G8B8A8Uint: + case Format.R8G8B8A8Sint: + case Format.R16G16B16A16Float: + case Format.R16G16B16A16Unorm: + case Format.R16G16B16A16Snorm: + case Format.R16G16B16A16Uint: + case Format.R16G16B16A16Sint: + case Format.R32G32B32A32Float: + case Format.R32G32B32A32Uint: + case Format.R32G32B32A32Sint: + case Format.R10G10B10A2Unorm: + case Format.R10G10B10A2Uint: + case Format.R11G11B10Float: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is valid to use as render target color format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture can be used as render target, false otherwise</returns> + public static bool IsRtColorCompatible(this Format format) + { + switch (format) + { + case Format.R32G32B32A32Float: + case Format.R32G32B32A32Sint: + case Format.R32G32B32A32Uint: + case Format.R16G16B16A16Unorm: + case Format.R16G16B16A16Snorm: + case Format.R16G16B16A16Sint: + case Format.R16G16B16A16Uint: + case Format.R16G16B16A16Float: + case Format.R32G32Float: + case Format.R32G32Sint: + case Format.R32G32Uint: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8A8Srgb: + case Format.R10G10B10A2Unorm: + case Format.R10G10B10A2Uint: + case Format.R8G8B8A8Unorm: + case Format.R8G8B8A8Srgb: + case Format.R8G8B8A8Snorm: + case Format.R8G8B8A8Sint: + case Format.R8G8B8A8Uint: + case Format.R16G16Unorm: + case Format.R16G16Snorm: + case Format.R16G16Sint: + case Format.R16G16Uint: + case Format.R16G16Float: + case Format.R11G11B10Float: + case Format.R32Sint: + case Format.R32Uint: + case Format.R32Float: + case Format.B5G6R5Unorm: + case Format.B5G5R5A1Unorm: + case Format.R8G8Unorm: + case Format.R8G8Snorm: + case Format.R8G8Sint: + case Format.R8G8Uint: + case Format.R16Unorm: + case Format.R16Snorm: + case Format.R16Sint: + case Format.R16Uint: + case Format.R16Float: + case Format.R8Unorm: + case Format.R8Snorm: + case Format.R8Sint: + case Format.R8Uint: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is 16 bit packed. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is 16 bit packed, false otherwise</returns> + public static bool Is16BitPacked(this Format format) + { + switch (format) + { + case Format.B5G6R5Unorm: + case Format.B5G5R5A1Unorm: + case Format.R5G5B5X1Unorm: + case Format.R5G5B5A1Unorm: + case Format.R5G6B5Unorm: + case Format.R4G4B4A4Unorm: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is an ASTC format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is an ASTC format, false otherwise</returns> + public static bool IsAstc(this Format format) + { + return format.IsAstcUnorm() || format.IsAstcSrgb(); + } + + /// <summary> + /// Checks if the texture format is an ASTC Unorm format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is an ASTC Unorm format, false otherwise</returns> + public static bool IsAstcUnorm(this Format format) + { + switch (format) + { + case Format.Astc4x4Unorm: + case Format.Astc5x4Unorm: + case Format.Astc5x5Unorm: + case Format.Astc6x5Unorm: + case Format.Astc6x6Unorm: + case Format.Astc8x5Unorm: + case Format.Astc8x6Unorm: + case Format.Astc8x8Unorm: + case Format.Astc10x5Unorm: + case Format.Astc10x6Unorm: + case Format.Astc10x8Unorm: + case Format.Astc10x10Unorm: + case Format.Astc12x10Unorm: + case Format.Astc12x12Unorm: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is an ASTC SRGB format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is an ASTC SRGB format, false otherwise</returns> + public static bool IsAstcSrgb(this Format format) + { + switch (format) + { + case Format.Astc4x4Srgb: + case Format.Astc5x4Srgb: + case Format.Astc5x5Srgb: + case Format.Astc6x5Srgb: + case Format.Astc6x6Srgb: + case Format.Astc8x5Srgb: + case Format.Astc8x6Srgb: + case Format.Astc8x8Srgb: + case Format.Astc10x5Srgb: + case Format.Astc10x6Srgb: + case Format.Astc10x8Srgb: + case Format.Astc10x10Srgb: + case Format.Astc12x10Srgb: + case Format.Astc12x12Srgb: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is an ETC2 format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is an ETC2 format, false otherwise</returns> + public static bool IsEtc2(this Format format) + { + switch (format) + { + case Format.Etc2RgbaSrgb: + case Format.Etc2RgbaUnorm: + case Format.Etc2RgbPtaSrgb: + case Format.Etc2RgbPtaUnorm: + case Format.Etc2RgbSrgb: + case Format.Etc2RgbUnorm: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is a BGR format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is a BGR format, false otherwise</returns> + public static bool IsBgr(this Format format) + { + switch (format) + { + case Format.B5G6R5Unorm: + case Format.B5G5R5A1Unorm: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8A8Srgb: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is a depth, stencil or depth-stencil format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the format is a depth, stencil or depth-stencil format, false otherwise</returns> + public static bool IsDepthOrStencil(this Format format) + { + switch (format) + { + case Format.D16Unorm: + case Format.D24UnormS8Uint: + case Format.S8UintD24Unorm: + case Format.D32Float: + case Format.D32FloatS8Uint: + case Format.S8Uint: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is an unsigned integer color format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is an unsigned integer color format, false otherwise</returns> + public static bool IsUint(this Format format) + { + switch (format) + { + case Format.R8Uint: + case Format.R16Uint: + case Format.R32Uint: + case Format.R8G8Uint: + case Format.R16G16Uint: + case Format.R32G32Uint: + case Format.R8G8B8Uint: + case Format.R16G16B16Uint: + case Format.R32G32B32Uint: + case Format.R8G8B8A8Uint: + case Format.R16G16B16A16Uint: + case Format.R32G32B32A32Uint: + case Format.R10G10B10A2Uint: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is a signed integer color format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is a signed integer color format, false otherwise</returns> + public static bool IsSint(this Format format) + { + switch (format) + { + case Format.R8Sint: + case Format.R16Sint: + case Format.R32Sint: + case Format.R8G8Sint: + case Format.R16G16Sint: + case Format.R32G32Sint: + case Format.R8G8B8Sint: + case Format.R16G16B16Sint: + case Format.R32G32B32Sint: + case Format.R8G8B8A8Sint: + case Format.R16G16B16A16Sint: + case Format.R32G32B32A32Sint: + case Format.R10G10B10A2Sint: + return true; + } + + return false; + } + + /// <summary> + /// Checks if the texture format is an integer color format. + /// </summary> + /// <param name="format">Texture format</param> + /// <returns>True if the texture format is an integer color format, false otherwise</returns> + public static bool IsInteger(this Format format) + { + return format.IsUint() || format.IsSint(); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/FrontFace.cs b/src/Ryujinx.Graphics.GAL/FrontFace.cs new file mode 100644 index 00000000..aa6bfdc5 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/FrontFace.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum FrontFace + { + Clockwise = 0x900, + CounterClockwise = 0x901 + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/HardwareInfo.cs b/src/Ryujinx.Graphics.GAL/HardwareInfo.cs new file mode 100644 index 00000000..4dd6849b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/HardwareInfo.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct HardwareInfo + { + public string GpuVendor { get; } + public string GpuModel { get; } + + public HardwareInfo(string gpuVendor, string gpuModel) + { + GpuVendor = gpuVendor; + GpuModel = gpuModel; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/ICounterEvent.cs b/src/Ryujinx.Graphics.GAL/ICounterEvent.cs new file mode 100644 index 00000000..13b15ae4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ICounterEvent.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface ICounterEvent : IDisposable + { + bool Invalid { get; set; } + + bool ReserveForHostAccess(); + + void Flush(); + } +} diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs new file mode 100644 index 00000000..0a362081 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -0,0 +1,113 @@ +using Ryujinx.Graphics.Shader; +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IPipeline + { + void Barrier(); + + void BeginTransformFeedback(PrimitiveTopology topology); + + void ClearBuffer(BufferHandle destination, int offset, int size, uint value); + + void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color); + + void ClearRenderTargetDepthStencil( + int layer, + int layerCount, + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask); + + void CommandBufferBarrier(); + + void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size); + + void DispatchCompute(int groupsX, int groupsY, int groupsZ); + + void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance); + void DrawIndexed( + int indexCount, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance); + void DrawIndexedIndirect(BufferRange indirectBuffer); + void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride); + void DrawIndirect(BufferRange indirectBuffer); + void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride); + void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion); + + void EndTransformFeedback(); + + void SetAlphaTest(bool enable, float reference, CompareOp op); + + void SetBlendState(AdvancedBlendDescriptor blend); + void SetBlendState(int index, BlendDescriptor blend); + + void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); + void SetDepthClamp(bool clamp); + void SetDepthMode(DepthMode mode); + void SetDepthTest(DepthTestDescriptor depthTest); + + void SetFaceCulling(bool enable, Face face); + + void SetFrontFace(FrontFace frontFace); + + void SetIndexBuffer(BufferRange buffer, IndexType type); + + void SetImage(int binding, ITexture texture, Format imageFormat); + + void SetLineParameters(float width, bool smooth); + + void SetLogicOpState(bool enable, LogicalOp op); + + void SetMultisampleState(MultisampleDescriptor multisample); + + void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel); + void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin); + + void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode); + + void SetPrimitiveRestart(bool enable, int index); + + void SetPrimitiveTopology(PrimitiveTopology topology); + + void SetProgram(IProgram program); + + void SetRasterizerDiscard(bool discard); + + void SetRenderTargetScale(float scale); + void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask); + void SetRenderTargets(ITexture[] colors, ITexture depthStencil); + + void SetScissors(ReadOnlySpan<Rectangle<int>> regions); + + void SetStencilTest(StencilTestDescriptor stencilTest); + + void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers); + + void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler); + + void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers); + void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers); + + void SetUserClipDistance(int index, bool enableClip); + + void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs); + void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers); + + void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform); + + void TextureBarrier(); + void TextureBarrierTiled(); + + bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual); + bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual); + void EndHostConditionalRendering(); + + void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount); + } +} diff --git a/src/Ryujinx.Graphics.GAL/IProgram.cs b/src/Ryujinx.Graphics.GAL/IProgram.cs new file mode 100644 index 00000000..272a2f7d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/IProgram.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IProgram : IDisposable + { + ProgramLinkStatus CheckProgramLink(bool blocking); + + byte[] GetBinary(); + } +} diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs new file mode 100644 index 00000000..2af7b5db --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -0,0 +1,65 @@ +using Ryujinx.Common.Configuration; +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IRenderer : IDisposable + { + event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; + + bool PreferThreading { get; } + + IPipeline Pipeline { get; } + + IWindow Window { get; } + + void BackgroundContextAction(Action action, bool alwaysBackground = false); + + BufferHandle CreateBuffer(int size, BufferHandle storageHint); + + BufferHandle CreateBuffer(int size) + { + return CreateBuffer(size, BufferHandle.Null); + } + + IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info); + + ISampler CreateSampler(SamplerCreateInfo info); + ITexture CreateTexture(TextureCreateInfo info, float scale); + + void CreateSync(ulong id, bool strict); + + void DeleteBuffer(BufferHandle buffer); + + PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size); + + Capabilities GetCapabilities(); + ulong GetCurrentSync(); + HardwareInfo GetHardwareInfo(); + + IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info); + + void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data); + + void UpdateCounters(); + + void PreFrame(); + + ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved); + + void ResetCounter(CounterType type); + + void RunLoop(Action gpuLoop) + { + gpuLoop(); + } + + void WaitSync(ulong id); + + void Initialize(GraphicsDebugLevel logLevel); + + void SetInterruptAction(Action<Action> interruptAction); + + void Screenshot(); + } +} diff --git a/src/Ryujinx.Graphics.GAL/ISampler.cs b/src/Ryujinx.Graphics.GAL/ISampler.cs new file mode 100644 index 00000000..3aefc6a7 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ISampler.cs @@ -0,0 +1,6 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface ISampler : IDisposable { } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs new file mode 100644 index 00000000..792c863c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -0,0 +1,27 @@ +using Ryujinx.Common.Memory; +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface ITexture + { + int Width { get; } + int Height { get; } + float ScaleFactor { get; } + + void CopyTo(ITexture destination, int firstLayer, int firstLevel); + void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel); + void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter); + + ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel); + + PinnedSpan<byte> GetData(); + PinnedSpan<byte> GetData(int layer, int level); + + void SetData(SpanOrArray<byte> data); + void SetData(SpanOrArray<byte> data, int layer, int level); + void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region); + void SetStorage(BufferRange buffer); + void Release(); + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/IWindow.cs b/src/Ryujinx.Graphics.GAL/IWindow.cs new file mode 100644 index 00000000..1221d685 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/IWindow.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IWindow + { + void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); + + void SetSize(int width, int height); + + void ChangeVSyncMode(bool vsyncEnabled); + + void SetAntiAliasing(AntiAliasing antialiasing); + void SetScalingFilter(ScalingFilter type); + void SetScalingFilterLevel(float level); + } +} diff --git a/src/Ryujinx.Graphics.GAL/ImageCrop.cs b/src/Ryujinx.Graphics.GAL/ImageCrop.cs new file mode 100644 index 00000000..e8220974 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ImageCrop.cs @@ -0,0 +1,37 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct ImageCrop + { + public int Left { get; } + public int Right { get; } + public int Top { get; } + public int Bottom { get; } + public bool FlipX { get; } + public bool FlipY { get; } + public bool IsStretched { get; } + public float AspectRatioX { get; } + public float AspectRatioY { get; } + + public ImageCrop( + int left, + int right, + int top, + int bottom, + bool flipX, + bool flipY, + bool isStretched, + float aspectRatioX, + float aspectRatioY) + { + Left = left; + Right = right; + Top = top; + Bottom = bottom; + FlipX = flipX; + FlipY = flipY; + IsStretched = isStretched; + AspectRatioX = aspectRatioX; + AspectRatioY = aspectRatioY; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/IndexType.cs b/src/Ryujinx.Graphics.GAL/IndexType.cs new file mode 100644 index 00000000..4abf28d9 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/IndexType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum IndexType + { + UByte, + UShort, + UInt + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/LogicalOp.cs b/src/Ryujinx.Graphics.GAL/LogicalOp.cs new file mode 100644 index 00000000..848215d0 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/LogicalOp.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum LogicalOp + { + Clear = 0x1500, + And = 0x1501, + AndReverse = 0x1502, + Copy = 0x1503, + AndInverted = 0x1504, + Noop = 0x1505, + Xor = 0x1506, + Or = 0x1507, + Nor = 0x1508, + Equiv = 0x1509, + Invert = 0x150A, + OrReverse = 0x150B, + CopyInverted = 0x150C, + OrInverted = 0x150D, + Nand = 0x150E, + Set = 0x150F + } +} diff --git a/src/Ryujinx.Graphics.GAL/MagFilter.cs b/src/Ryujinx.Graphics.GAL/MagFilter.cs new file mode 100644 index 00000000..f20d095e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/MagFilter.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum MagFilter + { + Nearest = 1, + Linear + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/MinFilter.cs b/src/Ryujinx.Graphics.GAL/MinFilter.cs new file mode 100644 index 00000000..b7a0740b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/MinFilter.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum MinFilter + { + Nearest = 1, + Linear, + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs b/src/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs new file mode 100644 index 00000000..a6fb65aa --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct MultisampleDescriptor + { + public bool AlphaToCoverageEnable { get; } + public bool AlphaToCoverageDitherEnable { get; } + public bool AlphaToOneEnable { get; } + + public MultisampleDescriptor( + bool alphaToCoverageEnable, + bool alphaToCoverageDitherEnable, + bool alphaToOneEnable) + { + AlphaToCoverageEnable = alphaToCoverageEnable; + AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable; + AlphaToOneEnable = alphaToOneEnable; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs new file mode 100644 index 00000000..24b0af2d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + /// <summary> + /// Buffer handles given to the client are not the same as those provided by the backend, + /// as their handle is created at a later point on the queue. + /// The handle returned is a unique identifier that will map to the real buffer when it is available. + /// Note that any uses within the queue should be safe, but outside you must use MapBufferBlocking. + /// </summary> + class BufferMap + { + private ulong _bufferHandle = 0; + + private Dictionary<BufferHandle, BufferHandle> _bufferMap = new Dictionary<BufferHandle, BufferHandle>(); + private HashSet<BufferHandle> _inFlight = new HashSet<BufferHandle>(); + private AutoResetEvent _inFlightChanged = new AutoResetEvent(false); + + internal BufferHandle CreateBufferHandle() + { + ulong handle64 = Interlocked.Increment(ref _bufferHandle); + + BufferHandle threadedHandle = Unsafe.As<ulong, BufferHandle>(ref handle64); + + lock (_inFlight) + { + _inFlight.Add(threadedHandle); + } + + return threadedHandle; + } + + internal void AssignBuffer(BufferHandle threadedHandle, BufferHandle realHandle) + { + lock (_bufferMap) + { + _bufferMap[threadedHandle] = realHandle; + } + + lock (_inFlight) + { + _inFlight.Remove(threadedHandle); + } + + _inFlightChanged.Set(); + } + + internal void UnassignBuffer(BufferHandle threadedHandle) + { + lock (_bufferMap) + { + _bufferMap.Remove(threadedHandle); + } + } + + internal BufferHandle MapBuffer(BufferHandle handle) + { + // Maps a threaded buffer to a backend one. + // Threaded buffers are returned on creation as the buffer + // isn't actually created until the queue runs the command. + + BufferHandle result; + + lock (_bufferMap) + { + if (!_bufferMap.TryGetValue(handle, out result)) + { + result = BufferHandle.Null; + } + + return result; + } + } + + internal BufferHandle MapBufferBlocking(BufferHandle handle) + { + // Blocks until the handle is available. + + BufferHandle result; + + lock (_bufferMap) + { + if (_bufferMap.TryGetValue(handle, out result)) + { + return result; + } + } + + bool signal = false; + + while (true) + { + lock (_inFlight) + { + if (!_inFlight.Contains(handle)) + { + break; + } + } + + _inFlightChanged.WaitOne(); + signal = true; + } + + if (signal) + { + // Signal other threads which might still be waiting. + _inFlightChanged.Set(); + } + + return MapBuffer(handle); + } + + internal BufferRange MapBufferRange(BufferRange range) + { + return new BufferRange(MapBuffer(range.Handle), range.Offset, range.Size); + } + + internal Span<BufferRange> MapBufferRanges(Span<BufferRange> ranges) + { + // Rewrite the buffer ranges to point to the mapped handles. + + lock (_bufferMap) + { + for (int i = 0; i < ranges.Length; i++) + { + ref BufferRange range = ref ranges[i]; + BufferHandle result; + + if (!_bufferMap.TryGetValue(range.Handle, out result)) + { + result = BufferHandle.Null; + } + + range = new BufferRange(result, range.Offset, range.Size); + } + } + + return ranges; + } + + internal Span<BufferAssignment> MapBufferRanges(Span<BufferAssignment> ranges) + { + // Rewrite the buffer ranges to point to the mapped handles. + + lock (_bufferMap) + { + for (int i = 0; i < ranges.Length; i++) + { + ref BufferAssignment assignment = ref ranges[i]; + BufferRange range = assignment.Range; + BufferHandle result; + + if (!_bufferMap.TryGetValue(range.Handle, out result)) + { + result = BufferHandle.Null; + } + + assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size)); + } + } + + return ranges; + } + + internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges) + { + // Rewrite the buffer ranges to point to the mapped handles. + + lock (_bufferMap) + { + for (int i = 0; i < ranges.Length; i++) + { + BufferRange range = ranges[i].Buffer; + BufferHandle result; + + if (!_bufferMap.TryGetValue(range.Handle, out result)) + { + result = BufferHandle.Null; + } + + range = new BufferRange(result, range.Offset, range.Size); + + ranges[i] = new VertexBufferDescriptor(range, ranges[i].Stride, ranges[i].Divisor); + } + } + + return ranges; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs new file mode 100644 index 00000000..063b7edf --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -0,0 +1,149 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer; +using Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Program; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + static class CommandHelper + { + private delegate void CommandDelegate(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer); + + private static int _totalCommands = (int)Enum.GetValues<CommandType>().Max() + 1; + private static CommandDelegate[] _lookup = new CommandDelegate[_totalCommands]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ref T GetCommand<T>(Span<byte> memory) + { + return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(memory)); + } + + public static int GetMaxCommandSize() + { + return InitLookup() + 1; // 1 byte reserved for command size. + } + + private static int InitLookup() + { + int maxCommandSize = 0; + + void Register<T>(CommandType commandType) where T : unmanaged, IGALCommand, IGALCommand<T> + { + maxCommandSize = Math.Max(maxCommandSize, Unsafe.SizeOf<T>()); + _lookup[(int)commandType] = (memory, threaded, renderer) => T.Run(ref GetCommand<T>(memory), threaded, renderer); + } + + Register<ActionCommand>(CommandType.Action); + Register<CreateBufferCommand>(CommandType.CreateBuffer); + Register<CreateProgramCommand>(CommandType.CreateProgram); + Register<CreateSamplerCommand>(CommandType.CreateSampler); + Register<CreateSyncCommand>(CommandType.CreateSync); + Register<CreateTextureCommand>(CommandType.CreateTexture); + Register<GetCapabilitiesCommand>(CommandType.GetCapabilities); + Register<PreFrameCommand>(CommandType.PreFrame); + Register<ReportCounterCommand>(CommandType.ReportCounter); + Register<ResetCounterCommand>(CommandType.ResetCounter); + Register<UpdateCountersCommand>(CommandType.UpdateCounters); + + Register<BufferDisposeCommand>(CommandType.BufferDispose); + Register<BufferGetDataCommand>(CommandType.BufferGetData); + Register<BufferSetDataCommand>(CommandType.BufferSetData); + + Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose); + Register<CounterEventFlushCommand>(CommandType.CounterEventFlush); + + Register<ProgramDisposeCommand>(CommandType.ProgramDispose); + Register<ProgramGetBinaryCommand>(CommandType.ProgramGetBinary); + Register<ProgramCheckLinkCommand>(CommandType.ProgramCheckLink); + + Register<SamplerDisposeCommand>(CommandType.SamplerDispose); + + Register<TextureCopyToCommand>(CommandType.TextureCopyTo); + Register<TextureCopyToScaledCommand>(CommandType.TextureCopyToScaled); + Register<TextureCopyToSliceCommand>(CommandType.TextureCopyToSlice); + Register<TextureCreateViewCommand>(CommandType.TextureCreateView); + Register<TextureGetDataCommand>(CommandType.TextureGetData); + Register<TextureGetDataSliceCommand>(CommandType.TextureGetDataSlice); + Register<TextureReleaseCommand>(CommandType.TextureRelease); + Register<TextureSetDataCommand>(CommandType.TextureSetData); + Register<TextureSetDataSliceCommand>(CommandType.TextureSetDataSlice); + Register<TextureSetDataSliceRegionCommand>(CommandType.TextureSetDataSliceRegion); + Register<TextureSetStorageCommand>(CommandType.TextureSetStorage); + + Register<WindowPresentCommand>(CommandType.WindowPresent); + + Register<BarrierCommand>(CommandType.Barrier); + Register<BeginTransformFeedbackCommand>(CommandType.BeginTransformFeedback); + Register<ClearBufferCommand>(CommandType.ClearBuffer); + Register<ClearRenderTargetColorCommand>(CommandType.ClearRenderTargetColor); + Register<ClearRenderTargetDepthStencilCommand>(CommandType.ClearRenderTargetDepthStencil); + Register<CommandBufferBarrierCommand>(CommandType.CommandBufferBarrier); + Register<CopyBufferCommand>(CommandType.CopyBuffer); + Register<DispatchComputeCommand>(CommandType.DispatchCompute); + Register<DrawCommand>(CommandType.Draw); + Register<DrawIndexedCommand>(CommandType.DrawIndexed); + Register<DrawIndexedIndirectCommand>(CommandType.DrawIndexedIndirect); + Register<DrawIndexedIndirectCountCommand>(CommandType.DrawIndexedIndirectCount); + Register<DrawIndirectCommand>(CommandType.DrawIndirect); + Register<DrawIndirectCountCommand>(CommandType.DrawIndirectCount); + Register<DrawTextureCommand>(CommandType.DrawTexture); + Register<EndHostConditionalRenderingCommand>(CommandType.EndHostConditionalRendering); + Register<EndTransformFeedbackCommand>(CommandType.EndTransformFeedback); + Register<SetAlphaTestCommand>(CommandType.SetAlphaTest); + Register<SetBlendStateAdvancedCommand>(CommandType.SetBlendStateAdvanced); + Register<SetBlendStateCommand>(CommandType.SetBlendState); + Register<SetDepthBiasCommand>(CommandType.SetDepthBias); + Register<SetDepthClampCommand>(CommandType.SetDepthClamp); + Register<SetDepthModeCommand>(CommandType.SetDepthMode); + Register<SetDepthTestCommand>(CommandType.SetDepthTest); + Register<SetFaceCullingCommand>(CommandType.SetFaceCulling); + Register<SetFrontFaceCommand>(CommandType.SetFrontFace); + Register<SetStorageBuffersCommand>(CommandType.SetStorageBuffers); + Register<SetTransformFeedbackBuffersCommand>(CommandType.SetTransformFeedbackBuffers); + Register<SetUniformBuffersCommand>(CommandType.SetUniformBuffers); + Register<SetImageCommand>(CommandType.SetImage); + Register<SetIndexBufferCommand>(CommandType.SetIndexBuffer); + Register<SetLineParametersCommand>(CommandType.SetLineParameters); + Register<SetLogicOpStateCommand>(CommandType.SetLogicOpState); + Register<SetMultisampleStateCommand>(CommandType.SetMultisampleState); + Register<SetPatchParametersCommand>(CommandType.SetPatchParameters); + Register<SetPointParametersCommand>(CommandType.SetPointParameters); + Register<SetPolygonModeCommand>(CommandType.SetPolygonMode); + Register<SetPrimitiveRestartCommand>(CommandType.SetPrimitiveRestart); + Register<SetPrimitiveTopologyCommand>(CommandType.SetPrimitiveTopology); + Register<SetProgramCommand>(CommandType.SetProgram); + Register<SetRasterizerDiscardCommand>(CommandType.SetRasterizerDiscard); + Register<SetRenderTargetColorMasksCommand>(CommandType.SetRenderTargetColorMasks); + Register<SetRenderTargetScaleCommand>(CommandType.SetRenderTargetScale); + Register<SetRenderTargetsCommand>(CommandType.SetRenderTargets); + Register<SetScissorsCommand>(CommandType.SetScissor); + Register<SetStencilTestCommand>(CommandType.SetStencilTest); + Register<SetTextureAndSamplerCommand>(CommandType.SetTextureAndSampler); + Register<SetUserClipDistanceCommand>(CommandType.SetUserClipDistance); + Register<SetVertexAttribsCommand>(CommandType.SetVertexAttribs); + Register<SetVertexBuffersCommand>(CommandType.SetVertexBuffers); + Register<SetViewportsCommand>(CommandType.SetViewports); + Register<TextureBarrierCommand>(CommandType.TextureBarrier); + Register<TextureBarrierTiledCommand>(CommandType.TextureBarrierTiled); + Register<TryHostConditionalRenderingCommand>(CommandType.TryHostConditionalRendering); + Register<TryHostConditionalRenderingFlushCommand>(CommandType.TryHostConditionalRenderingFlush); + Register<UpdateRenderScaleCommand>(CommandType.UpdateRenderScale); + + return maxCommandSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RunCommand(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) + { + _lookup[memory[memory.Length - 1]](memory, threaded, renderer); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs new file mode 100644 index 00000000..61e729b4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -0,0 +1,102 @@ +namespace Ryujinx.Graphics.GAL.Multithreading +{ + enum CommandType : byte + { + Action, + CreateBuffer, + CreateProgram, + CreateSampler, + CreateSync, + CreateTexture, + GetCapabilities, + Unused, + PreFrame, + ReportCounter, + ResetCounter, + UpdateCounters, + + BufferDispose, + BufferGetData, + BufferSetData, + + CounterEventDispose, + CounterEventFlush, + + ProgramDispose, + ProgramGetBinary, + ProgramCheckLink, + + SamplerDispose, + + TextureCopyTo, + TextureCopyToScaled, + TextureCopyToSlice, + TextureCreateView, + TextureGetData, + TextureGetDataSlice, + TextureRelease, + TextureSetData, + TextureSetDataSlice, + TextureSetDataSliceRegion, + TextureSetStorage, + + WindowPresent, + + Barrier, + BeginTransformFeedback, + ClearBuffer, + ClearRenderTargetColor, + ClearRenderTargetDepthStencil, + CommandBufferBarrier, + CopyBuffer, + DispatchCompute, + Draw, + DrawIndexed, + DrawIndexedIndirect, + DrawIndexedIndirectCount, + DrawIndirect, + DrawIndirectCount, + DrawTexture, + EndHostConditionalRendering, + EndTransformFeedback, + SetAlphaTest, + SetBlendStateAdvanced, + SetBlendState, + SetDepthBias, + SetDepthClamp, + SetDepthMode, + SetDepthTest, + SetFaceCulling, + SetFrontFace, + SetStorageBuffers, + SetTransformFeedbackBuffers, + SetUniformBuffers, + SetImage, + SetIndexBuffer, + SetLineParameters, + SetLogicOpState, + SetMultisampleState, + SetPatchParameters, + SetPointParameters, + SetPolygonMode, + SetPrimitiveRestart, + SetPrimitiveTopology, + SetProgram, + SetRasterizerDiscard, + SetRenderTargetColorMasks, + SetRenderTargetScale, + SetRenderTargets, + SetScissor, + SetStencilTest, + SetTextureAndSampler, + SetUserClipDistance, + SetVertexAttribs, + SetVertexBuffers, + SetViewports, + TextureBarrier, + TextureBarrierTiled, + TryHostConditionalRendering, + TryHostConditionalRenderingFlush, + UpdateRenderScale + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs new file mode 100644 index 00000000..4f8e1b08 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BarrierCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct BarrierCommand : IGALCommand, IGALCommand<BarrierCommand> + { + public CommandType CommandType => CommandType.Barrier; + + public static void Run(ref BarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.Barrier(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs new file mode 100644 index 00000000..50032635 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/BeginTransformFeedbackCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct BeginTransformFeedbackCommand : IGALCommand, IGALCommand<BeginTransformFeedbackCommand> + { + public CommandType CommandType => CommandType.BeginTransformFeedback; + private PrimitiveTopology _topology; + + public void Set(PrimitiveTopology topology) + { + _topology = topology; + } + + public static void Run(ref BeginTransformFeedbackCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.BeginTransformFeedback(command._topology); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs new file mode 100644 index 00000000..5be42fff --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferDisposeCommand.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer +{ + struct BufferDisposeCommand : IGALCommand, IGALCommand<BufferDisposeCommand> + { + public CommandType CommandType => CommandType.BufferDispose; + private BufferHandle _buffer; + + public void Set(BufferHandle buffer) + { + _buffer = buffer; + } + + public static void Run(ref BufferDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.DeleteBuffer(threaded.Buffers.MapBuffer(command._buffer)); + threaded.Buffers.UnassignBuffer(command._buffer); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs new file mode 100644 index 00000000..031c6153 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferGetDataCommand.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer +{ + struct BufferGetDataCommand : IGALCommand, IGALCommand<BufferGetDataCommand> + { + public CommandType CommandType => CommandType.BufferGetData; + private BufferHandle _buffer; + private int _offset; + private int _size; + private TableRef<ResultBox<PinnedSpan<byte>>> _result; + + public void Set(BufferHandle buffer, int offset, int size, TableRef<ResultBox<PinnedSpan<byte>>> result) + { + _buffer = buffer; + _offset = offset; + _size = size; + _result = result; + } + + public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + PinnedSpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs new file mode 100644 index 00000000..dcb8c2f2 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Buffer/BufferSetDataCommand.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer +{ + struct BufferSetDataCommand : IGALCommand, IGALCommand<BufferSetDataCommand> + { + public CommandType CommandType => CommandType.BufferSetData; + private BufferHandle _buffer; + private int _offset; + private SpanRef<byte> _data; + + public void Set(BufferHandle buffer, int offset, SpanRef<byte> data) + { + _buffer = buffer; + _offset = offset; + _data = data; + } + + public static void Run(ref BufferSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<byte> data = command._data.Get(threaded); + renderer.SetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, data); + command._data.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs new file mode 100644 index 00000000..1d70460a --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearBufferCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct ClearBufferCommand : IGALCommand, IGALCommand<ClearBufferCommand> + { + public CommandType CommandType => CommandType.ClearBuffer; + private BufferHandle _destination; + private int _offset; + private int _size; + private uint _value; + + public void Set(BufferHandle destination, int offset, int size, uint value) + { + _destination = destination; + _offset = offset; + _size = size; + _value = value; + } + + public static void Run(ref ClearBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.ClearBuffer(threaded.Buffers.MapBuffer(command._destination), command._offset, command._size, command._value); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs new file mode 100644 index 00000000..f8c2bdfe --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -0,0 +1,26 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct ClearRenderTargetColorCommand : IGALCommand, IGALCommand<ClearRenderTargetColorCommand> + { + public CommandType CommandType => CommandType.ClearRenderTargetColor; + private int _index; + private int _layer; + private int _layerCount; + private uint _componentMask; + private ColorF _color; + + public void Set(int index, int layer, int layerCount, uint componentMask, ColorF color) + { + _index = index; + _layer = layer; + _layerCount = layerCount; + _componentMask = componentMask; + _color = color; + } + + public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._layerCount, command._componentMask, command._color); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs new file mode 100644 index 00000000..ca86673e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -0,0 +1,28 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct ClearRenderTargetDepthStencilCommand : IGALCommand, IGALCommand<ClearRenderTargetDepthStencilCommand> + { + public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; + private int _layer; + private int _layerCount; + private float _depthValue; + private bool _depthMask; + private int _stencilValue; + private int _stencilMask; + + public void Set(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) + { + _layer = layer; + _layerCount = layerCount; + _depthValue = depthValue; + _depthMask = depthMask; + _stencilValue = stencilValue; + _stencilMask = stencilMask; + } + + public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._layerCount, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs new file mode 100644 index 00000000..ad3ab0f8 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CommandBufferBarrierCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct CommandBufferBarrierCommand : IGALCommand, IGALCommand<CommandBufferBarrierCommand> + { + public CommandType CommandType => CommandType.CommandBufferBarrier; + + public static void Run(ref CommandBufferBarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.CommandBufferBarrier(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs new file mode 100644 index 00000000..43111bce --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CopyBufferCommand.cs @@ -0,0 +1,26 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct CopyBufferCommand : IGALCommand, IGALCommand<CopyBufferCommand> + { + public CommandType CommandType => CommandType.CopyBuffer; + private BufferHandle _source; + private BufferHandle _destination; + private int _srcOffset; + private int _dstOffset; + private int _size; + + public void Set(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + { + _source = source; + _destination = destination; + _srcOffset = srcOffset; + _dstOffset = dstOffset; + _size = size; + } + + public static void Run(ref CopyBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.CopyBuffer(threaded.Buffers.MapBuffer(command._source), threaded.Buffers.MapBuffer(command._destination), command._srcOffset, command._dstOffset, command._size); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs new file mode 100644 index 00000000..e5250212 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent +{ + struct CounterEventDisposeCommand : IGALCommand, IGALCommand<CounterEventDisposeCommand> + { + public CommandType CommandType => CommandType.CounterEventDispose; + private TableRef<ThreadedCounterEvent> _event; + + public void Set(TableRef<ThreadedCounterEvent> evt) + { + _event = evt; + } + + public static void Run(ref CounterEventDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._event.Get(threaded).Base.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs new file mode 100644 index 00000000..608cf8f9 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/CounterEvent/CounterEventFlushCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent +{ + struct CounterEventFlushCommand : IGALCommand, IGALCommand<CounterEventFlushCommand> + { + public CommandType CommandType => CommandType.CounterEventFlush; + private TableRef<ThreadedCounterEvent> _event; + + public void Set(TableRef<ThreadedCounterEvent> evt) + { + _event = evt; + } + + public static void Run(ref CounterEventFlushCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._event.Get(threaded).Base.Flush(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs new file mode 100644 index 00000000..29568837 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DispatchComputeCommand : IGALCommand, IGALCommand<DispatchComputeCommand> + { + public CommandType CommandType => CommandType.DispatchCompute; + private int _groupsX; + private int _groupsY; + private int _groupsZ; + + public void Set(int groupsX, int groupsY, int groupsZ) + { + _groupsX = groupsX; + _groupsY = groupsY; + _groupsZ = groupsZ; + } + + public static void Run(ref DispatchComputeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs new file mode 100644 index 00000000..804eaa49 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawCommand.cs @@ -0,0 +1,26 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndexedCommand : IGALCommand, IGALCommand<DrawIndexedCommand> + { + public CommandType CommandType => CommandType.DrawIndexed; + private int _indexCount; + private int _instanceCount; + private int _firstIndex; + private int _firstVertex; + private int _firstInstance; + + public void Set(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + { + _indexCount = indexCount; + _instanceCount = instanceCount; + _firstIndex = firstIndex; + _firstVertex = firstVertex; + _firstInstance = firstInstance; + } + + public static void Run(ref DrawIndexedCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndexed(command._indexCount, command._instanceCount, command._firstIndex, command._firstVertex, command._firstInstance); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs new file mode 100644 index 00000000..1b28afcd --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawCommand : IGALCommand, IGALCommand<DrawCommand> + { + public CommandType CommandType => CommandType.Draw; + private int _vertexCount; + private int _instanceCount; + private int _firstVertex; + private int _firstInstance; + + public void Set(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + _vertexCount = vertexCount; + _instanceCount = instanceCount; + _firstVertex = firstVertex; + _firstInstance = firstInstance; + } + + public static void Run(ref DrawCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.Draw(command._vertexCount, command._instanceCount, command._firstVertex, command._firstInstance); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs new file mode 100644 index 00000000..521b2f0c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndexedIndirectCommand : IGALCommand, IGALCommand<DrawIndexedIndirectCommand> + { + public CommandType CommandType => CommandType.DrawIndexedIndirect; + private BufferRange _indirectBuffer; + + public void Set(BufferRange indirectBuffer) + { + _indirectBuffer = indirectBuffer; + } + + public static void Run(ref DrawIndexedIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndexedIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs new file mode 100644 index 00000000..6bdf376d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndexedIndirectCountCommand : IGALCommand, IGALCommand<DrawIndexedIndirectCountCommand> + { + public CommandType CommandType => CommandType.DrawIndexedIndirectCount; + private BufferRange _indirectBuffer; + private BufferRange _parameterBuffer; + private int _maxDrawCount; + private int _stride; + + public void Set(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _indirectBuffer = indirectBuffer; + _parameterBuffer = parameterBuffer; + _maxDrawCount = maxDrawCount; + _stride = stride; + } + + public static void Run(ref DrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndexedIndirectCount( + threaded.Buffers.MapBufferRange(command._indirectBuffer), + threaded.Buffers.MapBufferRange(command._parameterBuffer), + command._maxDrawCount, + command._stride + ); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs new file mode 100644 index 00000000..e1947084 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndirectCommand : IGALCommand, IGALCommand<DrawIndirectCommand> + { + public CommandType CommandType => CommandType.DrawIndirect; + private BufferRange _indirectBuffer; + + public void Set(BufferRange indirectBuffer) + { + _indirectBuffer = indirectBuffer; + } + + public static void Run(ref DrawIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs new file mode 100644 index 00000000..ef56ffb2 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawIndirectCountCommand : IGALCommand, IGALCommand<DrawIndirectCountCommand> + { + public CommandType CommandType => CommandType.DrawIndirectCount; + private BufferRange _indirectBuffer; + private BufferRange _parameterBuffer; + private int _maxDrawCount; + private int _stride; + + public void Set(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _indirectBuffer = indirectBuffer; + _parameterBuffer = parameterBuffer; + _maxDrawCount = maxDrawCount; + _stride = stride; + } + + public static void Run(ref DrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawIndirectCount( + threaded.Buffers.MapBufferRange(command._indirectBuffer), + threaded.Buffers.MapBufferRange(command._parameterBuffer), + command._maxDrawCount, + command._stride + ); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs new file mode 100644 index 00000000..b3e9c4b5 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawTextureCommand.cs @@ -0,0 +1,31 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct DrawTextureCommand : IGALCommand, IGALCommand<DrawTextureCommand> + { + public CommandType CommandType => CommandType.DrawTexture; + private TableRef<ITexture> _texture; + private TableRef<ISampler> _sampler; + private Extents2DF _srcRegion; + private Extents2DF _dstRegion; + + public void Set(TableRef<ITexture> texture, TableRef<ISampler> sampler, Extents2DF srcRegion, Extents2DF dstRegion) + { + _texture = texture; + _sampler = sampler; + _srcRegion = srcRegion; + _dstRegion = dstRegion; + } + + public static void Run(ref DrawTextureCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.DrawTexture( + command._texture.GetAs<ThreadedTexture>(threaded)?.Base, + command._sampler.GetAs<ThreadedSampler>(threaded)?.Base, + command._srcRegion, + command._dstRegion); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs new file mode 100644 index 00000000..877af23b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndHostConditionalRenderingCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct EndHostConditionalRenderingCommand : IGALCommand, IGALCommand<EndHostConditionalRenderingCommand> + { + public CommandType CommandType => CommandType.EndHostConditionalRendering; + + public static void Run(ref EndHostConditionalRenderingCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.EndHostConditionalRendering(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs new file mode 100644 index 00000000..33df325f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/EndTransformFeedbackCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct EndTransformFeedbackCommand : IGALCommand, IGALCommand<EndTransformFeedbackCommand> + { + public CommandType CommandType => CommandType.EndTransformFeedback; + + public static void Run(ref EndTransformFeedbackCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.EndTransformFeedback(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs new file mode 100644 index 00000000..ea831c8d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/IGALCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + interface IGALCommand + { + CommandType CommandType { get; } + } + + interface IGALCommand<T> where T : IGALCommand + { + abstract static void Run(ref T command, ThreadedRenderer threaded, IRenderer renderer); + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs new file mode 100644 index 00000000..f3662424 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramCheckLinkCommand.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program +{ + struct ProgramCheckLinkCommand : IGALCommand, IGALCommand<ProgramCheckLinkCommand> + { + public CommandType CommandType => CommandType.ProgramCheckLink; + private TableRef<ThreadedProgram> _program; + private bool _blocking; + private TableRef<ResultBox<ProgramLinkStatus>> _result; + + public void Set(TableRef<ThreadedProgram> program, bool blocking, TableRef<ResultBox<ProgramLinkStatus>> result) + { + _program = program; + _blocking = blocking; + _result = result; + } + + public static void Run(ref ProgramCheckLinkCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ProgramLinkStatus result = command._program.Get(threaded).Base.CheckProgramLink(command._blocking); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs new file mode 100644 index 00000000..d1ec4298 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program +{ + struct ProgramDisposeCommand : IGALCommand, IGALCommand<ProgramDisposeCommand> + { + public CommandType CommandType => CommandType.ProgramDispose; + private TableRef<ThreadedProgram> _program; + + public void Set(TableRef<ThreadedProgram> program) + { + _program = program; + } + + public static void Run(ref ProgramDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._program.Get(threaded).Base.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs new file mode 100644 index 00000000..16963245 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Program/ProgramGetBinaryCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Program +{ + struct ProgramGetBinaryCommand : IGALCommand, IGALCommand<ProgramGetBinaryCommand> + { + public CommandType CommandType => CommandType.ProgramGetBinary; + private TableRef<ThreadedProgram> _program; + private TableRef<ResultBox<byte[]>> _result; + + public void Set(TableRef<ThreadedProgram> program, TableRef<ResultBox<byte[]>> result) + { + _program = program; + _result = result; + } + + public static void Run(ref ProgramGetBinaryCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + byte[] result = command._program.Get(threaded).Base.GetBinary(); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs new file mode 100644 index 00000000..41987da1 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ActionCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct ActionCommand : IGALCommand, IGALCommand<ActionCommand> + { + public CommandType CommandType => CommandType.Action; + private TableRef<Action> _action; + + public void Set(TableRef<Action> action) + { + _action = action; + } + + public static void Run(ref ActionCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._action.Get(threaded)(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs new file mode 100644 index 00000000..b36d8bbe --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateBufferCommand.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateBufferCommand : IGALCommand, IGALCommand<CreateBufferCommand> + { + public CommandType CommandType => CommandType.CreateBuffer; + private BufferHandle _threadedHandle; + private int _size; + private BufferHandle _storageHint; + + public void Set(BufferHandle threadedHandle, int size, BufferHandle storageHint) + { + _threadedHandle = threadedHandle; + _size = size; + _storageHint = storageHint; + } + + public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + BufferHandle hint = BufferHandle.Null; + + if (command._storageHint != BufferHandle.Null) + { + hint = threaded.Buffers.MapBuffer(command._storageHint); + } + + threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, hint)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs new file mode 100644 index 00000000..19563e12 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateProgramCommand.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateProgramCommand : IGALCommand, IGALCommand<CreateProgramCommand> + { + public CommandType CommandType => CommandType.CreateProgram; + private TableRef<IProgramRequest> _request; + + public void Set(TableRef<IProgramRequest> request) + { + _request = request; + } + + public static void Run(ref CreateProgramCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + IProgramRequest request = command._request.Get(threaded); + + if (request.Threaded.Base == null) + { + request.Threaded.Base = request.Create(renderer); + } + + threaded.Programs.ProcessQueue(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs new file mode 100644 index 00000000..6ab862d4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSamplerCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateSamplerCommand : IGALCommand, IGALCommand<CreateSamplerCommand> + { + public CommandType CommandType => CommandType.CreateSampler; + private TableRef<ThreadedSampler> _sampler; + private SamplerCreateInfo _info; + + public void Set(TableRef<ThreadedSampler> sampler, SamplerCreateInfo info) + { + _sampler = sampler; + _info = info; + } + + public static void Run(ref CreateSamplerCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._sampler.Get(threaded).Base = renderer.CreateSampler(command._info); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs new file mode 100644 index 00000000..32afb051 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateSyncCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateSyncCommand : IGALCommand, IGALCommand<CreateSyncCommand> + { + public CommandType CommandType => CommandType.CreateSync; + private ulong _id; + private bool _strict; + + public void Set(ulong id, bool strict) + { + _id = id; + _strict = strict; + } + + public static void Run(ref CreateSyncCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.CreateSync(command._id, command._strict); + + threaded.Sync.AssignSync(command._id); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs new file mode 100644 index 00000000..0347ded4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct CreateTextureCommand : IGALCommand, IGALCommand<CreateTextureCommand> + { + public CommandType CommandType => CommandType.CreateTexture; + private TableRef<ThreadedTexture> _texture; + private TextureCreateInfo _info; + private float _scale; + + public void Set(TableRef<ThreadedTexture> texture, TextureCreateInfo info, float scale) + { + _texture = texture; + _info = info; + _scale = scale; + } + + public static void Run(ref CreateTextureCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base = renderer.CreateTexture(command._info, command._scale); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs new file mode 100644 index 00000000..4111dcfd --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/GetCapabilitiesCommand.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct GetCapabilitiesCommand : IGALCommand, IGALCommand<GetCapabilitiesCommand> + { + public CommandType CommandType => CommandType.GetCapabilities; + private TableRef<ResultBox<Capabilities>> _result; + + public void Set(TableRef<ResultBox<Capabilities>> result) + { + _result = result; + } + + public static void Run(ref GetCapabilitiesCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._result.Get(threaded).Result = renderer.GetCapabilities(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs new file mode 100644 index 00000000..820908f3 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/PreFrameCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct PreFrameCommand : IGALCommand, IGALCommand<PreFrameCommand> + { + public CommandType CommandType => CommandType.PreFrame; + + public static void Run(ref PreFrameCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.PreFrame(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs new file mode 100644 index 00000000..4b0210cb --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ReportCounterCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct ReportCounterCommand : IGALCommand, IGALCommand<ReportCounterCommand> + { + public CommandType CommandType => CommandType.ReportCounter; + private TableRef<ThreadedCounterEvent> _event; + private CounterType _type; + private TableRef<EventHandler<ulong>> _resultHandler; + private bool _hostReserved; + + public void Set(TableRef<ThreadedCounterEvent> evt, CounterType type, TableRef<EventHandler<ulong>> resultHandler, bool hostReserved) + { + _event = evt; + _type = type; + _resultHandler = resultHandler; + _hostReserved = hostReserved; + } + + public static void Run(ref ReportCounterCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedCounterEvent evt = command._event.Get(threaded); + + evt.Create(renderer, command._type, command._resultHandler.Get(threaded), command._hostReserved); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs new file mode 100644 index 00000000..3d796041 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/ResetCounterCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct ResetCounterCommand : IGALCommand, IGALCommand<ResetCounterCommand> + { + public CommandType CommandType => CommandType.ResetCounter; + private CounterType _type; + + public void Set(CounterType type) + { + _type = type; + } + + public static void Run(ref ResetCounterCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.ResetCounter(command._type); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs new file mode 100644 index 00000000..c7076c0e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/UpdateCountersCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer +{ + struct UpdateCountersCommand : IGALCommand, IGALCommand<UpdateCountersCommand> + { + public CommandType CommandType => CommandType.UpdateCounters; + + public static void Run(ref UpdateCountersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.UpdateCounters(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs new file mode 100644 index 00000000..9485e9a1 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Sampler/SamplerDisposeCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler +{ + struct SamplerDisposeCommand : IGALCommand, IGALCommand<SamplerDisposeCommand> + { + public CommandType CommandType => CommandType.SamplerDispose; + private TableRef<ThreadedSampler> _sampler; + + public void Set(TableRef<ThreadedSampler> sampler) + { + _sampler = sampler; + } + + public static void Run(ref SamplerDisposeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._sampler.Get(threaded).Base.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs new file mode 100644 index 00000000..a96879ff --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetAlphaTestCommand.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetAlphaTestCommand : IGALCommand, IGALCommand<SetAlphaTestCommand> + { + public CommandType CommandType => CommandType.SetAlphaTest; + private bool _enable; + private float _reference; + private CompareOp _op; + + public void Set(bool enable, float reference, CompareOp op) + { + _enable = enable; + _reference = reference; + _op = op; + } + + public static void Run(ref SetAlphaTestCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetAlphaTest(command._enable, command._reference, command._op); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs new file mode 100644 index 00000000..2ec10a50 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateAdvancedCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetBlendStateAdvancedCommand : IGALCommand, IGALCommand<SetBlendStateAdvancedCommand> + { + public CommandType CommandType => CommandType.SetBlendStateAdvanced; + private AdvancedBlendDescriptor _blend; + + public void Set(AdvancedBlendDescriptor blend) + { + _blend = blend; + } + + public static void Run(ref SetBlendStateAdvancedCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetBlendState(command._blend); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs new file mode 100644 index 00000000..68e48da5 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetBlendStateCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetBlendStateCommand : IGALCommand, IGALCommand<SetBlendStateCommand> + { + public CommandType CommandType => CommandType.SetBlendState; + private int _index; + private BlendDescriptor _blend; + + public void Set(int index, BlendDescriptor blend) + { + _index = index; + _blend = blend; + } + + public static void Run(ref SetBlendStateCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetBlendState(command._index, command._blend); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs new file mode 100644 index 00000000..eb8d4a72 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthBiasCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthBiasCommand : IGALCommand, IGALCommand<SetDepthBiasCommand> + { + public CommandType CommandType => CommandType.SetDepthBias; + private PolygonModeMask _enables; + private float _factor; + private float _units; + private float _clamp; + + public void Set(PolygonModeMask enables, float factor, float units, float clamp) + { + _enables = enables; + _factor = factor; + _units = units; + _clamp = clamp; + } + + public static void Run(ref SetDepthBiasCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthBias(command._enables, command._factor, command._units, command._clamp); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs new file mode 100644 index 00000000..15159cb4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthClampCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthClampCommand : IGALCommand, IGALCommand<SetDepthClampCommand> + { + public CommandType CommandType => CommandType.SetDepthClamp; + private bool _clamp; + + public void Set(bool clamp) + { + _clamp = clamp; + } + + public static void Run(ref SetDepthClampCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthClamp(command._clamp); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs new file mode 100644 index 00000000..3e169164 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthModeCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthModeCommand : IGALCommand, IGALCommand<SetDepthModeCommand> + { + public CommandType CommandType => CommandType.SetDepthMode; + private DepthMode _mode; + + public void Set(DepthMode mode) + { + _mode = mode; + } + + public static void Run(ref SetDepthModeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthMode(command._mode); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs new file mode 100644 index 00000000..2abaeb78 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetDepthTestCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetDepthTestCommand : IGALCommand, IGALCommand<SetDepthTestCommand> + { + public CommandType CommandType => CommandType.SetDepthTest; + private DepthTestDescriptor _depthTest; + + public void Set(DepthTestDescriptor depthTest) + { + _depthTest = depthTest; + } + + public static void Run(ref SetDepthTestCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetDepthTest(command._depthTest); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs new file mode 100644 index 00000000..54311e95 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFaceCullingCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand> + { + public CommandType CommandType => CommandType.SetFaceCulling; + private bool _enable; + private Face _face; + + public void Set(bool enable, Face face) + { + _enable = enable; + _face = face; + } + + public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetFaceCulling(command._enable, command._face); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs new file mode 100644 index 00000000..e4d7b814 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetFrontFaceCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetFrontFaceCommand : IGALCommand, IGALCommand<SetFrontFaceCommand> + { + public CommandType CommandType => CommandType.SetFrontFace; + private FrontFace _frontFace; + + public void Set(FrontFace frontFace) + { + _frontFace = frontFace; + } + + public static void Run(ref SetFrontFaceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetFrontFace(command._frontFace); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs new file mode 100644 index 00000000..7836acd7 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand> + { + public CommandType CommandType => CommandType.SetImage; + private int _binding; + private TableRef<ITexture> _texture; + private Format _imageFormat; + + public void Set(int binding, TableRef<ITexture> texture, Format imageFormat) + { + _binding = binding; + _texture = texture; + _imageFormat = imageFormat; + } + + public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetImage(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs new file mode 100644 index 00000000..ded44c55 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetIndexBufferCommand.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetIndexBufferCommand : IGALCommand, IGALCommand<SetIndexBufferCommand> + { + public CommandType CommandType => CommandType.SetIndexBuffer; + private BufferRange _buffer; + private IndexType _type; + + public void Set(BufferRange buffer, IndexType type) + { + _buffer = buffer; + _type = type; + } + + public static void Run(ref SetIndexBufferCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + BufferRange range = threaded.Buffers.MapBufferRange(command._buffer); + renderer.Pipeline.SetIndexBuffer(range, command._type); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs new file mode 100644 index 00000000..68331932 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLineParametersCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetLineParametersCommand : IGALCommand, IGALCommand<SetLineParametersCommand> + { + public CommandType CommandType => CommandType.SetLineParameters; + private float _width; + private bool _smooth; + + public void Set(float width, bool smooth) + { + _width = width; + _smooth = smooth; + } + + public static void Run(ref SetLineParametersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetLineParameters(command._width, command._smooth); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs new file mode 100644 index 00000000..2d7fc169 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetLogicOpStateCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetLogicOpStateCommand : IGALCommand, IGALCommand<SetLogicOpStateCommand> + { + public CommandType CommandType => CommandType.SetLogicOpState; + private bool _enable; + private LogicalOp _op; + + public void Set(bool enable, LogicalOp op) + { + _enable = enable; + _op = op; + } + + public static void Run(ref SetLogicOpStateCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetLogicOpState(command._enable, command._op); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs new file mode 100644 index 00000000..f7b4969a --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetMultisampleStateCommand : IGALCommand, IGALCommand<SetMultisampleStateCommand> + { + public CommandType CommandType => CommandType.SetMultisampleState; + private MultisampleDescriptor _multisample; + + public void Set(MultisampleDescriptor multisample) + { + _multisample = multisample; + } + + public static void Run(ref SetMultisampleStateCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetMultisampleState(command._multisample); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs new file mode 100644 index 00000000..815bc3c2 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Common.Memory; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPatchParametersCommand : IGALCommand, IGALCommand<SetPatchParametersCommand> + { + public CommandType CommandType => CommandType.SetPatchParameters; + private int _vertices; + private Array4<float> _defaultOuterLevel; + private Array2<float> _defaultInnerLevel; + + public void Set(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) + { + _vertices = vertices; + defaultOuterLevel.CopyTo(_defaultOuterLevel.AsSpan()); + defaultInnerLevel.CopyTo(_defaultInnerLevel.AsSpan()); + } + + public static void Run(ref SetPatchParametersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPatchParameters(command._vertices, command._defaultOuterLevel.AsSpan(), command._defaultInnerLevel.AsSpan()); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs new file mode 100644 index 00000000..e3fad0f8 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPointParametersCommand.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPointParametersCommand : IGALCommand, IGALCommand<SetPointParametersCommand> + { + public CommandType CommandType => CommandType.SetPointParameters; + private float _size; + private bool _isProgramPointSize; + private bool _enablePointSprite; + private Origin _origin; + + public void Set(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) + { + _size = size; + _isProgramPointSize = isProgramPointSize; + _enablePointSprite = enablePointSprite; + _origin = origin; + } + + public static void Run(ref SetPointParametersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPointParameters(command._size, command._isProgramPointSize, command._enablePointSprite, command._origin); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs new file mode 100644 index 00000000..ea2f838b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPolygonModeCommand : IGALCommand, IGALCommand<SetPolygonModeCommand> + { + public CommandType CommandType => CommandType.SetPolygonMode; + private PolygonMode _frontMode; + private PolygonMode _backMode; + + public void Set(PolygonMode frontMode, PolygonMode backMode) + { + _frontMode = frontMode; + _backMode = backMode; + } + + public static void Run(ref SetPolygonModeCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPolygonMode(command._frontMode, command._backMode); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs new file mode 100644 index 00000000..26b88b01 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveRestartCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPrimitiveRestartCommand : IGALCommand, IGALCommand<SetPrimitiveRestartCommand> + { + public CommandType CommandType => CommandType.SetPrimitiveRestart; + private bool _enable; + private int _index; + + public void Set(bool enable, int index) + { + _enable = enable; + _index = index; + } + + public static void Run(ref SetPrimitiveRestartCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPrimitiveRestart(command._enable, command._index); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs new file mode 100644 index 00000000..062c4e57 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPrimitiveTopologyCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetPrimitiveTopologyCommand : IGALCommand, IGALCommand<SetPrimitiveTopologyCommand> + { + public CommandType CommandType => CommandType.SetPrimitiveTopology; + private PrimitiveTopology _topology; + + public void Set(PrimitiveTopology topology) + { + _topology = topology; + } + + public static void Run(ref SetPrimitiveTopologyCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetPrimitiveTopology(command._topology); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs new file mode 100644 index 00000000..fa2e9a8a --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetProgramCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetProgramCommand : IGALCommand, IGALCommand<SetProgramCommand> + { + public CommandType CommandType => CommandType.SetProgram; + private TableRef<IProgram> _program; + + public void Set(TableRef<IProgram> program) + { + _program = program; + } + + public static void Run(ref SetProgramCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedProgram program = command._program.GetAs<ThreadedProgram>(threaded); + + threaded.Programs.WaitForProgram(program); + + renderer.Pipeline.SetProgram(program.Base); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs new file mode 100644 index 00000000..d2095a4f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRasterizerDiscardCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRasterizerDiscardCommand : IGALCommand, IGALCommand<SetRasterizerDiscardCommand> + { + public CommandType CommandType => CommandType.SetRasterizerDiscard; + private bool _discard; + + public void Set(bool discard) + { + _discard = discard; + } + + public static void Run(ref SetRasterizerDiscardCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetRasterizerDiscard(command._discard); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs new file mode 100644 index 00000000..c247ff3a --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetColorMasksCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRenderTargetColorMasksCommand : IGALCommand, IGALCommand<SetRenderTargetColorMasksCommand> + { + public CommandType CommandType => CommandType.SetRenderTargetColorMasks; + private SpanRef<uint> _componentMask; + + public void Set(SpanRef<uint> componentMask) + { + _componentMask = componentMask; + } + + public static void Run(ref SetRenderTargetColorMasksCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<uint> componentMask = command._componentMask.Get(threaded); + renderer.Pipeline.SetRenderTargetColorMasks(componentMask); + command._componentMask.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs new file mode 100644 index 00000000..7cb5ec11 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetScaleCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRenderTargetScaleCommand : IGALCommand, IGALCommand<SetRenderTargetScaleCommand> + { + public CommandType CommandType => CommandType.SetRenderTargetScale; + private float _scale; + + public void Set(float scale) + { + _scale = scale; + } + + public static void Run(ref SetRenderTargetScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetRenderTargetScale(command._scale); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs new file mode 100644 index 00000000..0b175a72 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs @@ -0,0 +1,24 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System.Linq; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand> + { + public CommandType CommandType => CommandType.SetRenderTargets; + private TableRef<ITexture[]> _colors; + private TableRef<ITexture> _depthStencil; + + public void Set(TableRef<ITexture[]> colors, TableRef<ITexture> depthStencil) + { + _colors = colors; + _depthStencil = depthStencil; + } + + public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetRenderTargets(command._colors.Get(threaded).Select(color => ((ThreadedTexture)color)?.Base).ToArray(), command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs new file mode 100644 index 00000000..985d775e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetScissorsCommand.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetScissorsCommand : IGALCommand, IGALCommand<SetScissorsCommand> + { + public CommandType CommandType => CommandType.SetScissor; + private SpanRef<Rectangle<int>> _scissors; + + public void Set(SpanRef<Rectangle<int>> scissors) + { + _scissors = scissors; + } + + public static void Run(ref SetScissorsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetScissors(command._scissors.Get(threaded)); + + command._scissors.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs new file mode 100644 index 00000000..41bff97e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStencilTestCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetStencilTestCommand : IGALCommand, IGALCommand<SetStencilTestCommand> + { + public CommandType CommandType => CommandType.SetStencilTest; + private StencilTestDescriptor _stencilTest; + + public void Set(StencilTestDescriptor stencilTest) + { + _stencilTest = stencilTest; + } + + public static void Run(ref SetStencilTestCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetStencilTest(command._stencilTest); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs new file mode 100644 index 00000000..6ecb0989 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetStorageBuffersCommand : IGALCommand, IGALCommand<SetStorageBuffersCommand> + { + public CommandType CommandType => CommandType.SetStorageBuffers; + private SpanRef<BufferAssignment> _buffers; + + public void Set(SpanRef<BufferAssignment> buffers) + { + _buffers = buffers; + } + + public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<BufferAssignment> buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetStorageBuffers(threaded.Buffers.MapBufferRanges(buffers)); + command._buffers.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs new file mode 100644 index 00000000..5e8e0854 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureAndSamplerCommand.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetTextureAndSamplerCommand : IGALCommand, IGALCommand<SetTextureAndSamplerCommand> + { + public CommandType CommandType => CommandType.SetTextureAndSampler; + private ShaderStage _stage; + private int _binding; + private TableRef<ITexture> _texture; + private TableRef<ISampler> _sampler; + + public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, TableRef<ISampler> sampler) + { + _stage = stage; + _binding = binding; + _texture = texture; + _sampler = sampler; + } + + public static void Run(ref SetTextureAndSamplerCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetTextureAndSampler(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._sampler.GetAs<ThreadedSampler>(threaded)?.Base); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs new file mode 100644 index 00000000..e0d4ef2d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTransformFeedbackBuffersCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetTransformFeedbackBuffersCommand : IGALCommand, IGALCommand<SetTransformFeedbackBuffersCommand> + { + public CommandType CommandType => CommandType.SetTransformFeedbackBuffers; + private SpanRef<BufferRange> _buffers; + + public void Set(SpanRef<BufferRange> buffers) + { + _buffers = buffers; + } + + public static void Run(ref SetTransformFeedbackBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<BufferRange> buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetTransformFeedbackBuffers(threaded.Buffers.MapBufferRanges(buffers)); + command._buffers.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs new file mode 100644 index 00000000..9e93db9e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetUniformBuffersCommand : IGALCommand, IGALCommand<SetUniformBuffersCommand> + { + public CommandType CommandType => CommandType.SetUniformBuffers; + private SpanRef<BufferAssignment> _buffers; + + public void Set(SpanRef<BufferAssignment> buffers) + { + _buffers = buffers; + } + + public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<BufferAssignment> buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetUniformBuffers(threaded.Buffers.MapBufferRanges(buffers)); + command._buffers.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs new file mode 100644 index 00000000..4336ce49 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUserClipDistanceCommand.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetUserClipDistanceCommand : IGALCommand, IGALCommand<SetUserClipDistanceCommand> + { + public CommandType CommandType => CommandType.SetUserClipDistance; + private int _index; + private bool _enableClip; + + public void Set(int index, bool enableClip) + { + _index = index; + _enableClip = enableClip; + } + + public static void Run(ref SetUserClipDistanceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetUserClipDistance(command._index, command._enableClip); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs new file mode 100644 index 00000000..e442c72d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexAttribsCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetVertexAttribsCommand : IGALCommand, IGALCommand<SetVertexAttribsCommand> + { + public CommandType CommandType => CommandType.SetVertexAttribs; + private SpanRef<VertexAttribDescriptor> _vertexAttribs; + + public void Set(SpanRef<VertexAttribDescriptor> vertexAttribs) + { + _vertexAttribs = vertexAttribs; + } + + public static void Run(ref SetVertexAttribsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<VertexAttribDescriptor> vertexAttribs = command._vertexAttribs.Get(threaded); + renderer.Pipeline.SetVertexAttribs(vertexAttribs); + command._vertexAttribs.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs new file mode 100644 index 00000000..585da2a4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetVertexBuffersCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetVertexBuffersCommand : IGALCommand, IGALCommand<SetVertexBuffersCommand> + { + public CommandType CommandType => CommandType.SetVertexBuffers; + private SpanRef<VertexBufferDescriptor> _vertexBuffers; + + public void Set(SpanRef<VertexBufferDescriptor> vertexBuffers) + { + _vertexBuffers = vertexBuffers; + } + + public static void Run(ref SetVertexBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + Span<VertexBufferDescriptor> vertexBuffers = command._vertexBuffers.Get(threaded); + renderer.Pipeline.SetVertexBuffers(threaded.Buffers.MapBufferRanges(vertexBuffers)); + command._vertexBuffers.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs new file mode 100644 index 00000000..c18bd811 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetViewportsCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetViewportsCommand : IGALCommand, IGALCommand<SetViewportsCommand> + { + public CommandType CommandType => CommandType.SetViewports; + private SpanRef<Viewport> _viewports; + private bool _disableTransform; + + public void Set(SpanRef<Viewport> viewports, bool disableTransform) + { + _viewports = viewports; + _disableTransform = disableTransform; + } + + public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded); + renderer.Pipeline.SetViewports(viewports, command._disableTransform); + command._viewports.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs new file mode 100644 index 00000000..02d0b639 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToCommand.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToCommand : IGALCommand, IGALCommand<TextureCopyToCommand> + { + public CommandType CommandType => CommandType.TextureCopyTo; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private int _firstLayer; + private int _firstLevel; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, int firstLayer, int firstLevel) + { + _texture = texture; + _destination = destination; + _firstLayer = firstLayer; + _firstLevel = firstLevel; + } + + public static void Run(ref TextureCopyToCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + source.Base.CopyTo(command._destination.Get(threaded).Base, command._firstLayer, command._firstLevel); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs new file mode 100644 index 00000000..6b83d3f8 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToScaledCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToScaledCommand : IGALCommand, IGALCommand<TextureCopyToScaledCommand> + { + public CommandType CommandType => CommandType.TextureCopyToScaled; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private Extents2D _srcRegion; + private Extents2D _dstRegion; + private bool _linearFilter; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + _texture = texture; + _destination = destination; + _srcRegion = srcRegion; + _dstRegion = dstRegion; + _linearFilter = linearFilter; + } + + public static void Run(ref TextureCopyToScaledCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + source.Base.CopyTo(command._destination.Get(threaded).Base, command._srcRegion, command._dstRegion, command._linearFilter); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs new file mode 100644 index 00000000..2a340a70 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCopyToSliceCommand.cs @@ -0,0 +1,32 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCopyToSliceCommand : IGALCommand, IGALCommand<TextureCopyToSliceCommand> + { + public CommandType CommandType => CommandType.TextureCopyToSlice; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private int _srcLayer; + private int _dstLayer; + private int _srcLevel; + private int _dstLevel; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + _texture = texture; + _destination = destination; + _srcLayer = srcLayer; + _dstLayer = dstLayer; + _srcLevel = srcLevel; + _dstLevel = dstLevel; + } + + public static void Run(ref TextureCopyToSliceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + source.Base.CopyTo(command._destination.Get(threaded).Base, command._srcLayer, command._dstLayer, command._srcLevel, command._dstLevel); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs new file mode 100644 index 00000000..09e9ca2f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureCreateViewCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureCreateViewCommand : IGALCommand, IGALCommand<TextureCreateViewCommand> + { + public CommandType CommandType => CommandType.TextureCreateView; + private TableRef<ThreadedTexture> _texture; + private TableRef<ThreadedTexture> _destination; + private TextureCreateInfo _info; + private int _firstLayer; + private int _firstLevel; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ThreadedTexture> destination, TextureCreateInfo info, int firstLayer, int firstLevel) + { + _texture = texture; + _destination = destination; + _info = info; + _firstLayer = firstLayer; + _firstLevel = firstLevel; + } + + public static void Run(ref TextureCreateViewCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture source = command._texture.Get(threaded); + command._destination.Get(threaded).Base = source.Base.CreateView(command._info, command._firstLayer, command._firstLevel); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs new file mode 100644 index 00000000..91320d45 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataCommand.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureGetDataCommand : IGALCommand, IGALCommand<TextureGetDataCommand> + { + public CommandType CommandType => CommandType.TextureGetData; + private TableRef<ThreadedTexture> _texture; + private TableRef<ResultBox<PinnedSpan<byte>>> _result; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ResultBox<PinnedSpan<byte>>> result) + { + _texture = texture; + _result = result; + } + + public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData(); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs new file mode 100644 index 00000000..ec06cc4d --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureGetDataSliceCommand.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureGetDataSliceCommand : IGALCommand, IGALCommand<TextureGetDataSliceCommand> + { + public CommandType CommandType => CommandType.TextureGetDataSlice; + private TableRef<ThreadedTexture> _texture; + private TableRef<ResultBox<PinnedSpan<byte>>> _result; + private int _layer; + private int _level; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<ResultBox<PinnedSpan<byte>>> result, int layer, int level) + { + _texture = texture; + _result = result; + _layer = layer; + _level = level; + } + + public static void Run(ref TextureGetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level); + + command._result.Get(threaded).Result = result; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs new file mode 100644 index 00000000..61486e09 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureReleaseCommand.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureReleaseCommand : IGALCommand, IGALCommand<TextureReleaseCommand> + { + public CommandType CommandType => CommandType.TextureRelease; + private TableRef<ThreadedTexture> _texture; + + public void Set(TableRef<ThreadedTexture> texture) + { + _texture = texture; + } + + public static void Run(ref TextureReleaseCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base.Release(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs new file mode 100644 index 00000000..cfbaffd3 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetDataCommand : IGALCommand, IGALCommand<TextureSetDataCommand> + { + public CommandType CommandType => CommandType.TextureSetData; + private TableRef<ThreadedTexture> _texture; + private TableRef<byte[]> _data; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data) + { + _texture = texture; + _data = data; + } + + public static void Run(ref TextureSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture texture = command._texture.Get(threaded); + texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded))); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs new file mode 100644 index 00000000..a7126f61 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetDataSliceCommand : IGALCommand, IGALCommand<TextureSetDataSliceCommand> + { + public CommandType CommandType => CommandType.TextureSetDataSlice; + private TableRef<ThreadedTexture> _texture; + private TableRef<byte[]> _data; + private int _layer; + private int _level; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data, int layer, int level) + { + _texture = texture; + _data = data; + _layer = layer; + _level = level; + } + + public static void Run(ref TextureSetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture texture = command._texture.Get(threaded); + texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)), command._layer, command._level); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs new file mode 100644 index 00000000..4df83e08 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs @@ -0,0 +1,31 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetDataSliceRegionCommand : IGALCommand, IGALCommand<TextureSetDataSliceRegionCommand> + { + public CommandType CommandType => CommandType.TextureSetDataSliceRegion; + private TableRef<ThreadedTexture> _texture; + private TableRef<byte[]> _data; + private int _layer; + private int _level; + private Rectangle<int> _region; + + public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data, int layer, int level, Rectangle<int> region) + { + _texture = texture; + _data = data; + _layer = layer; + _level = level; + _region = region; + } + + public static void Run(ref TextureSetDataSliceRegionCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + ThreadedTexture texture = command._texture.Get(threaded); + texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)), command._layer, command._level, command._region); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs new file mode 100644 index 00000000..2a1943a9 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetStorageCommand.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture +{ + struct TextureSetStorageCommand : IGALCommand, IGALCommand<TextureSetStorageCommand> + { + public CommandType CommandType => CommandType.TextureSetStorage; + private TableRef<ThreadedTexture> _texture; + private BufferRange _storage; + + public void Set(TableRef<ThreadedTexture> texture, BufferRange storage) + { + _texture = texture; + _storage = storage; + } + + public static void Run(ref TextureSetStorageCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + command._texture.Get(threaded).Base.SetStorage(threaded.Buffers.MapBufferRange(command._storage)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs new file mode 100644 index 00000000..ce1a83a7 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TextureBarrierCommand : IGALCommand, IGALCommand<TextureBarrierCommand> + { + public CommandType CommandType => CommandType.TextureBarrier; + + public static void Run(ref TextureBarrierCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TextureBarrier(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs new file mode 100644 index 00000000..c65ffe2e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TextureBarrierTiledCommand.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TextureBarrierTiledCommand : IGALCommand, IGALCommand<TextureBarrierTiledCommand> + { + public CommandType CommandType => CommandType.TextureBarrierTiled; + + public static void Run(ref TextureBarrierTiledCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TextureBarrierTiled(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs new file mode 100644 index 00000000..9124ca1f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TryHostConditionalRenderingCommand : IGALCommand, IGALCommand<TryHostConditionalRenderingCommand> + { + public CommandType CommandType => CommandType.TryHostConditionalRendering; + private TableRef<ThreadedCounterEvent> _value; + private ulong _compare; + private bool _isEqual; + + public void Set(TableRef<ThreadedCounterEvent> value, ulong compare, bool isEqual) + { + _value = value; + _compare = compare; + _isEqual = isEqual; + } + + public static void Run(ref TryHostConditionalRenderingCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TryHostConditionalRendering(command._value.Get(threaded)?.Base, command._compare, command._isEqual); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs new file mode 100644 index 00000000..a5d07640 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/TryHostConditionalRenderingFlushCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct TryHostConditionalRenderingFlushCommand : IGALCommand, IGALCommand<TryHostConditionalRenderingFlushCommand> + { + public CommandType CommandType => CommandType.TryHostConditionalRenderingFlush; + private TableRef<ThreadedCounterEvent> _value; + private TableRef<ThreadedCounterEvent> _compare; + private bool _isEqual; + + public void Set(TableRef<ThreadedCounterEvent> value, TableRef<ThreadedCounterEvent> compare, bool isEqual) + { + _value = value; + _compare = compare; + _isEqual = isEqual; + } + + public static void Run(ref TryHostConditionalRenderingFlushCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.TryHostConditionalRendering(command._value.Get(threaded)?.Base, command._compare.Get(threaded)?.Base, command._isEqual); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs new file mode 100644 index 00000000..ebe14150 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/UpdateRenderScaleCommand.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct UpdateRenderScaleCommand : IGALCommand, IGALCommand<UpdateRenderScaleCommand> + { + public CommandType CommandType => CommandType.UpdateRenderScale; + private SpanRef<float> _scales; + private int _totalCount; + private int _fragmentCount; + + public void Set(SpanRef<float> scales, int totalCount, int fragmentCount) + { + _scales = scales; + _totalCount = totalCount; + _fragmentCount = fragmentCount; + } + + public static void Run(ref UpdateRenderScaleCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.UpdateRenderScale(command._scales.Get(threaded), command._totalCount, command._fragmentCount); + command._scales.Dispose(threaded); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs new file mode 100644 index 00000000..6a24cd35 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Window +{ + struct WindowPresentCommand : IGALCommand, IGALCommand<WindowPresentCommand> + { + public CommandType CommandType => CommandType.WindowPresent; + private TableRef<ThreadedTexture> _texture; + private ImageCrop _crop; + private TableRef<Action> _swapBuffersCallback; + + public void Set(TableRef<ThreadedTexture> texture, ImageCrop crop, TableRef<Action> swapBuffersCallback) + { + _texture = texture; + _crop = crop; + _swapBuffersCallback = swapBuffersCallback; + } + + public static void Run(ref WindowPresentCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + threaded.SignalFrame(); + renderer.Window.Present(command._texture.Get(threaded)?.Base, command._crop, command._swapBuffersCallback.Get(threaded)); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs new file mode 100644 index 00000000..4ea1a2c7 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/CircularSpanPool.cs @@ -0,0 +1,89 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + /// <summary> + /// A memory pool for passing through Span<T> resources with one producer and consumer. + /// Data is copied on creation to part of the pool, then that region is reserved until it is disposed by the consumer. + /// Similar to the command queue, this pool assumes that data is created and disposed in the same order. + /// </summary> + class CircularSpanPool + { + private ThreadedRenderer _renderer; + private byte[] _pool; + private int _size; + + private int _producerPtr; + private int _producerSkipPosition = -1; + private int _consumerPtr; + + public CircularSpanPool(ThreadedRenderer renderer, int size) + { + _renderer = renderer; + _size = size; + _pool = new byte[size]; + } + + public SpanRef<T> Insert<T>(ReadOnlySpan<T> data) where T : unmanaged + { + int size = data.Length * Unsafe.SizeOf<T>(); + + // Wrapping aware circular queue. + // If there's no space at the end of the pool for this span, we can't fragment it. + // So just loop back around to the start. Remember the last skipped position. + + bool wraparound = _producerPtr + size >= _size; + int index = wraparound ? 0 : _producerPtr; + + // _consumerPtr is from another thread, and we're taking it without a lock, so treat this as a snapshot in the past. + // We know that it will always be before or equal to the producer pointer, and it cannot pass it. + // This is enough to reason about if there is space in the queue for the data, even if we're checking against an outdated value. + + int consumer = _consumerPtr; + bool beforeConsumer = _producerPtr < consumer; + + if (size > _size - 1 || (wraparound && beforeConsumer) || ((index < consumer || wraparound) && index + size >= consumer)) + { + // Just get an array in the following situations: + // - The data is too large to fit in the pool. + // - A wraparound would happen but the consumer would be covered by it. + // - The producer would catch up to the consumer as a result. + + return new SpanRef<T>(_renderer, data.ToArray()); + } + + data.CopyTo(MemoryMarshal.Cast<byte, T>(new Span<byte>(_pool).Slice(index, size))); + + if (wraparound) + { + _producerSkipPosition = _producerPtr; + } + + _producerPtr = index + size; + + return new SpanRef<T>(data.Length); + } + + public Span<T> Get<T>(int length) where T : unmanaged + { + int size = length * Unsafe.SizeOf<T>(); + + if (_consumerPtr == Interlocked.CompareExchange(ref _producerSkipPosition, -1, _consumerPtr)) + { + _consumerPtr = 0; + } + + return MemoryMarshal.Cast<byte, T>(new Span<byte>(_pool).Slice(_consumerPtr, size)); + } + + public void Dispose<T>(int length) where T : unmanaged + { + int size = length * Unsafe.SizeOf<T>(); + + _consumerPtr = _consumerPtr + size; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs new file mode 100644 index 00000000..7a0be785 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/ResultBox.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + public class ResultBox<T> + { + public T Result; + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs new file mode 100644 index 00000000..7dbebc76 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/SpanRef.cs @@ -0,0 +1,39 @@ +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + struct SpanRef<T> where T : unmanaged + { + private int _packedLengthId; + + public SpanRef(ThreadedRenderer renderer, T[] data) + { + _packedLengthId = -(renderer.AddTableRef(data) + 1); + } + + public SpanRef(int length) + { + _packedLengthId = length; + } + + public Span<T> Get(ThreadedRenderer renderer) + { + if (_packedLengthId >= 0) + { + return renderer.SpanPool.Get<T>(_packedLengthId); + } + else + { + return new Span<T>((T[])renderer.RemoveTableRef(-(_packedLengthId + 1))); + } + } + + public void Dispose(ThreadedRenderer renderer) + { + if (_packedLengthId > 0) + { + renderer.SpanPool.Dispose<T>(_packedLengthId); + } + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs new file mode 100644 index 00000000..166aa71a --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Model/TableRef.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Model +{ + struct TableRef<T> + { + private int _index; + + public TableRef(ThreadedRenderer renderer, T reference) + { + _index = renderer.AddTableRef(reference); + } + + public T Get(ThreadedRenderer renderer) + { + return (T)renderer.RemoveTableRef(_index); + } + + public T2 GetAs<T2>(ThreadedRenderer renderer) where T2 : T + { + return (T2)renderer.RemoveTableRef(_index); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs new file mode 100644 index 00000000..3f982d31 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ProgramQueue.cs @@ -0,0 +1,107 @@ +using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + /// <summary> + /// A structure handling multithreaded compilation for programs. + /// </summary> + class ProgramQueue + { + private const int MaxConcurrentCompilations = 8; + + private IRenderer _renderer; + + private Queue<IProgramRequest> _toCompile; + private List<ThreadedProgram> _inProgress; + + public ProgramQueue(IRenderer renderer) + { + _renderer = renderer; + + _toCompile = new Queue<IProgramRequest>(); + _inProgress = new List<ThreadedProgram>(); + } + + public void Add(IProgramRequest request) + { + lock (_toCompile) + { + _toCompile.Enqueue(request); + } + } + + public void ProcessQueue() + { + for (int i = 0; i < _inProgress.Count; i++) + { + ThreadedProgram program = _inProgress[i]; + + ProgramLinkStatus status = program.Base.CheckProgramLink(false); + + if (status != ProgramLinkStatus.Incomplete) + { + program.Compiled = true; + _inProgress.RemoveAt(i--); + } + } + + int freeSpace = MaxConcurrentCompilations - _inProgress.Count; + + for (int i = 0; i < freeSpace; i++) + { + // Begin compilation of some programs in the compile queue. + IProgramRequest program; + + lock (_toCompile) + { + if (!_toCompile.TryDequeue(out program)) + { + break; + } + } + + if (program.Threaded.Base != null) + { + ProgramLinkStatus status = program.Threaded.Base.CheckProgramLink(false); + + if (status != ProgramLinkStatus.Incomplete) + { + // This program is already compiled. Keep going through the queue. + program.Threaded.Compiled = true; + i--; + continue; + } + } + else + { + program.Threaded.Base = program.Create(_renderer); + } + + _inProgress.Add(program.Threaded); + } + } + + /// <summary> + /// Process the queue until the given program has finished compiling. + /// This will begin compilation of other programs on the queue as well. + /// </summary> + /// <param name="program">The program to wait for</param> + public void WaitForProgram(ThreadedProgram program) + { + Span<SpinWait> spinWait = stackalloc SpinWait[1]; + + while (!program.Compiled) + { + ProcessQueue(); + + if (!program.Compiled) + { + spinWait[0].SpinOnce(-1); + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs new file mode 100644 index 00000000..b4c6853f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/BinaryProgramRequest.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs +{ + class BinaryProgramRequest : IProgramRequest + { + public ThreadedProgram Threaded { get; set; } + + private byte[] _data; + private bool _hasFragmentShader; + private ShaderInfo _info; + + public BinaryProgramRequest(ThreadedProgram program, byte[] data, bool hasFragmentShader, ShaderInfo info) + { + Threaded = program; + + _data = data; + _hasFragmentShader = hasFragmentShader; + _info = info; + } + + public IProgram Create(IRenderer renderer) + { + return renderer.LoadProgramBinary(_data, _hasFragmentShader, _info); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs new file mode 100644 index 00000000..cdbfe03c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/IProgramRequest.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs +{ + interface IProgramRequest + { + ThreadedProgram Threaded { get; set; } + IProgram Create(IRenderer renderer); + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs new file mode 100644 index 00000000..ff06abb1 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/Programs/SourceProgramRequest.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Resources.Programs +{ + class SourceProgramRequest : IProgramRequest + { + public ThreadedProgram Threaded { get; set; } + + private ShaderSource[] _shaders; + private ShaderInfo _info; + + public SourceProgramRequest(ThreadedProgram program, ShaderSource[] shaders, ShaderInfo info) + { + Threaded = program; + + _shaders = shaders; + _info = info; + } + + public IProgram Create(IRenderer renderer) + { + return renderer.CreateProgram(_shaders, _info); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs new file mode 100644 index 00000000..4b7471d6 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs @@ -0,0 +1,80 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedCounterEvent : ICounterEvent + { + private ThreadedRenderer _renderer; + public ICounterEvent Base; + + public bool Invalid { get; set; } + + public CounterType Type { get; } + public bool ClearCounter { get; } + + private bool _reserved; + private int _createLock; + + public ThreadedCounterEvent(ThreadedRenderer renderer, CounterType type, bool clearCounter) + { + _renderer = renderer; + Type = type; + ClearCounter = clearCounter; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void Dispose() + { + _renderer.New<CounterEventDisposeCommand>().Set(Ref(this)); + _renderer.QueueCommand(); + } + + public void Flush() + { + ThreadedHelpers.SpinUntilNonNull(ref Base); + + Base.Flush(); + } + + public bool ReserveForHostAccess() + { + if (Base != null) + { + return Base.ReserveForHostAccess(); + } + else + { + bool result = true; + + // A very light lock, as this case is uncommon. + ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0); + + if (Base != null) + { + result = Base.ReserveForHostAccess(); + } + else + { + _reserved = true; + } + + Volatile.Write(ref _createLock, 0); + + return result; + } + } + + public void Create(IRenderer renderer, CounterType type, System.EventHandler<ulong> eventHandler, bool hostReserved) + { + ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0); + Base = renderer.ReportCounter(type, eventHandler, hostReserved || _reserved); + Volatile.Write(ref _createLock, 0); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs new file mode 100644 index 00000000..068d058e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs @@ -0,0 +1,48 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Program; +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedProgram : IProgram + { + private ThreadedRenderer _renderer; + + public IProgram Base; + + internal bool Compiled; + + public ThreadedProgram(ThreadedRenderer renderer) + { + _renderer = renderer; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void Dispose() + { + _renderer.New<ProgramDisposeCommand>().Set(Ref(this)); + _renderer.QueueCommand(); + } + + public byte[] GetBinary() + { + ResultBox<byte[]> box = new ResultBox<byte[]>(); + _renderer.New<ProgramGetBinaryCommand>().Set(Ref(this), Ref(box)); + _renderer.InvokeCommand(); + + return box.Result; + } + + public ProgramLinkStatus CheckProgramLink(bool blocking) + { + ResultBox<ProgramLinkStatus> box = new ResultBox<ProgramLinkStatus>(); + _renderer.New<ProgramCheckLinkCommand>().Set(Ref(this), blocking, Ref(box)); + _renderer.InvokeCommand(); + + return box.Result; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs new file mode 100644 index 00000000..d8de9a70 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler; +using Ryujinx.Graphics.GAL.Multithreading.Model; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + class ThreadedSampler : ISampler + { + private ThreadedRenderer _renderer; + public ISampler Base; + + public ThreadedSampler(ThreadedRenderer renderer) + { + _renderer = renderer; + } + + public void Dispose() + { + _renderer.New<SamplerDisposeCommand>().Set(new TableRef<ThreadedSampler>(_renderer, this)); + _renderer.QueueCommand(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs new file mode 100644 index 00000000..ee1cfa29 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -0,0 +1,141 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading.Resources +{ + /// <summary> + /// Threaded representation of a texture. + /// </summary> + class ThreadedTexture : ITexture + { + private ThreadedRenderer _renderer; + private TextureCreateInfo _info; + public ITexture Base; + + public int Width => _info.Width; + + public int Height => _info.Height; + + public float ScaleFactor { get; } + + public ThreadedTexture(ThreadedRenderer renderer, TextureCreateInfo info, float scale) + { + _renderer = renderer; + _info = info; + ScaleFactor = scale; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + _renderer.New<TextureCopyToCommand>().Set(Ref(this), Ref((ThreadedTexture)destination), firstLayer, firstLevel); + _renderer.QueueCommand(); + } + + public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + { + _renderer.New<TextureCopyToSliceCommand>().Set(Ref(this), Ref((ThreadedTexture)destination), srcLayer, dstLayer, srcLevel, dstLevel); + _renderer.QueueCommand(); + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + ThreadedTexture dest = (ThreadedTexture)destination; + + if (_renderer.IsGpuThread()) + { + _renderer.New<TextureCopyToScaledCommand>().Set(Ref(this), Ref(dest), srcRegion, dstRegion, linearFilter); + _renderer.QueueCommand(); + } + else + { + // Scaled copy can happen on another thread for a res scale flush. + ThreadedHelpers.SpinUntilNonNull(ref Base); + ThreadedHelpers.SpinUntilNonNull(ref dest.Base); + + Base.CopyTo(dest.Base, srcRegion, dstRegion, linearFilter); + } + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + ThreadedTexture newTex = new ThreadedTexture(_renderer, info, ScaleFactor); + _renderer.New<TextureCreateViewCommand>().Set(Ref(this), Ref(newTex), info, firstLayer, firstLevel); + _renderer.QueueCommand(); + + return newTex; + } + + public PinnedSpan<byte> GetData() + { + if (_renderer.IsGpuThread()) + { + ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + _renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box)); + _renderer.InvokeCommand(); + + return box.Result; + } + else + { + ThreadedHelpers.SpinUntilNonNull(ref Base); + + return Base.GetData(); + } + } + + public PinnedSpan<byte> GetData(int layer, int level) + { + if (_renderer.IsGpuThread()) + { + ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + _renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level); + _renderer.InvokeCommand(); + + return box.Result; + } + else + { + ThreadedHelpers.SpinUntilNonNull(ref Base); + + return Base.GetData(layer, level); + } + } + + public void SetData(SpanOrArray<byte> data) + { + _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray())); + _renderer.QueueCommand(); + } + + public void SetData(SpanOrArray<byte> data, int layer, int level) + { + _renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level); + _renderer.QueueCommand(); + } + + public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region) + { + _renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region); + _renderer.QueueCommand(); + } + + public void SetStorage(BufferRange buffer) + { + _renderer.New<TextureSetStorageCommand>().Set(Ref(this), buffer); + _renderer.QueueCommand(); + } + + public void Release() + { + _renderer.New<TextureReleaseCommand>().Set(Ref(this)); + _renderer.QueueCommand(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs b/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs new file mode 100644 index 00000000..ae09e852 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/SyncMap.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + class SyncMap : IDisposable + { + private HashSet<ulong> _inFlight = new HashSet<ulong>(); + private AutoResetEvent _inFlightChanged = new AutoResetEvent(false); + + internal void CreateSyncHandle(ulong id) + { + lock (_inFlight) + { + _inFlight.Add(id); + } + } + + internal void AssignSync(ulong id) + { + lock (_inFlight) + { + _inFlight.Remove(id); + } + + _inFlightChanged.Set(); + } + + internal void WaitSyncAvailability(ulong id) + { + // Blocks until the handle is available. + + bool signal = false; + + while (true) + { + lock (_inFlight) + { + if (!_inFlight.Contains(id)) + { + break; + } + } + + _inFlightChanged.WaitOne(); + signal = true; + } + + if (signal) + { + // Signal other threads which might still be waiting. + _inFlightChanged.Set(); + } + } + + public void Dispose() + { + _inFlightChanged.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs new file mode 100644 index 00000000..7ddb19e6 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedHelpers.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + static class ThreadedHelpers + { + public static void SpinUntilNonNull<T>(ref T obj) where T : class + { + Span<SpinWait> spinWait = stackalloc SpinWait[1]; + + while (obj == null) + { + spinWait[0].SpinOnce(-1); + } + } + + public static void SpinUntilExchange(ref int target, int value, int comparand) + { + Span<SpinWait> spinWait = stackalloc SpinWait[1]; + + while (Interlocked.CompareExchange(ref target, value, comparand) != comparand) + { + spinWait[0].SpinOnce(-1); + } + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs new file mode 100644 index 00000000..1bdc9cf4 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -0,0 +1,380 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using Ryujinx.Graphics.Shader; +using System; +using System.Linq; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + public class ThreadedPipeline : IPipeline + { + private ThreadedRenderer _renderer; + private IPipeline _impl; + + public ThreadedPipeline(ThreadedRenderer renderer, IPipeline impl) + { + _renderer = renderer; + _impl = impl; + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(_renderer, reference); + } + + public void Barrier() + { + _renderer.New<BarrierCommand>(); + _renderer.QueueCommand(); + } + + public void BeginTransformFeedback(PrimitiveTopology topology) + { + _renderer.New<BeginTransformFeedbackCommand>().Set(topology); + _renderer.QueueCommand(); + } + + public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) + { + _renderer.New<ClearBufferCommand>().Set(destination, offset, size, value); + _renderer.QueueCommand(); + } + + public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) + { + _renderer.New<ClearRenderTargetColorCommand>().Set(index, layer, layerCount, componentMask, color); + _renderer.QueueCommand(); + } + + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) + { + _renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask); + _renderer.QueueCommand(); + } + + public void CommandBufferBarrier() + { + _renderer.New<CommandBufferBarrierCommand>(); + _renderer.QueueCommand(); + } + + public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + { + _renderer.New<CopyBufferCommand>().Set(source, destination, srcOffset, dstOffset, size); + _renderer.QueueCommand(); + } + + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + { + _renderer.New<DispatchComputeCommand>().Set(groupsX, groupsY, groupsZ); + _renderer.QueueCommand(); + } + + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + _renderer.New<DrawCommand>().Set(vertexCount, instanceCount, firstVertex, firstInstance); + _renderer.QueueCommand(); + } + + public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + { + _renderer.New<DrawIndexedCommand>().Set(indexCount, instanceCount, firstIndex, firstVertex, firstInstance); + _renderer.QueueCommand(); + } + + public void DrawIndexedIndirect(BufferRange indirectBuffer) + { + _renderer.New<DrawIndexedIndirectCommand>().Set(indirectBuffer); + _renderer.QueueCommand(); + } + + public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _renderer.New<DrawIndexedIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.QueueCommand(); + } + + public void DrawIndirect(BufferRange indirectBuffer) + { + _renderer.New<DrawIndirectCommand>().Set(indirectBuffer); + _renderer.QueueCommand(); + } + + public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + { + _renderer.New<DrawIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.QueueCommand(); + } + + public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) + { + _renderer.New<DrawTextureCommand>().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion); + _renderer.QueueCommand(); + } + + public void EndHostConditionalRendering() + { + _renderer.New<EndHostConditionalRenderingCommand>(); + _renderer.QueueCommand(); + } + + public void EndTransformFeedback() + { + _renderer.New<EndTransformFeedbackCommand>(); + _renderer.QueueCommand(); + } + + public void SetAlphaTest(bool enable, float reference, CompareOp op) + { + _renderer.New<SetAlphaTestCommand>().Set(enable, reference, op); + _renderer.QueueCommand(); + } + + public void SetBlendState(AdvancedBlendDescriptor blend) + { + _renderer.New<SetBlendStateAdvancedCommand>().Set(blend); + _renderer.QueueCommand(); + } + + public void SetBlendState(int index, BlendDescriptor blend) + { + _renderer.New<SetBlendStateCommand>().Set(index, blend); + _renderer.QueueCommand(); + } + + public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) + { + _renderer.New<SetDepthBiasCommand>().Set(enables, factor, units, clamp); + _renderer.QueueCommand(); + } + + public void SetDepthClamp(bool clamp) + { + _renderer.New<SetDepthClampCommand>().Set(clamp); + _renderer.QueueCommand(); + } + + public void SetDepthMode(DepthMode mode) + { + _renderer.New<SetDepthModeCommand>().Set(mode); + _renderer.QueueCommand(); + } + + public void SetDepthTest(DepthTestDescriptor depthTest) + { + _renderer.New<SetDepthTestCommand>().Set(depthTest); + _renderer.QueueCommand(); + } + + public void SetFaceCulling(bool enable, Face face) + { + _renderer.New<SetFaceCullingCommand>().Set(enable, face); + _renderer.QueueCommand(); + } + + public void SetFrontFace(FrontFace frontFace) + { + _renderer.New<SetFrontFaceCommand>().Set(frontFace); + _renderer.QueueCommand(); + } + + public void SetImage(int binding, ITexture texture, Format imageFormat) + { + _renderer.New<SetImageCommand>().Set(binding, Ref(texture), imageFormat); + _renderer.QueueCommand(); + } + + public void SetIndexBuffer(BufferRange buffer, IndexType type) + { + _renderer.New<SetIndexBufferCommand>().Set(buffer, type); + _renderer.QueueCommand(); + } + + public void SetLineParameters(float width, bool smooth) + { + _renderer.New<SetLineParametersCommand>().Set(width, smooth); + _renderer.QueueCommand(); + } + + public void SetLogicOpState(bool enable, LogicalOp op) + { + _renderer.New<SetLogicOpStateCommand>().Set(enable, op); + _renderer.QueueCommand(); + } + + public void SetMultisampleState(MultisampleDescriptor multisample) + { + _renderer.New<SetMultisampleStateCommand>().Set(multisample); + _renderer.QueueCommand(); + } + + public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) + { + _renderer.New<SetPatchParametersCommand>().Set(vertices, defaultOuterLevel, defaultInnerLevel); + _renderer.QueueCommand(); + } + + public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) + { + _renderer.New<SetPointParametersCommand>().Set(size, isProgramPointSize, enablePointSprite, origin); + _renderer.QueueCommand(); + } + + public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) + { + _renderer.New<SetPolygonModeCommand>().Set(frontMode, backMode); + _renderer.QueueCommand(); + } + + public void SetPrimitiveRestart(bool enable, int index) + { + _renderer.New<SetPrimitiveRestartCommand>().Set(enable, index); + _renderer.QueueCommand(); + } + + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + _renderer.New<SetPrimitiveTopologyCommand>().Set(topology); + _renderer.QueueCommand(); + } + + public void SetProgram(IProgram program) + { + _renderer.New<SetProgramCommand>().Set(Ref(program)); + _renderer.QueueCommand(); + } + + public void SetRasterizerDiscard(bool discard) + { + _renderer.New<SetRasterizerDiscardCommand>().Set(discard); + _renderer.QueueCommand(); + } + + public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) + { + _renderer.New<SetRenderTargetColorMasksCommand>().Set(_renderer.CopySpan(componentMask)); + _renderer.QueueCommand(); + } + + public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + { + _renderer.New<SetRenderTargetsCommand>().Set(Ref(colors.ToArray()), Ref(depthStencil)); + _renderer.QueueCommand(); + } + + public void SetRenderTargetScale(float scale) + { + _renderer.New<SetRenderTargetScaleCommand>().Set(scale); + _renderer.QueueCommand(); + } + + public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors) + { + _renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors)); + _renderer.QueueCommand(); + } + + public void SetStencilTest(StencilTestDescriptor stencilTest) + { + _renderer.New<SetStencilTestCommand>().Set(stencilTest); + _renderer.QueueCommand(); + } + + public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers) + { + _renderer.New<SetStorageBuffersCommand>().Set(_renderer.CopySpan(buffers)); + _renderer.QueueCommand(); + } + + public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) + { + _renderer.New<SetTextureAndSamplerCommand>().Set(stage, binding, Ref(texture), Ref(sampler)); + _renderer.QueueCommand(); + } + + public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) + { + _renderer.New<SetTransformFeedbackBuffersCommand>().Set(_renderer.CopySpan(buffers)); + _renderer.QueueCommand(); + } + + public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers) + { + _renderer.New<SetUniformBuffersCommand>().Set(_renderer.CopySpan(buffers)); + _renderer.QueueCommand(); + } + + public void SetUserClipDistance(int index, bool enableClip) + { + _renderer.New<SetUserClipDistanceCommand>().Set(index, enableClip); + _renderer.QueueCommand(); + } + + public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) + { + _renderer.New<SetVertexAttribsCommand>().Set(_renderer.CopySpan(vertexAttribs)); + _renderer.QueueCommand(); + } + + public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers) + { + _renderer.New<SetVertexBuffersCommand>().Set(_renderer.CopySpan(vertexBuffers)); + _renderer.QueueCommand(); + } + + public void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform) + { + _renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports), disableTransform); + _renderer.QueueCommand(); + } + + public void TextureBarrier() + { + _renderer.New<TextureBarrierCommand>(); + _renderer.QueueCommand(); + } + + public void TextureBarrierTiled() + { + _renderer.New<TextureBarrierTiledCommand>(); + _renderer.QueueCommand(); + } + + public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) + { + var evt = value as ThreadedCounterEvent; + if (evt != null) + { + if (compare == 0 && evt.Type == CounterType.SamplesPassed && evt.ClearCounter) + { + if (!evt.ReserveForHostAccess()) + { + return false; + } + + _renderer.New<TryHostConditionalRenderingCommand>().Set(Ref(evt), compare, isEqual); + _renderer.QueueCommand(); + return true; + } + } + + _renderer.New<TryHostConditionalRenderingFlushCommand>().Set(Ref(evt), Ref<ThreadedCounterEvent>(null), isEqual); + _renderer.QueueCommand(); + return false; + } + + public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) + { + _renderer.New<TryHostConditionalRenderingFlushCommand>().Set(Ref(value as ThreadedCounterEvent), Ref(compare as ThreadedCounterEvent), isEqual); + _renderer.QueueCommand(); + return false; + } + + public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount) + { + _renderer.New<UpdateRenderScaleCommand>().Set(_renderer.CopySpan(scales.Slice(0, totalCount)), totalCount, fragmentCount); + _renderer.QueueCommand(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs new file mode 100644 index 00000000..2148f43f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -0,0 +1,488 @@ +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Graphics.GAL.Multithreading.Commands; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer; +using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + /// <summary> + /// The ThreadedRenderer is a layer that can be put in front of any Renderer backend to make + /// its processing happen on a separate thread, rather than intertwined with the GPU emulation. + /// A new thread is created to handle the GPU command processing, separate from the renderer thread. + /// Calls to the renderer, pipeline and resources are queued to happen on the renderer thread. + /// </summary> + public class ThreadedRenderer : IRenderer + { + private const int SpanPoolBytes = 4 * 1024 * 1024; + private const int MaxRefsPerCommand = 2; + private const int QueueCount = 10000; + + private int _elementSize; + private IRenderer _baseRenderer; + private Thread _gpuThread; + private Thread _backendThread; + private bool _disposed; + private bool _running; + + private AutoResetEvent _frameComplete = new AutoResetEvent(true); + + private ManualResetEventSlim _galWorkAvailable; + private CircularSpanPool _spanPool; + + private ManualResetEventSlim _invokeRun; + private AutoResetEvent _interruptRun; + + private bool _lastSampleCounterClear = true; + + private byte[] _commandQueue; + private object[] _refQueue; + + private int _consumerPtr; + private int _commandCount; + + private int _producerPtr; + private int _lastProducedPtr; + private int _invokePtr; + + private int _refProducerPtr; + private int _refConsumerPtr; + + private Action _interruptAction; + + public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; + + internal BufferMap Buffers { get; } + internal SyncMap Sync { get; } + internal CircularSpanPool SpanPool { get; } + internal ProgramQueue Programs { get; } + + public IPipeline Pipeline { get; } + public IWindow Window { get; } + + public IRenderer BaseRenderer => _baseRenderer; + + public bool PreferThreading => _baseRenderer.PreferThreading; + + public ThreadedRenderer(IRenderer renderer) + { + _baseRenderer = renderer; + + renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info); + renderer.SetInterruptAction(Interrupt); + + Pipeline = new ThreadedPipeline(this, renderer.Pipeline); + Window = new ThreadedWindow(this, renderer); + Buffers = new BufferMap(); + Sync = new SyncMap(); + Programs = new ProgramQueue(renderer); + + _galWorkAvailable = new ManualResetEventSlim(false); + _invokeRun = new ManualResetEventSlim(); + _interruptRun = new AutoResetEvent(false); + _spanPool = new CircularSpanPool(this, SpanPoolBytes); + SpanPool = _spanPool; + + _elementSize = BitUtils.AlignUp(CommandHelper.GetMaxCommandSize(), 4); + + _commandQueue = new byte[_elementSize * QueueCount]; + _refQueue = new object[MaxRefsPerCommand * QueueCount]; + } + + public void RunLoop(Action gpuLoop) + { + _running = true; + + _backendThread = Thread.CurrentThread; + + _gpuThread = new Thread(() => { + gpuLoop(); + _running = false; + _galWorkAvailable.Set(); + }); + + _gpuThread.Name = "GPU.MainThread"; + _gpuThread.Start(); + + RenderLoop(); + } + + public void RenderLoop() + { + // Power through the render queue until the Gpu thread work is done. + + while (_running && !_disposed) + { + _galWorkAvailable.Wait(); + _galWorkAvailable.Reset(); + + if (Volatile.Read(ref _interruptAction) != null) + { + _interruptAction(); + _interruptRun.Set(); + + Interlocked.Exchange(ref _interruptAction, null); + } + + // The other thread can only increase the command count. + // We can assume that if it is above 0, it will stay there or get higher. + + while (Volatile.Read(ref _commandCount) > 0 && Volatile.Read(ref _interruptAction) == null) + { + int commandPtr = _consumerPtr; + + Span<byte> command = new Span<byte>(_commandQueue, commandPtr * _elementSize, _elementSize); + + // Run the command. + + CommandHelper.RunCommand(command, this, _baseRenderer); + + if (Interlocked.CompareExchange(ref _invokePtr, -1, commandPtr) == commandPtr) + { + _invokeRun.Set(); + } + + _consumerPtr = (_consumerPtr + 1) % QueueCount; + + Interlocked.Decrement(ref _commandCount); + } + } + } + + internal SpanRef<T> CopySpan<T>(ReadOnlySpan<T> data) where T : unmanaged + { + return _spanPool.Insert(data); + } + + private TableRef<T> Ref<T>(T reference) + { + return new TableRef<T>(this, reference); + } + + internal ref T New<T>() where T : struct + { + while (_producerPtr == (Volatile.Read(ref _consumerPtr) + QueueCount - 1) % QueueCount) + { + // If incrementing the producer pointer would overflow, we need to wait. + // _consumerPtr can only move forward, so there's no race to worry about here. + + Thread.Sleep(1); + } + + int taken = _producerPtr; + _lastProducedPtr = taken; + + _producerPtr = (_producerPtr + 1) % QueueCount; + + Span<byte> memory = new Span<byte>(_commandQueue, taken * _elementSize, _elementSize); + ref T result = ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(memory)); + + memory[memory.Length - 1] = (byte)((IGALCommand)result).CommandType; + + return ref result; + } + + internal int AddTableRef(object obj) + { + // The reference table is sized so that it will never overflow, so long as the references are taken after the command is allocated. + + int index = _refProducerPtr; + + _refQueue[index] = obj; + + _refProducerPtr = (_refProducerPtr + 1) % _refQueue.Length; + + return index; + } + + internal object RemoveTableRef(int index) + { + Debug.Assert(index == _refConsumerPtr); + + object result = _refQueue[_refConsumerPtr]; + _refQueue[_refConsumerPtr] = null; + + _refConsumerPtr = (_refConsumerPtr + 1) % _refQueue.Length; + + return result; + } + + internal void QueueCommand() + { + int result = Interlocked.Increment(ref _commandCount); + + if (result == 1) + { + _galWorkAvailable.Set(); + } + } + + internal void InvokeCommand() + { + _invokeRun.Reset(); + _invokePtr = _lastProducedPtr; + + QueueCommand(); + + // Wait for the command to complete. + _invokeRun.Wait(); + } + + internal void WaitForFrame() + { + _frameComplete.WaitOne(); + } + + internal void SignalFrame() + { + _frameComplete.Set(); + } + + internal bool IsGpuThread() + { + return Thread.CurrentThread == _gpuThread; + } + + public void BackgroundContextAction(Action action, bool alwaysBackground = false) + { + if (IsGpuThread() && !alwaysBackground) + { + // The action must be performed on the render thread. + New<ActionCommand>().Set(Ref(action)); + InvokeCommand(); + } + else + { + _baseRenderer.BackgroundContextAction(action, true); + } + } + + public BufferHandle CreateBuffer(int size, BufferHandle storageHint) + { + BufferHandle handle = Buffers.CreateBufferHandle(); + New<CreateBufferCommand>().Set(handle, size, storageHint); + QueueCommand(); + + return handle; + } + + public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) + { + var program = new ThreadedProgram(this); + + SourceProgramRequest request = new SourceProgramRequest(program, shaders, info); + + Programs.Add(request); + + New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); + QueueCommand(); + + return program; + } + + public ISampler CreateSampler(SamplerCreateInfo info) + { + var sampler = new ThreadedSampler(this); + New<CreateSamplerCommand>().Set(Ref(sampler), info); + QueueCommand(); + + return sampler; + } + + public void CreateSync(ulong id, bool strict) + { + Sync.CreateSyncHandle(id); + New<CreateSyncCommand>().Set(id, strict); + QueueCommand(); + } + + public ITexture CreateTexture(TextureCreateInfo info, float scale) + { + if (IsGpuThread()) + { + var texture = new ThreadedTexture(this, info, scale); + New<CreateTextureCommand>().Set(Ref(texture), info, scale); + QueueCommand(); + + return texture; + } + else + { + var texture = new ThreadedTexture(this, info, scale); + texture.Base = _baseRenderer.CreateTexture(info, scale); + + return texture; + } + } + + public void DeleteBuffer(BufferHandle buffer) + { + New<BufferDisposeCommand>().Set(buffer); + QueueCommand(); + } + + public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) + { + if (IsGpuThread()) + { + ResultBox<PinnedSpan<byte>> box = new ResultBox<PinnedSpan<byte>>(); + New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box)); + InvokeCommand(); + + return box.Result; + } + else + { + return _baseRenderer.GetBufferData(Buffers.MapBufferBlocking(buffer), offset, size); + } + } + + public Capabilities GetCapabilities() + { + ResultBox<Capabilities> box = new ResultBox<Capabilities>(); + New<GetCapabilitiesCommand>().Set(Ref(box)); + InvokeCommand(); + + return box.Result; + } + + public ulong GetCurrentSync() + { + return _baseRenderer.GetCurrentSync(); + } + + public HardwareInfo GetHardwareInfo() + { + return _baseRenderer.GetHardwareInfo(); + } + + /// <summary> + /// Initialize the base renderer. Must be called on the render thread. + /// </summary> + /// <param name="logLevel">Log level to use</param> + public void Initialize(GraphicsDebugLevel logLevel) + { + _baseRenderer.Initialize(logLevel); + } + + public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) + { + var program = new ThreadedProgram(this); + + BinaryProgramRequest request = new BinaryProgramRequest(program, programBinary, hasFragmentShader, info); + Programs.Add(request); + + New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); + QueueCommand(); + + return program; + } + + public void PreFrame() + { + New<PreFrameCommand>(); + QueueCommand(); + } + + public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) + { + ThreadedCounterEvent evt = new ThreadedCounterEvent(this, type, _lastSampleCounterClear); + New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), hostReserved); + QueueCommand(); + + if (type == CounterType.SamplesPassed) + { + _lastSampleCounterClear = false; + } + + return evt; + } + + public void ResetCounter(CounterType type) + { + New<ResetCounterCommand>().Set(type); + QueueCommand(); + _lastSampleCounterClear = true; + } + + public void Screenshot() + { + _baseRenderer.Screenshot(); + } + + public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) + { + New<BufferSetDataCommand>().Set(buffer, offset, CopySpan(data)); + QueueCommand(); + } + + public void UpdateCounters() + { + New<UpdateCountersCommand>(); + QueueCommand(); + } + + public void WaitSync(ulong id) + { + Sync.WaitSyncAvailability(id); + + _baseRenderer.WaitSync(id); + } + + private void Interrupt(Action action) + { + // Interrupt the backend thread from any external thread and invoke the given action. + + if (Thread.CurrentThread == _backendThread) + { + // If this is called from the backend thread, the action can run immediately. + action(); + } + else + { + while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { } + + _galWorkAvailable.Set(); + + _interruptRun.WaitOne(); + } + } + + public void SetInterruptAction(Action<Action> interruptAction) + { + // Threaded renderer ignores given interrupt action, as it provides its own to the child renderer. + } + + public void Dispose() + { + // Dispose must happen from the render thread, after all commands have completed. + + // Stop the GPU thread. + _disposed = true; + + if (_gpuThread != null && _gpuThread.IsAlive) + { + _gpuThread.Join(); + } + + // Dispose the renderer. + _baseRenderer.Dispose(); + + // Dispose events. + _frameComplete.Dispose(); + _galWorkAvailable.Dispose(); + _invokeRun.Dispose(); + _interruptRun.Dispose(); + + Sync.Dispose(); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs new file mode 100644 index 00000000..a647d37e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -0,0 +1,42 @@ +using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; +using Ryujinx.Graphics.GAL.Multithreading.Model; +using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; + +namespace Ryujinx.Graphics.GAL.Multithreading +{ + public class ThreadedWindow : IWindow + { + private ThreadedRenderer _renderer; + private IRenderer _impl; + + public ThreadedWindow(ThreadedRenderer renderer, IRenderer impl) + { + _renderer = renderer; + _impl = impl; + } + + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + { + // If there's already a frame in the pipeline, wait for it to be presented first. + // This is a multithread rate limit - we can't be more than one frame behind the command queue. + + _renderer.WaitForFrame(); + _renderer.New<WindowPresentCommand>().Set(new TableRef<ThreadedTexture>(_renderer, texture as ThreadedTexture), crop, new TableRef<Action>(_renderer, swapBuffersCallback)); + _renderer.QueueCommand(); + } + + public void SetSize(int width, int height) + { + _impl.Window.SetSize(width, height); + } + + public void ChangeVSyncMode(bool vsyncEnabled) { } + + public void SetAntiAliasing(AntiAliasing effect) { } + + public void SetScalingFilter(ScalingFilter type) { } + + public void SetScalingFilterLevel(float level) { } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Origin.cs b/src/Ryujinx.Graphics.GAL/Origin.cs new file mode 100644 index 00000000..d1b69cfd --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Origin.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum Origin + { + UpperLeft, + LowerLeft + } +} diff --git a/src/Ryujinx.Graphics.GAL/PinnedSpan.cs b/src/Ryujinx.Graphics.GAL/PinnedSpan.cs new file mode 100644 index 00000000..275b3b86 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/PinnedSpan.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL +{ + public unsafe struct PinnedSpan<T> : IDisposable where T : unmanaged + { + private void* _ptr; + private int _size; + private Action _disposeAction; + + /// <summary> + /// Creates a new PinnedSpan from an existing ReadOnlySpan. The span *must* be pinned in memory. + /// The data must be guaranteed to live until disposeAction is called. + /// </summary> + /// <param name="span">Existing span</param> + /// <param name="disposeAction">Action to call on dispose</param> + /// <remarks> + /// If a dispose action is not provided, it is safe to assume the resource will be available until the next call. + /// </remarks> + public static PinnedSpan<T> UnsafeFromSpan(ReadOnlySpan<T> span, Action disposeAction = null) + { + return new PinnedSpan<T>(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)), span.Length, disposeAction); + } + + /// <summary> + /// Creates a new PinnedSpan from an existing unsafe region. The data must be guaranteed to live until disposeAction is called. + /// </summary> + /// <param name="ptr">Pointer to the region</param> + /// <param name="size">The total items of T the region contains</param> + /// <param name="disposeAction">Action to call on dispose</param> + /// <remarks> + /// If a dispose action is not provided, it is safe to assume the resource will be available until the next call. + /// </remarks> + public PinnedSpan(void* ptr, int size, Action disposeAction = null) + { + _ptr = ptr; + _size = size; + _disposeAction = disposeAction; + } + + public ReadOnlySpan<T> Get() + { + return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>()); + } + + public void Dispose() + { + _disposeAction?.Invoke(); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/PolygonMode.cs b/src/Ryujinx.Graphics.GAL/PolygonMode.cs new file mode 100644 index 00000000..d6110c1b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/PolygonMode.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum PolygonMode + { + Point = 0x1b00, + Line = 0x1b01, + Fill = 0x1b02 + } +} diff --git a/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs b/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs new file mode 100644 index 00000000..514b4231 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/PolygonModeMask.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + [Flags] + public enum PolygonModeMask + { + Point = 1 << 0, + Line = 1 << 1, + Fill = 1 << 2 + } +} diff --git a/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs b/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs new file mode 100644 index 00000000..ed567a68 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/PrimitiveTopology.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum PrimitiveTopology + { + Points, + Lines, + LineLoop, + LineStrip, + Triangles, + TriangleStrip, + TriangleFan, + Quads, + QuadStrip, + Polygon, + LinesAdjacency, + LineStripAdjacency, + TrianglesAdjacency, + TriangleStripAdjacency, + Patches + } +} diff --git a/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs b/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs new file mode 100644 index 00000000..5ca1be8c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ProgramLinkStatus.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum ProgramLinkStatus + { + Incomplete, + Success, + Failure + } +} diff --git a/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs b/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs new file mode 100644 index 00000000..41afb34b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ProgramPipelineState.cs @@ -0,0 +1,78 @@ +using Ryujinx.Common.Memory; +using System; + +namespace Ryujinx.Graphics.GAL +{ + /// <summary> + /// Descriptor for a pipeline buffer binding. + /// </summary> + public readonly struct BufferPipelineDescriptor + { + public bool Enable { get; } + public int Stride { get; } + public int Divisor { get; } + + public BufferPipelineDescriptor(bool enable, int stride, int divisor) + { + Enable = enable; + Stride = stride; + Divisor = divisor; + } + } + + /// <summary> + /// State required for a program to compile shaders. + /// </summary> + public struct ProgramPipelineState + { + // Some state is considered always dynamic and should not be included: + // - Viewports/Scissors + // - Bias values (not enable) + + public int SamplesCount; + public Array8<bool> AttachmentEnable; + public Array8<Format> AttachmentFormats; + public bool DepthStencilEnable; + public Format DepthStencilFormat; + + public bool LogicOpEnable; + public LogicalOp LogicOp; + public Array8<BlendDescriptor> BlendDescriptors; + public Array8<uint> ColorWriteMask; + + public int VertexAttribCount; + public Array32<VertexAttribDescriptor> VertexAttribs; + + public int VertexBufferCount; + public Array32<BufferPipelineDescriptor> VertexBuffers; + + // TODO: Min/max depth bounds. + public DepthTestDescriptor DepthTest; + public StencilTestDescriptor StencilTest; + public FrontFace FrontFace; + public Face CullMode; + public bool CullEnable; + + public PolygonModeMask BiasEnable; + + public float LineWidth; + // TODO: Polygon mode. + public bool DepthClampEnable; + public bool RasterizerDiscard; + public PrimitiveTopology Topology; + public bool PrimitiveRestartEnable; + public uint PatchControlPoints; + + public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) + { + VertexAttribCount = vertexAttribs.Length; + vertexAttribs.CopyTo(VertexAttribs.AsSpan()); + } + + public void SetLogicOpState(bool enable, LogicalOp op) + { + LogicOp = op; + LogicOpEnable = enable; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Rectangle.cs b/src/Ryujinx.Graphics.GAL/Rectangle.cs new file mode 100644 index 00000000..c8fa93d9 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Rectangle.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct Rectangle<T> where T : unmanaged + { + public T X { get; } + public T Y { get; } + public T Width { get; } + public T Height { get; } + + public Rectangle(T x, T y, T width, T height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj b/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj new file mode 100644 index 00000000..189108a3 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net7.0</TargetFramework> + </PropertyGroup> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" /> + <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs b/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs new file mode 100644 index 00000000..990c302e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs @@ -0,0 +1,72 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct SamplerCreateInfo + { + public MinFilter MinFilter { get; } + public MagFilter MagFilter { get; } + + public bool SeamlessCubemap { get; } + + public AddressMode AddressU { get; } + public AddressMode AddressV { get; } + public AddressMode AddressP { get; } + + public CompareMode CompareMode { get; } + public CompareOp CompareOp { get; } + + public ColorF BorderColor { get; } + + public float MinLod { get; } + public float MaxLod { get; } + public float MipLodBias { get; } + public float MaxAnisotropy { get; } + + public SamplerCreateInfo( + MinFilter minFilter, + MagFilter magFilter, + bool seamlessCubemap, + AddressMode addressU, + AddressMode addressV, + AddressMode addressP, + CompareMode compareMode, + CompareOp compareOp, + ColorF borderColor, + float minLod, + float maxLod, + float mipLodBias, + float maxAnisotropy) + { + MinFilter = minFilter; + MagFilter = magFilter; + SeamlessCubemap = seamlessCubemap; + AddressU = addressU; + AddressV = addressV; + AddressP = addressP; + CompareMode = compareMode; + CompareOp = compareOp; + BorderColor = borderColor; + MinLod = minLod; + MaxLod = maxLod; + MipLodBias = mipLodBias; + MaxAnisotropy = maxAnisotropy; + } + + public static SamplerCreateInfo Create(MinFilter minFilter, MagFilter magFilter) + { + return new SamplerCreateInfo( + minFilter, + magFilter, + false, + AddressMode.ClampToEdge, + AddressMode.ClampToEdge, + AddressMode.ClampToEdge, + CompareMode.None, + CompareOp.Always, + new ColorF(0f, 0f, 0f, 0f), + 0f, + 0f, + 0f, + 1f); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs b/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs new file mode 100644 index 00000000..129913ec --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct ScreenCaptureImageInfo + { + public ScreenCaptureImageInfo(int width, int height, bool isBgra, byte[] data, bool flipX, bool flipY) + { + Width = width; + Height = height; + IsBgra = isBgra; + Data = data; + FlipX = flipX; + FlipY = flipY; + } + + public int Width { get; } + public int Height { get; } + public byte[] Data { get; } + public bool IsBgra { get; } + public bool FlipX { get; } + public bool FlipY { get; } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/ShaderBindings.cs b/src/Ryujinx.Graphics.GAL/ShaderBindings.cs new file mode 100644 index 00000000..6ab29382 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ShaderBindings.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.GAL +{ + public readonly struct ShaderBindings + { + public IReadOnlyCollection<int> UniformBufferBindings { get; } + public IReadOnlyCollection<int> StorageBufferBindings { get; } + public IReadOnlyCollection<int> TextureBindings { get; } + public IReadOnlyCollection<int> ImageBindings { get; } + + public ShaderBindings( + IReadOnlyCollection<int> uniformBufferBindings, + IReadOnlyCollection<int> storageBufferBindings, + IReadOnlyCollection<int> textureBindings, + IReadOnlyCollection<int> imageBindings) + { + UniformBufferBindings = uniformBufferBindings; + StorageBufferBindings = storageBufferBindings; + TextureBindings = textureBindings; + ImageBindings = imageBindings; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs new file mode 100644 index 00000000..b4c87117 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct ShaderInfo + { + public int FragmentOutputMap { get; } + public ProgramPipelineState? State { get; } + public bool FromCache { get; set; } + + public ShaderInfo(int fragmentOutputMap, ProgramPipelineState state, bool fromCache = false) + { + FragmentOutputMap = fragmentOutputMap; + State = state; + FromCache = fromCache; + } + + public ShaderInfo(int fragmentOutputMap, bool fromCache = false) + { + FragmentOutputMap = fragmentOutputMap; + State = null; + FromCache = fromCache; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/ShaderSource.cs b/src/Ryujinx.Graphics.GAL/ShaderSource.cs new file mode 100644 index 00000000..91d3a632 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ShaderSource.cs @@ -0,0 +1,31 @@ +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.GAL +{ + public readonly struct ShaderSource + { + public string Code { get; } + public byte[] BinaryCode { get; } + public ShaderBindings Bindings { get; } + public ShaderStage Stage { get; } + public TargetLanguage Language { get; } + + public ShaderSource(string code, byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) + { + Code = code; + BinaryCode = binaryCode; + Bindings = bindings; + Stage = stage; + Language = language; + } + + public ShaderSource(string code, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(code, null, bindings, stage, language) + { + } + + public ShaderSource(byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, bindings, stage, language) + { + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/StencilOp.cs b/src/Ryujinx.Graphics.GAL/StencilOp.cs new file mode 100644 index 00000000..fe999b0f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/StencilOp.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum StencilOp + { + Keep = 1, + Zero, + Replace, + IncrementAndClamp, + DecrementAndClamp, + Invert, + IncrementAndWrap, + DecrementAndWrap, + + ZeroGl = 0x0, + InvertGl = 0x150a, + KeepGl = 0x1e00, + ReplaceGl = 0x1e01, + IncrementAndClampGl = 0x1e02, + DecrementAndClampGl = 0x1e03, + IncrementAndWrapGl = 0x8507, + DecrementAndWrapGl = 0x8508 + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs b/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs new file mode 100644 index 00000000..db46c957 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs @@ -0,0 +1,56 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct StencilTestDescriptor + { + public bool TestEnable { get; } + + public CompareOp FrontFunc { get; } + public StencilOp FrontSFail { get; } + public StencilOp FrontDpPass { get; } + public StencilOp FrontDpFail { get; } + public int FrontFuncRef { get; } + public int FrontFuncMask { get; } + public int FrontMask { get; } + public CompareOp BackFunc { get; } + public StencilOp BackSFail { get; } + public StencilOp BackDpPass { get; } + public StencilOp BackDpFail { get; } + public int BackFuncRef { get; } + public int BackFuncMask { get; } + public int BackMask { get; } + + public StencilTestDescriptor( + bool testEnable, + CompareOp frontFunc, + StencilOp frontSFail, + StencilOp frontDpPass, + StencilOp frontDpFail, + int frontFuncRef, + int frontFuncMask, + int frontMask, + CompareOp backFunc, + StencilOp backSFail, + StencilOp backDpPass, + StencilOp backDpFail, + int backFuncRef, + int backFuncMask, + int backMask) + { + TestEnable = testEnable; + FrontFunc = frontFunc; + FrontSFail = frontSFail; + FrontDpPass = frontDpPass; + FrontDpFail = frontDpFail; + FrontFuncRef = frontFuncRef; + FrontFuncMask = frontFuncMask; + FrontMask = frontMask; + BackFunc = backFunc; + BackSFail = backSFail; + BackDpPass = backDpPass; + BackDpFail = backDpFail; + BackFuncRef = backFuncRef; + BackFuncMask = backFuncMask; + BackMask = backMask; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs new file mode 100644 index 00000000..6eeddb6c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs @@ -0,0 +1,101 @@ +using Ryujinx.Graphics.Shader; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.GAL +{ + public class SupportBufferUpdater : IDisposable + { + public SupportBuffer Data; + public BufferHandle Handle; + + private IRenderer _renderer; + private int _startOffset = -1; + private int _endOffset = -1; + + public SupportBufferUpdater(IRenderer renderer) + { + _renderer = renderer; + Handle = renderer.CreateBuffer(SupportBuffer.RequiredSize); + renderer.Pipeline.ClearBuffer(Handle, 0, SupportBuffer.RequiredSize, 0); + } + + private void MarkDirty(int startOffset, int byteSize) + { + int endOffset = startOffset + byteSize; + + if (_startOffset == -1) + { + _startOffset = startOffset; + _endOffset = endOffset; + } + else + { + if (startOffset < _startOffset) + { + _startOffset = startOffset; + } + + if (endOffset > _endOffset) + { + _endOffset = endOffset; + } + } + } + + public void UpdateFragmentRenderScaleCount(int count) + { + if (Data.FragmentRenderScaleCount.X != count) + { + Data.FragmentRenderScaleCount.X = count; + + MarkDirty(SupportBuffer.FragmentRenderScaleCountOffset, sizeof(int)); + } + } + + private void UpdateGenericField<T>(int baseOffset, ReadOnlySpan<T> data, Span<T> target, int offset, int count) where T : unmanaged + { + data.Slice(0, count).CopyTo(target.Slice(offset)); + + int elemSize = Unsafe.SizeOf<T>(); + + MarkDirty(baseOffset + offset * elemSize, count * elemSize); + } + + public void UpdateRenderScale(ReadOnlySpan<Vector4<float>> data, int offset, int count) + { + UpdateGenericField(SupportBuffer.GraphicsRenderScaleOffset, data, Data.RenderScale.AsSpan(), offset, count); + } + + public void UpdateFragmentIsBgra(ReadOnlySpan<Vector4<int>> data, int offset, int count) + { + UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.AsSpan(), offset, count); + } + + public void UpdateViewportInverse(Vector4<float> data) + { + Data.ViewportInverse = data; + + MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize); + } + + public void Commit() + { + if (_startOffset != -1) + { + ReadOnlySpan<byte> data = MemoryMarshal.Cast<SupportBuffer, byte>(MemoryMarshal.CreateSpan(ref Data, 1)); + + _renderer.SetBufferData(Handle, _startOffset, data.Slice(_startOffset, _endOffset - _startOffset)); + + _startOffset = -1; + _endOffset = -1; + } + } + + public void Dispose() + { + _renderer.DeleteBuffer(Handle); + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs b/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs new file mode 100644 index 00000000..a405bd13 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/SwizzleComponent.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum SwizzleComponent + { + Zero, + One, + Red, + Green, + Blue, + Alpha + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/Target.cs b/src/Ryujinx.Graphics.GAL/Target.cs new file mode 100644 index 00000000..711eea24 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Target.cs @@ -0,0 +1,34 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum Target + { + Texture1D, + Texture2D, + Texture3D, + Texture1DArray, + Texture2DArray, + Texture2DMultisample, + Texture2DMultisampleArray, + Cubemap, + CubemapArray, + TextureBuffer + } + + public static class TargetExtensions + { + public static bool IsMultisample(this Target target) + { + return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray; + } + + public static bool HasDepthOrLayers(this Target target) + { + return target == Target.Texture3D || + target == Target.Texture1DArray || + target == Target.Texture2DArray || + target == Target.Texture2DMultisampleArray || + target == Target.Cubemap || + target == Target.CubemapArray; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs new file mode 100644 index 00000000..52b3b11f --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs @@ -0,0 +1,164 @@ +using Ryujinx.Common; +using System; +using System.Numerics; + +namespace Ryujinx.Graphics.GAL +{ + public readonly struct TextureCreateInfo : IEquatable<TextureCreateInfo> + { + public int Width { get; } + public int Height { get; } + public int Depth { get; } + public int Levels { get; } + public int Samples { get; } + public int BlockWidth { get; } + public int BlockHeight { get; } + public int BytesPerPixel { get; } + + public bool IsCompressed => (BlockWidth | BlockHeight) != 1; + + public Format Format { get; } + + public DepthStencilMode DepthStencilMode { get; } + + public Target Target { get; } + + public SwizzleComponent SwizzleR { get; } + public SwizzleComponent SwizzleG { get; } + public SwizzleComponent SwizzleB { get; } + public SwizzleComponent SwizzleA { get; } + + public TextureCreateInfo( + int width, + int height, + int depth, + int levels, + int samples, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + DepthStencilMode depthStencilMode, + Target target, + SwizzleComponent swizzleR, + SwizzleComponent swizzleG, + SwizzleComponent swizzleB, + SwizzleComponent swizzleA) + { + Width = width; + Height = height; + Depth = depth; + Levels = levels; + Samples = samples; + BlockWidth = blockWidth; + BlockHeight = blockHeight; + BytesPerPixel = bytesPerPixel; + Format = format; + DepthStencilMode = depthStencilMode; + Target = target; + SwizzleR = swizzleR; + SwizzleG = swizzleG; + SwizzleB = swizzleB; + SwizzleA = swizzleA; + } + + public int GetMipSize(int level) + { + return GetMipStride(level) * GetLevelHeight(level) * GetLevelDepth(level); + } + + public int GetMipSize2D(int level) + { + return GetMipStride(level) * GetLevelHeight(level); + } + + public int GetMipStride(int level) + { + return BitUtils.AlignUp(GetLevelWidth(level) * BytesPerPixel, 4); + } + + private int GetLevelWidth(int level) + { + return BitUtils.DivRoundUp(GetLevelSize(Width, level), BlockWidth); + } + + private int GetLevelHeight(int level) + { + return BitUtils.DivRoundUp(GetLevelSize(Height, level), BlockHeight); + } + + private int GetLevelDepth(int level) + { + return Target == Target.Texture3D ? GetLevelSize(Depth, level) : GetLayers(); + } + + public int GetDepthOrLayers() + { + return Target == Target.Texture3D ? Depth : GetLayers(); + } + + public int GetLayers() + { + if (Target == Target.Texture2DArray || + Target == Target.Texture2DMultisampleArray || + Target == Target.CubemapArray) + { + return Depth; + } + else if (Target == Target.Cubemap) + { + return 6; + } + + return 1; + } + + public int GetLevelsClamped() + { + int maxSize = Width; + + if (Target != Target.Texture1D && + Target != Target.Texture1DArray) + { + maxSize = Math.Max(maxSize, Height); + } + + if (Target == Target.Texture3D) + { + maxSize = Math.Max(maxSize, Depth); + } + + int maxLevels = BitOperations.Log2((uint)maxSize) + 1; + return Math.Min(Levels, maxLevels); + } + + private static int GetLevelSize(int size, int level) + { + return Math.Max(1, size >> level); + } + + public override int GetHashCode() + { + return HashCode.Combine(Width, Height); + } + + bool IEquatable<TextureCreateInfo>.Equals(TextureCreateInfo other) + { + return Width == other.Width && + Height == other.Height && + Depth == other.Depth && + Levels == other.Levels && + Samples == other.Samples && + BlockWidth == other.BlockWidth && + BlockHeight == other.BlockHeight && + BytesPerPixel == other.BytesPerPixel && + Format == other.Format && + DepthStencilMode == other.DepthStencilMode && + Target == other.Target && + SwizzleR == other.SwizzleR && + SwizzleG == other.SwizzleG && + SwizzleB == other.SwizzleB && + SwizzleA == other.SwizzleA; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs b/src/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs new file mode 100644 index 00000000..c058df2b --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.GAL +{ + public delegate void TextureReleaseCallback(object context); +} diff --git a/src/Ryujinx.Graphics.GAL/UpscaleType.cs b/src/Ryujinx.Graphics.GAL/UpscaleType.cs new file mode 100644 index 00000000..442b65f2 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/UpscaleType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum ScalingFilter + { + Bilinear, + Nearest, + Fsr + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs b/src/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs new file mode 100644 index 00000000..4f5ea6a6 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly record struct VertexAttribDescriptor(int BufferIndex, int Offset, bool IsZero, Format Format); +} diff --git a/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs b/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs new file mode 100644 index 00000000..15f0dff8 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct VertexBufferDescriptor + { + public BufferRange Buffer { get; } + + public int Stride { get; } + public int Divisor { get; } + + public VertexBufferDescriptor(BufferRange buffer, int stride, int divisor) + { + Buffer = buffer; + Stride = stride; + Divisor = divisor; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Viewport.cs b/src/Ryujinx.Graphics.GAL/Viewport.cs new file mode 100644 index 00000000..94012c00 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/Viewport.cs @@ -0,0 +1,33 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct Viewport + { + public Rectangle<float> Region { get; } + + public ViewportSwizzle SwizzleX { get; } + public ViewportSwizzle SwizzleY { get; } + public ViewportSwizzle SwizzleZ { get; } + public ViewportSwizzle SwizzleW { get; } + + public float DepthNear { get; } + public float DepthFar { get; } + + public Viewport( + Rectangle<float> region, + ViewportSwizzle swizzleX, + ViewportSwizzle swizzleY, + ViewportSwizzle swizzleZ, + ViewportSwizzle swizzleW, + float depthNear, + float depthFar) + { + Region = region; + SwizzleX = swizzleX; + SwizzleY = swizzleY; + SwizzleZ = swizzleZ; + SwizzleW = swizzleW; + DepthNear = depthNear; + DepthFar = depthFar; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs b/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs new file mode 100644 index 00000000..c24a2246 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ViewportSwizzle.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum ViewportSwizzle + { + PositiveX = 0, + NegativeX = 1, + PositiveY = 2, + NegativeY = 3, + PositiveZ = 4, + NegativeZ = 5, + PositiveW = 6, + NegativeW = 7, + + NegativeFlag = 1 + } +} |
