diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index d0c63bf257..937c3f5b74 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -2,37 +2,44 @@ namespace Ryujinx.Graphics.GAL { public struct Capabilities { - public bool SupportsAstcCompression { get; } - public bool SupportsImageLoadFormatted { get; } - public bool SupportsMismatchingViewFormat { get; } - public bool SupportsNonConstantTextureOffset { get; } - public bool SupportsTextureShadowLod { get; } - public bool SupportsViewportSwizzle { get; } + public bool HasFrontFacingBug { get; } + public bool HasVectorIndexingBug { get; } - public int MaximumComputeSharedMemorySize { get; } - public float MaximumSupportedAnisotropy { get; } - public int StorageBufferOffsetAlignment { get; } + public bool SupportsAstcCompression { get; } + public bool SupportsImageLoadFormatted { get; } + public bool SupportsMismatchingViewFormat { get; } + public bool SupportsNonConstantTextureOffset { get; } + public bool SupportsTextureShadowLod { get; } + public bool SupportsViewportSwizzle { get; } + + public int MaximumComputeSharedMemorySize { get; } + public float MaximumSupportedAnisotropy { get; } + public int StorageBufferOffsetAlignment { get; } public Capabilities( - bool supportsAstcCompression, - bool supportsImageLoadFormatted, - bool supportsMismatchingViewFormat, - bool supportsNonConstantTextureOffset, - bool supportsTextureShadowLod, - bool supportsViewportSwizzle, - int maximumComputeSharedMemorySize, + bool hasFrontFacingBug, + bool hasVectorIndexingBug, + bool supportsAstcCompression, + bool supportsImageLoadFormatted, + bool supportsMismatchingViewFormat, + bool supportsNonConstantTextureOffset, + bool supportsTextureShadowLod, + bool supportsViewportSwizzle, + int maximumComputeSharedMemorySize, float maximumSupportedAnisotropy, - int storageBufferOffsetAlignment) + int storageBufferOffsetAlignment) { - SupportsAstcCompression = supportsAstcCompression; - SupportsImageLoadFormatted = supportsImageLoadFormatted; - SupportsMismatchingViewFormat = supportsMismatchingViewFormat; + HasFrontFacingBug = hasFrontFacingBug; + HasVectorIndexingBug = hasVectorIndexingBug; + SupportsAstcCompression = supportsAstcCompression; + SupportsImageLoadFormatted = supportsImageLoadFormatted; + SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; - SupportsTextureShadowLod = supportsTextureShadowLod; - SupportsViewportSwizzle = supportsViewportSwizzle; - MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; - MaximumSupportedAnisotropy = maximumSupportedAnisotropy; - StorageBufferOffsetAlignment = storageBufferOffsetAlignment; + SupportsTextureShadowLod = supportsTextureShadowLod; + SupportsViewportSwizzle = supportsViewportSwizzle; + MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; + MaximumSupportedAnisotropy = maximumSupportedAnisotropy; + StorageBufferOffsetAlignment = storageBufferOffsetAlignment; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs index 452dfd837a..625b8bac15 100644 --- a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs @@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Shader { class CachedGpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor { - private readonly GpuContext _context; private readonly ReadOnlyMemory _data; private readonly ReadOnlyMemory _cb1Data; private readonly GuestGpuAccessorHeader _header; @@ -28,9 +27,8 @@ namespace Ryujinx.Graphics.Gpu.Shader ReadOnlyMemory data, ReadOnlyMemory cb1Data, GuestGpuAccessorHeader header, - Dictionary guestTextureDescriptors) + IReadOnlyDictionary guestTextureDescriptors) : base(context) { - _context = context; _data = data; _cb1Data = cb1Data; _header = header; @@ -136,24 +134,6 @@ namespace Ryujinx.Graphics.Gpu.Shader return _header.PrimitiveTopology; } - /// - /// Queries host storage buffer alignment required. - /// - /// Host storage buffer alignment in bytes - public int QueryStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment; - - /// - /// Queries host support for readable images without a explicit format declaration on the shader. - /// - /// True if formatted image load is supported, false otherwise - public bool QuerySupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted; - - /// - /// Queries host GPU non-constant texture offset support. - /// - /// True if the GPU and driver supports non-constant texture offsets, false otherwise - public bool QuerySupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset; - /// /// Gets the texture descriptor for a given texture on the pool. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 6254b1c2d8..65c8c287ef 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -7,9 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Represents a GPU state and memory accessor. /// - class GpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor + class GpuAccessor : TextureDescriptorCapableGpuAccessor { - private readonly GpuContext _context; private readonly GpuChannel _channel; private readonly GpuAccessorState _state; private readonly int _stageIndex; @@ -29,9 +28,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU channel /// Current GPU state /// Graphics shader stage index (0 = Vertex, 4 = Fragment) - public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) + public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context) { - _context = context; _channel = channel; _state = state; _stageIndex = stageIndex; @@ -56,9 +54,8 @@ namespace Ryujinx.Graphics.Gpu.Shader int localSizeY, int localSizeZ, int localMemorySize, - int sharedMemorySize) + int sharedMemorySize) : base(context) { - _context = context; _channel = channel; _state = state; _compute = true; @@ -182,30 +179,6 @@ namespace Ryujinx.Graphics.Gpu.Shader }; } - /// - /// Queries host storage buffer alignment required. - /// - /// Host storage buffer alignment in bytes - public int QueryStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment; - - /// - /// Queries host support for readable images without a explicit format declaration on the shader. - /// - /// True if formatted image load is supported, false otherwise - public bool QuerySupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted; - - /// - /// Queries host GPU non-constant texture offset support. - /// - /// True if the GPU and driver supports non-constant texture offsets, false otherwise - public bool QuerySupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset; - - /// - /// Queries host GPU texture shadow LOD support. - /// - /// True if the GPU and driver supports texture shadow LOD, false otherwise - public bool QuerySupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; - /// /// Gets the texture descriptor for a given texture on the pool. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs new file mode 100644 index 0000000000..fb990cfe99 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -0,0 +1,55 @@ +namespace Ryujinx.Graphics.Gpu.Shader +{ + /// + /// Represents a GPU state and memory accessor. + /// + class GpuAccessorBase + { + private readonly GpuContext _context; + + /// + /// Creates a new instance of the GPU state accessor. + /// + /// GPU context + public GpuAccessorBase(GpuContext context) + { + _context = context; + } + + /// + /// Queries host about the presence of the FrontFacing built-in variable bug. + /// + /// True if the bug is present on the host device used, false otherwise + public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug; + + /// + /// Queries host about the presence of the vector indexing bug. + /// + /// True if the bug is present on the host device used, false otherwise + public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug; + + /// + /// Queries host storage buffer alignment required. + /// + /// Host storage buffer alignment in bytes + public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment; + + /// + /// Queries host support for readable images without a explicit format declaration on the shader. + /// + /// True if formatted image load is supported, false otherwise + public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted; + + /// + /// Queries host GPU non-constant texture offset support. + /// + /// True if the GPU and driver supports non-constant texture offsets, false otherwise + public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset; + + /// + /// Queries host GPU texture shadow LOD support. + /// + /// True if the GPU and driver supports texture shadow LOD, false otherwise + public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod; + } +} diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 2c1fb0843f..fada667c85 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2538; + private const ulong ShaderCodeGenVersion = 2540; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs index 904a0fd453..3550744998 100644 --- a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs @@ -4,8 +4,12 @@ using Ryujinx.Graphics.Shader; namespace Ryujinx.Graphics.Gpu.Shader { - abstract class TextureDescriptorCapableGpuAccessor : IGpuAccessor + abstract class TextureDescriptorCapableGpuAccessor : GpuAccessorBase, IGpuAccessor { + public TextureDescriptorCapableGpuAccessor(GpuContext context) : base(context) + { + } + public abstract T MemoryRead(ulong address) where T : unmanaged; public abstract ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot); diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index c774922864..22648fd131 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -99,6 +99,8 @@ namespace Ryujinx.Graphics.OpenGL public Capabilities GetCapabilities() { return new Capabilities( + HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows, + HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows, HwCapabilities.SupportsAstcCompression, HwCapabilities.SupportsImageLoadFormatted, HwCapabilities.SupportsMismatchingViewFormat, diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index f6aab74d1b..ef3b0bedd3 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -157,15 +157,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true); + var config = context.Config; + bool indexElement = !config.GpuAccessor.QueryHostHasVectorIndexingBug(); + if (src1 is AstOperand oper && oper.Type == OperandType.Constant) { - bool cbIndexable = context.Config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing); - return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, cbIndexable); + bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing); + return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, config.Stage, cbIndexable, indexElement); } else { string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); - return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, context.Config.Stage); + return OperandManager.GetConstantBufferName(slotExpr, offsetExpr, config.Stage, indexElement); } } @@ -314,7 +317,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // 2D Array and Cube shadow samplers with LOD level or bias requires an extension. // If the extension is not supported, just remove the LOD parameter. - if (isArray && isShadow && (is2D || isCube) && !context.Config.GpuAccessor.QuerySupportsTextureShadowLod()) + if (isArray && isShadow && (is2D || isCube) && !context.Config.GpuAccessor.QueryHostSupportsTextureShadowLod()) { hasLodBias = false; hasLodLevel = false; @@ -322,7 +325,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // Cube shadow samplers with LOD level requires an extension. // If the extension is not supported, just remove the LOD level parameter. - if (isShadow && isCube && !context.Config.GpuAccessor.QuerySupportsTextureShadowLod()) + if (isShadow && isCube && !context.Config.GpuAccessor.QueryHostSupportsTextureShadowLod()) { hasLodLevel = false; } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 03fbb8a414..60a471eb92 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -117,8 +117,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}"; } - private static string GetVec4Indexed(string vectorName, string indexExpr) + private static string GetVec4Indexed(string vectorName, string indexExpr, bool indexElement) { + if (indexElement) + { + return $"{vectorName}[{indexExpr}]"; + } + string result = $"{vectorName}.x"; for (int i = 1; i < 4; i++) { @@ -127,14 +132,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return $"({result})"; } - public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable) + public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable, bool indexElement) { - return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3"); + return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } - public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage) + public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage, bool indexElement) { - return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3"); + return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); } public static string GetOutAttributeName(AstOperand attr, ShaderConfig config) @@ -198,6 +203,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])"; case AttributeConsts.PositionZ: return "gl_FragCoord.z"; case AttributeConsts.PositionW: return "gl_FragCoord.w"; + + case AttributeConsts.FrontFacing: + if (config.GpuAccessor.QueryHostHasFrontFacingBug()) + { + // This is required for Intel on Windows, gl_FrontFacing sometimes returns incorrect + // (flipped) values. Doing this seems to fix it. + return "(-floatBitsToInt(float(gl_FrontFacing)) < 0)"; + } + break; } } diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 04f23061b9..84c30479b6 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -49,6 +49,36 @@ return 0; } + bool QueryHostHasFrontFacingBug() + { + return false; + } + + bool QueryHostHasVectorIndexingBug() + { + return false; + } + + int QueryHostStorageBufferOffsetAlignment() + { + return 16; + } + + bool QueryHostSupportsImageLoadFormatted() + { + return true; + } + + bool QueryHostSupportsNonConstantTextureOffset() + { + return true; + } + + bool QueryHostSupportsTextureShadowLod() + { + return true; + } + bool QueryIsTextureBuffer(int handle, int cbufSlot = -1) { return false; @@ -64,26 +94,6 @@ return InputTopology.Points; } - int QueryStorageBufferOffsetAlignment() - { - return 16; - } - - bool QuerySupportsImageLoadFormatted() - { - return true; - } - - bool QuerySupportsNonConstantTextureOffset() - { - return true; - } - - bool QuerySupportsTextureShadowLod() - { - return true; - } - TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1) { return TextureFormat.R8G8B8A8Unorm; diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index bccb0cbe37..cc57102cb8 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand baseAddrTrunc = Local(); - Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); + Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); @@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand baseAddrTrunc = Local(); - Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); + Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index fba586c8cf..47428520ad 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Shader.Translation sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot); } - Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); + Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment()); Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask); Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc); @@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QuerySupportsNonConstantTextureOffset(); + bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QueryHostSupportsNonConstantTextureOffset(); bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index c7704c2bbb..02e995f9a9 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation { // When the formatted load extension is supported, we don't need to // specify a format, we can just declare it without a format and the GPU will handle it. - if (GpuAccessor.QuerySupportsImageLoadFormatted()) + if (GpuAccessor.QueryHostSupportsImageLoadFormatted()) { return TextureFormat.Unknown; }