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

View file

@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
class CachedGpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor
{
private readonly GpuContext _context;
private readonly ReadOnlyMemory<byte> _data;
private readonly ReadOnlyMemory<byte> _cb1Data;
private readonly GuestGpuAccessorHeader _header;
@ -28,9 +27,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
ReadOnlyMemory<byte> data,
ReadOnlyMemory<byte> cb1Data,
GuestGpuAccessorHeader header,
Dictionary<int, GuestTextureDescriptor> guestTextureDescriptors)
IReadOnlyDictionary<int, GuestTextureDescriptor> guestTextureDescriptors) : base(context)
{
_context = context;
_data = data;
_cb1Data = cb1Data;
_header = header;
@ -136,24 +134,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
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>
/// Gets the texture descriptor for a given texture on the pool.
/// </summary>

View file

@ -7,9 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Represents a GPU state and memory accessor.
/// </summary>
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
/// <param name="channel">GPU channel</param>
/// <param name="state">Current GPU state</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;
_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
};
}
/// <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>
/// Gets the texture descriptor for a given texture on the pool.
/// </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>
/// Version of the codegen (to be changed when codegen or guest format change).
/// </summary>
private const ulong ShaderCodeGenVersion = 2538;
private const ulong ShaderCodeGenVersion = 2540;
// Progress reporting helpers
private volatile int _shaderCount;

View file

@ -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<T>(ulong address) where T : unmanaged;
public abstract ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot);

View file

@ -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,

View file

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

View file

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

View file

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

View file

@ -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);

View file

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

View file

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