Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for bindless textures from storage buffer on Vulkan #6721

Merged
merged 3 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Ryujinx.Graphics.GAL/Capabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsMismatchingViewFormat;
public readonly bool SupportsCubemapView;
public readonly bool SupportsNonConstantTextureOffset;
public readonly bool SupportsQuads;
public readonly bool SupportsSeparateSampler;
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
Expand Down Expand Up @@ -93,6 +94,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsMismatchingViewFormat,
bool supportsCubemapView,
bool supportsNonConstantTextureOffset,
bool supportsQuads,
bool supportsSeparateSampler,
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
Expand Down Expand Up @@ -146,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsCubemapView = supportsCubemapView;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
SupportsQuads = supportsQuads;
SupportsSeparateSampler = supportsSeparateSampler;
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
Expand Down
14 changes: 12 additions & 2 deletions src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class DiskCacheGpuAccessor : GpuAccessorBase, IGpuAccessor
private readonly ShaderSpecializationState _newSpecState;
private readonly int _stageIndex;
private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;

/// <summary>
/// Creates a new instance of the cached GPU state accessor for shader translation.
Expand All @@ -29,21 +31,25 @@ class DiskCacheGpuAccessor : GpuAccessorBase, IGpuAccessor
/// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
/// <param name="counts">Resource counts shared across all shader stages</param>
/// <param name="stageIndex">Shader stage index</param>
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
public DiskCacheGpuAccessor(
GpuContext context,
ReadOnlyMemory<byte> data,
ReadOnlyMemory<byte> cb1Data,
ShaderSpecializationState oldSpecState,
ShaderSpecializationState newSpecState,
ResourceCounts counts,
int stageIndex) : base(context, counts, stageIndex)
int stageIndex,
bool hasGeometryShader) : base(context, counts, stageIndex)
{
_data = data;
_cb1Data = cb1Data;
_oldSpecState = oldSpecState;
_newSpecState = newSpecState;
_stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;

if (stageIndex == (int)ShaderStage.Geometry - 1)
{
Expand Down Expand Up @@ -100,7 +106,11 @@ public uint QueryConstantBufferUse()
/// <inheritdoc/>
public GpuGraphicsState QueryGraphicsState()
{
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DiskCacheHostStorage
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 6577;
private const uint CodeGenVersion = 5936;

private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@ private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders,

TargetApi api = _context.Capabilities.Api;

bool hasCachedGs = guestShaders[4].HasValue;

for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
{
if (guestShaders[stageIndex + 1].HasValue)
Expand All @@ -610,7 +612,7 @@ private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders,
byte[] guestCode = shader.Code;
byte[] cb1Data = shader.Cb1Data;

DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex);
DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex, hasCachedGs);
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);

if (nextStage != null)
Expand All @@ -623,7 +625,7 @@ private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders,
byte[] guestCodeA = guestShaders[0].Value.Code;
byte[] cb1DataA = guestShaders[0].Value.Cb1Data;

DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0);
DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0, hasCachedGs);
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
}

Expand Down Expand Up @@ -711,7 +713,7 @@ private void RecompileComputeFromGuestCode(GuestCodeAndCbData?[] guestShaders, S
GuestCodeAndCbData shader = guestShaders[0].Value;
ResourceCounts counts = new();
ShaderSpecializationState newSpecState = new(ref specState.ComputeState);
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0);
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0, false);
gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);

TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);
Expand Down
20 changes: 17 additions & 3 deletions src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class GpuAccessor : GpuAccessorBase, IGpuAccessor
private readonly int _stageIndex;
private readonly bool _compute;
private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;

/// <summary>
/// Creates a new instance of the GPU state accessor for graphics shader translation.
Expand All @@ -25,12 +27,20 @@ class GpuAccessor : GpuAccessorBase, IGpuAccessor
/// <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) : base(context, state.ResourceCounts, stageIndex)
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
public GpuAccessor(
GpuContext context,
GpuChannel channel,
GpuAccessorState state,
int stageIndex,
bool hasGeometryShader) : base(context, state.ResourceCounts, stageIndex)
{
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_channel = channel;
_state = state;
_stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;

if (stageIndex == (int)ShaderStage.Geometry - 1)
{
Expand Down Expand Up @@ -105,7 +115,11 @@ public uint QueryConstantBufferUse()
/// <inheritdoc/>
public GpuGraphicsState QueryGraphicsState()
{
return _state.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _state.GraphicsState.YNegateEnabled);
return _state.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _state.GraphicsState.YNegateEnabled);
}

/// <inheritdoc/>
Expand Down
11 changes: 9 additions & 2 deletions src/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,11 @@ struct GpuChannelGraphicsState
/// Creates a new graphics state from this state that can be used for shader generation.
/// </summary>
/// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param>
/// <param name="hostSupportsQuads">Indicates if the host API supports quad primitives</param>
/// <param name="hasGeometryShader">Indicates if a geometry shader is used</param>
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
/// <returns>GPU graphics state that can be used for shader translation</returns>
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool originUpperLeft)
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool hostSupportsQuads, bool hasGeometryShader, bool originUpperLeft)
{
AlphaTestOp alphaTestOp;

Expand All @@ -130,6 +133,9 @@ struct GpuChannelGraphicsState
};
}

bool isQuad = Topology == PrimitiveTopology.Quads || Topology == PrimitiveTopology.QuadStrip;
bool halvePrimitiveId = !hostSupportsQuads && !hasGeometryShader && isQuad;

return new GpuGraphicsState(
EarlyZForce,
ConvertToInputTopology(Topology, TessellationMode),
Expand All @@ -149,7 +155,8 @@ struct GpuChannelGraphicsState
in FragmentOutputTypes,
DualSourceBlendEnable,
YNegateEnabled,
originUpperLeft);
originUpperLeft,
halvePrimitiveId);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int tot

if (gpuVa != 0)
{
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex);
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex, addresses.Geometry != 0);
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);

if (nextStage != null)
Expand Down
3 changes: 2 additions & 1 deletion src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public Capabilities GetCapabilities()
supportsBgraFormat: false,
supportsR4G4Format: false,
supportsR4G4B4A4Format: true,
supportsScaledVertexFormats: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsSparseBuffer: false,
Expand All @@ -175,7 +176,7 @@ public Capabilities GetCapabilities()
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
supportsCubemapView: true,
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
supportsScaledVertexFormats: true,
supportsQuads: HwCapabilities.SupportsQuads,
supportsSeparateSampler: false,
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
Expand Down
10 changes: 9 additions & 1 deletion src/Ryujinx.Graphics.Shader/GpuGraphicsState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ namespace Ryujinx.Graphics.Shader
/// </summary>
public readonly bool OriginUpperLeft;

/// <summary>
/// Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion.
/// </summary>
public readonly bool HalvePrimitiveId;

/// <summary>
/// Creates a new GPU graphics state.
/// </summary>
Expand All @@ -124,6 +129,7 @@ namespace Ryujinx.Graphics.Shader
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
/// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param>
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
/// <param name="halvePrimitiveId">Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion</param>
public GpuGraphicsState(
bool earlyZForce,
InputTopology topology,
Expand All @@ -143,7 +149,8 @@ namespace Ryujinx.Graphics.Shader
in Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable,
bool yNegateEnabled,
bool originUpperLeft)
bool originUpperLeft,
bool halvePrimitiveId)
{
EarlyZForce = earlyZForce;
Topology = topology;
Expand All @@ -164,6 +171,7 @@ namespace Ryujinx.Graphics.Shader
DualSourceBlendEnable = dualSourceBlendEnable;
YNegateEnabled = yNegateEnabled;
OriginUpperLeft = originUpperLeft;
HalvePrimitiveId = halvePrimitiveId;
}
}
}
1 change: 1 addition & 0 deletions src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ GpuGraphicsState QueryGraphicsState()
default,
false,
false,
false,
false);
}

Expand Down
10 changes: 10 additions & 0 deletions src/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ public static void Ald(EmitterContext context)
value = context.IConvertU32ToFP32(value);
}
}
else if (offset == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
{
value = context.ShiftRightS32(value, Const(1));
}

context.Copy(Register(rd), value);
}
Expand Down Expand Up @@ -187,6 +191,12 @@ public static void Ipa(EmitterContext context)
}
}
}
else if (op.Imm10 == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
{
// If quads are used, but the host does not support them, they need to be converted to triangles.
// Since each quad becomes 2 triangles, we need to compensate here and divide primitive ID by 2.
res = context.ShiftRightS32(res, Const(1));
}
else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug())
{
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ public static void RunPass(BasicBlock block, ResourceManager resourceManager, IG

if (nvHandle.AsgOp is not Operation handleOp ||
handleOp.Inst != Instruction.Load ||
handleOp.StorageKind != StorageKind.Input)
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
{
// Right now, we only allow bindless access when the handle comes from a shader input.
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
// This is an artificial limitation to prevent it from being used in cases where it
// would have a large performance impact of loading all textures in the pool.
// It might be removed in the future, if we can mitigate the performance impact.
Expand Down
2 changes: 2 additions & 0 deletions src/Ryujinx.Graphics.Shader/Translation/ShaderDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class ShaderDefinitions
public bool YNegateEnabled => _graphicsState.YNegateEnabled;
public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;

public bool HalvePrimitiveId => _graphicsState.HalvePrimitiveId;

public ImapPixelType[] ImapTypes { get; }
public bool IaIndexing { get; private set; }
public bool OaIndexing { get; private set; }
Expand Down
3 changes: 2 additions & 1 deletion src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@ public unsafe Capabilities GetCapabilities()
supportsBgraFormat: true,
supportsR4G4Format: false,
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
Expand All @@ -705,7 +706,7 @@ public unsafe Capabilities GetCapabilities()
supportsMismatchingViewFormat: true,
supportsCubemapView: !IsAmdGcn,
supportsNonConstantTextureOffset: false,
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
supportsQuads: false,
supportsSeparateSampler: true,
supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
Expand Down