Explorar el Código

Implement scaled vertex format emulation (#5564)

* Implement scaled vertex format emulation

* Auto-format (whitespace)

* Delete ToVec4Type
gdkchan hace 2 años
padre
commit
effd546331

+ 3 - 0
src/Ryujinx.Graphics.GAL/Capabilities.cs

@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.GAL
         public readonly bool SupportsBgraFormat;
         public readonly bool SupportsR4G4Format;
         public readonly bool SupportsR4G4B4A4Format;
+        public readonly bool SupportsScaledVertexFormats;
         public readonly bool SupportsSnormBufferTextureFormat;
         public readonly bool Supports5BitComponentFormat;
         public readonly bool SupportsBlendEquationAdvanced;
@@ -71,6 +72,7 @@ namespace Ryujinx.Graphics.GAL
             bool supportsBgraFormat,
             bool supportsR4G4Format,
             bool supportsR4G4B4A4Format,
+            bool supportsScaledVertexFormats,
             bool supportsSnormBufferTextureFormat,
             bool supports5BitComponentFormat,
             bool supportsBlendEquationAdvanced,
@@ -117,6 +119,7 @@ namespace Ryujinx.Graphics.GAL
             SupportsBgraFormat = supportsBgraFormat;
             SupportsR4G4Format = supportsR4G4Format;
             SupportsR4G4B4A4Format = supportsR4G4B4A4Format;
+            SupportsScaledVertexFormats = supportsScaledVertexFormats;
             SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
             Supports5BitComponentFormat = supports5BitComponentFormat;
             SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;

+ 22 - 5
src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs

@@ -218,17 +218,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         {
             bool changed = false;
             ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
+            bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats;
 
             for (int location = 0; location < state.Length; location++)
             {
                 VertexAttribType type = state[location].UnpackType();
 
-                AttributeType value = type switch
+                AttributeType value;
+
+                if (supportsScaledFormats)
+                {
+                    value = type switch
+                    {
+                        VertexAttribType.Sint => AttributeType.Sint,
+                        VertexAttribType.Uint => AttributeType.Uint,
+                        _ => AttributeType.Float,
+                    };
+                }
+                else
                 {
-                    VertexAttribType.Sint => AttributeType.Sint,
-                    VertexAttribType.Uint => AttributeType.Uint,
-                    _ => AttributeType.Float,
-                };
+                    value = type switch
+                    {
+                        VertexAttribType.Sint => AttributeType.Sint,
+                        VertexAttribType.Uint => AttributeType.Uint,
+                        VertexAttribType.Uscaled => AttributeType.Uscaled,
+                        VertexAttribType.Sscaled => AttributeType.Sscaled,
+                        _ => AttributeType.Float,
+                    };
+                }
 
                 if (attributeTypes[location] != value)
                 {

+ 14 - 1
src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs

@@ -932,6 +932,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
         /// </summary>
         private void UpdateVertexAttribState()
         {
+            bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats;
             uint vbEnableMask = _vbEnableMask;
 
             Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
@@ -949,7 +950,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
                     continue;
                 }
 
-                if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
+                uint packedFormat = vertexAttrib.UnpackFormat();
+
+                if (!supportsScaledFormats)
+                {
+                    packedFormat = vertexAttrib.UnpackType() switch
+                    {
+                        VertexAttribType.Uscaled => ((uint)VertexAttribType.Uint << 27) | (packedFormat & (0x3f << 21)),
+                        VertexAttribType.Sscaled => ((uint)VertexAttribType.Sint << 27) | (packedFormat & (0x3f << 21)),
+                        _ => packedFormat,
+                    };
+                }
+
+                if (!FormatTable.TryGetAttribFormat(packedFormat, out Format format))
                 {
                     Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
 

+ 2 - 0
src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs

@@ -153,6 +153,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
         public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
 
+        public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats;
+
         public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
 
         public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence;

+ 1 - 0
src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs

@@ -159,6 +159,7 @@ namespace Ryujinx.Graphics.OpenGL
                 supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
                 supportsCubemapView: true,
                 supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
+                supportsScaledVertexFormats: true,
                 supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
                 supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
                 supportsShaderFloat64: true,

+ 15 - 0
src/Ryujinx.Graphics.Shader/AttributeType.cs

@@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.Shader
         Float,
         Sint,
         Uint,
+        Sscaled,
+        Uscaled,
     }
 
     static class AttributeTypeExtensions
@@ -23,5 +25,18 @@ namespace Ryujinx.Graphics.Shader
                 _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."),
             };
         }
+
+        public static AggregateType ToAggregateType(this AttributeType type, bool supportsScaledFormats)
+        {
+            return type switch
+            {
+                AttributeType.Float => AggregateType.FP32,
+                AttributeType.Sint => AggregateType.S32,
+                AttributeType.Uint => AggregateType.U32,
+                AttributeType.Sscaled => supportsScaledFormats ? AggregateType.FP32 : AggregateType.S32,
+                AttributeType.Uscaled => supportsScaledFormats ? AggregateType.FP32 : AggregateType.U32,
+                _ => throw new ArgumentException($"Invalid attribute type \"{type}\"."),
+            };
+        }
     }
 }

+ 9 - 0
src/Ryujinx.Graphics.Shader/IGpuAccessor.cs

@@ -266,6 +266,15 @@ namespace Ryujinx.Graphics.Shader
             return true;
         }
 
+        /// <summary>
+        /// Queries host support scaled vertex formats, where a integer value is converted to floating-point.
+        /// </summary>
+        /// <returns>True if the host support scaled vertex formats, false otherwise</returns>
+        bool QueryHostSupportsScaledVertexFormats()
+        {
+            return true;
+        }
+
         /// <summary>
         /// Queries host GPU shader ballot support.
         /// </summary>

+ 25 - 1
src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs

@@ -61,7 +61,31 @@ namespace Ryujinx.Graphics.Shader.Instructions
                     }
                     else
                     {
-                        context.Copy(Register(rd), AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, op.P));
+                        value = AttributeMap.GenerateAttributeLoad(context, primVertex, offset, isOutput, op.P);
+
+                        if (!context.TranslatorContext.Definitions.SupportsScaledVertexFormats &&
+                            context.TranslatorContext.Stage == ShaderStage.Vertex &&
+                            !op.O &&
+                            offset >= 0x80 &&
+                            offset < 0x280)
+                        {
+                            // The host does not support scaled vertex formats,
+                            // the emulator should use a integer format, and
+                            // we compensate here inserting the conversion to float.
+
+                            AttributeType type = context.TranslatorContext.Definitions.GetAttributeType((offset - 0x80) >> 4);
+
+                            if (type == AttributeType.Sscaled)
+                            {
+                                value = context.IConvertS32ToFP32(value);
+                            }
+                            else if (type == AttributeType.Uscaled)
+                            {
+                                value = context.IConvertU32ToFP32(value);
+                            }
+                        }
+
+                        context.Copy(Register(rd), value);
                     }
                 }
                 else

+ 10 - 1
src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs

@@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.Shader.Translation
         public bool OmapSampleMask { get; }
         public bool OmapDepth { get; }
 
+        public bool SupportsScaledVertexFormats { get; }
+
         public bool TransformFeedbackEnabled { get; }
 
         private readonly TransformFeedbackOutput[] _transformFeedbackOutputs;
@@ -139,6 +141,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             int omapTargets,
             bool omapSampleMask,
             bool omapDepth,
+            bool supportsScaledVertexFormats,
             bool transformFeedbackEnabled,
             ulong transformFeedbackVecMap,
             TransformFeedbackOutput[] transformFeedbackOutputs)
@@ -154,6 +157,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             OmapSampleMask = omapSampleMask;
             OmapDepth = omapDepth;
             LastInVertexPipeline = stage < ShaderStage.Fragment;
+            SupportsScaledVertexFormats = supportsScaledVertexFormats;
             TransformFeedbackEnabled = transformFeedbackEnabled;
             _transformFeedbackOutputs = transformFeedbackOutputs;
             _transformFeedbackDefinitions = new();
@@ -302,7 +306,7 @@ namespace Ryujinx.Graphics.Shader.Translation
 
             if (Stage == ShaderStage.Vertex && !isOutput)
             {
-                type |= _graphicsState.AttributeTypes[location].ToAggregateType();
+                type |= _graphicsState.AttributeTypes[location].ToAggregateType(SupportsScaledVertexFormats);
             }
             else
             {
@@ -311,5 +315,10 @@ namespace Ryujinx.Graphics.Shader.Translation
 
             return type;
         }
+
+        public AttributeType GetAttributeType(int location)
+        {
+            return _graphicsState.AttributeTypes[location];
+        }
     }
 }

+ 1 - 0
src/Ryujinx.Graphics.Shader/Translation/Translator.cs

@@ -116,6 +116,7 @@ namespace Ryujinx.Graphics.Shader.Translation
                 header.OmapTargets,
                 header.OmapSampleMask,
                 header.OmapDepth,
+                gpuAccessor.QueryHostSupportsScaledVertexFormats(),
                 transformFeedbackEnabled,
                 transformFeedbackVecMap,
                 transformFeedbackOutputs);

+ 61 - 0
src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs

@@ -9,6 +9,48 @@ namespace Ryujinx.Graphics.Vulkan
 {
     class FormatCapabilities
     {
+        private static readonly GAL.Format[] _scaledFormats = {
+            GAL.Format.R8Uscaled,
+            GAL.Format.R8Sscaled,
+            GAL.Format.R16Uscaled,
+            GAL.Format.R16Sscaled,
+            GAL.Format.R8G8Uscaled,
+            GAL.Format.R8G8Sscaled,
+            GAL.Format.R16G16Uscaled,
+            GAL.Format.R16G16Sscaled,
+            GAL.Format.R8G8B8Uscaled,
+            GAL.Format.R8G8B8Sscaled,
+            GAL.Format.R16G16B16Uscaled,
+            GAL.Format.R16G16B16Sscaled,
+            GAL.Format.R8G8B8A8Uscaled,
+            GAL.Format.R8G8B8A8Sscaled,
+            GAL.Format.R16G16B16A16Uscaled,
+            GAL.Format.R16G16B16A16Sscaled,
+            GAL.Format.R10G10B10A2Uscaled,
+            GAL.Format.R10G10B10A2Sscaled,
+        };
+
+        private static readonly GAL.Format[] _intFormats = {
+            GAL.Format.R8Uint,
+            GAL.Format.R8Sint,
+            GAL.Format.R16Uint,
+            GAL.Format.R16Sint,
+            GAL.Format.R8G8Uint,
+            GAL.Format.R8G8Sint,
+            GAL.Format.R16G16Uint,
+            GAL.Format.R16G16Sint,
+            GAL.Format.R8G8B8Uint,
+            GAL.Format.R8G8B8Sint,
+            GAL.Format.R16G16B16Uint,
+            GAL.Format.R16G16B16Sint,
+            GAL.Format.R8G8B8A8Uint,
+            GAL.Format.R8G8B8A8Sint,
+            GAL.Format.R16G16B16A16Uint,
+            GAL.Format.R16G16B16A16Sint,
+            GAL.Format.R10G10B10A2Uint,
+            GAL.Format.R10G10B10A2Sint,
+        };
+
         private readonly FormatFeatureFlags[] _bufferTable;
         private readonly FormatFeatureFlags[] _optimalTable;
 
@@ -66,6 +108,25 @@ namespace Ryujinx.Graphics.Vulkan
             return (formatFeatureFlags & flags) == flags;
         }
 
+        public bool SupportsScaledVertexFormats()
+        {
+            // We want to check is all scaled formats are supported,
+            // but if the integer variant is not supported either,
+            // then the format is likely not supported at all,
+            // we ignore formats that are entirely unsupported here.
+
+            for (int i = 0; i < _scaledFormats.Length; i++)
+            {
+                if (!BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, _scaledFormats[i]) &&
+                    BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, _intFormats[i]))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
         public bool BufferFormatSupports(FormatFeatureFlags flags, VkFormat format)
         {
             _api.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var fp);

+ 1 - 0
src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs

@@ -604,6 +604,7 @@ namespace Ryujinx.Graphics.Vulkan
                 supportsMismatchingViewFormat: true,
                 supportsCubemapView: !IsAmdGcn,
                 supportsNonConstantTextureOffset: false,
+                supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
                 supportsShaderBallot: false,
                 supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
                 supportsShaderFloat64: Capabilities.SupportsShaderFloat64,