diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index bb43309bc..fdf8f8222 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1317,10 +1317,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed for (int location = 0; location < attributeTypes.Length; location++) { - attributeTypes[location] = vertexAttribState[location].UnpackType() switch + VertexAttribType type = vertexAttribState[location].UnpackType(); + + attributeTypes[location] = type switch { - 3 => AttributeType.Sint, - 4 => AttributeType.Uint, + VertexAttribType.Sint => AttributeType.Sint, + VertexAttribType.Uint => AttributeType.Uint, _ => AttributeType.Float }; } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index c90dea41e..e416cd583 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -267,6 +267,41 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed #pragma warning restore CS0649 } + /// + /// Vertex attribute vector and component size. + /// + enum VertexAttribSize + { + Size32x4 = 1, + Size32x3 = 2, + Size16x4 = 3, + Size32x2 = 4, + Size16x3 = 5, + Size8x4 = 0xa, + Size16x2 = 0xf, + Size32 = 0x12, + Size8x3 = 0x13, + Size8x2 = 0x18, + Size16 = 0x1b, + Size8 = 0x1d, + Rgb10A2 = 0x30, + Rg11B10 = 0x31 + } + + /// + /// Vertex attribute component type. + /// + enum VertexAttribType + { + Snorm = 1, + Unorm = 2, + Sint = 3, + Uint = 4, + Uscaled = 5, + Sscaled = 6, + Float = 7 + } + /// /// Vertex buffer attribute state. /// @@ -312,13 +347,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return Attribute & 0x3fe00000; } - /// + /// + /// Unpacks the Maxwell attribute size. + /// + /// Attribute size + public VertexAttribSize UnpackSize() + { + return (VertexAttribSize)((Attribute >> 21) & 0x3f); + } + + /// /// Unpacks the Maxwell attribute component type. /// /// Attribute component type - public uint UnpackType() + public VertexAttribType UnpackType() { - return (Attribute >> 27) & 7; + return (VertexAttribType)((Attribute >> 27) & 7); } } diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index 67edd8427..5913ac947 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu /// /// Host hardware capabilities. /// - internal Capabilities Capabilities { get; private set; } + internal Capabilities Capabilities; /// /// Event for signalling shader cache loading progress. diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index cb70811b9..04d93bba5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -573,9 +573,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache RecompileGraphicsFromGuestCode(guestShaders, specState, programIndex); } } - catch (DiskCacheLoadException diskCacheLoadException) + catch (Exception exception) { - Logger.Error?.Print(LogClass.Gpu, $"Error translating guest shader. {diskCacheLoadException.Message}"); + Logger.Error?.Print(LogClass.Gpu, $"Error translating guest shader. {exception.Message}"); ErrorCount++; SignalCompiled(); diff --git a/Ryujinx.Graphics.Shader/AttributeType.cs b/Ryujinx.Graphics.Shader/AttributeType.cs index 466f06cc8..1ede15600 100644 --- a/Ryujinx.Graphics.Shader/AttributeType.cs +++ b/Ryujinx.Graphics.Shader/AttributeType.cs @@ -1,9 +1,12 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; namespace Ryujinx.Graphics.Shader { public enum AttributeType : byte { + // Generic types. Float, Sint, Uint @@ -11,18 +14,7 @@ namespace Ryujinx.Graphics.Shader static class AttributeTypeExtensions { - public static string GetScalarType(this AttributeType type) - { - return type switch - { - AttributeType.Float => "float", - AttributeType.Sint => "int", - AttributeType.Uint => "uint", - _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") - }; - } - - public static string GetVec4Type(this AttributeType type) + public static string ToVec4Type(this AttributeType type) { return type switch { @@ -32,5 +24,27 @@ namespace Ryujinx.Graphics.Shader _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") }; } + + public static VariableType ToVariableType(this AttributeType type) + { + return type switch + { + AttributeType.Float => VariableType.F32, + AttributeType.Sint => VariableType.S32, + AttributeType.Uint => VariableType.U32, + _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") + }; + } + + public static AggregateType ToAggregateType(this AttributeType type) + { + return type switch + { + AttributeType.Float => AggregateType.FP32, + AttributeType.Sint => AggregateType.S32, + AttributeType.Uint => AggregateType.U32, + _ => throw new ArgumentException($"Invalid attribute type \"{type}\".") + }; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index f9dfb839f..65e781216 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -553,11 +553,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Vertex) { - type = context.Config.GpuAccessor.QueryAttributeType(attr).GetVec4Type(); + type = context.Config.GpuAccessor.QueryAttributeType(attr).ToVec4Type(); } else { - type = AttributeType.Float.GetVec4Type(); + type = AttributeType.Float.ToVec4Type(); } context.AppendLine($"layout ({pass}location = {attr}) {iq}in {type} {name}{suffix};"); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index da720f4dc..1ab91f77f 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -454,12 +454,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location); - return type switch - { - AttributeType.Sint => VariableType.S32, - AttributeType.Uint => VariableType.U32, - _ => VariableType.F32 - }; + return type.ToVariableType(); } } diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs index 9af5bacfd..2a97ee523 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs @@ -93,12 +93,7 @@ namespace Ryujinx.Graphics.Shader.Translation if (config.Stage == ShaderStage.Vertex && !isOutAttr) { - elemType = config.GpuAccessor.QueryAttributeType(location) switch - { - AttributeType.Sint => AggregateType.S32, - AttributeType.Uint => AggregateType.U32, - _ => AggregateType.FP32 - }; + elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType(); } else { diff --git a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 6159f2cca..741d89565 100644 --- a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -8,7 +8,8 @@ namespace Ryujinx.Graphics.Vulkan { class FormatCapabilities { - private readonly FormatFeatureFlags[] _table; + private readonly FormatFeatureFlags[] _bufferTable; + private readonly FormatFeatureFlags[] _optimalTable; private readonly Vk _api; private readonly PhysicalDevice _physicalDevice; @@ -17,14 +18,18 @@ namespace Ryujinx.Graphics.Vulkan { _api = api; _physicalDevice = physicalDevice; - _table = new FormatFeatureFlags[Enum.GetNames(typeof(GAL.Format)).Length]; + + int totalFormats = Enum.GetNames(typeof(GAL.Format)).Length; + + _bufferTable = new FormatFeatureFlags[totalFormats]; + _optimalTable = new FormatFeatureFlags[totalFormats]; } - public bool FormatsSupports(FormatFeatureFlags flags, params GAL.Format[] formats) + public bool BufferFormatsSupport(FormatFeatureFlags flags, params GAL.Format[] formats) { foreach (GAL.Format format in formats) { - if (!FormatSupports(flags, format)) + if (!BufferFormatSupports(flags, format)) { return false; } @@ -33,15 +38,42 @@ namespace Ryujinx.Graphics.Vulkan return true; } - public bool FormatSupports(FormatFeatureFlags flags, GAL.Format format) + public bool OptimalFormatsSupport(FormatFeatureFlags flags, params GAL.Format[] formats) { - var formatFeatureFlags = _table[(int)format]; + foreach (GAL.Format format in formats) + { + if (!OptimalFormatSupports(flags, format)) + { + return false; + } + } + + return true; + } + + public bool BufferFormatSupports(FormatFeatureFlags flags, GAL.Format format) + { + var formatFeatureFlags = _bufferTable[(int)format]; + + if (formatFeatureFlags == 0) + { + _api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp); + formatFeatureFlags = fp.BufferFeatures; + _bufferTable[(int)format] = formatFeatureFlags; + } + + return (formatFeatureFlags & flags) == flags; + } + + public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format) + { + var formatFeatureFlags = _optimalTable[(int)format]; if (formatFeatureFlags == 0) { _api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp); formatFeatureFlags = fp.OptimalTilingFeatures; - _table[(int)format] = formatFeatureFlags; + _optimalTable[(int)format] = formatFeatureFlags; } return (formatFeatureFlags & flags) == flags; @@ -69,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan requiredFeatures |= FormatFeatureFlags.FormatFeatureStorageImageBit; } - if (!FormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported)) + if (!OptimalFormatSupports(requiredFeatures, srcFormat) || (IsD24S8(srcFormat) && VulkanConfiguration.ForceD24S8Unsupported)) { // The format is not supported. Can we convert it to a higher precision format? if (IsD24S8(srcFormat)) @@ -85,9 +117,44 @@ namespace Ryujinx.Graphics.Vulkan return format; } + public VkFormat ConvertToVertexVkFormat(GAL.Format srcFormat) + { + var format = FormatTable.GetFormat(srcFormat); + + if (!BufferFormatSupports(FormatFeatureFlags.FormatFeatureVertexBufferBit, srcFormat) || + (IsRGB16IntFloat(srcFormat) && VulkanConfiguration.ForceRGB16IntFloatUnsupported)) + { + // The format is not supported. Can we convert it to an alternative format? + switch (srcFormat) + { + case GAL.Format.R16G16B16Float: + format = VkFormat.R16G16B16A16Sfloat; + break; + case GAL.Format.R16G16B16Sint: + format = VkFormat.R16G16B16A16Sint; + break; + case GAL.Format.R16G16B16Uint: + format = VkFormat.R16G16B16A16Uint; + break; + default: + Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host."); + break; + } + } + + return format; + } + public static bool IsD24S8(GAL.Format format) { return format == GAL.Format.D24UnormS8Uint || format == GAL.Format.S8UintD24Unorm; } + + private static bool IsRGB16IntFloat(GAL.Format format) + { + return format == GAL.Format.R16G16B16Float || + format == GAL.Format.R16G16B16Sint || + format == GAL.Format.R16G16B16Uint; + } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index b66124f5e..30eeafb86 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -730,6 +730,8 @@ namespace Ryujinx.Graphics.Vulkan public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { + var formatCapabilities = Gd.FormatCapabilities; + int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length); for (int i = 0; i < count; i++) @@ -740,7 +742,7 @@ namespace Ryujinx.Graphics.Vulkan _newState.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( (uint)i, (uint)bufferIndex, - FormatTable.GetFormat(attribute.Format), + formatCapabilities.ConvertToVertexVkFormat(attribute.Format), (uint)attribute.Offset); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index ff194d717..315df1b19 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -211,7 +211,7 @@ namespace Ryujinx.Graphics.Vulkan pipeline.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( (uint)i, (uint)bufferIndex, - FormatTable.GetFormat(attribute.Format), + gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format), (uint)attribute.Offset); } diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index bb79b09c6..d94587bd6 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -451,8 +451,8 @@ namespace Ryujinx.Graphics.Vulkan return; } - else if (_gd.FormatCapabilities.FormatSupports(FormatFeatureFlags.FormatFeatureBlitSrcBit, srcFormat) && - _gd.FormatCapabilities.FormatSupports(FormatFeatureFlags.FormatFeatureBlitDstBit, dstFormat)) + else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.FormatFeatureBlitSrcBit, srcFormat) && + _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.FormatFeatureBlitDstBit, dstFormat)) { TextureCopy.Blit( _gd.Api, @@ -761,8 +761,8 @@ namespace Ryujinx.Graphics.Vulkan private bool SupportsBlitFromD32FS8ToD32FAndS8() { var formatFeatureFlags = FormatFeatureFlags.FormatFeatureBlitSrcBit | FormatFeatureFlags.FormatFeatureBlitDstBit; - return _gd.FormatCapabilities.FormatSupports(formatFeatureFlags, GAL.Format.D32Float) && - _gd.FormatCapabilities.FormatSupports(formatFeatureFlags, GAL.Format.S8Uint); + return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float) && + _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint); } public TextureView GetView(GAL.Format format) diff --git a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs b/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs index 75b45809d..752d4f7cc 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs @@ -7,5 +7,6 @@ public const bool UsePushDescriptors = false; public const bool ForceD24S8Unsupported = false; + public const bool ForceRGB16IntFloatUnsupported = false; } } diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index c73966309..b2f69636f 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -377,7 +377,7 @@ namespace Ryujinx.Graphics.Vulkan FormatFeatureFlags.FormatFeatureTransferSrcBit | FormatFeatureFlags.FormatFeatureTransferDstBit; - bool supportsBc123CompressionFormat = FormatCapabilities.FormatsSupports(compressedFormatFeatureFlags, + bool supportsBc123CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, GAL.Format.Bc1RgbaSrgb, GAL.Format.Bc1RgbaUnorm, GAL.Format.Bc2Srgb, @@ -385,13 +385,13 @@ namespace Ryujinx.Graphics.Vulkan GAL.Format.Bc3Srgb, GAL.Format.Bc3Unorm); - bool supportsBc45CompressionFormat = FormatCapabilities.FormatsSupports(compressedFormatFeatureFlags, + bool supportsBc45CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, GAL.Format.Bc4Snorm, GAL.Format.Bc4Unorm, GAL.Format.Bc5Snorm, GAL.Format.Bc5Unorm); - bool supportsBc67CompressionFormat = FormatCapabilities.FormatsSupports(compressedFormatFeatureFlags, + bool supportsBc67CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, GAL.Format.Bc6HSfloat, GAL.Format.Bc6HUfloat, GAL.Format.Bc7Srgb,