Workaround for Intel FrontFacing built-in variable bug (#2540)

This commit is contained in:
gdkchan 2021-08-11 18:01:06 -03:00 committed by GitHub
parent 0a80a837cb
commit c3e2646f9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 161 additions and 113 deletions

View file

@ -2,37 +2,44 @@ namespace Ryujinx.Graphics.GAL
{ {
public struct Capabilities public struct Capabilities
{ {
public bool SupportsAstcCompression { get; } public bool HasFrontFacingBug { get; }
public bool SupportsImageLoadFormatted { get; } public bool HasVectorIndexingBug { get; }
public bool SupportsMismatchingViewFormat { get; }
public bool SupportsNonConstantTextureOffset { get; }
public bool SupportsTextureShadowLod { get; }
public bool SupportsViewportSwizzle { get; }
public int MaximumComputeSharedMemorySize { get; } public bool SupportsAstcCompression { get; }
public float MaximumSupportedAnisotropy { get; } public bool SupportsImageLoadFormatted { get; }
public int StorageBufferOffsetAlignment { 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( public Capabilities(
bool supportsAstcCompression, bool hasFrontFacingBug,
bool supportsImageLoadFormatted, bool hasVectorIndexingBug,
bool supportsMismatchingViewFormat, bool supportsAstcCompression,
bool supportsNonConstantTextureOffset, bool supportsImageLoadFormatted,
bool supportsTextureShadowLod, bool supportsMismatchingViewFormat,
bool supportsViewportSwizzle, bool supportsNonConstantTextureOffset,
int maximumComputeSharedMemorySize, bool supportsTextureShadowLod,
bool supportsViewportSwizzle,
int maximumComputeSharedMemorySize,
float maximumSupportedAnisotropy, float maximumSupportedAnisotropy,
int storageBufferOffsetAlignment) int storageBufferOffsetAlignment)
{ {
SupportsAstcCompression = supportsAstcCompression; HasFrontFacingBug = hasFrontFacingBug;
SupportsImageLoadFormatted = supportsImageLoadFormatted; HasVectorIndexingBug = hasVectorIndexingBug;
SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsAstcCompression = supportsAstcCompression;
SupportsImageLoadFormatted = supportsImageLoadFormatted;
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
SupportsTextureShadowLod = supportsTextureShadowLod; SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsViewportSwizzle = supportsViewportSwizzle; SupportsViewportSwizzle = supportsViewportSwizzle;
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
MaximumSupportedAnisotropy = maximumSupportedAnisotropy; MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
StorageBufferOffsetAlignment = storageBufferOffsetAlignment; StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
} }
} }
} }

View file

@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
class CachedGpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor class CachedGpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor
{ {
private readonly GpuContext _context;
private readonly ReadOnlyMemory<byte> _data; private readonly ReadOnlyMemory<byte> _data;
private readonly ReadOnlyMemory<byte> _cb1Data; private readonly ReadOnlyMemory<byte> _cb1Data;
private readonly GuestGpuAccessorHeader _header; private readonly GuestGpuAccessorHeader _header;
@ -28,9 +27,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> data,
ReadOnlyMemory<byte> cb1Data, ReadOnlyMemory<byte> cb1Data,
GuestGpuAccessorHeader header, GuestGpuAccessorHeader header,
Dictionary<int, GuestTextureDescriptor> guestTextureDescriptors) IReadOnlyDictionary<int, GuestTextureDescriptor> guestTextureDescriptors) : base(context)
{ {
_context = context;
_data = data; _data = data;
_cb1Data = cb1Data; _cb1Data = cb1Data;
_header = header; _header = header;
@ -136,24 +134,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _header.PrimitiveTopology; return _header.PrimitiveTopology;
} }
/// <summary>
/// Queries host storage buffer alignment required.
/// </summary>
/// <returns>Host storage buffer alignment in bytes</returns>
public int QueryStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
/// <summary>
/// Queries host support for readable images without a explicit format declaration on the shader.
/// </summary>
/// <returns>True if formatted image load is supported, false otherwise</returns>
public bool QuerySupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
/// <summary>
/// Queries host GPU non-constant texture offset support.
/// </summary>
/// <returns>True if the GPU and driver supports non-constant texture offsets, false otherwise</returns>
public bool QuerySupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
/// <summary> /// <summary>
/// Gets the texture descriptor for a given texture on the pool. /// Gets the texture descriptor for a given texture on the pool.
/// </summary> /// </summary>

View file

@ -7,9 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Represents a GPU state and memory accessor. /// Represents a GPU state and memory accessor.
/// </summary> /// </summary>
class GpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor class GpuAccessor : TextureDescriptorCapableGpuAccessor
{ {
private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly GpuAccessorState _state; private readonly GpuAccessorState _state;
private readonly int _stageIndex; private readonly int _stageIndex;
@ -29,9 +28,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="state">Current GPU state</param> /// <param name="state">Current GPU state</param>
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param> /// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
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; _channel = channel;
_state = state; _state = state;
_stageIndex = stageIndex; _stageIndex = stageIndex;
@ -56,9 +54,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
int localSizeY, int localSizeY,
int localSizeZ, int localSizeZ,
int localMemorySize, int localMemorySize,
int sharedMemorySize) int sharedMemorySize) : base(context)
{ {
_context = context;
_channel = channel; _channel = channel;
_state = state; _state = state;
_compute = true; _compute = true;
@ -182,30 +179,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
}; };
} }
/// <summary>
/// Queries host storage buffer alignment required.
/// </summary>
/// <returns>Host storage buffer alignment in bytes</returns>
public int QueryStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
/// <summary>
/// Queries host support for readable images without a explicit format declaration on the shader.
/// </summary>
/// <returns>True if formatted image load is supported, false otherwise</returns>
public bool QuerySupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
/// <summary>
/// Queries host GPU non-constant texture offset support.
/// </summary>
/// <returns>True if the GPU and driver supports non-constant texture offsets, false otherwise</returns>
public bool QuerySupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
/// <summary>
/// Queries host GPU texture shadow LOD support.
/// </summary>
/// <returns>True if the GPU and driver supports texture shadow LOD, false otherwise</returns>
public bool QuerySupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
/// <summary> /// <summary>
/// Gets the texture descriptor for a given texture on the pool. /// Gets the texture descriptor for a given texture on the pool.
/// </summary> /// </summary>

View file

@ -0,0 +1,55 @@
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Represents a GPU state and memory accessor.
/// </summary>
class GpuAccessorBase
{
private readonly GpuContext _context;
/// <summary>
/// Creates a new instance of the GPU state accessor.
/// </summary>
/// <param name="context">GPU context</param>
public GpuAccessorBase(GpuContext context)
{
_context = context;
}
/// <summary>
/// Queries host about the presence of the FrontFacing built-in variable bug.
/// </summary>
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
/// <summary>
/// Queries host about the presence of the vector indexing bug.
/// </summary>
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
/// <summary>
/// Queries host storage buffer alignment required.
/// </summary>
/// <returns>Host storage buffer alignment in bytes</returns>
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
/// <summary>
/// Queries host support for readable images without a explicit format declaration on the shader.
/// </summary>
/// <returns>True if formatted image load is supported, false otherwise</returns>
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
/// <summary>
/// Queries host GPU non-constant texture offset support.
/// </summary>
/// <returns>True if the GPU and driver supports non-constant texture offsets, false otherwise</returns>
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
/// <summary>
/// Queries host GPU texture shadow LOD support.
/// </summary>
/// <returns>True if the GPU and driver supports texture shadow LOD, false otherwise</returns>
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
}
}

View file

@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Version of the codegen (to be changed when codegen or guest format change). /// Version of the codegen (to be changed when codegen or guest format change).
/// </summary> /// </summary>
private const ulong ShaderCodeGenVersion = 2538; private const ulong ShaderCodeGenVersion = 2540;
// Progress reporting helpers // Progress reporting helpers
private volatile int _shaderCount; private volatile int _shaderCount;

View file

@ -4,8 +4,12 @@ using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.Gpu.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<T>(ulong address) where T : unmanaged; public abstract T MemoryRead<T>(ulong address) where T : unmanaged;
public abstract ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot); public abstract ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot);

View file

@ -99,6 +99,8 @@ namespace Ryujinx.Graphics.OpenGL
public Capabilities GetCapabilities() public Capabilities GetCapabilities()
{ {
return new Capabilities( return new Capabilities(
HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
HwCapabilities.SupportsAstcCompression, HwCapabilities.SupportsAstcCompression,
HwCapabilities.SupportsImageLoadFormatted, HwCapabilities.SupportsImageLoadFormatted,
HwCapabilities.SupportsMismatchingViewFormat, HwCapabilities.SupportsMismatchingViewFormat,

View file

@ -157,15 +157,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true); 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) if (src1 is AstOperand oper && oper.Type == OperandType.Constant)
{ {
bool cbIndexable = context.Config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing); bool cbIndexable = config.UsedFeatures.HasFlag(Translation.FeatureFlags.CbIndexing);
return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, context.Config.Stage, cbIndexable); return OperandManager.GetConstantBufferName(oper.Value, offsetExpr, config.Stage, cbIndexable, indexElement);
} }
else else
{ {
string slotExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); 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. // 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 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; hasLodBias = false;
hasLodLevel = false; hasLodLevel = false;
@ -322,7 +325,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
// Cube shadow samplers with LOD level requires an extension. // Cube shadow samplers with LOD level requires an extension.
// If the extension is not supported, just remove the LOD level parameter. // 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; hasLodLevel = false;
} }

View file

@ -117,8 +117,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}"; 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"; string result = $"{vectorName}.x";
for (int i = 1; i < 4; i++) for (int i = 1; i < 4; i++)
{ {
@ -127,14 +132,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return $"({result})"; 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) 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.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])";
case AttributeConsts.PositionZ: return "gl_FragCoord.z"; case AttributeConsts.PositionZ: return "gl_FragCoord.z";
case AttributeConsts.PositionW: return "gl_FragCoord.w"; 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;
} }
} }

View file

@ -49,6 +49,36 @@
return 0; 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) bool QueryIsTextureBuffer(int handle, int cbufSlot = -1)
{ {
return false; return false;
@ -64,26 +94,6 @@
return InputTopology.Points; 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) TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
{ {
return TextureFormat.R8G8B8A8Unorm; return TextureFormat.R8G8B8A8Unorm;

View file

@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand baseAddrTrunc = Local(); Operand baseAddrTrunc = Local();
Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask);
@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand baseAddrTrunc = Local(); Operand baseAddrTrunc = Local();
Operand alignMask = Const(-config.GpuAccessor.QueryStorageBufferOffsetAlignment()); Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask);

View file

@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Shader.Translation
sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot); 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 baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask);
Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc); Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc);
@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 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; bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;

View file

@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
// When the formatted load extension is supported, we don't need to // 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. // 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; return TextureFormat.Unknown;
} }