diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index 8ea4c02a..a42cbb07 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.GAL
public struct Capabilities
{
public bool SupportsAstcCompression { get; }
+ public bool SupportsImageLoadFormatted { get; }
public bool SupportsNonConstantTextureOffset { get; }
public int MaximumComputeSharedMemorySize { get; }
@@ -12,12 +13,14 @@ namespace Ryujinx.Graphics.GAL
public Capabilities(
bool supportsAstcCompression,
+ bool supportsImageLoadFormatted,
bool supportsNonConstantTextureOffset,
int maximumComputeSharedMemorySize,
int storageBufferOffsetAlignment,
float maxSupportedAnisotropy)
{
SupportsAstcCompression = supportsAstcCompression;
+ SupportsImageLoadFormatted = supportsImageLoadFormatted;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 7dc175e1..76b06ea5 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -175,6 +175,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Host storage buffer alignment in bytes
public int QueryStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
+ ///
+ /// Queries host support for readable images without a explicit format declaration on the shader.
+ ///
+ /// True if formatted image load is supported, false otherwise
+ public bool QuerySupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
+
///
/// Queries host GPU non-constant texture offset support.
///
diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
index 4b572af8..229b3561 100644
--- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
+++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs
@@ -5,7 +5,8 @@ namespace Ryujinx.Graphics.OpenGL
{
static class HwCapabilities
{
- private static readonly Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
+ private static readonly Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
+ private static readonly Lazy _supportsImageLoadFormatted = new Lazy(() => HasExtension("GL_EXT_shader_image_load_formatted"));
private static readonly Lazy _maximumComputeSharedMemorySize = new Lazy(() => GetLimit(All.MaxComputeSharedMemorySize));
private static readonly Lazy _storageBufferOffsetAlignment = new Lazy(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
@@ -25,6 +26,7 @@ namespace Ryujinx.Graphics.OpenGL
private static Lazy _maxSupportedAnisotropy = new Lazy(GL.GetFloat((GetPName)All.MaxTextureMaxAnisotropy));
public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
+ public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value;
public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia;
public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value;
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index 15b223f5..c839c296 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -73,6 +73,7 @@ namespace Ryujinx.Graphics.OpenGL
{
return new Capabilities(
HwCapabilities.SupportsAstcCompression,
+ HwCapabilities.SupportsImageLoadFormatted,
HwCapabilities.SupportsNonConstantTextureOffset,
HwCapabilities.MaximumComputeSharedMemorySize,
HwCapabilities.StorageBufferOffsetAlignment,
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index da902aa3..a45c10c3 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable");
context.AppendLine("#extension GL_ARB_shader_ballot : enable");
context.AppendLine("#extension GL_ARB_shader_group_vote : enable");
+ context.AppendLine("#extension GL_EXT_shader_image_load_formatted : enable");
if (context.Config.Stage == ShaderStage.Compute)
{
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs
index 42fe677b..265d45a9 100644
--- a/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs
@@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
Size = (IntegerSize)opCode.Extract(20, 4);
}
- ByteAddress = !opCode.Extract(23);
+ ByteAddress = opCode.Extract(23);
Dimensions = (ImageDimensions)opCode.Extract(33, 3);
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 13281bf0..e10d869b 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -54,6 +54,11 @@
return 16;
}
+ public bool QuerySupportsImageLoadFormatted()
+ {
+ return true;
+ }
+
public bool QuerySupportsNonConstantTextureOffset()
{
return true;
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
index af5122be..7bed3f30 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
@@ -1217,6 +1217,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
private static TextureFormat GetTextureFormat(EmitterContext context, int handle)
{
+ // 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 (context.Config.GpuAccessor.QuerySupportsImageLoadFormatted())
+ {
+ return TextureFormat.Unknown;
+ }
+
var format = context.Config.GpuAccessor.QueryTextureFormat(handle);
if (format == TextureFormat.Unknown)