diff --git a/src/Ryujinx.Graphics.GAL/Capabilities.cs b/src/Ryujinx.Graphics.GAL/Capabilities.cs
index 8959bf93e..8658d7344 100644
--- a/src/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/src/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.GAL
public readonly string VendorName;
public readonly bool HasFrontFacingBug;
+ public readonly bool HasUnsizedDescriptorArrayBug;
public readonly bool HasVectorIndexingBug;
public readonly bool NeedsFragmentOutputSpecialization;
public readonly bool ReduceShaderPrecision;
@@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsScaledVertexFormats;
public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool Supports5BitComponentFormat;
+ public readonly bool SupportsBindlessTextures;
public readonly bool SupportsBlendEquationAdvanced;
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel;
@@ -64,6 +66,7 @@ namespace Ryujinx.Graphics.GAL
TargetApi api,
string vendorName,
bool hasFrontFacingBug,
+ bool hasUnsizedDescriptorArrayBug,
bool hasVectorIndexingBug,
bool needsFragmentOutputSpecialization,
bool reduceShaderPrecision,
@@ -79,6 +82,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsScaledVertexFormats,
bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat,
+ bool supportsBindlessTextures,
bool supportsBlendEquationAdvanced,
bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel,
@@ -115,6 +119,7 @@ namespace Ryujinx.Graphics.GAL
Api = api;
VendorName = vendorName;
HasFrontFacingBug = hasFrontFacingBug;
+ HasUnsizedDescriptorArrayBug = hasUnsizedDescriptorArrayBug;
HasVectorIndexingBug = hasVectorIndexingBug;
NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
ReduceShaderPrecision = reduceShaderPrecision;
@@ -130,6 +135,7 @@ namespace Ryujinx.Graphics.GAL
SupportsScaledVertexFormats = supportsScaledVertexFormats;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat;
+ SupportsBindlessTextures = supportsBindlessTextures;
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index 8deb83c74..b1184e919 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -157,6 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
+ public bool QueryHostHasUnsizedDescriptorArrayBug() => _context.Capabilities.HasUnsizedDescriptorArrayBug;
+
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
@@ -165,6 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
+ public bool QueryHostSupportsBindlessTextures() => _context.Capabilities.SupportsBindlessTextures;
+
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
index 6c97274c0..f15d3b12b 100644
--- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
+++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
@@ -134,6 +134,7 @@ namespace Ryujinx.Graphics.OpenGL
api: TargetApi.OpenGL,
vendorName: GpuVendor,
hasFrontFacingBug: intelWindows,
+ hasUnsizedDescriptorArrayBug: false,
hasVectorIndexingBug: amdWindows,
needsFragmentOutputSpecialization: false,
reduceShaderPrecision: false,
@@ -148,6 +149,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
+ supportsBindlessTextures: HwCapabilities.SupportsArbBindlessTexture,
supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 85bd8ab48..421a35143 100644
--- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -190,7 +190,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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);
}
else if (sampler.ArraySize != 1)
@@ -241,7 +243,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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);
}
else if (image.ArraySize != 1)
diff --git a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 2df2b1442..22572ad51 100644
--- a/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/src/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -195,6 +195,15 @@ namespace Ryujinx.Graphics.Shader
return false;
}
+ ///
+ /// Queries host about the presence of the unsized descriptor array bug.
+ ///
+ /// True if the bug is present on the host device used, false otherwise
+ bool QueryHostHasUnsizedDescriptorArrayBug()
+ {
+ return false;
+ }
+
///
/// Queries host about the presence of the vector indexing bug.
///
@@ -231,6 +240,15 @@ namespace Ryujinx.Graphics.Shader
return true;
}
+ ///
+ /// Queries host support for accessing bindless textures.
+ ///
+ /// True if bindless textures are supported, false otherwise
+ bool QueryHostSupportsBindlessTextures()
+ {
+ return true;
+ }
+
///
/// Queries host support for fragment shader ordering critical sections on the shader code.
///
diff --git a/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs b/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs
index 2523272b0..ccdec2210 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/HostCapabilities.cs
@@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.Shader.Translation
class HostCapabilities
{
public readonly bool ReducedPrecision;
+ public readonly bool HasUnsizedDescriptorArrayBug;
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough;
@@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public HostCapabilities(
bool reducedPrecision,
+ bool hasUnsizedDescriptorArrayBug,
bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough,
@@ -22,6 +24,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool supportsViewportMask)
{
ReducedPrecision = reducedPrecision;
+ HasUnsizedDescriptorArrayBug = hasUnsizedDescriptorArrayBug;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
index 6abe1532a..0c2677b89 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs
@@ -195,12 +195,14 @@ namespace Ryujinx.Graphics.Shader.Translation
private void AddBindlessDefinition(int set, int binding, string name, SamplerType samplerType)
{
TextureDefinition definition = new(set, binding, name, samplerType, TextureFormat.Unknown, TextureUsageFlags.None, 0);
+
Properties.AddOrUpdateTexture(definition, samplerType & ~(SamplerType.Separate | SamplerType.Shadow));
}
private void AddBindlessSeparateDefinition(int set, int binding, string name, SamplerType samplerType)
{
samplerType = (samplerType & ~SamplerType.Shadow) | SamplerType.Separate;
+
AddBindlessDefinition(set, binding, name, samplerType);
}
@@ -212,6 +214,7 @@ namespace Ryujinx.Graphics.Shader.Translation
binding = _gpuAccessor.QueryBindingConstantBuffer(slot);
_cbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
+
AddNewConstantBuffer(binding, $"{_stagePrefix}_c{slotNumber}");
}
@@ -234,6 +237,7 @@ namespace Ryujinx.Graphics.Shader.Translation
binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
_sbSlotToBindingMap[slot] = binding;
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
+
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
}
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs
index 706c3347b..495b331d7 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/TexturePass.cs
@@ -24,7 +24,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
ref context.BindlessTextureFlags,
ref context.BindlessIndexedBuffersMask,
context.BindlessTexturesAllowed,
- context.GpuAccessor.QueryTextureBufferIndex());
+ context.GpuAccessor.QueryTextureBufferIndex(),
+ context.GpuAccessor.QueryHostSupportsBindlessTextures());
if (prevNode != node)
{
@@ -787,7 +788,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
ref BindlessTextureFlags bindlessTextureFlags,
ref uint bindlessIndexedBuffersMask,
bool bindlessTexturesAllowed,
- int textureBufferIndex)
+ int textureBufferIndex,
+ bool supportsBindlessTextures)
{
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 (texOp.Flags.HasFlag(TextureFlags.Bindless))
{
- resourceManager.EnsureBindlessBinding(targetApi, texOp.Type, texOp.Inst.IsImage());
-
- if (IsIndexedAccess(resourceManager, texOp, ref bindlessTextureFlags, ref bindlessIndexedBuffersMask, textureBufferIndex))
+ if (SupportsBindlessAccess(texOp.Type, supportsBindlessTextures))
{
- return node;
- }
+ resourceManager.EnsureBindlessBinding(targetApi, texOp.Type, texOp.Inst.IsImage());
- if (bindlessTexturesAllowed)
- {
- 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++)
+ if (IsIndexedAccess(resourceManager, texOp, ref bindlessTextureFlags, ref bindlessIndexedBuffersMask, textureBufferIndex))
{
- Operand dest = texOp.GetDest(destIndex);
- node.List.AddBefore(node, new Operation(Instruction.Copy, dest, Const(0)));
+ return node;
}
- LinkedListNode prevNode = node.Previous;
- node.List.Remove(node);
-
- return prevNode;
+ if (bindlessTexturesAllowed)
+ {
+ bindlessTextureFlags |= BindlessTextureFlags.BindlessFull;
+ 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 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.
@@ -866,6 +869,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
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(
ResourceManager resourceManager,
TextureOperation texOp,
diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
index d198f8f9c..2fb16141a 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
@@ -371,6 +371,7 @@ namespace Ryujinx.Graphics.Shader.Translation
var hostCapabilities = new HostCapabilities(
GpuAccessor.QueryHostReducedPrecision(),
+ GpuAccessor.QueryHostHasUnsizedDescriptorArrayBug(),
GpuAccessor.QueryHostSupportsFragmentShaderInterlock(),
GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(),
GpuAccessor.QueryHostSupportsGeometryShaderPassthrough(),
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 84cb38b31..b8aa589fd 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -572,10 +572,13 @@ namespace Ryujinx.Graphics.Vulkan
var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
+ bool supportsDescriptorIndexing = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing");
+
return new Capabilities(
api: TargetApi.Vulkan,
GpuVendor,
hasFrontFacingBug: IsIntelWindows,
+ hasUnsizedDescriptorArrayBug: IsMoltenVk,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
needsFragmentOutputSpecialization: IsMoltenVk,
reduceShaderPrecision: IsMoltenVk,
@@ -590,6 +593,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
+ supportsBindlessTextures: supportsDescriptorIndexing,
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false,