Просмотр исходного кода

Replace BGRA and scale uniforms with a uniform block (#2496)

* Replace BGRA and scale uniforms with a uniform block

* Setting the data again on program change is no longer needed

* Optimize and resolve some warnings

* Avoid redundant support buffer updates

* Some optimizations to BindBuffers (now inlined)

* Unify render scale arrays
gdkchan 4 лет назад
Родитель
Сommit
0f6ec446ea

+ 2 - 2
Ryujinx.Graphics.GAL/IPipeline.cs

@@ -73,12 +73,12 @@ namespace Ryujinx.Graphics.GAL
 
 
         void SetStencilTest(StencilTestDescriptor stencilTest);
         void SetStencilTest(StencilTestDescriptor stencilTest);
 
 
-        void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers);
+        void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers);
 
 
         void SetTexture(int binding, ITexture texture);
         void SetTexture(int binding, ITexture texture);
 
 
         void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
         void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
-        void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers);
+        void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers);
 
 
         void SetUserClipDistance(int index, bool enableClip);
         void SetUserClipDistance(int index, bool enableClip);
 
 

+ 0 - 16
Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs

@@ -957,9 +957,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
                 UpdateUserClipState();
                 UpdateUserClipState();
             }
             }
 
 
-            int storageBufferBindingsCount = 0;
-            int uniformBufferBindingsCount = 0;
-
             for (int stage = 0; stage < Constants.ShaderStages; stage++)
             for (int stage = 0; stage < Constants.ShaderStages; stage++)
             {
             {
                 ShaderProgramInfo info = gs.Shaders[stage]?.Info;
                 ShaderProgramInfo info = gs.Shaders[stage]?.Info;
@@ -1015,21 +1012,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
 
 
                 _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
                 _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
                 _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
                 _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
-
-                if (info.SBuffers.Count != 0)
-                {
-                    storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
-                }
-
-                if (info.CBuffers.Count != 0)
-                {
-                    uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
-                }
             }
             }
 
 
-            _channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
-            _channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
-
             _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
             _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
         }
         }
 
 

+ 107 - 86
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs

@@ -5,7 +5,7 @@ using Ryujinx.Graphics.Shader;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
-using System.Linq;
+using System.Runtime.CompilerServices;
 
 
 namespace Ryujinx.Graphics.Gpu.Memory
 namespace Ryujinx.Graphics.Gpu.Memory
 {
 {
@@ -14,8 +14,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// </summary>
     /// </summary>
     class BufferManager
     class BufferManager
     {
     {
-        private const int StackToHeapThreshold = 16;
-
         private readonly GpuContext _context;
         private readonly GpuContext _context;
         private readonly GpuChannel _channel;
         private readonly GpuChannel _channel;
 
 
@@ -23,6 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private readonly VertexBuffer[] _vertexBuffers;
         private readonly VertexBuffer[] _vertexBuffers;
         private readonly BufferBounds[] _transformFeedbackBuffers;
         private readonly BufferBounds[] _transformFeedbackBuffers;
         private readonly List<BufferTextureBinding> _bufferTextures;
         private readonly List<BufferTextureBinding> _bufferTextures;
+        private readonly BufferRange[] _ranges;
 
 
         /// <summary>
         /// <summary>
         /// Holds shader stage buffer state and binding information.
         /// Holds shader stage buffer state and binding information.
@@ -88,11 +87,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private readonly BuffersPerStage[] _gpStorageBuffers;
         private readonly BuffersPerStage[] _gpStorageBuffers;
         private readonly BuffersPerStage[] _gpUniformBuffers;
         private readonly BuffersPerStage[] _gpUniformBuffers;
 
 
-        private int _cpStorageBufferBindings;
-        private int _cpUniformBufferBindings;
-        private int _gpStorageBufferBindings;
-        private int _gpUniformBufferBindings;
-
         private bool _gpStorageBuffersDirty;
         private bool _gpStorageBuffersDirty;
         private bool _gpUniformBuffersDirty;
         private bool _gpUniformBuffersDirty;
 
 
@@ -130,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
             }
             }
 
 
             _bufferTextures = new List<BufferTextureBinding>();
             _bufferTextures = new List<BufferTextureBinding>();
+
+            _ranges = new BufferRange[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
         }
         }
 
 
 
 
@@ -288,7 +284,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
         public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
         public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
         {
         {
             _cpStorageBuffers.SetBindings(descriptors);
             _cpStorageBuffers.SetBindings(descriptors);
-            _cpStorageBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -302,15 +297,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             _gpStorageBuffersDirty = true;
             _gpStorageBuffersDirty = true;
         }
         }
 
 
-        /// <summary>
-        /// 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>
         /// <summary>
         /// Sets the binding points for the uniform buffers bound on the compute pipeline.
         /// Sets the binding points for the uniform buffers bound on the compute pipeline.
         /// </summary>
         /// </summary>
@@ -318,7 +304,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
         public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
         public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
         {
         {
             _cpUniformBuffers.SetBindings(descriptors);
             _cpUniformBuffers.SetBindings(descriptors);
-            _cpUniformBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -333,15 +318,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             _gpUniformBuffersDirty = true;
             _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>
         /// <summary>
         /// Gets a bit mask indicating which compute uniform buffers are currently bound.
         /// Gets a bit mask indicating which compute uniform buffers are currently bound.
         /// </summary>
         /// </summary>
@@ -381,7 +357,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             return mask;
             return mask;
         }
         }
 
 
-
         /// <summary>
         /// <summary>
         /// Gets the address of the compute uniform buffer currently bound at the given index.
         /// Gets the address of the compute uniform buffer currently bound at the given index.
         /// </summary>
         /// </summary>
@@ -409,46 +384,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// </summary>
         /// </summary>
         public void CommitComputeBindings()
         public void CommitComputeBindings()
         {
         {
-            int sCount = _cpStorageBufferBindings;
-
-            Span<BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount];
-
-            for (int index = 0; index < _cpStorageBuffers.Count; index++)
-            {
-                ref var bindingInfo = ref _cpStorageBuffers.Bindings[index];
-
-                BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot];
-
-                if (bounds.Address != 0)
-                {
-                    // The storage buffer size is not reliable (it might be lower than the actual size),
-                    // so we bind the entire buffer to allow otherwise out of range accesses to work.
-                    sRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(
-                        bounds.Address,
-                        bounds.Size,
-                        bounds.Flags.HasFlag(BufferUsageFlags.Write));
-                }
-            }
-
-            _context.Renderer.Pipeline.SetStorageBuffers(sRanges);
-
-            int uCount = _cpUniformBufferBindings;
-
-            Span<BufferRange> uRanges = uCount < StackToHeapThreshold ? stackalloc BufferRange[uCount] : new BufferRange[uCount];
-
-            for (int index = 0; index < _cpUniformBuffers.Count; index++)
-            {
-                ref var bindingInfo = ref _cpUniformBuffers.Bindings[index];
-
-                BufferBounds bounds = _cpUniformBuffers.Buffers[bindingInfo.Slot];
-
-                if (bounds.Address != 0)
-                {
-                    uRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
-                }
-            }
+            var bufferCache = _channel.MemoryManager.Physical.BufferCache;
 
 
-            _context.Renderer.Pipeline.SetUniformBuffers(uRanges);
+            BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
+            BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
 
 
             CommitBufferTextureBindings();
             CommitBufferTextureBindings();
 
 
@@ -491,20 +430,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// </summary>
         /// </summary>
         public void CommitGraphicsBindings()
         public void CommitGraphicsBindings()
         {
         {
+            var bufferCache = _channel.MemoryManager.Physical.BufferCache;
+
             if (_indexBufferDirty || _rebind)
             if (_indexBufferDirty || _rebind)
             {
             {
                 _indexBufferDirty = false;
                 _indexBufferDirty = false;
 
 
                 if (_indexBuffer.Address != 0)
                 if (_indexBuffer.Address != 0)
                 {
                 {
-                    BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
+                    BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
 
 
                     _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
                     _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
                 }
                 }
             }
             }
             else if (_indexBuffer.Address != 0)
             else if (_indexBuffer.Address != 0)
             {
             {
-                _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
+                bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
             }
             }
 
 
             uint vbEnableMask = _vertexBuffersEnableMask;
             uint vbEnableMask = _vertexBuffersEnableMask;
@@ -524,7 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                         continue;
                         continue;
                     }
                     }
 
 
-                    BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(vb.Address, vb.Size);
+                    BufferRange buffer = bufferCache.GetBufferRange(vb.Address, vb.Size);
 
 
                     vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
                     vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
                 }
                 }
@@ -542,7 +483,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                         continue;
                         continue;
                     }
                     }
 
 
-                    _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
+                    bufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
                 }
                 }
             }
             }
 
 
@@ -562,7 +503,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                         continue;
                         continue;
                     }
                     }
 
 
-                    tfbs[index] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
+                    tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size);
                 }
                 }
 
 
                 _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
                 _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -578,7 +519,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                         continue;
                         continue;
                     }
                     }
 
 
-                    _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
+                    bufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
                 }
                 }
             }
             }
 
 
@@ -586,7 +527,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
             {
             {
                 _gpStorageBuffersDirty = false;
                 _gpStorageBuffersDirty = false;
 
 
-                BindBuffers(_gpStorageBuffers, isStorage: true);
+                BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
             }
             }
             else
             else
             {
             {
@@ -597,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
             {
             {
                 _gpUniformBuffersDirty = false;
                 _gpUniformBuffersDirty = false;
 
 
-                BindBuffers(_gpUniformBuffers, isStorage: false);
+                BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
             }
             }
             else
             else
             {
             {
@@ -612,13 +553,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <summary>
         /// <summary>
         /// Bind respective buffer bindings on the host API.
         /// Bind respective buffer bindings on the host API.
         /// </summary>
         /// </summary>
-        /// <param name="bindings">Bindings to bind</param>
-        /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param>
-        private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
+        /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
+        /// <param name="bindings">Buffer memory ranges to bind</param>
+        /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
         {
         {
-            int count = isStorage ? _gpStorageBufferBindings : _gpUniformBufferBindings;
+            int rangesFirst = 0;
+            int rangesCount = 0;
 
 
-            Span<BufferRange> ranges = count < StackToHeapThreshold ? stackalloc BufferRange[count] : new BufferRange[count];
+            Span<BufferRange> ranges = _ranges;
 
 
             for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
             for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
             {
             {
@@ -633,20 +577,97 @@ namespace Ryujinx.Graphics.Gpu.Memory
                     if (bounds.Address != 0)
                     if (bounds.Address != 0)
                     {
                     {
                         var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
                         var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
-                        ranges[bindingInfo.Binding] = isStorage
-                            ? _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
-                            : _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
+                        var range = isStorage
+                            ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
+                            : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
+
+                        if (rangesCount == 0)
+                        {
+                            rangesFirst = bindingInfo.Binding;
+                        }
+                        else if (bindingInfo.Binding != rangesFirst + rangesCount)
+                        {
+                            SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
+                            rangesFirst = bindingInfo.Binding;
+                            rangesCount = 0;
+                        }
+
+                        ranges[rangesCount++] = range;
                     }
                     }
                 }
                 }
             }
             }
 
 
+            if (rangesCount != 0)
+            {
+                SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
+            }
+        }
+
+        /// <summary>
+        /// Bind respective buffer bindings on the host API.
+        /// </summary>
+        /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
+        /// <param name="buffers">Buffer memory ranges to bind</param>
+        /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
+        {
+            int rangesFirst = 0;
+            int rangesCount = 0;
+
+            Span<BufferRange> ranges = _ranges;
+
+            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)
+                {
+                    var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
+                    var range = isStorage
+                        ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
+                        : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
+
+                    if (rangesCount == 0)
+                    {
+                        rangesFirst = bindingInfo.Binding;
+                    }
+                    else if (bindingInfo.Binding != rangesFirst + rangesCount)
+                    {
+                        SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
+                        rangesFirst = bindingInfo.Binding;
+                        rangesCount = 0;
+                    }
+
+                    ranges[rangesCount++] = range;
+                }
+            }
+
+            if (rangesCount != 0)
+            {
+                SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
+            }
+        }
+
+        /// <summary>
+        /// Bind respective buffer bindings on the host API.
+        /// </summary>
+        /// <param name="ranges">Host buffers to bind, with their offsets and sizes</param>
+        /// <param name="first">First binding point</param>
+        /// <param name="count">Number of bindings</param>
+        /// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void SetHostBuffers(ReadOnlySpan<BufferRange> ranges, int first, int count, bool isStorage)
+        {
             if (isStorage)
             if (isStorage)
             {
             {
-                _context.Renderer.Pipeline.SetStorageBuffers(ranges);
+                _context.Renderer.Pipeline.SetStorageBuffers(first, ranges.Slice(0, count));
             }
             }
             else
             else
             {
             {
-                _context.Renderer.Pipeline.SetUniformBuffers(ranges);
+                _context.Renderer.Pipeline.SetUniformBuffers(first, ranges.Slice(0, count));
             }
             }
         }
         }
 
 

+ 1 - 1
Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs

@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// <summary>
         /// <summary>
         /// Version of the codegen (to be changed when codegen or guest format change).
         /// Version of the codegen (to be changed when codegen or guest format change).
         /// </summary>
         /// </summary>
-        private const ulong ShaderCodeGenVersion = 2530;
+        private const ulong ShaderCodeGenVersion = 2494;
 
 
         // Progress reporting helpers
         // Progress reporting helpers
         private volatile int _shaderCount;
         private volatile int _shaderCount;

+ 66 - 56
Ryujinx.Graphics.OpenGL/Pipeline.cs

@@ -5,6 +5,8 @@ using Ryujinx.Graphics.OpenGL.Image;
 using Ryujinx.Graphics.OpenGL.Queries;
 using Ryujinx.Graphics.OpenGL.Queries;
 using Ryujinx.Graphics.Shader;
 using Ryujinx.Graphics.Shader;
 using System;
 using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 
 namespace Ryujinx.Graphics.OpenGL
 namespace Ryujinx.Graphics.OpenGL
 {
 {
@@ -31,9 +33,16 @@ namespace Ryujinx.Graphics.OpenGL
         private int _boundDrawFramebuffer;
         private int _boundDrawFramebuffer;
         private int _boundReadFramebuffer;
         private int _boundReadFramebuffer;
 
 
-        private int[] _fpIsBgra = new int[8];
-        private float[] _fpRenderScale = new float[65];
-        private float[] _cpRenderScale = new float[64];
+        private struct Vector4<T>
+        {
+            public T X;
+            public T Y;
+            public T Z;
+            public T W;
+        }
+
+        private Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
+        private Vector4<float>[] _renderScale = new Vector4<float>[65];
 
 
         private TextureBase _unit0Texture;
         private TextureBase _unit0Texture;
 
 
@@ -48,6 +57,7 @@ namespace Ryujinx.Graphics.OpenGL
         private bool _tfEnabled;
         private bool _tfEnabled;
         private TransformFeedbackPrimitiveType _tfTopology;
         private TransformFeedbackPrimitiveType _tfTopology;
 
 
+        private BufferHandle _supportBuffer;
         private readonly BufferHandle[] _tfbs;
         private readonly BufferHandle[] _tfbs;
         private readonly BufferRange[] _tfbTargets;
         private readonly BufferRange[] _tfbTargets;
 
 
@@ -66,15 +76,8 @@ namespace Ryujinx.Graphics.OpenGL
                 _componentMasks[index] = 0xf;
                 _componentMasks[index] = 0xf;
             }
             }
 
 
-            for (int index = 0; index < _fpRenderScale.Length; index++)
-            {
-                _fpRenderScale[index] = 1f;
-            }
-
-            for (int index = 0; index < _cpRenderScale.Length; index++)
-            {
-                _cpRenderScale[index] = 1f;
-            }
+            var v4Zero = new Vector4<float> { X = 0f, Y = 0f, Z = 0f, W = 0f };
+            new Span<Vector4<float>>(_renderScale).Fill(v4Zero);
 
 
             _tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
             _tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
             _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
             _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
@@ -823,9 +826,6 @@ namespace Ryujinx.Graphics.OpenGL
             {
             {
                 _program.Bind();
                 _program.Bind();
             }
             }
-
-            UpdateFpIsBgra();
-            SetRenderTargetScale(_fpRenderScale[0]);
         }
         }
 
 
         public void SetRasterizerDiscard(bool discard)
         public void SetRasterizerDiscard(bool discard)
@@ -844,12 +844,8 @@ namespace Ryujinx.Graphics.OpenGL
 
 
         public void SetRenderTargetScale(float scale)
         public void SetRenderTargetScale(float scale)
         {
         {
-            _fpRenderScale[0] = scale;
-
-            if (_program != null && _program.FragmentRenderScaleUniform != -1)
-            {
-                GL.Uniform1(_program.FragmentRenderScaleUniform, 1, _fpRenderScale); // Just the first element.
-            }
+            _renderScale[0].X = scale;
+            SetSupportBufferData<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1); // Just the first element.
         }
         }
 
 
         public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
         public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
@@ -866,6 +862,8 @@ namespace Ryujinx.Graphics.OpenGL
         {
         {
             EnsureFramebuffer();
             EnsureFramebuffer();
 
 
+            bool isBgraChanged = false;
+
             for (int index = 0; index < colors.Length; index++)
             for (int index = 0; index < colors.Length; index++)
             {
             {
                 TextureView color = (TextureView)colors[index];
                 TextureView color = (TextureView)colors[index];
@@ -874,15 +872,19 @@ namespace Ryujinx.Graphics.OpenGL
 
 
                 int isBgra = color != null && color.Format.IsBgra8() ? 1 : 0;
                 int isBgra = color != null && color.Format.IsBgra8() ? 1 : 0;
 
 
-                if (_fpIsBgra[index] != isBgra)
+                if (_fpIsBgra[index].X != isBgra)
                 {
                 {
-                    _fpIsBgra[index] = isBgra;
+                    _fpIsBgra[index].X = isBgra;
+                    isBgraChanged = true;
 
 
                     RestoreComponentMask(index);
                     RestoreComponentMask(index);
                 }
                 }
             }
             }
 
 
-            UpdateFpIsBgra();
+            if (isBgraChanged)
+            {
+                SetSupportBufferData<Vector4<int>>(SupportBuffer.FragmentIsBgraOffset, _fpIsBgra, SupportBuffer.FragmentIsBgraCount);
+            }
 
 
             TextureView depthStencilView = (TextureView)depthStencil;
             TextureView depthStencilView = (TextureView)depthStencil;
 
 
@@ -965,9 +967,9 @@ namespace Ryujinx.Graphics.OpenGL
             _stencilFrontMask = stencilTest.FrontMask;
             _stencilFrontMask = stencilTest.FrontMask;
         }
         }
 
 
-        public void SetStorageBuffers(ReadOnlySpan<BufferRange> buffers)
+        public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
         {
         {
-            SetBuffers(buffers, isStorage: true);
+            SetBuffers(first, buffers, isStorage: true);
         }
         }
 
 
         public void SetTexture(int binding, ITexture texture)
         public void SetTexture(int binding, ITexture texture)
@@ -1023,9 +1025,9 @@ namespace Ryujinx.Graphics.OpenGL
             }
             }
         }
         }
 
 
-        public void SetUniformBuffers(ReadOnlySpan<BufferRange> buffers)
+        public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
         {
         {
-            SetBuffers(buffers, isStorage: false);
+            SetBuffers(first, buffers, isStorage: false);
         }
         }
 
 
         public void SetUserClipDistance(int index, bool enableClip)
         public void SetUserClipDistance(int index, bool enableClip)
@@ -1103,7 +1105,7 @@ namespace Ryujinx.Graphics.OpenGL
             GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
             GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
         }
         }
 
 
-        private void SetBuffers(ReadOnlySpan<BufferRange> buffers, bool isStorage)
+        private void SetBuffers(int first, ReadOnlySpan<BufferRange> buffers, bool isStorage)
         {
         {
             BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
             BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
 
 
@@ -1113,11 +1115,11 @@ namespace Ryujinx.Graphics.OpenGL
 
 
                 if (buffer.Handle == BufferHandle.Null)
                 if (buffer.Handle == BufferHandle.Null)
                 {
                 {
-                    GL.BindBufferRange(target, index, 0, IntPtr.Zero, 0);
+                    GL.BindBufferRange(target, first + index, 0, IntPtr.Zero, 0);
                     continue;
                     continue;
                 }
                 }
 
 
-                GL.BindBufferRange(target, index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
+                GL.BindBufferRange(target, first + index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
             }
             }
         }
         }
 
 
@@ -1179,37 +1181,39 @@ namespace Ryujinx.Graphics.OpenGL
             return (_boundDrawFramebuffer, _boundReadFramebuffer);
             return (_boundDrawFramebuffer, _boundReadFramebuffer);
         }
         }
 
 
-        private void UpdateFpIsBgra()
+        public void UpdateRenderScale(ShaderStage stage, float[] scales, int textureCount, int imageCount)
         {
         {
-            if (_program != null)
+            if (stage != ShaderStage.Compute && stage != ShaderStage.Fragment)
             {
             {
-                GL.Uniform1(_program.FragmentIsBgraUniform, 8, _fpIsBgra);
+                return;
             }
             }
-        }
 
 
-        public void UpdateRenderScale(ShaderStage stage, float[] scales, int textureCount, int imageCount)
-        {
-            if (_program != null)
+            bool changed = false;
+
+            for (int index = 0; index < textureCount + imageCount; index++)
             {
             {
-                switch (stage)
+                if (_renderScale[1 + index].X != scales[index])
                 {
                 {
-                    case ShaderStage.Fragment:
-                        if (_program.FragmentRenderScaleUniform != -1)
-                        {
-                            Array.Copy(scales, 0, _fpRenderScale, 1, textureCount + imageCount);
-                            GL.Uniform1(_program.FragmentRenderScaleUniform, 1 + textureCount + imageCount, _fpRenderScale);
-                        }
-                        break;
-
-                    case ShaderStage.Compute:
-                        if (_program.ComputeRenderScaleUniform != -1)
-                        {
-                            Array.Copy(scales, 0, _cpRenderScale, 0, textureCount + imageCount);
-                            GL.Uniform1(_program.ComputeRenderScaleUniform, textureCount + imageCount, _cpRenderScale);
-                        }
-                        break;
+                    _renderScale[1 + index].X = scales[index];
+                    changed = true;
                 }
                 }
             }
             }
+
+            if (changed)
+            {
+                SetSupportBufferData<Vector4<float>>(SupportBuffer.FragmentRenderScaleOffset, _renderScale, 1 + textureCount + imageCount);
+            }
+        }
+
+        private void SetSupportBufferData<T>(int offset, ReadOnlySpan<T> data, int count) where T : unmanaged
+        {
+            if (_supportBuffer == BufferHandle.Null)
+            {
+                _supportBuffer = Buffer.Create(SupportBuffer.RequiredSize);
+                GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, Unsafe.As<BufferHandle, int>(ref _supportBuffer));
+            }
+
+            Buffer.SetData(_supportBuffer, offset, MemoryMarshal.Cast<T, byte>(data.Slice(0, count)));
         }
         }
 
 
         private void PrepareForDispatch()
         private void PrepareForDispatch()
@@ -1249,8 +1253,8 @@ namespace Ryujinx.Graphics.OpenGL
         public void RestoreComponentMask(int index)
         public void RestoreComponentMask(int index)
         {
         {
             // If the bound render target is bgra, swap the red and blue masks.
             // If the bound render target is bgra, swap the red and blue masks.
-            uint redMask = _fpIsBgra[index] == 0 ? 1u : 4u;
-            uint blueMask = _fpIsBgra[index] == 0 ? 4u : 1u;
+            uint redMask = _fpIsBgra[index].X == 0 ? 1u : 4u;
+            uint blueMask = _fpIsBgra[index].X == 0 ? 4u : 1u;
 
 
             GL.ColorMask(
             GL.ColorMask(
                 index,
                 index,
@@ -1322,6 +1326,12 @@ namespace Ryujinx.Graphics.OpenGL
 
 
         public void Dispose()
         public void Dispose()
         {
         {
+            if (_supportBuffer != BufferHandle.Null)
+            {
+                Buffer.Delete(_supportBuffer);
+                _supportBuffer = BufferHandle.Null;
+            }
+
             for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
             for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
             {
             {
                 if (_tfbs[i] != BufferHandle.Null)
                 if (_tfbs[i] != BufferHandle.Null)

+ 0 - 14
Ryujinx.Graphics.OpenGL/Program.cs

@@ -13,10 +13,6 @@ namespace Ryujinx.Graphics.OpenGL
     {
     {
         public int Handle { get; private set; }
         public int Handle { get; private set; }
 
 
-        public int FragmentIsBgraUniform { get; private set; }
-        public int FragmentRenderScaleUniform { get; private set; }
-        public int ComputeRenderScaleUniform { get; private set; }
-
         public bool IsLinked
         public bool IsLinked
         {
         {
             get
             get
@@ -30,7 +26,6 @@ namespace Ryujinx.Graphics.OpenGL
             }
             }
         }
         }
 
 
-        private bool _initialized;
         private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
         private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
         private IShader[] _shaders;
         private IShader[] _shaders;
 
 
@@ -117,15 +112,6 @@ namespace Ryujinx.Graphics.OpenGL
 
 
         public void Bind()
         public void Bind()
         {
         {
-            if (!_initialized)
-            {
-                FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra");
-                FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale");
-                ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale");
-
-                _initialized = true;
-            }
-
             GL.UseProgram(Handle);
             GL.UseProgram(Handle);
         }
         }
 
 

+ 45 - 30
Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs

@@ -1,9 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
-using Ryujinx.Graphics.Shader.IntermediateRepresentation;
 using Ryujinx.Graphics.Shader.StructuredIr;
 using Ryujinx.Graphics.Shader.StructuredIr;
 using Ryujinx.Graphics.Shader.Translation;
 using Ryujinx.Graphics.Shader.Translation;
 using System;
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 
 
 namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
@@ -159,23 +157,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                 context.AppendLine();
                 context.AppendLine();
             }
             }
 
 
-            if (context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute)
+            bool isFragment = context.Config.Stage == ShaderStage.Fragment;
+
+            if (isFragment || context.Config.Stage == ShaderStage.Compute)
             {
             {
-                if (context.Config.Stage == ShaderStage.Fragment)
+                if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce())
+                {
+                    context.AppendLine("layout(early_fragment_tests) in;");
+                    context.AppendLine();
+                }
+
+                if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0)
                 {
                 {
-                    if (context.Config.GpuAccessor.QueryEarlyZForce())
+                    string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
+
+                    int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
+
+                    if (isFragment)
                     {
                     {
-                        context.AppendLine("layout(early_fragment_tests) in;");
-                        context.AppendLine();
+                        scaleElements++; // Also includes render target scale, for gl_FragCoord.
                     }
                     }
 
 
-                    context.AppendLine($"uniform bool {DefaultNames.IsBgraName}[8];");
-                    context.AppendLine();
-                }
+                    DeclareSupportUniformBlock(context, isFragment, scaleElements);
 
 
-                if (DeclareRenderScale(context))
+                    if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling))
+                    {
+                        AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
+                        context.AppendLine();
+                    }
+                }
+                else if (isFragment)
                 {
                 {
-                    context.AppendLine();
+                    DeclareSupportUniformBlock(context, true, 0);
                 }
                 }
             }
             }
 
 
@@ -498,31 +511,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
             }
             }
         }
         }
 
 
-        private static bool DeclareRenderScale(CodeGenContext context)
+        private static void DeclareSupportUniformBlock(CodeGenContext context, bool isFragment, int scaleElements)
         {
         {
-            if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0)
+            if (!isFragment && scaleElements == 0)
             {
             {
-                string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
-
-                int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
-
-                if (context.Config.Stage == ShaderStage.Fragment)
-                {
-                    scaleElements++; // Also includes render target scale, for gl_FragCoord.
-                }
+                return;
+            }
 
 
-                context.AppendLine($"uniform float {stage}_renderScale[{scaleElements}];");
+            context.AppendLine($"layout (binding = 0, std140) uniform {DefaultNames.SupportBlockName}");
+            context.EnterScope();
 
 
-                if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling))
-                {
-                    context.AppendLine();
-                    AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
-                }
+            if (isFragment)
+            {
+                context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
+                context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
+            }
+            else
+            {
+                context.AppendLine($"uint s_reserved[{SupportBuffer.ComputeRenderScaleOffset / SupportBuffer.FieldSize}];");
+            }
 
 
-                return true;
+            if (scaleElements != 0)
+            {
+                context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{scaleElements}];");
             }
             }
 
 
-            return false;
+            context.LeaveScope(";");
+            context.AppendLine();
         }
         }
 
 
         private static void AppendHelperFunction(CodeGenContext context, string filename)
         private static void AppendHelperFunction(CodeGenContext context, string filename)

+ 5 - 2
Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs

@@ -14,6 +14,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
 
 
         public const string DataName = "data";
         public const string DataName = "data";
 
 
+        public const string SupportBlockName = "support_block";
+        public const string SupportBlockAlphaTestName = "s_alpha_test";
+        public const string SupportBlockIsBgraName = "s_is_bgra";
+        public const string SupportBlockRenderScaleName = "s_render_scale";
+
         public const string BlockSuffix = "block";
         public const string BlockSuffix = "block";
 
 
         public const string UniformNamePrefix = "c";
         public const string UniformNamePrefix = "c";
@@ -25,7 +30,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
         public const string ArgumentNamePrefix = "a";
         public const string ArgumentNamePrefix = "a";
 
 
         public const string UndefinedName = "undef";
         public const string UndefinedName = "undef";
-
-        public const string IsBgraName = "is_bgra";
     }
     }
 }
 }

+ 2 - 2
Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_cp.glsl

@@ -1,6 +1,6 @@
 ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
 ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
 {
 {
-    float scale = cp_renderScale[samplerIndex];
+    float scale = s_render_scale[samplerIndex];
     if (scale == 1.0)
     if (scale == 1.0)
     {
     {
         return inputVec;
         return inputVec;
@@ -10,7 +10,7 @@
 
 
 int Helper_TextureSizeUnscale(int size, int samplerIndex)
 int Helper_TextureSizeUnscale(int size, int samplerIndex)
 {
 {
-    float scale = cp_renderScale[samplerIndex];
+    float scale = s_render_scale[samplerIndex];
     if (scale == 1.0)
     if (scale == 1.0)
     {
     {
         return size;
         return size;

+ 2 - 2
Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_fp.glsl

@@ -1,6 +1,6 @@
 ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
 ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
 {
 {
-    float scale = fp_renderScale[1 + samplerIndex];
+    float scale = s_render_scale[1 + samplerIndex];
     if (scale == 1.0)
     if (scale == 1.0)
     {
     {
         return inputVec;
         return inputVec;
@@ -17,7 +17,7 @@
 
 
 int Helper_TextureSizeUnscale(int size, int samplerIndex)
 int Helper_TextureSizeUnscale(int size, int samplerIndex)
 {
 {
-    float scale = abs(fp_renderScale[1 + samplerIndex]);
+    float scale = abs(s_render_scale[1 + samplerIndex]);
     if (scale == 1.0)
     if (scale == 1.0)
     {
     {
         return size;
         return size;

+ 10 - 10
Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs

@@ -68,14 +68,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
             { AttributeConsts.LtMask,              new BuiltInAttribute("unpackUint2x32(gl_SubGroupLtMaskARB).x", VariableType.U32)  },
             { AttributeConsts.LtMask,              new BuiltInAttribute("unpackUint2x32(gl_SubGroupLtMaskARB).x", VariableType.U32)  },
 
 
             // Support uniforms.
             // Support uniforms.
-            { AttributeConsts.FragmentOutputIsBgraBase + 0,  new BuiltInAttribute($"{DefaultNames.IsBgraName}[0]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 4,  new BuiltInAttribute($"{DefaultNames.IsBgraName}[1]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 8,  new BuiltInAttribute($"{DefaultNames.IsBgraName}[2]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.IsBgraName}[3]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.IsBgraName}[4]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.IsBgraName}[5]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.IsBgraName}[6]",  VariableType.Bool) },
-            { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.IsBgraName}[7]",  VariableType.Bool) }
+            { AttributeConsts.FragmentOutputIsBgraBase + 0,  new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 4,  new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 8,  new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]",  VariableType.Bool) },
+            { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]",  VariableType.Bool) }
         };
         };
 
 
         private Dictionary<AstOperand, string> _locals;
         private Dictionary<AstOperand, string> _locals;
@@ -194,8 +194,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
                     {
                     {
                         switch (value & ~3)
                         switch (value & ~3)
                         {
                         {
-                            case AttributeConsts.PositionX: return "(gl_FragCoord.x / fp_renderScale[0])";
-                            case AttributeConsts.PositionY: return "(gl_FragCoord.y / fp_renderScale[0])";
+                            case AttributeConsts.PositionX: return $"(gl_FragCoord.x / {DefaultNames.SupportBlockRenderScaleName}[0])";
+                            case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])";
                             case AttributeConsts.PositionZ: return "gl_FragCoord.z";
                             case AttributeConsts.PositionZ: return "gl_FragCoord.z";
                             case AttributeConsts.PositionW: return "gl_FragCoord.w";
                             case AttributeConsts.PositionW: return "gl_FragCoord.w";
                         }
                         }

+ 18 - 0
Ryujinx.Graphics.Shader/SupportBuffer.cs

@@ -0,0 +1,18 @@
+namespace Ryujinx.Graphics.Shader
+{
+    public static class SupportBuffer
+    {
+        public const int FieldSize = 16; // Each field takes 16 bytes on default layout, even bool.
+
+        public const int FragmentAlphaTestOffset = 0;
+        public const int FragmentIsBgraOffset = FieldSize;
+        public const int FragmentIsBgraCount = 8;
+        public const int FragmentRenderScaleOffset = FragmentIsBgraOffset + FragmentIsBgraCount * FieldSize;
+        public const int ComputeRenderScaleOffset = FragmentRenderScaleOffset + FieldSize; // Skip first scale that is used for the render target
+
+        // One for the render target, 32 for the textures, and 8 for the images.
+        private const int RenderScaleMaxCount = 1 + 32 + 8;
+
+        public const int RequiredSize = FragmentRenderScaleOffset + RenderScaleMaxCount * FieldSize;
+    }
+}

+ 6 - 0
Ryujinx.Graphics.Shader/Translation/TranslationCounts.cs

@@ -7,6 +7,12 @@ namespace Ryujinx.Graphics.Shader.Translation
         public int TexturesCount { get; private set; }
         public int TexturesCount { get; private set; }
         public int ImagesCount { get; private set; }
         public int ImagesCount { get; private set; }
 
 
+        public TranslationCounts()
+        {
+            // The first binding is reserved for the support buffer.
+            UniformBuffersCount = 1;
+        }
+
         internal int IncrementUniformBuffersCount()
         internal int IncrementUniformBuffersCount()
         {
         {
             return UniformBuffersCount++;
             return UniformBuffersCount++;