From 8d168574eb04ae1e7026ac2b058e3b184f068fae Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sun, 8 Nov 2020 08:10:00 -0300
Subject: [PATCH] Use explicit buffer and texture bindings on shaders (#1666)

* Use explicit buffer and texture bindings on shaders

* More XML docs and other nits
---
 Ryujinx.Graphics.GAL/IPipeline.cs             |  10 +-
 Ryujinx.Graphics.GAL/IRenderer.cs             |   2 +-
 Ryujinx.Graphics.Gpu/Engine/Compute.cs        |  36 +--
 Ryujinx.Graphics.Gpu/Engine/Methods.cs        |  32 +-
 .../Image/TextureBindingInfo.cs               |  28 +-
 .../Image/TextureBindingsManager.cs           |  36 +--
 Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs   |  22 +-
 Ryujinx.Graphics.Gpu/Memory/BufferManager.cs  | 282 ++++++++++--------
 Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs    |  31 +-
 Ryujinx.Graphics.OpenGL/Pipeline.cs           |  94 +++---
 Ryujinx.Graphics.OpenGL/Program.cs            | 149 ---------
 Ryujinx.Graphics.OpenGL/Renderer.cs           |   4 +-
 Ryujinx.Graphics.OpenGL/Shader.cs             |  31 +-
 Ryujinx.Graphics.Shader/BufferDescriptor.cs   |   7 +-
 .../CodeGen/Glsl/Declarations.cs              | 171 +++++------
 Ryujinx.Graphics.Shader/IGpuAccessor.cs       |  30 +-
 Ryujinx.Graphics.Shader/TextureDescriptor.cs  |  10 +-
 .../Translation/ShaderConfig.cs               |   8 +-
 .../Translation/TranslationCounts.cs          |  30 ++
 .../Translation/Translator.cs                 |  34 ++-
 20 files changed, 496 insertions(+), 551 deletions(-)
 create mode 100644 Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs

diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs
index 3baf272f92..1da34b9671 100644
--- a/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.GAL
 
         void SetIndexBuffer(BufferRange buffer, IndexType type);
 
-        void SetImage(int index, ShaderStage stage, ITexture texture, Format imageFormat);
+        void SetImage(int binding, ITexture texture, Format imageFormat);
 
         void SetLogicOpState(bool enable, LogicalOp op);
 
@@ -64,19 +64,19 @@ namespace Ryujinx.Graphics.GAL
         void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
         void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
 
-        void SetSampler(int index, ShaderStage stage, ISampler sampler);
+        void SetSampler(int binding, ISampler sampler);
 
         void SetScissorEnable(int index, bool enable);
         void SetScissor(int index, int x, int y, int width, int height);
 
         void SetStencilTest(StencilTestDescriptor stencilTest);
 
-        void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer);
+        void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers);
 
-        void SetTexture(int index, ShaderStage stage, ITexture texture);
+        void SetTexture(int binding, ITexture texture);
 
         void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
-        void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer);
+        void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers);
 
         void SetUserClipDistance(int index, bool enableClip);
 
diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs
index 9fc39b4be5..35c2146f2c 100644
--- a/Ryujinx.Graphics.GAL/IRenderer.cs
+++ b/Ryujinx.Graphics.GAL/IRenderer.cs
@@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.GAL
 
         void BackgroundContextAction(Action action);
 
-        IShader CompileShader(ShaderProgram shader);
+        IShader CompileShader(ShaderStage stage, string code);
 
         BufferHandle CreateBuffer(int size);
 
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs
index 1219ef0cfe..3ea98f3e48 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs
@@ -28,9 +28,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
             int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize);
 
-            uint sbEnableMask = 0;
-            uint ubEnableMask = 0;
-
             for (int index = 0; index < Constants.TotalCpUniformBuffers; index++)
             {
                 if (!qmd.ConstantBufferValid(index))
@@ -38,8 +35,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
                     continue;
                 }
 
-                ubEnableMask |= 1u << index;
-
                 ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32;
                 ulong size = (ulong)qmd.ConstantBufferSize(index);
 
@@ -58,13 +53,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
             _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
 
             var samplerPool = state.Get<PoolState>(MethodOffset.SamplerPoolState);
-
-            TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex);
-
             var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
 
+            TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex);
             TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
-
             TextureManager.SetComputeTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
 
             ShaderProgramInfo info = cs.Shaders[0].Program.Info;
@@ -82,8 +74,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
                     continue;
                 }
 
-                ubEnableMask |= 1u << cb.Slot;
-
                 ulong cbDescAddress = BufferManager.GetComputeUniformBufferAddress(0);
 
                 int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10;
@@ -99,8 +89,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
             {
                 BufferDescriptor sb = info.SBuffers[index];
 
-                sbEnableMask |= 1u << sb.Slot;
-
                 ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0);
 
                 int sbDescOffset = 0x310 + sb.Slot * 0x10;
@@ -112,15 +100,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size);
             }
 
-            ubEnableMask = 0;
-
-            for (int index = 0; index < info.CBuffers.Count; index++)
-            {
-                ubEnableMask |= 1u << info.CBuffers[index].Slot;
-            }
-
-            BufferManager.SetComputeStorageBufferEnableMask(sbEnableMask);
-            BufferManager.SetComputeUniformBufferEnableMask(ubEnableMask);
+            BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
+            BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
 
             var textureBindings = new TextureBindingInfo[info.Textures.Count];
 
@@ -132,11 +113,16 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
                 if (descriptor.IsBindless)
                 {
-                    textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufOffset, descriptor.CbufSlot, descriptor.Flags);
+                    textureBindings[index] = new TextureBindingInfo(
+                        target,
+                        descriptor.Binding,
+                        descriptor.CbufOffset,
+                        descriptor.CbufSlot,
+                        descriptor.Flags);
                 }
                 else
                 {
-                    textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex, descriptor.Flags);
+                    textureBindings[index] = new TextureBindingInfo(target, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags);
                 }
             }
 
@@ -151,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 Target target = ShaderTexture.GetTarget(descriptor.Type);
                 Format format = ShaderTexture.GetFormat(descriptor.Format);
 
-                imageBindings[index] = new TextureBindingInfo(target, format, descriptor.HandleIndex, descriptor.Flags);
+                imageBindings[index] = new TextureBindingInfo(target, format, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags);
             }
 
             TextureManager.SetComputeImages(imageBindings);
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index d4988f356d..8bc2c22d0b 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Shader;
 using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Shader;
 using System;
+using System.Linq;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.Graphics.Gpu.Engine
@@ -997,6 +998,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
             _vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false;
 
+            int storageBufferBindingsCount = 0;
+            int uniformBufferBindingsCount = 0;
+
             for (int stage = 0; stage < Constants.ShaderStages; stage++)
             {
                 ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info;
@@ -1005,6 +1009,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
                 if (info == null)
                 {
+                    TextureManager.SetGraphicsTextures(stage, Array.Empty<TextureBindingInfo>());
+                    TextureManager.SetGraphicsImages(stage, Array.Empty<TextureBindingInfo>());
+                    BufferManager.SetGraphicsStorageBufferBindings(stage, null);
+                    BufferManager.SetGraphicsUniformBufferBindings(stage, null);
                     continue;
                 }
 
@@ -1018,11 +1026,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
                     if (descriptor.IsBindless)
                     {
-                        textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufSlot, descriptor.CbufOffset, descriptor.Flags);
+                        textureBindings[index] = new TextureBindingInfo(target, descriptor.Binding, descriptor.CbufSlot, descriptor.CbufOffset, descriptor.Flags);
                     }
                     else
                     {
-                        textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex, descriptor.Flags);
+                        textureBindings[index] = new TextureBindingInfo(target, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags);
                     }
                 }
 
@@ -1037,28 +1045,28 @@ namespace Ryujinx.Graphics.Gpu.Engine
                     Target target = ShaderTexture.GetTarget(descriptor.Type);
                     Format format = ShaderTexture.GetFormat(descriptor.Format);
 
-                    imageBindings[index] = new TextureBindingInfo(target, format, descriptor.HandleIndex, descriptor.Flags);
+                    imageBindings[index] = new TextureBindingInfo(target, format, descriptor.Binding, descriptor.HandleIndex, descriptor.Flags);
                 }
 
                 TextureManager.SetGraphicsImages(stage, imageBindings);
 
-                uint sbEnableMask = 0;
-                uint ubEnableMask = 0;
+                BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
+                BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
 
-                for (int index = 0; index < info.SBuffers.Count; index++)
+                if (info.SBuffers.Count != 0)
                 {
-                    sbEnableMask |= 1u << info.SBuffers[index].Slot;
+                    storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
                 }
 
-                for (int index = 0; index < info.CBuffers.Count; index++)
+                if (info.CBuffers.Count != 0)
                 {
-                    ubEnableMask |= 1u << info.CBuffers[index].Slot;
+                    uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
                 }
-
-                BufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask);
-                BufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask);
             }
 
+            BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
+            BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
+
             _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
         }
 
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
index 422b66e20a..a328fc2b69 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
@@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// </summary>
         public Format Format { get; }
 
+        /// <summary>
+        /// Shader texture host binding point.
+        /// </summary>
+        public int Binding { get; }
+
         /// <summary>
         /// Shader texture handle.
         /// This is an index into the texture constant buffer.
@@ -53,13 +58,15 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// </summary>
         /// <param name="target">The shader sampler target type</param>
         /// <param name="format">Format of the image as declared on the shader</param>
+        /// <param name="binding">The shader texture binding point</param>
         /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
         /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
-        public TextureBindingInfo(Target target, Format format, int handle, TextureUsageFlags flags)
+        public TextureBindingInfo(Target target, Format format, int binding, int handle, TextureUsageFlags flags)
         {
-            Target = target;
-            Format = format;
-            Handle = handle;
+            Target  = target;
+            Format  = format;
+            Binding = binding;
+            Handle  = handle;
 
             IsBindless = false;
 
@@ -73,9 +80,10 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// Constructs the texture binding information structure.
         /// </summary>
         /// <param name="target">The shader sampler target type</param>
+        /// <param name="binding">The shader texture binding point</param>
         /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
         /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
-        public TextureBindingInfo(Target target, int handle, TextureUsageFlags flags) : this(target, (Format)0, handle, flags)
+        public TextureBindingInfo(Target target, int binding, int handle, TextureUsageFlags flags) : this(target, (Format)0, binding, handle, flags)
         {
         }
 
@@ -83,14 +91,16 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// Constructs the bindless texture binding information structure.
         /// </summary>
         /// <param name="target">The shader sampler target type</param>
+        /// <param name="binding">The shader texture binding point</param>
         /// <param name="cbufSlot">Constant buffer slot where the bindless texture handle is located</param>
         /// <param name="cbufOffset">Constant buffer offset of the bindless texture handle</param>
         /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
-        public TextureBindingInfo(Target target, int cbufSlot, int cbufOffset, TextureUsageFlags flags)
+        public TextureBindingInfo(Target target, int binding, int cbufSlot, int cbufOffset, TextureUsageFlags flags)
         {
-            Target = target;
-            Format = 0;
-            Handle = 0;
+            Target  = target;
+            Format  = 0;
+            Binding = binding;
+            Handle  = 0;
 
             IsBindless = true;
 
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index 08c4082ef0..bfb6da2513 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -265,11 +265,11 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             for (int index = 0; index < _textureBindings[stageIndex].Length; index++)
             {
-                TextureBindingInfo binding = _textureBindings[stageIndex][index];
+                TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index];
 
                 int packedId;
 
-                if (binding.IsBindless)
+                if (bindingInfo.IsBindless)
                 {
                     ulong address;
 
@@ -277,18 +277,18 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                     if (_isCompute)
                     {
-                        address = bufferManager.GetComputeUniformBufferAddress(binding.CbufSlot);
+                        address = bufferManager.GetComputeUniformBufferAddress(bindingInfo.CbufSlot);
                     }
                     else
                     {
-                        address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, binding.CbufSlot);
+                        address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, bindingInfo.CbufSlot);
                     }
 
-                    packedId = _context.PhysicalMemory.Read<int>(address + (ulong)binding.CbufOffset * 4);
+                    packedId = _context.PhysicalMemory.Read<int>(address + (ulong)bindingInfo.CbufOffset * 4);
                 }
                 else
                 {
-                    packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex);
+                    packedId = ReadPackedId(stageIndex, bindingInfo.Handle, _textureBufferIndex);
                 }
 
                 int textureId = UnpackTextureId(packedId);
@@ -305,18 +305,18 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                 Texture texture = pool.Get(textureId);
 
-                ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
+                ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
 
                 if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
                 {
-                    if (UpdateScale(texture, binding, index, stage))
+                    if (UpdateScale(texture, bindingInfo, index, stage))
                     {
-                        hostTexture = texture?.GetTargetTexture(binding.Target);
+                        hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                     }
 
                     _textureState[stageIndex][index].Texture = hostTexture;
 
-                    _context.Renderer.Pipeline.SetTexture(index, stage, hostTexture);
+                    _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
                 }
 
                 if (hostTexture != null && texture.Info.Target == Target.TextureBuffer)
@@ -335,7 +335,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                     _textureState[stageIndex][index].Sampler = hostSampler;
 
-                    _context.Renderer.Pipeline.SetSampler(index, stage, hostSampler);
+                    _context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
                 }
             }
         }
@@ -359,14 +359,14 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             for (int index = 0; index < _imageBindings[stageIndex].Length; index++)
             {
-                TextureBindingInfo binding = _imageBindings[stageIndex][index];
+                TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index];
 
-                int packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex);
+                int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, _textureBufferIndex);
                 int textureId = UnpackTextureId(packedId);
 
                 Texture texture = pool.Get(textureId);
 
-                ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
+                ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
 
                 if (hostTexture != null && texture.Info.Target == Target.TextureBuffer)
                 {
@@ -378,21 +378,21 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                 if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
                 {
-                    if (UpdateScale(texture, binding, baseScaleIndex + index, stage))
+                    if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage))
                     {
-                        hostTexture = texture?.GetTargetTexture(binding.Target);
+                        hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                     }
 
                     _imageState[stageIndex][index].Texture = hostTexture;
 
-                    Format format = binding.Format;
+                    Format format = bindingInfo.Format;
 
                     if (format == 0 && texture != null)
                     {
                         format = texture.Format;
                     }
 
-                    _context.Renderer.Pipeline.SetImage(index, stage, hostTexture, format);
+                    _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
                 }
             }
         }
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
index 42500342fc..060171fb08 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
@@ -5,7 +5,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// </summary>
     struct BufferBounds
     {
-        public ulong Address;
-        public ulong Size;
+        /// <summary>
+        /// Region virtual address.
+        /// </summary>
+        public ulong Address { get; }
+
+        /// <summary>
+        /// Region size in bytes.
+        /// </summary>
+        public ulong Size { get; }
+
+        /// <summary>
+        /// Creates a new buffer region.
+        /// </summary>
+        /// <param name="address">Region address</param>
+        /// <param name="size">Region size</param>
+        public BufferBounds(ulong address, ulong size)
+        {
+            Address = address;
+            Size = size;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index eec545b917..568133ca54 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -4,6 +4,8 @@ using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Shader;
 using Ryujinx.Memory.Range;
 using System;
+using System.Collections.ObjectModel;
+using System.Linq;
 
 namespace Ryujinx.Graphics.Gpu.Memory
 {
@@ -12,6 +14,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// </summary>
     class BufferManager
     {
+        private const int StackToHeapThreshold = 16;
+
         private const int OverlapsBufferInitialCapacity = 10;
         private const int OverlapsBufferMaxCapacity     = 10000;
 
@@ -28,21 +32,61 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private VertexBuffer[] _vertexBuffers;
         private BufferBounds[] _transformFeedbackBuffers;
 
+        /// <summary>
+        /// Holds shader stage buffer state and binding information.
+        /// </summary>
         private class BuffersPerStage
         {
-            public uint EnableMask { get; set; }
+            /// <summary>
+            /// Shader buffer binding information.
+            /// </summary>
+            public BufferDescriptor[] Bindings { get; }
 
+            /// <summary>
+            /// Buffer regions.
+            /// </summary>
             public BufferBounds[] Buffers { get; }
 
+            /// <summary>
+            /// Total amount of buffers used on the shader.
+            /// </summary>
+            public int Count { get; private set; }
+
+            /// <summary>
+            /// Creates a new instance of the shader stage buffer information.
+            /// </summary>
+            /// <param name="count">Maximum amount of buffers that the shader stage can use</param>
             public BuffersPerStage(int count)
             {
+                Bindings = new BufferDescriptor[count];
                 Buffers = new BufferBounds[count];
             }
 
-            public void Bind(int index, ulong address, ulong size)
+            /// <summary>
+            /// Sets the region of a buffer at a given slot.
+            /// </summary>
+            /// <param name="index">Buffer slot</param>
+            /// <param name="address">Region virtual address</param>
+            /// <param name="size">Region size in bytes</param>
+            public void SetBounds(int index, ulong address, ulong size)
             {
-                Buffers[index].Address = address;
-                Buffers[index].Size    = size;
+                Buffers[index] = new BufferBounds(address, size);
+            }
+
+            /// <summary>
+            /// Sets shader buffer binding information.
+            /// </summary>
+            /// <param name="descriptors">Buffer binding information</param>
+            public void SetBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
+            {
+                if (descriptors == null)
+                {
+                    Count = 0;
+                    return;
+                }
+
+                descriptors.CopyTo(Bindings, 0);
+                Count = descriptors.Count;
             }
         }
 
@@ -51,6 +95,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private BuffersPerStage[] _gpStorageBuffers;
         private BuffersPerStage[] _gpUniformBuffers;
 
+        private int _cpStorageBufferBindings;
+        private int _cpUniformBufferBindings;
+        private int _gpStorageBufferBindings;
+        private int _gpUniformBufferBindings;
+
         private bool _gpStorageBuffersDirty;
         private bool _gpUniformBuffersDirty;
 
@@ -159,9 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         {
             ulong address = TranslateAndCreateBuffer(gpuVa, size);
 
-            _transformFeedbackBuffers[index].Address = address;
-            _transformFeedbackBuffers[index].Size    = size;
-
+            _transformFeedbackBuffers[index] = new BufferBounds(address, size);
             _transformFeedbackBuffersDirty = true;
         }
 
@@ -180,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             ulong address = TranslateAndCreateBuffer(gpuVa, size);
 
-            _cpStorageBuffers.Bind(index, address, size);
+            _cpStorageBuffers.SetBounds(index, address, size);
         }
 
         /// <summary>
@@ -205,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 _gpStorageBuffersDirty = true;
             }
 
-            _gpStorageBuffers[stage].Bind(index, address, size);
+            _gpStorageBuffers[stage].SetBounds(index, address, size);
         }
 
         /// <summary>
@@ -219,7 +266,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         {
             ulong address = TranslateAndCreateBuffer(gpuVa, size);
 
-            _cpUniformBuffers.Bind(index, address, size);
+            _cpUniformBuffers.SetBounds(index, address, size);
         }
 
         /// <summary>
@@ -234,42 +281,69 @@ namespace Ryujinx.Graphics.Gpu.Memory
         {
             ulong address = TranslateAndCreateBuffer(gpuVa, size);
 
-            _gpUniformBuffers[stage].Bind(index, address, size);
-
+            _gpUniformBuffers[stage].SetBounds(index, address, size);
             _gpUniformBuffersDirty = true;
         }
 
         /// <summary>
-        /// Sets the enabled storage buffers mask on the compute pipeline.
-        /// Each bit set on the mask indicates that the respective buffer index is enabled.
+        /// Sets the binding points for the storage buffers bound on the compute pipeline.
         /// </summary>
-        /// <param name="mask">Buffer enable mask</param>
-        public void SetComputeStorageBufferEnableMask(uint mask)
+        /// <param name="descriptors">Buffer descriptors with the binding point values</param>
+        public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
         {
-            _cpStorageBuffers.EnableMask = mask;
+            _cpStorageBuffers.SetBindings(descriptors);
+            _cpStorageBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
         }
 
         /// <summary>
-        /// Sets the enabled storage buffers mask on the graphics pipeline.
-        /// Each bit set on the mask indicates that the respective buffer index is enabled.
+        /// Sets the binding points for the storage buffers bound on the graphics pipeline.
         /// </summary>
         /// <param name="stage">Index of the shader stage</param>
-        /// <param name="mask">Buffer enable mask</param>
-        public void SetGraphicsStorageBufferEnableMask(int stage, uint mask)
+        /// <param name="descriptors">Buffer descriptors with the binding point values</param>
+        public void SetGraphicsStorageBufferBindings(int stage, ReadOnlyCollection<BufferDescriptor> descriptors)
         {
-            _gpStorageBuffers[stage].EnableMask = mask;
-
+            _gpStorageBuffers[stage].SetBindings(descriptors);
             _gpStorageBuffersDirty = true;
         }
 
         /// <summary>
-        /// Sets the enabled uniform buffers mask on the compute pipeline.
+        /// Sets the total number of storage buffer bindings used.
+        /// </summary>
+        /// <param name="count">Number of storage buffer bindings used</param>
+        public void SetGraphicsStorageBufferBindingsCount(int count)
+        {
+            _gpStorageBufferBindings = count;
+        }
+
+        /// <summary>
+        /// Sets the binding points for the uniform buffers bound on the compute pipeline.
+        /// </summary>
+        /// <param name="descriptors">Buffer descriptors with the binding point values</param>
+        public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
+        {
+            _cpUniformBuffers.SetBindings(descriptors);
+            _cpUniformBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
+        }
+
+        /// <summary>
+        /// Sets the enabled uniform buffers mask on the graphics pipeline.
         /// Each bit set on the mask indicates that the respective buffer index is enabled.
         /// </summary>
-        /// <param name="mask">Buffer enable mask</param>
-        public void SetComputeUniformBufferEnableMask(uint mask)
+        /// <param name="stage">Index of the shader stage</param>
+        /// <param name="descriptors">Buffer descriptors with the binding point values</param>
+        public void SetGraphicsUniformBufferBindings(int stage, ReadOnlyCollection<BufferDescriptor> descriptors)
         {
-            _cpUniformBuffers.EnableMask = mask;
+            _gpUniformBuffers[stage].SetBindings(descriptors);
+            _gpUniformBuffersDirty = true;
+        }
+
+        /// <summary>
+        /// Sets the total number of uniform buffer bindings used.
+        /// </summary>
+        /// <param name="count">Number of uniform buffer bindings used</param>
+        public void SetGraphicsUniformBufferBindingsCount(int count)
+        {
+            _gpUniformBufferBindings = count;
         }
 
         /// <summary>
@@ -291,19 +365,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             return mask;
         }
 
-        /// <summary>
-        /// Sets the enabled uniform buffers mask on the graphics pipeline.
-        /// Each bit set on the mask indicates that the respective buffer index is enabled.
-        /// </summary>
-        /// <param name="stage">Index of the shader stage</param>
-        /// <param name="mask">Buffer enable mask</param>
-        public void SetGraphicsUniformBufferEnableMask(int stage, uint mask)
-        {
-            _gpUniformBuffers[stage].EnableMask = mask;
-
-            _gpUniformBuffersDirty = true;
-        }
-
         /// <summary>
         /// Gets a bit mask indicating which graphics uniform buffers are currently bound.
         /// </summary>
@@ -476,48 +537,42 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// </summary>
         public void CommitComputeBindings()
         {
-            uint enableMask = _cpStorageBuffers.EnableMask;
+            int sCount = _cpStorageBufferBindings;
 
-            for (int index = 0; (enableMask >> index) != 0; index++)
+            Span<BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount];
+
+            for (int index = 0; index < _cpStorageBuffers.Count; index++)
             {
-                if ((enableMask & (1u << index)) == 0)
+                ref var bindingInfo = ref _cpStorageBuffers.Bindings[index];
+
+                BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot];
+
+                if (bounds.Address != 0)
                 {
-                    continue;
+                    sRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size);
                 }
-
-                BufferBounds bounds = _cpStorageBuffers.Buffers[index];
-
-                if (bounds.Address == 0)
-                {
-                    continue;
-                }
-
-                BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
-
-                _context.Renderer.Pipeline.SetStorageBuffer(index, ShaderStage.Compute, buffer);
             }
 
-            enableMask = _cpUniformBuffers.EnableMask;
+            _context.Renderer.Pipeline.SetStorageBuffers(sRanges);
 
-            for (int index = 0; (enableMask >> index) != 0; index++)
+            int uCount = _cpUniformBufferBindings;
+
+            Span<BufferRange> uRanges = uCount < StackToHeapThreshold ? stackalloc BufferRange[uCount] : new BufferRange[uCount];
+
+            for (int index = 0; index < _cpUniformBuffers.Count; index++)
             {
-                if ((enableMask & (1u << index)) == 0)
+                ref var bindingInfo = ref _cpUniformBuffers.Bindings[index];
+
+                BufferBounds bounds = _cpUniformBuffers.Buffers[bindingInfo.Slot];
+
+                if (bounds.Address != 0)
                 {
-                    continue;
+                    uRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size);
                 }
-
-                BufferBounds bounds = _cpUniformBuffers.Buffers[index];
-
-                if (bounds.Address == 0)
-                {
-                    continue;
-                }
-
-                BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
-
-                _context.Renderer.Pipeline.SetUniformBuffer(index, ShaderStage.Compute, buffer);
             }
 
+            _context.Renderer.Pipeline.SetUniformBuffers(uRanges);
+
             // Force rebind after doing compute work.
             _rebind = true;
         }
@@ -651,7 +706,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param>
         private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
         {
-            BindOrUpdateBuffers(bindings, bind: true, isStorage);
+            int count = isStorage ? _gpStorageBufferBindings : _gpUniformBufferBindings;
+
+            Span<BufferRange> ranges = count < StackToHeapThreshold ? stackalloc BufferRange[count] : new BufferRange[count];
+
+            for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
+            {
+                ref var buffers = ref bindings[(int)stage - 1];
+
+                for (int index = 0; index < buffers.Count; index++)
+                {
+                    ref var bindingInfo = ref buffers.Bindings[index];
+
+                    BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
+
+                    if (bounds.Address != 0)
+                    {
+                        ranges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size);
+                    }
+                }
+            }
+
+            if (isStorage)
+            {
+                _context.Renderer.Pipeline.SetStorageBuffers(ranges);
+            }
+            else
+            {
+                _context.Renderer.Pipeline.SetUniformBuffers(ranges);
+            }
         }
 
         /// <summary>
@@ -659,74 +742,27 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// </summary>
         /// <param name="bindings">Bindings to update</param>
         private void UpdateBuffers(BuffersPerStage[] bindings)
-        {
-            BindOrUpdateBuffers(bindings, bind: false);
-        }
-
-        /// <summary>
-        /// This binds buffers into the host API, or updates data for already bound buffers.
-        /// </summary>
-        /// <param name="bindings">Bindings to bind or update</param>
-        /// <param name="bind">True to bind, false to update</param>
-        /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
-        private void BindOrUpdateBuffers(BuffersPerStage[] bindings, bool bind, bool isStorage = false)
         {
             for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
             {
-                uint enableMask = bindings[(int)stage - 1].EnableMask;
+                ref var buffers = ref bindings[(int)stage - 1];
 
-                if (enableMask == 0)
+                for (int index = 0; index < buffers.Count; index++)
                 {
-                    continue;
-                }
+                    ref var binding = ref buffers.Bindings[index];
 
-                for (int index = 0; (enableMask >> index) != 0; index++)
-                {
-                    if ((enableMask & (1u << index)) == 0)
-                    {
-                        continue;
-                    }
-
-                    BufferBounds bounds = bindings[(int)stage - 1].Buffers[index];
+                    BufferBounds bounds = buffers.Buffers[binding.Slot];
 
                     if (bounds.Address == 0)
                     {
                         continue;
                     }
 
-                    if (bind)
-                    {
-                        BindBuffer(index, stage, bounds, isStorage);
-                    }
-                    else
-                    {
-                        SynchronizeBufferRange(bounds.Address, bounds.Size);
-                    }
+                    SynchronizeBufferRange(bounds.Address, bounds.Size);
                 }
             }
         }
 
-        /// <summary>
-        /// Binds a buffer on the host API.
-        /// </summary>
-        /// <param name="index">Index to bind the buffer into</param>
-        /// <param name="stage">Shader stage to bind the buffer into</param>
-        /// <param name="bounds">Buffer address and size</param>
-        /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
-        private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage)
-        {
-            BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
-
-            if (isStorage)
-            {
-                _context.Renderer.Pipeline.SetStorageBuffer(index, stage, buffer);
-            }
-            else
-            {
-                _context.Renderer.Pipeline.SetUniformBuffer(index, stage, buffer);
-            }
-        }
-
         /// <summary>
         /// Sets the buffer storage of a buffer texture.
         /// </summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 853351dbac..131aa6b7f5 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 localMemorySize,
                 sharedMemorySize);
 
-            shader.HostShader = _context.Renderer.CompileShader(shader.Program);
+            shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code);
 
             IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null);
 
@@ -134,19 +134,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 flags |= TranslationFlags.Feedback;
             }
 
+            TranslationCounts counts = new TranslationCounts();
+
             if (addresses.VertexA != 0)
             {
-                shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA);
+                shaders[0] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA);
             }
             else
             {
-                shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex);
+                shaders[0] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex);
             }
 
-            shaders[1] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationControl,    addresses.TessControl);
-            shaders[2] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
-            shaders[3] = TranslateGraphicsShader(state, flags, ShaderStage.Geometry,               addresses.Geometry);
-            shaders[4] = TranslateGraphicsShader(state, flags, ShaderStage.Fragment,               addresses.Fragment);
+            shaders[1] = TranslateGraphicsShader(state, counts, flags, ShaderStage.TessellationControl,    addresses.TessControl);
+            shaders[2] = TranslateGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
+            shaders[3] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Geometry,               addresses.Geometry);
+            shaders[4] = TranslateGraphicsShader(state, counts, flags, ShaderStage.Fragment,               addresses.Fragment);
 
             List<IShader> hostShaders = new List<IShader>();
 
@@ -159,7 +161,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
                     continue;
                 }
 
-                IShader hostShader = _context.Renderer.CompileShader(program);
+                IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code);
 
                 shaders[stage].HostShader = hostShader;
 
@@ -334,12 +336,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
         /// </remarks>
         /// <param name="state">Current GPU state</param>
+        /// <param name="counts">Cumulative shader resource counts</param>
         /// <param name="flags">Flags that controls shader translation</param>
         /// <param name="stage">Shader stage</param>
         /// <param name="gpuVa">GPU virtual address of the shader code</param>
         /// <param name="gpuVaA">Optional GPU virtual address of the "Vertex A" shader code</param>
         /// <returns>Compiled graphics shader code</returns>
-        private ShaderCodeHolder TranslateGraphicsShader(GpuState state, TranslationFlags flags, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0)
+        private ShaderCodeHolder TranslateGraphicsShader(
+            GpuState state,
+            TranslationCounts counts,
+            TranslationFlags flags,
+            ShaderStage stage,
+            ulong gpuVa,
+            ulong gpuVaA = 0)
         {
             if (gpuVa == 0)
             {
@@ -350,7 +359,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
             if (gpuVaA != 0)
             {
-                ShaderProgram program = Translator.Translate(gpuVaA, gpuVa, gpuAccessor, flags);
+                ShaderProgram program = Translator.Translate(gpuVaA, gpuVa, gpuAccessor, flags, counts);
 
                 byte[] codeA = _context.MemoryManager.GetSpan(gpuVaA, program.SizeA).ToArray();
                 byte[] codeB = _context.MemoryManager.GetSpan(gpuVa,  program.Size).ToArray();
@@ -370,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
             }
             else
             {
-                ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, flags);
+                ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, flags, counts);
 
                 byte[] code = _context.MemoryManager.GetSpan(gpuVa, program.Size).ToArray();
 
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 06cf5ef420..5687329102 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -697,20 +697,20 @@ namespace Ryujinx.Graphics.OpenGL
             SetFrontFace(_frontFace = frontFace.Convert());
         }
 
-        public void SetImage(int index, ShaderStage stage, ITexture texture, Format imageFormat)
+        public void SetImage(int binding, ITexture texture, Format imageFormat)
         {
-            int unit = _program.GetImageUnit(stage, index);
-
-            if (unit != -1 && texture != null)
+            if (texture == null)
             {
-                TextureBase texBase = (TextureBase)texture;
+                return;
+            }
 
-                SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
+            TextureBase texBase = (TextureBase)texture;
 
-                if (format != 0)
-                {
-                    GL.BindImageTexture(unit, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
-                }
+            SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
+
+            if (format != 0)
+            {
+                GL.BindImageTexture(binding, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
             }
         }
 
@@ -866,14 +866,14 @@ namespace Ryujinx.Graphics.OpenGL
             UpdateDepthTest();
         }
 
-        public void SetSampler(int index, ShaderStage stage, ISampler sampler)
+        public void SetSampler(int binding, ISampler sampler)
         {
-            int unit = _program.GetTextureUnit(stage, index);
-
-            if (unit != -1 && sampler != null)
+            if (sampler == null)
             {
-                ((Sampler)sampler).Bind(unit);
+                return;
             }
+
+            ((Sampler)sampler).Bind(binding);
         }
 
         public void SetScissorEnable(int index, bool enable)
@@ -939,25 +939,25 @@ namespace Ryujinx.Graphics.OpenGL
             _stencilFrontMask = stencilTest.FrontMask;
         }
 
-        public void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer)
+        public void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers)
         {
-            SetBuffer(index, stage, buffer, isStorage: true);
+            SetBuffers(buffers, isStorage: true);
         }
 
-        public void SetTexture(int index, ShaderStage stage, ITexture texture)
+        public void SetTexture(int binding, ITexture texture)
         {
-            int unit = _program.GetTextureUnit(stage, index);
-
-            if (unit != -1 && texture != null)
+            if (texture == null)
             {
-                if (unit == 0)
-                {
-                    _unit0Texture = (TextureBase)texture;
-                }
-                else
-                {
-                    ((TextureBase)texture).Bind(unit);
-                }
+                return;
+            }
+
+            if (binding == 0)
+            {
+                _unit0Texture = (TextureBase)texture;
+            }
+            else
+            {
+                ((TextureBase)texture).Bind(binding);
             }
         }
 
@@ -997,9 +997,9 @@ namespace Ryujinx.Graphics.OpenGL
             }
         }
 
-        public void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer)
+        public void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers)
         {
-            SetBuffer(index, stage, buffer, isStorage: false);
+            SetBuffers(buffers, isStorage: false);
         }
 
         public void SetUserClipDistance(int index, bool enableClip)
@@ -1077,30 +1077,22 @@ namespace Ryujinx.Graphics.OpenGL
             GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
         }
 
-        private void SetBuffer(int index, ShaderStage stage, BufferRange buffer, bool isStorage)
+        private void SetBuffers(ReadOnlySpan<BufferRange> buffers, bool isStorage)
         {
-            int bindingPoint = isStorage
-                ? _program.GetStorageBufferBindingPoint(stage, index)
-                : _program.GetUniformBufferBindingPoint(stage, index);
+            BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
 
-            if (bindingPoint == -1)
+            for (int index = 0; index < buffers.Length; index++)
             {
-                return;
+                BufferRange buffer = buffers[index];
+
+                if (buffer.Handle == BufferHandle.Null)
+                {
+                    GL.BindBufferRange(target, index, 0, IntPtr.Zero, 0);
+                    continue;
+                }
+
+                GL.BindBufferRange(target, index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
             }
-
-            BufferRangeTarget target = isStorage
-                ? BufferRangeTarget.ShaderStorageBuffer
-                : BufferRangeTarget.UniformBuffer;
-
-            if (buffer.Handle == BufferHandle.Null)
-            {
-                GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0);
-                return;
-            }
-
-            IntPtr bufferOffset = (IntPtr)buffer.Offset;
-
-            GL.BindBufferRange(target, bindingPoint, buffer.Handle.ToInt32(), bufferOffset, buffer.Size);
         }
 
         private void SetOrigin(ClipOrigin origin)
diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs
index babe17a0d7..17e14df6db 100644
--- a/Ryujinx.Graphics.OpenGL/Program.cs
+++ b/Ryujinx.Graphics.OpenGL/Program.cs
@@ -1,7 +1,6 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Shader;
 using Ryujinx.Graphics.Shader.CodeGen.Glsl;
 using System;
 using System.Collections.Generic;
@@ -11,18 +10,6 @@ namespace Ryujinx.Graphics.OpenGL
 {
     class Program : IProgram
     {
-        private const int ShaderStages = 6;
-
-        private const int UbStageShift  = 5;
-        private const int SbStageShift  = 4;
-        private const int TexStageShift = 5;
-        private const int ImgStageShift = 3;
-
-        private const int UbsPerStage  = 1 << UbStageShift;
-        private const int SbsPerStage  = 1 << SbStageShift;
-        private const int TexsPerStage = 1 << TexStageShift;
-        private const int ImgsPerStage = 1 << ImgStageShift;
-
         public int Handle { get; private set; }
 
         public int FragmentIsBgraUniform { get; }
@@ -31,38 +18,8 @@ namespace Ryujinx.Graphics.OpenGL
 
         public bool IsLinked { get; private set; }
 
-        private int[] _ubBindingPoints;
-        private int[] _sbBindingPoints;
-        private int[] _textureUnits;
-        private int[] _imageUnits;
-
         public Program(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors)
         {
-            _ubBindingPoints = new int[UbsPerStage  * ShaderStages];
-            _sbBindingPoints = new int[SbsPerStage  * ShaderStages];
-            _textureUnits    = new int[TexsPerStage * ShaderStages];
-            _imageUnits      = new int[ImgsPerStage * ShaderStages];
-
-            for (int index = 0; index < _ubBindingPoints.Length; index++)
-            {
-                _ubBindingPoints[index] = -1;
-            }
-
-            for (int index = 0; index < _sbBindingPoints.Length; index++)
-            {
-                _sbBindingPoints[index] = -1;
-            }
-
-            for (int index = 0; index < _textureUnits.Length; index++)
-            {
-                _textureUnits[index] = -1;
-            }
-
-            for (int index = 0; index < _imageUnits.Length; index++)
-            {
-                _imageUnits[index] = -1;
-            }
-
             Handle = GL.CreateProgram();
 
             for (int index = 0; index < shaders.Length; index++)
@@ -131,92 +88,6 @@ namespace Ryujinx.Graphics.OpenGL
 
             CheckProgramLink();
 
-            int ubBindingPoint = 0;
-            int sbBindingPoint = 0;
-            int textureUnit    = 0;
-            int imageUnit      = 0;
-
-            for (int index = 0; index < shaders.Length; index++)
-            {
-                Shader shader = (Shader)shaders[index];
-
-                foreach (BufferDescriptor descriptor in shader.Info.CBuffers)
-                {
-                    int location = GL.GetUniformBlockIndex(Handle, descriptor.Name);
-
-                    if (location < 0)
-                    {
-                        continue;
-                    }
-
-                    GL.UniformBlockBinding(Handle, location, ubBindingPoint);
-
-                    int bpIndex = (int)shader.Stage << UbStageShift | descriptor.Slot;
-
-                    _ubBindingPoints[bpIndex] = ubBindingPoint;
-
-                    ubBindingPoint++;
-                }
-
-                foreach (BufferDescriptor descriptor in shader.Info.SBuffers)
-                {
-                    int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, descriptor.Name);
-
-                    if (location < 0)
-                    {
-                        continue;
-                    }
-
-                    GL.ShaderStorageBlockBinding(Handle, location, sbBindingPoint);
-
-                    int bpIndex = (int)shader.Stage << SbStageShift | descriptor.Slot;
-
-                    _sbBindingPoints[bpIndex] = sbBindingPoint;
-
-                    sbBindingPoint++;
-                }
-
-                int samplerIndex = 0;
-
-                foreach (TextureDescriptor descriptor in shader.Info.Textures)
-                {
-                    int location = GL.GetUniformLocation(Handle, descriptor.Name);
-
-                    if (location < 0)
-                    {
-                        continue;
-                    }
-
-                    GL.ProgramUniform1(Handle, location, textureUnit);
-
-                    int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++;
-
-                    _textureUnits[uIndex] = textureUnit;
-
-                    textureUnit++;
-                }
-
-                int imageIndex = 0;
-
-                foreach (TextureDescriptor descriptor in shader.Info.Images)
-                {
-                    int location = GL.GetUniformLocation(Handle, descriptor.Name);
-
-                    if (location < 0)
-                    {
-                        continue;
-                    }
-
-                    GL.ProgramUniform1(Handle, location, imageUnit);
-
-                    int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++;
-
-                    _imageUnits[uIndex] = imageUnit;
-
-                    imageUnit++;
-                }
-            }
-
             FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra");
             FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale");
             ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale");
@@ -227,26 +98,6 @@ namespace Ryujinx.Graphics.OpenGL
             GL.UseProgram(Handle);
         }
 
-        public int GetUniformBufferBindingPoint(ShaderStage stage, int index)
-        {
-            return _ubBindingPoints[(int)stage << UbStageShift | index];
-        }
-
-        public int GetStorageBufferBindingPoint(ShaderStage stage, int index)
-        {
-            return _sbBindingPoints[(int)stage << SbStageShift | index];
-        }
-
-        public int GetTextureUnit(ShaderStage stage, int index)
-        {
-            return _textureUnits[(int)stage << TexStageShift | index];
-        }
-
-        public int GetImageUnit(ShaderStage stage, int index)
-        {
-            return _imageUnits[(int)stage << ImgStageShift | index];
-        }
-
         private void CheckProgramLink()
         {
             GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status);
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index aac3c69e4a..75bcda122a 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -42,9 +42,9 @@ namespace Ryujinx.Graphics.OpenGL
             ResourcePool = new ResourcePool();
         }
 
-        public IShader CompileShader(ShaderProgram shader)
+        public IShader CompileShader(ShaderStage stage, string code)
         {
-            return new Shader(shader);
+            return new Shader(stage, code);
         }
 
         public BufferHandle CreateBuffer(int size)
diff --git a/Ryujinx.Graphics.OpenGL/Shader.cs b/Ryujinx.Graphics.OpenGL/Shader.cs
index f25845cfd4..1df07ee42d 100644
--- a/Ryujinx.Graphics.OpenGL/Shader.cs
+++ b/Ryujinx.Graphics.OpenGL/Shader.cs
@@ -8,31 +8,22 @@ namespace Ryujinx.Graphics.OpenGL
     {
         public int Handle { get; private set; }
 
-        private ShaderProgram _program;
-
-        public ShaderProgramInfo Info => _program.Info;
-
-        public ShaderStage Stage => _program.Stage;
-
-        public Shader(ShaderProgram program)
+        public Shader(ShaderStage stage, string code)
         {
-            _program = program;
-
-            ShaderType type = ShaderType.VertexShader;
-
-            switch (program.Stage)
+            ShaderType type = stage switch
             {
-                case ShaderStage.Compute:                type = ShaderType.ComputeShader;        break;
-                case ShaderStage.Vertex:                 type = ShaderType.VertexShader;         break;
-                case ShaderStage.TessellationControl:    type = ShaderType.TessControlShader;    break;
-                case ShaderStage.TessellationEvaluation: type = ShaderType.TessEvaluationShader; break;
-                case ShaderStage.Geometry:               type = ShaderType.GeometryShader;       break;
-                case ShaderStage.Fragment:               type = ShaderType.FragmentShader;       break;
-            }
+                ShaderStage.Compute => ShaderType.ComputeShader,
+                ShaderStage.Vertex => ShaderType.VertexShader,
+                ShaderStage.TessellationControl => ShaderType.TessControlShader,
+                ShaderStage.TessellationEvaluation => ShaderType.TessEvaluationShader,
+                ShaderStage.Geometry => ShaderType.GeometryShader,
+                ShaderStage.Fragment => ShaderType.FragmentShader,
+                _ => ShaderType.VertexShader
+            };
 
             Handle = GL.CreateShader(type);
 
-            GL.ShaderSource(Handle, program.Code);
+            GL.ShaderSource(Handle, code);
             GL.CompileShader(Handle);
         }
 
diff --git a/Ryujinx.Graphics.Shader/BufferDescriptor.cs b/Ryujinx.Graphics.Shader/BufferDescriptor.cs
index 4cc999989e..99855518f2 100644
--- a/Ryujinx.Graphics.Shader/BufferDescriptor.cs
+++ b/Ryujinx.Graphics.Shader/BufferDescriptor.cs
@@ -2,13 +2,12 @@ namespace Ryujinx.Graphics.Shader
 {
     public struct BufferDescriptor
     {
-        public string Name { get; }
-
+        public int Binding { get; }
         public int Slot { get; }
 
-        public BufferDescriptor(string name, int slot)
+        public BufferDescriptor(int binding, int slot)
         {
-            Name = name;
+            Binding = binding;
             Slot = slot;
         }
     }
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 734546a0ee..56a0126412 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -218,50 +218,46 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
             if (info.UsesCbIndexing)
             {
+                int count = info.CBuffers.Max() + 1;
+
+                int[] bindings = new int[count];
+
+                for (int i = 0; i < count; i++)
+                {
+                    bindings[i] = context.Config.Counts.IncrementUniformBuffersCount();
+                }
+
+                foreach (int cbufSlot in info.CBuffers.OrderBy(x => x))
+                {
+                    context.CBufferDescriptors.Add(new BufferDescriptor(bindings[cbufSlot], cbufSlot));
+                }
+
                 string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
 
                 ubName += "_" + DefaultNames.UniformNamePrefix;
 
                 string blockName = $"{ubName}_{DefaultNames.BlockSuffix}";
 
-                int maxSlot = 0;
-
-                foreach (int cbufSlot in info.CBuffers.OrderBy(x => x))
-                {
-                    context.CBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{cbufSlot}]", cbufSlot));
-
-                    if (maxSlot < cbufSlot)
-                    {
-                        maxSlot = cbufSlot;
-                    }
-                }
-
-                context.AppendLine("layout (std140) uniform " + blockName);
-
+                context.AppendLine($"layout (binding = {bindings[0]}, std140) uniform {blockName}");
                 context.EnterScope();
-
                 context.AppendLine("vec4 " + DefaultNames.DataName + ubSize + ";");
-
-                string arraySize = NumberFormatter.FormatInt(maxSlot + 1);
-
-                context.LeaveScope($" {ubName}[{arraySize}];");
+                context.LeaveScope($" {ubName}[{NumberFormatter.FormatInt(count)}];");
             }
             else
             {
                 foreach (int cbufSlot in info.CBuffers.OrderBy(x => x))
                 {
+                    int binding = context.Config.Counts.IncrementUniformBuffersCount();
+
+                    context.CBufferDescriptors.Add(new BufferDescriptor(binding, cbufSlot));
+
                     string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
 
                     ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot;
 
-                    context.CBufferDescriptors.Add(new BufferDescriptor(ubName, cbufSlot));
-
-                    context.AppendLine("layout (std140) uniform " + ubName);
-
+                    context.AppendLine($"layout (binding = {binding}, std140) uniform {ubName}");
                     context.EnterScope();
-
                     context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot, false) + ubSize + ";");
-
                     context.LeaveScope(";");
                 }
             }
@@ -275,32 +271,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
             string blockName = $"{sbName}_{DefaultNames.BlockSuffix}";
 
-            int maxSlot = 0;
+            int count = info.SBuffers.Max() + 1;
+
+            int[] bindings = new int[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                bindings[i] = context.Config.Counts.IncrementStorageBuffersCount();
+            }
 
             foreach (int sbufSlot in info.SBuffers)
             {
-                context.SBufferDescriptors.Add(new BufferDescriptor($"{blockName}[{sbufSlot}]", sbufSlot));
-
-                if (maxSlot < sbufSlot)
-                {
-                    maxSlot = sbufSlot;
-                }
+                context.SBufferDescriptors.Add(new BufferDescriptor(bindings[sbufSlot], sbufSlot));
             }
 
-            context.AppendLine("layout (std430) buffer " + blockName);
-
+            context.AppendLine($"layout (binding = {bindings[0]}, std430) buffer {blockName}");
             context.EnterScope();
-
             context.AppendLine("uint " + DefaultNames.DataName + "[];");
-
-            string arraySize = NumberFormatter.FormatInt(maxSlot + 1);
-
-            context.LeaveScope($" {sbName}[{arraySize}];");
+            context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(count)}];");
         }
 
         private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info)
         {
-            Dictionary<string, AstTextureOperation> samplers = new Dictionary<string, AstTextureOperation>();
+            HashSet<string> samplers = new HashSet<string>();
 
             // Texture instructions other than TextureSample (like TextureSize)
             // may have incomplete sampler type information. In those cases,
@@ -312,29 +305,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
                 string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
 
-                if (!samplers.TryAdd(samplerName, texOp))
+                if (!samplers.Add(samplerName))
                 {
                     continue;
                 }
 
-                string samplerTypeName = texOp.Type.ToGlslSamplerType();
-
-                context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";");
-            }
-
-            foreach (KeyValuePair<string, AstTextureOperation> kv in samplers)
-            {
-                string samplerName = kv.Key;
-
-                AstTextureOperation texOp = kv.Value;
-
-                TextureDescriptor desc;
+                int firstBinding = -1;
 
                 if ((texOp.Flags & TextureFlags.Bindless) != 0)
                 {
                     AstOperand operand = texOp.GetSource(0) as AstOperand;
 
-                    desc = new TextureDescriptor(samplerName, texOp.Type, operand.CbufSlot, operand.CbufOffset);
+                    firstBinding = context.Config.Counts.IncrementTexturesCount();
+
+                    var desc = new TextureDescriptor(firstBinding, texOp.Type, operand.CbufSlot, operand.CbufOffset);
 
                     context.TextureDescriptors.Add(desc);
                 }
@@ -342,27 +326,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                 {
                     for (int index = 0; index < texOp.ArraySize; index++)
                     {
-                        string indexExpr = NumberFormatter.FormatInt(index);
+                        int binding = context.Config.Counts.IncrementTexturesCount();
 
-                        string indexedSamplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
+                        if (firstBinding < 0)
+                        {
+                            firstBinding = binding;
+                        }
 
-                        desc = new TextureDescriptor(indexedSamplerName, texOp.Type, texOp.Format, texOp.Handle + index * 2);
+                        var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.Handle + index * 2);
 
                         context.TextureDescriptors.Add(desc);
                     }
                 }
                 else
                 {
-                    desc = new TextureDescriptor(samplerName, texOp.Type, texOp.Format, texOp.Handle);
+                    firstBinding = context.Config.Counts.IncrementTexturesCount();
+
+                    var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.Handle);
 
                     context.TextureDescriptors.Add(desc);
                 }
+
+                string samplerTypeName = texOp.Type.ToGlslSamplerType();
+
+                context.AppendLine($"layout (binding = {firstBinding}) uniform {samplerTypeName} {samplerName};");
             }
         }
 
         private static void DeclareImages(CodeGenContext context, StructuredProgramInfo info)
         {
-            Dictionary<string, AstTextureOperation> images = new Dictionary<string, AstTextureOperation>();
+            HashSet<string> images = new HashSet<string>();
 
             foreach (AstTextureOperation texOp in info.Images.OrderBy(x => x.Handle))
             {
@@ -370,48 +363,48 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
                 string imageName = OperandManager.GetImageName(context.Config.Stage, texOp, indexExpr);
 
-                if (!images.TryAdd(imageName, texOp))
+                if (!images.Add(imageName))
                 {
                     continue;
                 }
 
+                int firstBinding = -1;
+
+                if ((texOp.Type & SamplerType.Indexed) != 0)
+                {
+                    for (int index = 0; index < texOp.ArraySize; index++)
+                    {
+                        int binding = context.Config.Counts.IncrementImagesCount();
+
+                        if (firstBinding < 0)
+                        {
+                            firstBinding = binding;
+                        }
+
+                        var desc = new TextureDescriptor(binding, texOp.Type, texOp.Format, texOp.Handle + index * 2);
+
+                        context.ImageDescriptors.Add(desc);
+                    }
+                }
+                else
+                {
+                    firstBinding = context.Config.Counts.IncrementImagesCount();
+
+                    var desc = new TextureDescriptor(firstBinding, texOp.Type, texOp.Format, texOp.Handle);
+
+                    context.ImageDescriptors.Add(desc);
+                }
+
                 string layout = texOp.Format.ToGlslFormat();
 
                 if (!string.IsNullOrEmpty(layout))
                 {
-                    layout = "layout(" + layout + ") ";
+                    layout = ", " + layout;
                 }
 
                 string imageTypeName = texOp.Type.ToGlslImageType(texOp.Format.GetComponentType());
 
-                context.AppendLine("uniform " + layout + imageTypeName + " " + imageName + ";");
-            }
-
-            foreach (KeyValuePair<string, AstTextureOperation> kv in images)
-            {
-                string imageName = kv.Key;
-
-                AstTextureOperation texOp = kv.Value;
-
-                if ((texOp.Type & SamplerType.Indexed) != 0)
-                {
-                    for (int index = 0; index < texOp.ArraySize; index++)
-                    {
-                        string indexExpr = NumberFormatter.FormatInt(index);
-
-                        string indexedSamplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
-
-                        var desc = new TextureDescriptor(indexedSamplerName, texOp.Type, texOp.Format, texOp.Handle + index * 2);
-
-                        context.TextureDescriptors.Add(desc);
-                    }
-                }
-                else
-                {
-                    var desc = new TextureDescriptor(imageName, texOp.Type, texOp.Format, texOp.Handle);
-
-                    context.ImageDescriptors.Add(desc);
-                }
+                context.AppendLine($"layout (binding = {firstBinding}{layout}) uniform {imageTypeName} {imageName};");
             }
         }
 
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index ea936c6281..cb9db92218 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -2,79 +2,79 @@
 {
     public interface IGpuAccessor
     {
-        public void Log(string message)
+        void Log(string message)
         {
             // No default log output.
         }
 
         T MemoryRead<T>(ulong address) where T : unmanaged;
 
-        public bool MemoryMapped(ulong address)
+        bool MemoryMapped(ulong address)
         {
             return true;
         }
 
-        public int QueryComputeLocalSizeX()
+        int QueryComputeLocalSizeX()
         {
             return 1;
         }
 
-        public int QueryComputeLocalSizeY()
+        int QueryComputeLocalSizeY()
         {
             return 1;
         }
 
-        public int QueryComputeLocalSizeZ()
+        int QueryComputeLocalSizeZ()
         {
             return 1;
         }
 
-        public int QueryComputeLocalMemorySize()
+        int QueryComputeLocalMemorySize()
         {
             return 0x1000;
         }
 
-        public int QueryComputeSharedMemorySize()
+        int QueryComputeSharedMemorySize()
         {
             return 0xc000;
         }
 
-        public uint QueryConstantBufferUse()
+        uint QueryConstantBufferUse()
         {
             return 0xffff;
         }
 
-        public bool QueryIsTextureBuffer(int handle)
+        bool QueryIsTextureBuffer(int handle)
         {
             return false;
         }
 
-        public bool QueryIsTextureRectangle(int handle)
+        bool QueryIsTextureRectangle(int handle)
         {
             return false;
         }
 
-        public InputTopology QueryPrimitiveTopology()
+        InputTopology QueryPrimitiveTopology()
         {
             return InputTopology.Points;
         }
 
-        public int QueryStorageBufferOffsetAlignment()
+        int QueryStorageBufferOffsetAlignment()
         {
             return 16;
         }
 
-        public bool QuerySupportsImageLoadFormatted()
+        bool QuerySupportsImageLoadFormatted()
         {
             return true;
         }
 
-        public bool QuerySupportsNonConstantTextureOffset()
+        bool QuerySupportsNonConstantTextureOffset()
         {
             return true;
         }
 
-        public TextureFormat QueryTextureFormat(int handle)
+        TextureFormat QueryTextureFormat(int handle)
         {
             return TextureFormat.R8G8B8A8Unorm;
         }
diff --git a/Ryujinx.Graphics.Shader/TextureDescriptor.cs b/Ryujinx.Graphics.Shader/TextureDescriptor.cs
index 7cf868efae..b39d6c3d1a 100644
--- a/Ryujinx.Graphics.Shader/TextureDescriptor.cs
+++ b/Ryujinx.Graphics.Shader/TextureDescriptor.cs
@@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Shader
 {
     public struct TextureDescriptor
     {
-        public string Name { get; }
+        public int Binding { get; }
 
         public SamplerType Type { get; }
 
@@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Shader
 
         public TextureUsageFlags Flags { get; set; }
 
-        public TextureDescriptor(string name, SamplerType type, TextureFormat format, int handleIndex)
+        public TextureDescriptor(int binding, SamplerType type, TextureFormat format, int handleIndex)
         {
-            Name        = name;
+            Binding     = binding;
             Type        = type;
             Format      = format;
             HandleIndex = handleIndex;
@@ -32,9 +32,9 @@ namespace Ryujinx.Graphics.Shader
             Flags = TextureUsageFlags.None;
         }
 
-        public TextureDescriptor(string name, SamplerType type, int cbufSlot, int cbufOffset)
+        public TextureDescriptor(int binding, SamplerType type, int cbufSlot, int cbufOffset)
         {
-            Name        = name;
+            Binding     = binding;
             Type        = type;
             Format      = TextureFormat.Unknown;
             HandleIndex = 0;
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index 7b1587ae77..08e7df3b1a 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -20,11 +20,13 @@ namespace Ryujinx.Graphics.Shader.Translation
 
         public TranslationFlags Flags { get; }
 
+        public TranslationCounts Counts { get; }
+
         public int Size { get; private set; }
 
         public FeatureFlags UsedFeatures { get; private set; }
 
-        public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags)
+        public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
         {
             Stage             = ShaderStage.Compute;
             OutputTopology    = OutputTopology.PointList;
@@ -38,9 +40,10 @@ namespace Ryujinx.Graphics.Shader.Translation
             Flags             = flags;
             Size              = 0;
             UsedFeatures      = FeatureFlags.None;
+            Counts            = counts;
         }
 
-        public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags)
+        public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
         {
             Stage             = header.Stage;
             OutputTopology    = header.OutputTopology;
@@ -54,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             Flags             = flags;
             Size              = 0;
             UsedFeatures      = FeatureFlags.None;
+            Counts            = counts;
         }
 
         public int GetDepthRegister()
diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs b/Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs
new file mode 100644
index 0000000000..18f4725d53
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs
@@ -0,0 +1,30 @@
+namespace Ryujinx.Graphics.Shader.Translation
+{
+    public class TranslationCounts
+    {
+        public int UniformBuffersCount { get; private set; }
+        public int StorageBuffersCount { get; private set; }
+        public int TexturesCount { get; private set; }
+        public int ImagesCount { get; private set; }
+
+        internal int IncrementUniformBuffersCount()
+        {
+            return UniformBuffersCount++;
+        }
+
+        internal int IncrementStorageBuffersCount()
+        {
+            return StorageBuffersCount++;
+        }
+
+        internal int IncrementTexturesCount()
+        {
+            return TexturesCount++;
+        }
+
+        internal int IncrementImagesCount()
+        {
+            return ImagesCount++;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index f8093c8440..f86b69647c 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -24,15 +24,28 @@ namespace Ryujinx.Graphics.Shader.Translation
             }
         }
 
-        public static ShaderProgram Translate(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags)
+        public static ShaderProgram Translate(
+            ulong address,
+            IGpuAccessor gpuAccessor,
+            TranslationFlags flags,
+            TranslationCounts counts = null)
         {
-            return Translate(DecodeShader(address, gpuAccessor, flags, out ShaderConfig config), config);
+            counts ??= new TranslationCounts();
+
+            return Translate(DecodeShader(address, gpuAccessor, flags, counts, out ShaderConfig config), config);
         }
 
-        public static ShaderProgram Translate(ulong addressA, ulong addressB, IGpuAccessor gpuAccessor, TranslationFlags flags)
+        public static ShaderProgram Translate(
+            ulong addressA,
+            ulong addressB,
+            IGpuAccessor gpuAccessor,
+            TranslationFlags flags,
+            TranslationCounts counts = null)
         {
-            FunctionCode[] funcA = DecodeShader(addressA, gpuAccessor, flags | TranslationFlags.VertexA, out ShaderConfig configA);
-            FunctionCode[] funcB = DecodeShader(addressB, gpuAccessor, flags, out ShaderConfig config);
+            counts ??= new TranslationCounts();
+
+            FunctionCode[] funcA = DecodeShader(addressA, gpuAccessor, flags | TranslationFlags.VertexA, counts, out ShaderConfig configA);
+            FunctionCode[] funcB = DecodeShader(addressB, gpuAccessor, flags, counts, out ShaderConfig config);
 
             config.SetUsedFeature(configA.UsedFeatures);
 
@@ -105,19 +118,24 @@ namespace Ryujinx.Graphics.Shader.Translation
             return new ShaderProgram(spInfo, config.Stage, glslCode, config.Size, sizeA);
         }
 
-        private static FunctionCode[] DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationFlags flags, out ShaderConfig config)
+        private static FunctionCode[] DecodeShader(
+            ulong address,
+            IGpuAccessor gpuAccessor,
+            TranslationFlags flags,
+            TranslationCounts counts,
+            out ShaderConfig config)
         {
             Block[][] cfg;
 
             if ((flags & TranslationFlags.Compute) != 0)
             {
-                config = new ShaderConfig(gpuAccessor, flags);
+                config = new ShaderConfig(gpuAccessor, flags, counts);
 
                 cfg = Decoder.Decode(gpuAccessor, address);
             }
             else
             {
-                config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, flags);
+                config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, flags, counts);
 
                 cfg = Decoder.Decode(gpuAccessor, address + HeaderSize);
             }