aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader
diff options
context:
space:
mode:
authorgdk <gab.dark.100@gmail.com>2019-10-17 23:41:18 -0300
committerThog <thog@protonmail.com>2020-01-09 02:13:00 +0100
commit1b7d95519569639135a68e7ebda5148f3263217c (patch)
tree52a5e471418bf28ce970a268e1b86b64abc9048f /Ryujinx.Graphics.Shader
parent717ace6f6ed65118148dc78976c6e818a095fa4d (diff)
Initial support for image stores, support texture sample on compute
Diffstat (limited to 'Ryujinx.Graphics.Shader')
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs2
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs90
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs1
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs1
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs3
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs3
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs2
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs84
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/ImageComponents.cs11
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/ImageDimensions.cs12
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs48
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs1
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs192
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs2
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs8
-rw-r--r--Ryujinx.Graphics.Shader/SamplerType.cs37
-rw-r--r--Ryujinx.Graphics.Shader/ShaderProgramInfo.cs3
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs8
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs40
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs2
-rw-r--r--Ryujinx.Graphics.Shader/TextureDescriptor.cs10
-rw-r--r--Ryujinx.Graphics.Shader/TextureTarget.cs35
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Translator.cs1
25 files changed, 498 insertions, 121 deletions
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs
index 322bfbf5..abfe55a5 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs
@@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public List<BufferDescriptor> CBufferDescriptors { get; }
public List<BufferDescriptor> SBufferDescriptors { get; }
public List<TextureDescriptor> TextureDescriptors { get; }
+ public List<TextureDescriptor> ImageDescriptors { get; }
public OperandManager OperandManager { get; }
@@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
CBufferDescriptors = new List<BufferDescriptor>();
SBufferDescriptors = new List<BufferDescriptor>();
TextureDescriptors = new List<TextureDescriptor>();
+ ImageDescriptors = new List<TextureDescriptor>();
OperandManager = new OperandManager();
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 3644b21a..ab10d91a 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -88,6 +88,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
+ if (info.Images.Count != 0)
+ {
+ DeclareImages(context, info);
+
+ context.AppendLine();
+ }
+
if (context.Config.Stage != ShaderStage.Compute)
{
if (info.IAttributes.Count != 0)
@@ -204,7 +211,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
continue;
}
- string samplerTypeName = GetSamplerTypeName(texOp.Target);
+ string samplerTypeName = GetSamplerTypeName(texOp.Type);
context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";");
}
@@ -221,17 +228,47 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
AstOperand operand = texOp.GetSource(0) as AstOperand;
- desc = new TextureDescriptor(samplerName, texOp.Target, operand.CbufSlot, operand.CbufOffset);
+ desc = new TextureDescriptor(samplerName, texOp.Type, operand.CbufSlot, operand.CbufOffset);
}
else
{
- desc = new TextureDescriptor(samplerName, texOp.Target, texOp.Handle);
+ desc = new TextureDescriptor(samplerName, texOp.Type, texOp.Handle);
}
context.TextureDescriptors.Add(desc);
}
}
+ private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info)
+ {
+ Dictionary<string, AstTextureOperation> images = new Dictionary<string, AstTextureOperation>();
+
+ foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle))
+ {
+ string imageName = OperandManager.GetImageName(context.Config.Stage, texOp);
+
+ if (!images.TryAdd(imageName, texOp))
+ {
+ continue;
+ }
+
+ string imageTypeName = GetImageTypeName(texOp.Type);
+
+ context.AppendLine("writeonly uniform " + imageTypeName + " " + imageName + ";");
+ }
+
+ foreach (KeyValuePair<string, AstTextureOperation> kv in images)
+ {
+ string imageName = kv.Key;
+
+ AstTextureOperation texOp = kv.Value;
+
+ TextureDescriptor desc = new TextureDescriptor(imageName, texOp.Type, texOp.Handle);
+
+ context.ImageDescriptors.Add(desc);
+ }
+ }
+
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
{
string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty;
@@ -284,36 +321,65 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
}
- private static string GetSamplerTypeName(TextureTarget type)
+ private static string GetSamplerTypeName(SamplerType type)
{
string typeName;
- switch (type & TextureTarget.Mask)
+ switch (type & SamplerType.Mask)
{
- case TextureTarget.Texture1D: typeName = "sampler1D"; break;
- case TextureTarget.Texture2D: typeName = "sampler2D"; break;
- case TextureTarget.Texture3D: typeName = "sampler3D"; break;
- case TextureTarget.TextureCube: typeName = "samplerCube"; break;
+ case SamplerType.Texture1D: typeName = "sampler1D"; break;
+ case SamplerType.TextureBuffer: typeName = "samplerBuffer"; break;
+ case SamplerType.Texture2D: typeName = "sampler2D"; break;
+ case SamplerType.Texture3D: typeName = "sampler3D"; break;
+ case SamplerType.TextureCube: typeName = "samplerCube"; break;
default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
}
- if ((type & TextureTarget.Multisample) != 0)
+ if ((type & SamplerType.Multisample) != 0)
{
typeName += "MS";
}
- if ((type & TextureTarget.Array) != 0)
+ if ((type & SamplerType.Array) != 0)
{
typeName += "Array";
}
- if ((type & TextureTarget.Shadow) != 0)
+ if ((type & SamplerType.Shadow) != 0)
{
typeName += "Shadow";
}
return typeName;
}
+
+ private static string GetImageTypeName(SamplerType type)
+ {
+ string typeName;
+
+ switch (type & SamplerType.Mask)
+ {
+ case SamplerType.Texture1D: typeName = "image1D"; break;
+ case SamplerType.TextureBuffer: typeName = "imageBuffer"; break;
+ case SamplerType.Texture2D: typeName = "image2D"; break;
+ case SamplerType.Texture3D: typeName = "image3D"; break;
+ case SamplerType.TextureCube: typeName = "imageCube"; break;
+
+ default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
+ }
+
+ if ((type & SamplerType.Multisample) != 0)
+ {
+ typeName += "MS";
+ }
+
+ if ((type & SamplerType.Array) != 0)
+ {
+ typeName += "Array";
+ }
+
+ return typeName;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
index 67de3b43..a06b0cc8 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
@@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string LocalNamePrefix = "temp";
public const string SamplerNamePrefix = "tex";
+ public const string ImageNamePrefix = "img";
public const string IAttributePrefix = "in_attr";
public const string OAttributePrefix = "out_attr";
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
index 65246d97..b5407eb8 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.CBufferDescriptors.ToArray(),
context.SBufferDescriptors.ToArray(),
context.TextureDescriptors.ToArray(),
+ context.ImageDescriptors.ToArray(),
context.GetCode());
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs
index 7807cb49..31b7f312 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs
@@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public BufferDescriptor[] CBufferDescriptors { get; }
public BufferDescriptor[] SBufferDescriptors { get; }
public TextureDescriptor[] TextureDescriptors { get; }
+ public TextureDescriptor[] ImageDescriptors { get; }
public string Code { get; }
@@ -12,11 +13,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
BufferDescriptor[] cBufferDescriptors,
BufferDescriptor[] sBufferDescriptors,
TextureDescriptor[] textureDescriptors,
+ TextureDescriptor[] imageDescriptors,
string code)
{
CBufferDescriptors = cBufferDescriptors;
SBufferDescriptors = sBufferDescriptors;
TextureDescriptors = textureDescriptors;
+ ImageDescriptors = imageDescriptors;
Code = code;
}
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
index 350de348..3bf31c16 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs
@@ -87,6 +87,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
switch (inst)
{
+ case Instruction.ImageStore:
+ return InstGenMemory.ImageStore(context, operation);
+
case Instruction.LoadAttribute:
return InstGenMemory.LoadAttribute(context, operation);
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
index 6989e997..6dfbe61a 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
@@ -48,6 +48,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.ExponentB2, InstType.CallUnary, "exp2");
Add(Instruction.Floor, InstType.CallUnary, "floor");
Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma");
+ Add(Instruction.ImageLoad, InstType.Special);
+ Add(Instruction.ImageStore, InstType.Special);
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
Add(Instruction.LoadAttribute, InstType.Special);
Add(Instruction.LoadConstant, InstType.Special);
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index 5c9ec830..f2f6ae0c 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -9,6 +9,80 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
static class InstGenMemory
{
+ public static string ImageStore(CodeGenContext context, AstOperation operation)
+ {
+ AstTextureOperation texOp = (AstTextureOperation)operation;
+
+ bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
+
+ bool isArray = (texOp.Type & SamplerType.Array) != 0;
+
+ string texCall = "imageStore";
+
+ string imageName = OperandManager.GetImageName(context.Config.Stage, texOp);
+
+ texCall += "(" + imageName;
+
+ int coordsCount = texOp.Type.GetDimensions();
+
+ int pCount = coordsCount;
+
+ int arrayIndexElem = -1;
+
+ if (isArray)
+ {
+ arrayIndexElem = pCount++;
+ }
+
+ int srcIndex = isBindless ? 1 : 0;
+
+ string Src(VariableType type)
+ {
+ return GetSoureExpr(context, texOp.GetSource(srcIndex++), type);
+ }
+
+ void Append(string str)
+ {
+ texCall += ", " + str;
+ }
+
+ if (pCount > 1)
+ {
+ string[] elems = new string[pCount];
+
+ for (int index = 0; index < pCount; index++)
+ {
+ elems[index] = Src(VariableType.S32);
+ }
+
+ Append("ivec" + pCount + "(" + string.Join(", ", elems) + ")");
+ }
+ else
+ {
+ Append(Src(VariableType.S32));
+ }
+
+ string[] cElems = new string[4];
+
+ for (int index = 0; index < 4; index++)
+ {
+ if (srcIndex < texOp.SourcesCount)
+ {
+ cElems[index] = Src(VariableType.F32);
+ }
+ else
+ {
+ cElems[index] = NumberFormatter.FormatFloat(0);
+ }
+ }
+
+ Append("vec4(" + string.Join(", ", cElems) + ")");
+
+ texCall += ")";
+
+ return texCall;
+ }
+
public static string LoadAttribute(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
@@ -98,9 +172,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
- bool isArray = (texOp.Target & TextureTarget.Array) != 0;
- bool isMultisample = (texOp.Target & TextureTarget.Multisample) != 0;
- bool isShadow = (texOp.Target & TextureTarget.Shadow) != 0;
+ bool isArray = (texOp.Type & SamplerType.Array) != 0;
+ bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
+ bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
// This combination is valid, but not available on GLSL.
// For now, ignore the LOD level and do a normal sample.
@@ -134,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
texCall += "(" + samplerName;
- int coordsCount = texOp.Target.GetDimensions();
+ int coordsCount = texOp.Type.GetDimensions();
int pCount = coordsCount;
@@ -147,7 +221,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
// The sampler 1D shadow overload expects a
// dummy value on the middle of the vector, who knows why...
- bool hasDummy1DShadowElem = texOp.Target == (TextureTarget.Texture1D | TextureTarget.Shadow);
+ bool hasDummy1DShadowElem = texOp.Type == (SamplerType.Texture1D | SamplerType.Shadow);
if (hasDummy1DShadowElem)
{
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index 88095920..fe307396 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -241,6 +241,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix;
}
+ public static string GetImageName(ShaderStage stage, AstTextureOperation texOp)
+ {
+ string suffix = texOp.Handle.ToString();
+
+ return GetShaderStagePrefix(stage) + "_" + DefaultNames.ImageNamePrefix + suffix;
+ }
+
public static string GetShaderStagePrefix(ShaderStage stage)
{
int index = (int)stage;
diff --git a/Ryujinx.Graphics.Shader/Decoders/ImageComponents.cs b/Ryujinx.Graphics.Shader/Decoders/ImageComponents.cs
new file mode 100644
index 00000000..b8a4f6d5
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/ImageComponents.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum ImageComponents
+ {
+ Red = 1 << 0,
+ Green = 1 << 1,
+ Blue = 1 << 2,
+ Alpha = 1 << 3
+
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/ImageDimensions.cs b/Ryujinx.Graphics.Shader/Decoders/ImageDimensions.cs
new file mode 100644
index 00000000..ecf41a82
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/ImageDimensions.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum ImageDimensions
+ {
+ Image1D,
+ ImageBuffer,
+ Image1DArray,
+ Image2D,
+ Image2DArray,
+ Image3D
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs
new file mode 100644
index 00000000..42fe677b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeImage.cs
@@ -0,0 +1,48 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeImage : OpCode
+ {
+ public Register Ra { get; }
+ public Register Rb { get; }
+ public Register Rc { get; }
+
+ public ImageComponents Components { get; }
+ public IntegerSize Size { get; }
+
+ public bool ByteAddress { get; }
+
+ public ImageDimensions Dimensions { get; }
+
+ public int Immediate { get; }
+
+ public bool UseComponents { get; }
+ public bool IsBindless { get; }
+
+ public OpCodeImage(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+ Rb = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr);
+
+ UseComponents = !opCode.Extract(52);
+
+ if (UseComponents)
+ {
+ Components = (ImageComponents)opCode.Extract(20, 4);
+ }
+ else
+ {
+ Size = (IntegerSize)opCode.Extract(20, 4);
+ }
+
+ ByteAddress = !opCode.Extract(23);
+
+ Dimensions = (ImageDimensions)opCode.Extract(33, 3);
+
+ Immediate = opCode.Extract(36, 13);
+ IsBindless = !opCode.Extract(51);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
index 3fd1de86..5128dae3 100644
--- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
@@ -143,6 +143,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy));
Set("1110111101010x", InstEmit.St, typeof(OpCodeMemory));
Set("1110111011011x", InstEmit.Stg, typeof(OpCodeMemory));
+ Set("11101011001xxx", InstEmit.Sust, typeof(OpCodeImage));
Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync));
Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex));
Set("1101111010111x", InstEmit.TexB, typeof(OpCodeTexB));
diff --git a/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs b/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs
index 478cac44..e5a0c004 100644
--- a/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs
@@ -2,14 +2,14 @@ namespace Ryujinx.Graphics.Shader.Decoders
{
enum TexelLoadTarget
{
- Texture1DLodZero = 0x0,
- Texture1DLodLevel = 0x1,
- Texture2DLodZero = 0x2,
- Texture2DLodZeroOffset = 0x4,
- Texture2DLodLevel = 0x5,
+ Texture1DLodZero = 0x0,
+ Texture1DLodLevel = 0x1,
+ Texture2DLodZero = 0x2,
+ Texture2DLodZeroOffset = 0x4,
+ Texture2DLodLevel = 0x5,
Texture2DLodZeroMultisample = 0x6,
- Texture3DLodZero = 0x7,
- Texture2DArrayLodZero = 0x8,
- Texture2DLodLevelOffset = 0xc
+ Texture3DLodZero = 0x7,
+ Texture2DArrayLodZero = 0x8,
+ Texture2DLodLevelOffset = 0xc
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
index 554deb7b..39672789 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
@@ -10,6 +10,96 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
static partial class InstEmit
{
+ public static void Sust(EmitterContext context)
+ {
+ OpCodeImage op = (OpCodeImage)context.CurrOp;
+
+ int raIndex = op.Ra.Index;
+ int rbIndex = op.Rb.Index;
+
+ Operand Ra()
+ {
+ if (raIndex > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (rbIndex > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(rbIndex++, RegisterType.Gpr));
+ }
+
+ bool isArray = op.Dimensions == ImageDimensions.Image1DArray ||
+ op.Dimensions == ImageDimensions.Image2DArray;
+
+ Operand arrayIndex = isArray ? Ra() : null;
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (op.IsBindless)
+ {
+ sourcesList.Add(context.Copy(Register(op.Rc)));
+ }
+
+ SamplerType type = GetSamplerType(op.Dimensions);
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ if (isArray)
+ {
+ sourcesList.Add(arrayIndex);
+
+ type |= SamplerType.Array;
+ }
+
+ if (op.UseComponents)
+ {
+ int componentMask = (int)op.Components;
+
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ {
+ if ((compMask & 1) != 0)
+ {
+ sourcesList.Add(Rb());
+ }
+ }
+ }
+ else
+ {
+ // TODO.
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+
+ int handle = !op.IsBindless ? op.Immediate : 0;
+
+ TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None;
+
+ TextureOperation operation = new TextureOperation(
+ Instruction.ImageStore,
+ type,
+ flags,
+ handle,
+ 0,
+ null,
+ sources);
+
+ context.Add(operation);
+ }
+
public static void Tex(EmitterContext context)
{
Tex(context, TextureFlags.None);
@@ -74,15 +164,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- TextureTarget type;
+ SamplerType type;
TextureFlags flags;
if (op is OpCodeTexs texsOp)
{
- type = GetTextureType (texsOp.Target);
- flags = GetTextureFlags(texsOp.Target);
+ type = GetSamplerType (texsOp.Target);
+ flags = GetSamplerFlags(texsOp.Target);
- if ((type & TextureTarget.Array) != 0)
+ if ((type & SamplerType.Array) != 0)
{
Operand arrayIndex = Ra();
@@ -91,7 +181,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(arrayIndex);
- if ((type & TextureTarget.Shadow) != 0)
+ if ((type & SamplerType.Shadow) != 0)
{
sourcesList.Add(Rb());
}
@@ -149,8 +239,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
else if (op is OpCodeTlds tldsOp)
{
- type = GetTextureType (tldsOp.Target);
- flags = GetTextureFlags(tldsOp.Target) | TextureFlags.IntCoords;
+ type = GetSamplerType (tldsOp.Target);
+ flags = GetSamplerFlags(tldsOp.Target) | TextureFlags.IntCoords;
switch (tldsOp.Target)
{
@@ -217,14 +307,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Ra());
}
- type = TextureTarget.Texture2D;
+ type = SamplerType.Texture2D;
flags = TextureFlags.Gather;
if (tld4sOp.HasDepthCompare)
{
sourcesList.Add(Rb());
- type |= TextureTarget.Shadow;
+ type |= SamplerType.Shadow;
}
if (tld4sOp.HasOffset)
@@ -338,7 +428,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
List<Operand> sourcesList = new List<Operand>();
- TextureTarget type = GetTextureType(op.Dimensions);
+ SamplerType type = GetSamplerType(op.Dimensions);
TextureFlags flags = TextureFlags.Gather;
@@ -353,7 +443,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
sourcesList.Add(arrayIndex);
- type |= TextureTarget.Array;
+ type |= SamplerType.Array;
}
Operand[] packedOffs = new Operand[2];
@@ -365,7 +455,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
sourcesList.Add(Rb());
- type |= TextureTarget.Shadow;
+ type |= SamplerType.Shadow;
}
if (op.Offset != TextureGatherOffset.None)
@@ -446,7 +536,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
// TODO: Validate and use property.
Instruction inst = Instruction.TextureSize;
- TextureTarget type = TextureTarget.Texture2D;
+ SamplerType type = SamplerType.Texture2D;
TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None;
@@ -551,7 +641,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Rb());
}
- TextureTarget type = GetTextureType(op.Dimensions);
+ SamplerType type = GetSamplerType(op.Dimensions);
int coordsCount = type.GetDimensions();
@@ -564,7 +654,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
sourcesList.Add(arrayIndex);
- type |= TextureTarget.Array;
+ type |= SamplerType.Array;
}
bool hasLod = op.LodMode > TextureLodMode.LodZero;
@@ -577,7 +667,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
sourcesList.Add(Rb());
- type |= TextureTarget.Shadow;
+ type |= SamplerType.Shadow;
}
if ((op.LodMode == TextureLodMode.LodZero ||
@@ -611,7 +701,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
sourcesList.Add(Rb());
- type |= TextureTarget.Multisample;
+ type |= SamplerType.Multisample;
}
Operand[] sources = sourcesList.ToArray();
@@ -650,83 +740,109 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- private static TextureTarget GetTextureType(TextureDimensions dimensions)
+ private static SamplerType GetSamplerType(ImageDimensions target)
+ {
+ switch (target)
+ {
+ case ImageDimensions.Image1D:
+ return SamplerType.Texture1D;
+
+ case ImageDimensions.ImageBuffer:
+ return SamplerType.TextureBuffer;
+
+ case ImageDimensions.Image1DArray:
+ return SamplerType.Texture1D | SamplerType.Array;
+
+ case ImageDimensions.Image2D:
+ return SamplerType.Texture2D;
+
+ case ImageDimensions.Image2DArray:
+ return SamplerType.Texture2D | SamplerType.Array;
+
+ case ImageDimensions.Image3D:
+ return SamplerType.Texture3D;
+ }
+
+ throw new ArgumentException($"Invalid image target \"{target}\".");
+ }
+
+ private static SamplerType GetSamplerType(TextureDimensions dimensions)
{
switch (dimensions)
{
- case TextureDimensions.Texture1D: return TextureTarget.Texture1D;
- case TextureDimensions.Texture2D: return TextureTarget.Texture2D;
- case TextureDimensions.Texture3D: return TextureTarget.Texture3D;
- case TextureDimensions.TextureCube: return TextureTarget.TextureCube;
+ case TextureDimensions.Texture1D: return SamplerType.Texture1D;
+ case TextureDimensions.Texture2D: return SamplerType.Texture2D;
+ case TextureDimensions.Texture3D: return SamplerType.Texture3D;
+ case TextureDimensions.TextureCube: return SamplerType.TextureCube;
}
throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".");
}
- private static TextureTarget GetTextureType(Decoders.TextureTarget type)
+ private static SamplerType GetSamplerType(Decoders.TextureTarget type)
{
switch (type)
{
case Decoders.TextureTarget.Texture1DLodZero:
- return TextureTarget.Texture1D;
+ return SamplerType.Texture1D;
case Decoders.TextureTarget.Texture2D:
case Decoders.TextureTarget.Texture2DLodZero:
case Decoders.TextureTarget.Texture2DLodLevel:
- return TextureTarget.Texture2D;
+ return SamplerType.Texture2D;
case Decoders.TextureTarget.Texture2DDepthCompare:
case Decoders.TextureTarget.Texture2DLodLevelDepthCompare:
case Decoders.TextureTarget.Texture2DLodZeroDepthCompare:
- return TextureTarget.Texture2D | TextureTarget.Shadow;
+ return SamplerType.Texture2D | SamplerType.Shadow;
case Decoders.TextureTarget.Texture2DArray:
case Decoders.TextureTarget.Texture2DArrayLodZero:
- return TextureTarget.Texture2D | TextureTarget.Array;
+ return SamplerType.Texture2D | SamplerType.Array;
case Decoders.TextureTarget.Texture2DArrayLodZeroDepthCompare:
- return TextureTarget.Texture2D | TextureTarget.Array | TextureTarget.Shadow;
+ return SamplerType.Texture2D | SamplerType.Array | SamplerType.Shadow;
case Decoders.TextureTarget.Texture3D:
case Decoders.TextureTarget.Texture3DLodZero:
- return TextureTarget.Texture3D;
+ return SamplerType.Texture3D;
case Decoders.TextureTarget.TextureCube:
case Decoders.TextureTarget.TextureCubeLodLevel:
- return TextureTarget.TextureCube;
+ return SamplerType.TextureCube;
}
throw new ArgumentException($"Invalid texture type \"{type}\".");
}
- private static TextureTarget GetTextureType(TexelLoadTarget type)
+ private static SamplerType GetSamplerType(TexelLoadTarget type)
{
switch (type)
{
case TexelLoadTarget.Texture1DLodZero:
case TexelLoadTarget.Texture1DLodLevel:
- return TextureTarget.Texture1D;
+ return SamplerType.Texture1D;
case TexelLoadTarget.Texture2DLodZero:
case TexelLoadTarget.Texture2DLodZeroOffset:
case TexelLoadTarget.Texture2DLodLevel:
case TexelLoadTarget.Texture2DLodLevelOffset:
- return TextureTarget.Texture2D;
+ return SamplerType.Texture2D;
case TexelLoadTarget.Texture2DLodZeroMultisample:
- return TextureTarget.Texture2D | TextureTarget.Multisample;
+ return SamplerType.Texture2D | SamplerType.Multisample;
case TexelLoadTarget.Texture3DLodZero:
- return TextureTarget.Texture3D;
+ return SamplerType.Texture3D;
case TexelLoadTarget.Texture2DArrayLodZero:
- return TextureTarget.Texture2D | TextureTarget.Array;
+ return SamplerType.Texture2D | SamplerType.Array;
}
throw new ArgumentException($"Invalid texture type \"{type}\".");
}
- private static TextureFlags GetTextureFlags(Decoders.TextureTarget type)
+ private static TextureFlags GetSamplerFlags(Decoders.TextureTarget type)
{
switch (type)
{
@@ -752,7 +868,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
throw new ArgumentException($"Invalid texture type \"{type}\".");
}
- private static TextureFlags GetTextureFlags(TexelLoadTarget type)
+ private static TextureFlags GetSamplerFlags(TexelLoadTarget type)
{
switch (type)
{
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
index e580965e..74194f35 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
@@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
ExponentB2,
Floor,
FusedMultiplyAdd,
+ ImageLoad,
+ ImageStore,
IsNan,
LoadAttribute,
LoadConstant,
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
index 0c768345..b96c55e8 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs
@@ -2,21 +2,21 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
{
class TextureOperation : Operation
{
- public TextureTarget Target { get; }
- public TextureFlags Flags { get; }
+ public SamplerType Type { get; }
+ public TextureFlags Flags { get; }
public int Handle { get; }
public TextureOperation(
Instruction inst,
- TextureTarget target,
+ SamplerType type,
TextureFlags flags,
int handle,
int compIndex,
Operand dest,
params Operand[] sources) : base(inst, compIndex, dest, sources)
{
- Target = target;
+ Type = type;
Flags = flags;
Handle = handle;
}
diff --git a/Ryujinx.Graphics.Shader/SamplerType.cs b/Ryujinx.Graphics.Shader/SamplerType.cs
new file mode 100644
index 00000000..42854c97
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/SamplerType.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Ryujinx.Graphics.Shader
+{
+ [Flags]
+ public enum SamplerType
+ {
+ Texture1D,
+ TextureBuffer,
+ Texture2D,
+ Texture3D,
+ TextureCube,
+
+ Mask = 0xff,
+
+ Array = 1 << 8,
+ Multisample = 1 << 9,
+ Shadow = 1 << 10
+ }
+
+ static class SamplerTypeExtensions
+ {
+ public static int GetDimensions(this SamplerType type)
+ {
+ switch (type & SamplerType.Mask)
+ {
+ case SamplerType.Texture1D: return 1;
+ case SamplerType.TextureBuffer: return 1;
+ case SamplerType.Texture2D: return 2;
+ case SamplerType.Texture3D: return 3;
+ case SamplerType.TextureCube: return 3;
+ }
+
+ throw new ArgumentException($"Invalid texture type \"{type}\".");
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
index 841e636d..1ff602a2 100644
--- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Shader
public ReadOnlyCollection<BufferDescriptor> CBuffers { get; }
public ReadOnlyCollection<BufferDescriptor> SBuffers { get; }
public ReadOnlyCollection<TextureDescriptor> Textures { get; }
+ public ReadOnlyCollection<TextureDescriptor> Images { get; }
public ReadOnlyCollection<InterpolationQualifier> InterpolationQualifiers { get; }
@@ -17,12 +18,14 @@ namespace Ryujinx.Graphics.Shader
BufferDescriptor[] cBuffers,
BufferDescriptor[] sBuffers,
TextureDescriptor[] textures,
+ TextureDescriptor[] images,
InterpolationQualifier[] interpolationQualifiers,
bool usesInstanceId)
{
CBuffers = Array.AsReadOnly(cBuffers);
SBuffers = Array.AsReadOnly(sBuffers);
Textures = Array.AsReadOnly(textures);
+ Images = Array.AsReadOnly(images);
InterpolationQualifiers = Array.AsReadOnly(interpolationQualifiers);
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
index c9bd5750..7261b9ff 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs
@@ -4,20 +4,20 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
class AstTextureOperation : AstOperation
{
- public TextureTarget Target { get; }
- public TextureFlags Flags { get; }
+ public SamplerType Type { get; }
+ public TextureFlags Flags { get; }
public int Handle { get; }
public AstTextureOperation(
Instruction inst,
- TextureTarget target,
+ SamplerType type,
TextureFlags flags,
int handle,
int compMask,
params IAstNode[] sources) : base(inst, compMask, sources)
{
- Target = target;
+ Type = type;
Flags = flags;
Handle = handle;
}
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
index dc822621..53ca6700 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
@@ -51,6 +51,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
sources[index] = context.GetOperandUse(operation.GetSource(index));
}
+ int componentMask = 1 << operation.ComponentIndex;
+
+ AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
+ {
+ return new AstTextureOperation(
+ inst,
+ texOp.Type,
+ texOp.Flags,
+ texOp.Handle,
+ componentMask,
+ sources);
+ }
+
if (operation.Dest != null)
{
AstOperand dest = context.GetOperandDef(operation.Dest);
@@ -108,21 +121,20 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
dest.VarType = InstructionInfo.GetDestVarType(inst);
}
- int componentMask = 1 << operation.ComponentIndex;
-
IAstNode source;
if (operation is TextureOperation texOp)
{
- AstTextureOperation astTexOp = new AstTextureOperation(
- inst,
- texOp.Target,
- texOp.Flags,
- texOp.Handle,
- componentMask,
- sources);
+ AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
- context.Info.Samplers.Add(astTexOp);
+ if (texOp.Inst == Instruction.ImageLoad)
+ {
+ context.Info.Images.Add(astTexOp);
+ }
+ else
+ {
+ context.Info.Samplers.Add(astTexOp);
+ }
source = astTexOp;
}
@@ -143,6 +155,14 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
context.AddNode(new AstComment(((CommentNode)operation).Comment));
}
+ else if (operation is TextureOperation texOp)
+ {
+ AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
+
+ context.Info.Images.Add(astTexOp);
+
+ context.AddNode(astTexOp);
+ }
else
{
if (inst == Instruction.StoreStorage)
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
index 27fd1487..1094fba2 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public bool UsesInstanceId { get; set; }
public HashSet<AstTextureOperation> Samplers { get; }
+ public HashSet<AstTextureOperation> Images { get; }
public StructuredProgramInfo(AstBlock mainBlock)
{
@@ -35,6 +36,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
InterpolationQualifiers = new InterpolationQualifier[32];
Samplers = new HashSet<AstTextureOperation>();
+ Images = new HashSet<AstTextureOperation>();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/Ryujinx.Graphics.Shader/TextureDescriptor.cs
index 484fa1bc..bfcdde59 100644
--- a/Ryujinx.Graphics.Shader/TextureDescriptor.cs
+++ b/Ryujinx.Graphics.Shader/TextureDescriptor.cs
@@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader
{
public string Name { get; }
- public TextureTarget Target { get; }
+ public SamplerType Type { get; }
public int HandleIndex { get; }
@@ -13,10 +13,10 @@ namespace Ryujinx.Graphics.Shader
public int CbufSlot { get; }
public int CbufOffset { get; }
- public TextureDescriptor(string name, TextureTarget target, int hIndex)
+ public TextureDescriptor(string name, SamplerType type, int hIndex)
{
Name = name;
- Target = target;
+ Type = type;
HandleIndex = hIndex;
IsBindless = false;
@@ -25,10 +25,10 @@ namespace Ryujinx.Graphics.Shader
CbufOffset = 0;
}
- public TextureDescriptor(string name, TextureTarget target, int cbufSlot, int cbufOffset)
+ public TextureDescriptor(string name, SamplerType type, int cbufSlot, int cbufOffset)
{
Name = name;
- Target = target;
+ Type = type;
HandleIndex = 0;
IsBindless = true;
diff --git a/Ryujinx.Graphics.Shader/TextureTarget.cs b/Ryujinx.Graphics.Shader/TextureTarget.cs
deleted file mode 100644
index 3642ef23..00000000
--- a/Ryujinx.Graphics.Shader/TextureTarget.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Shader
-{
- [Flags]
- public enum TextureTarget
- {
- Texture1D,
- Texture2D,
- Texture3D,
- TextureCube,
-
- Mask = 0xff,
-
- Array = 1 << 8,
- Multisample = 1 << 9,
- Shadow = 1 << 10
- }
-
- static class TextureTargetExtensions
- {
- public static int GetDimensions(this TextureTarget type)
- {
- switch (type & TextureTarget.Mask)
- {
- case TextureTarget.Texture1D: return 1;
- case TextureTarget.Texture2D: return 2;
- case TextureTarget.Texture3D: return 3;
- case TextureTarget.TextureCube: return 3;
- }
-
- throw new ArgumentException($"Invalid texture type \"{type}\".");
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index 4838b1e2..aaf618e9 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -81,6 +81,7 @@ namespace Ryujinx.Graphics.Shader.Translation
program.CBufferDescriptors,
program.SBufferDescriptors,
program.TextureDescriptors,
+ program.ImageDescriptors,
sInfo.InterpolationQualifiers,
sInfo.UsesInstanceId);