Work around unsized array MoltenVK bug, disable bindless if not supported

This commit is contained in:
Gabriel A 2023-11-18 00:42:48 -03:00
parent 764849b05e
commit deb2c9a981
10 changed files with 87 additions and 27 deletions

View file

@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.GAL
public readonly string VendorName; public readonly string VendorName;
public readonly bool HasFrontFacingBug; public readonly bool HasFrontFacingBug;
public readonly bool HasUnsizedDescriptorArrayBug;
public readonly bool HasVectorIndexingBug; public readonly bool HasVectorIndexingBug;
public readonly bool NeedsFragmentOutputSpecialization; public readonly bool NeedsFragmentOutputSpecialization;
public readonly bool ReduceShaderPrecision; public readonly bool ReduceShaderPrecision;
@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsScaledVertexFormats; public readonly bool SupportsScaledVertexFormats;
public readonly bool SupportsSnormBufferTextureFormat; public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool Supports5BitComponentFormat; public readonly bool Supports5BitComponentFormat;
public readonly bool SupportsBindlessTextures;
public readonly bool SupportsBlendEquationAdvanced; public readonly bool SupportsBlendEquationAdvanced;
public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsFragmentShaderOrderingIntel;
@ -64,6 +66,7 @@ namespace Ryujinx.Graphics.GAL
TargetApi api, TargetApi api,
string vendorName, string vendorName,
bool hasFrontFacingBug, bool hasFrontFacingBug,
bool hasUnsizedDescriptorArrayBug,
bool hasVectorIndexingBug, bool hasVectorIndexingBug,
bool needsFragmentOutputSpecialization, bool needsFragmentOutputSpecialization,
bool reduceShaderPrecision, bool reduceShaderPrecision,
@ -79,6 +82,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsScaledVertexFormats, bool supportsScaledVertexFormats,
bool supportsSnormBufferTextureFormat, bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat, bool supports5BitComponentFormat,
bool supportsBindlessTextures,
bool supportsBlendEquationAdvanced, bool supportsBlendEquationAdvanced,
bool supportsFragmentShaderInterlock, bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel, bool supportsFragmentShaderOrderingIntel,
@ -115,6 +119,7 @@ namespace Ryujinx.Graphics.GAL
Api = api; Api = api;
VendorName = vendorName; VendorName = vendorName;
HasFrontFacingBug = hasFrontFacingBug; HasFrontFacingBug = hasFrontFacingBug;
HasUnsizedDescriptorArrayBug = hasUnsizedDescriptorArrayBug;
HasVectorIndexingBug = hasVectorIndexingBug; HasVectorIndexingBug = hasVectorIndexingBug;
NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization; NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
ReduceShaderPrecision = reduceShaderPrecision; ReduceShaderPrecision = reduceShaderPrecision;
@ -130,6 +135,7 @@ namespace Ryujinx.Graphics.GAL
SupportsScaledVertexFormats = supportsScaledVertexFormats; SupportsScaledVertexFormats = supportsScaledVertexFormats;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat; Supports5BitComponentFormat = supports5BitComponentFormat;
SupportsBindlessTextures = supportsBindlessTextures;
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced; SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;

View file

@ -157,6 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug; public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
public bool QueryHostHasUnsizedDescriptorArrayBug() => _context.Capabilities.HasUnsizedDescriptorArrayBug;
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug; public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment; public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
@ -165,6 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat; public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
public bool QueryHostSupportsBindlessTextures() => _context.Capabilities.SupportsBindlessTextures;
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock; public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel; public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;

View file

@ -134,6 +134,7 @@ namespace Ryujinx.Graphics.OpenGL
api: TargetApi.OpenGL, api: TargetApi.OpenGL,
vendorName: GpuVendor, vendorName: GpuVendor,
hasFrontFacingBug: intelWindows, hasFrontFacingBug: intelWindows,
hasUnsizedDescriptorArrayBug: false,
hasVectorIndexingBug: amdWindows, hasVectorIndexingBug: amdWindows,
needsFragmentOutputSpecialization: false, needsFragmentOutputSpecialization: false,
reduceShaderPrecision: false, reduceShaderPrecision: false,
@ -148,6 +149,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsR4G4B4A4Format: true, supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false, supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true, supports5BitComponentFormat: true,
supportsBindlessTextures: HwCapabilities.SupportsArbBindlessTexture,
supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced, supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,

View file

@ -190,7 +190,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (sampler.ArraySize == 0) if (sampler.ArraySize == 0)
{ {
var sampledImageArrayType = context.TypeRuntimeArray(imageTypeForSampler); var sampledImageArrayType = context.HostCapabilities.HasUnsizedDescriptorArrayBug
? context.TypeArray(imageTypeForSampler, context.Constant(context.TypeU32(), 1))
: context.TypeRuntimeArray(imageTypeForSampler);
sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType); sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
} }
else if (sampler.ArraySize != 1) else if (sampler.ArraySize != 1)
@ -241,7 +243,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (image.ArraySize == 0) if (image.ArraySize == 0)
{ {
var imageArrayType = context.TypeRuntimeArray(imageType); var imageArrayType = context.HostCapabilities.HasUnsizedDescriptorArrayBug
? context.TypeArray(imageType, context.Constant(context.TypeU32(), 1))
: context.TypeRuntimeArray(imageType);
imageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, imageArrayType); imageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, imageArrayType);
} }
else if (image.ArraySize != 1) else if (image.ArraySize != 1)

View file

@ -195,6 +195,15 @@ namespace Ryujinx.Graphics.Shader
return false; return false;
} }
/// <summary>
/// Queries host about the presence of the unsized descriptor array bug.
/// </summary>
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
bool QueryHostHasUnsizedDescriptorArrayBug()
{
return false;
}
/// <summary> /// <summary>
/// Queries host about the presence of the vector indexing bug. /// Queries host about the presence of the vector indexing bug.
/// </summary> /// </summary>
@ -231,6 +240,15 @@ namespace Ryujinx.Graphics.Shader
return true; return true;
} }
/// <summary>
/// Queries host support for accessing bindless textures.
/// </summary>
/// <returns>True if bindless textures are supported, false otherwise</returns>
bool QueryHostSupportsBindlessTextures()
{
return true;
}
/// <summary> /// <summary>
/// Queries host support for fragment shader ordering critical sections on the shader code. /// Queries host support for fragment shader ordering critical sections on the shader code.
/// </summary> /// </summary>

View file

@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.Shader.Translation
class HostCapabilities class HostCapabilities
{ {
public readonly bool ReducedPrecision; public readonly bool ReducedPrecision;
public readonly bool HasUnsizedDescriptorArrayBug;
public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsGeometryShaderPassthrough;
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public HostCapabilities( public HostCapabilities(
bool reducedPrecision, bool reducedPrecision,
bool hasUnsizedDescriptorArrayBug,
bool supportsFragmentShaderInterlock, bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel, bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough, bool supportsGeometryShaderPassthrough,
@ -22,6 +24,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool supportsViewportMask) bool supportsViewportMask)
{ {
ReducedPrecision = reducedPrecision; ReducedPrecision = reducedPrecision;
HasUnsizedDescriptorArrayBug = hasUnsizedDescriptorArrayBug;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;

View file

@ -195,12 +195,14 @@ namespace Ryujinx.Graphics.Shader.Translation
private void AddBindlessDefinition(int set, int binding, string name, SamplerType samplerType) private void AddBindlessDefinition(int set, int binding, string name, SamplerType samplerType)
{ {
TextureDefinition definition = new(set, binding, name, samplerType, TextureFormat.Unknown, TextureUsageFlags.None, 0); TextureDefinition definition = new(set, binding, name, samplerType, TextureFormat.Unknown, TextureUsageFlags.None, 0);
Properties.AddOrUpdateTexture(definition, samplerType & ~(SamplerType.Separate | SamplerType.Shadow)); Properties.AddOrUpdateTexture(definition, samplerType & ~(SamplerType.Separate | SamplerType.Shadow));
} }
private void AddBindlessSeparateDefinition(int set, int binding, string name, SamplerType samplerType) private void AddBindlessSeparateDefinition(int set, int binding, string name, SamplerType samplerType)
{ {
samplerType = (samplerType & ~SamplerType.Shadow) | SamplerType.Separate; samplerType = (samplerType & ~SamplerType.Shadow) | SamplerType.Separate;
AddBindlessDefinition(set, binding, name, samplerType); AddBindlessDefinition(set, binding, name, samplerType);
} }
@ -212,6 +214,7 @@ namespace Ryujinx.Graphics.Shader.Translation
binding = _gpuAccessor.QueryBindingConstantBuffer(slot); binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
_cbSlotToBindingMap[slot] = binding; _cbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture); string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}"); AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
} }
@ -234,6 +237,7 @@ namespace Ryujinx.Graphics.Shader.Translation
binding = _gpuAccessor.QueryBindingStorageBuffer(slot); binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
_sbSlotToBindingMap[slot] = binding; _sbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture); string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}"); AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
} }

View file

@ -24,7 +24,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
ref context.BindlessTextureFlags, ref context.BindlessTextureFlags,
ref context.BindlessIndexedBuffersMask, ref context.BindlessIndexedBuffersMask,
context.BindlessTexturesAllowed, context.BindlessTexturesAllowed,
context.GpuAccessor.QueryTextureBufferIndex()); context.GpuAccessor.QueryTextureBufferIndex(),
context.GpuAccessor.QueryHostSupportsBindlessTextures());
if (prevNode != node) if (prevNode != node)
{ {
@ -787,7 +788,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
ref BindlessTextureFlags bindlessTextureFlags, ref BindlessTextureFlags bindlessTextureFlags,
ref uint bindlessIndexedBuffersMask, ref uint bindlessIndexedBuffersMask,
bool bindlessTexturesAllowed, bool bindlessTexturesAllowed,
int textureBufferIndex) int textureBufferIndex,
bool supportsBindlessTextures)
{ {
if (node.Value is not TextureOperation texOp) if (node.Value is not TextureOperation texOp)
{ {
@ -797,35 +799,36 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
// If it's already bindless, then we have nothing to do. // If it's already bindless, then we have nothing to do.
if (texOp.Flags.HasFlag(TextureFlags.Bindless)) if (texOp.Flags.HasFlag(TextureFlags.Bindless))
{ {
resourceManager.EnsureBindlessBinding(targetApi, texOp.Type, texOp.Inst.IsImage()); if (SupportsBindlessAccess(texOp.Type, supportsBindlessTextures))
if (IsIndexedAccess(resourceManager, texOp, ref bindlessTextureFlags, ref bindlessIndexedBuffersMask, textureBufferIndex))
{ {
return node; resourceManager.EnsureBindlessBinding(targetApi, texOp.Type, texOp.Inst.IsImage());
}
if (bindlessTexturesAllowed) if (IsIndexedAccess(resourceManager, texOp, ref bindlessTextureFlags, ref bindlessIndexedBuffersMask, textureBufferIndex))
{
bindlessTextureFlags |= BindlessTextureFlags.BindlessFull;
return node;
}
else
{
// Set any destination operand to zero and remove the texture access.
// This is a case where bindless elimination failed, and we assume
// it's too risky to try using full bindless emulation.
for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++)
{ {
Operand dest = texOp.GetDest(destIndex); return node;
node.List.AddBefore(node, new Operation(Instruction.Copy, dest, Const(0)));
} }
LinkedListNode<INode> prevNode = node.Previous; if (bindlessTexturesAllowed)
node.List.Remove(node); {
bindlessTextureFlags |= BindlessTextureFlags.BindlessFull;
return prevNode; return node;
}
} }
// Set any destination operand to zero and remove the texture access.
// This is a case where bindless elimination failed, and we assume
// it's too risky or not possible to try using full bindless emulation.
for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++)
{
Operand dest = texOp.GetDest(destIndex);
node.List.AddBefore(node, new Operation(Instruction.Copy, dest, Const(0)));
}
LinkedListNode<INode> prevNode = node.Previous;
node.List.Remove(node);
return prevNode;
} }
// If the index is within the host API limits, then we don't need to make it bindless. // If the index is within the host API limits, then we don't need to make it bindless.
@ -866,6 +869,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
return node; return node;
} }
private static bool SupportsBindlessAccess(SamplerType type, bool supportsBindlessTextures)
{
// TODO: Support bindless buffer texture access.
if ((type & SamplerType.Mask) == SamplerType.TextureBuffer)
{
return false;
}
return supportsBindlessTextures;
}
private static bool IsIndexedAccess( private static bool IsIndexedAccess(
ResourceManager resourceManager, ResourceManager resourceManager,
TextureOperation texOp, TextureOperation texOp,

View file

@ -371,6 +371,7 @@ namespace Ryujinx.Graphics.Shader.Translation
var hostCapabilities = new HostCapabilities( var hostCapabilities = new HostCapabilities(
GpuAccessor.QueryHostReducedPrecision(), GpuAccessor.QueryHostReducedPrecision(),
GpuAccessor.QueryHostHasUnsizedDescriptorArrayBug(),
GpuAccessor.QueryHostSupportsFragmentShaderInterlock(), GpuAccessor.QueryHostSupportsFragmentShaderInterlock(),
GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(), GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(),
GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(), GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(),

View file

@ -572,10 +572,13 @@ namespace Ryujinx.Graphics.Vulkan
var limits = _physicalDevice.PhysicalDeviceProperties.Limits; var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
bool supportsDescriptorIndexing = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing");
return new Capabilities( return new Capabilities(
api: TargetApi.Vulkan, api: TargetApi.Vulkan,
GpuVendor, GpuVendor,
hasFrontFacingBug: IsIntelWindows, hasFrontFacingBug: IsIntelWindows,
hasUnsizedDescriptorArrayBug: IsMoltenVk,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm, hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
needsFragmentOutputSpecialization: IsMoltenVk, needsFragmentOutputSpecialization: IsMoltenVk,
reduceShaderPrecision: IsMoltenVk, reduceShaderPrecision: IsMoltenVk,
@ -590,6 +593,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsR4G4B4A4Format: supportsR4G4B4A4Format, supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true, supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat, supports5BitComponentFormat: supports5BitComponentFormat,
supportsBindlessTextures: supportsDescriptorIndexing,
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced, supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false, supportsFragmentShaderOrderingIntel: false,