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,6 +2,9 @@ namespace Ryujinx.Graphics.GAL
{ {
public struct Capabilities public struct Capabilities
{ {
public bool HasFrontFacingBug { get; }
public bool HasVectorIndexingBug { get; }
public bool SupportsAstcCompression { get; } public bool SupportsAstcCompression { get; }
public bool SupportsImageLoadFormatted { get; } public bool SupportsImageLoadFormatted { get; }
public bool SupportsMismatchingViewFormat { get; } public bool SupportsMismatchingViewFormat { get; }
@ -14,6 +17,8 @@ namespace Ryujinx.Graphics.GAL
public int StorageBufferOffsetAlignment { get; } public int StorageBufferOffsetAlignment { get; }
public Capabilities( public Capabilities(
bool hasFrontFacingBug,
bool hasVectorIndexingBug,
bool supportsAstcCompression, bool supportsAstcCompression,
bool supportsImageLoadFormatted, bool supportsImageLoadFormatted,
bool supportsMismatchingViewFormat, bool supportsMismatchingViewFormat,
@ -24,6 +29,8 @@ namespace Ryujinx.Graphics.GAL
float maximumSupportedAnisotropy, float maximumSupportedAnisotropy,
int storageBufferOffsetAlignment) int storageBufferOffsetAlignment)
{ {
HasFrontFacingBug = hasFrontFacingBug;
HasVectorIndexingBug = hasVectorIndexingBug;
SupportsAstcCompression = supportsAstcCompression; SupportsAstcCompression = supportsAstcCompression;
SupportsImageLoadFormatted = supportsImageLoadFormatted; SupportsImageLoadFormatted = supportsImageLoadFormatted;
SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsMismatchingViewFormat = supportsMismatchingViewFormat;

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;
} }