From 1df6c07f78c4c3b8c7fc679d7466f79a10c2d496 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Mon, 4 Dec 2023 16:30:19 -0300
Subject: Implement support for multi-range buffers using Vulkan sparse
 mappings (#5427)

* Pass MultiRange to BufferManager

* Implement support for multi-range buffers using Vulkan sparse mappings

* Use multi-range for remaining buffers, delete old methods

* Assume that more buffers are contiguous

* Dispose multi-range buffers after they are removed from the list

* Properly init BufferBounds for constant and storage buffers

* Do not try reading zero bytes data from an unmapped address on the shader cache + PR feedback

* Fix misaligned sparse buffer offsets

* Null check can be simplified

* PR feedback
---
 src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 136 +++++++++++++----------
 1 file changed, 76 insertions(+), 60 deletions(-)

(limited to 'src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs')

diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 01f59477..c65602b5 100644
--- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Image;
 using Ryujinx.Graphics.Gpu.Shader;
 using Ryujinx.Graphics.Shader;
+using Ryujinx.Memory.Range;
 using System;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
@@ -62,18 +63,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 Bindings = new BufferDescriptor[count];
                 Buffers = new BufferBounds[count];
                 Unaligned = new bool[count];
+
+                Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
             }
 
             /// <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>
+            /// <param name="range">Physical memory regions where the buffer is mapped</param>
             /// <param name="flags">Buffer usage flags</param>
-            public void SetBounds(int index, ulong address, ulong size, BufferUsageFlags flags = BufferUsageFlags.None)
+            public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
             {
-                Buffers[index] = new BufferBounds(address, size, flags);
+                Buffers[index] = new BufferBounds(range, flags);
             }
 
             /// <summary>
@@ -120,6 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
             _context = context;
             _channel = channel;
 
+            _indexBuffer.Range = new MultiRange(MemoryManager.PteUnmapped, 0UL);
             _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
 
             _transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
@@ -150,10 +153,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="type">Type of each index buffer element</param>
         public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
         {
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
 
-            _indexBuffer.Address = address;
-            _indexBuffer.Size = size;
+            _indexBuffer.Range = range;
             _indexBuffer.Type = type;
 
             _indexBufferDirty = true;
@@ -181,16 +183,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
         public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
         {
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
 
-            _vertexBuffers[index].Address = address;
-            _vertexBuffers[index].Size = size;
+            _vertexBuffers[index].Range = range;
             _vertexBuffers[index].Stride = stride;
             _vertexBuffers[index].Divisor = divisor;
 
             _vertexBuffersDirty = true;
 
-            if (address != 0)
+            if (!range.IsUnmapped)
             {
                 _vertexBuffersEnableMask |= 1u << index;
             }
@@ -209,9 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="size">Size in bytes of the transform feedback buffer</param>
         public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
         {
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size);
 
-            _transformFeedbackBuffers[index] = new BufferBounds(address, size);
+            _transformFeedbackBuffers[index] = new BufferBounds(range);
             _transformFeedbackBuffersDirty = true;
         }
 
@@ -256,9 +257,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
 
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size);
 
-            _cpStorageBuffers.SetBounds(index, address, size, flags);
+            _cpStorageBuffers.SetBounds(index, range, flags);
         }
 
         /// <summary>
@@ -280,15 +281,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
 
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size);
 
-            if (buffers.Buffers[index].Address != address ||
-                buffers.Buffers[index].Size != size)
+            if (!buffers.Buffers[index].Range.Equals(range))
             {
                 _gpStorageBuffersDirty = true;
             }
 
-            buffers.SetBounds(index, address, size, flags);
+            buffers.SetBounds(index, range, flags);
         }
 
         /// <summary>
@@ -300,9 +300,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="size">Size in bytes of the storage buffer</param>
         public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
         {
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
 
-            _cpUniformBuffers.SetBounds(index, address, size);
+            _cpUniformBuffers.SetBounds(index, range);
         }
 
         /// <summary>
@@ -315,9 +315,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="size">Size in bytes of the storage buffer</param>
         public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
         {
-            ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
+            MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
 
-            _gpUniformBuffers[stage].SetBounds(index, address, size);
+            _gpUniformBuffers[stage].SetBounds(index, range);
             _gpUniformBuffersDirty = true;
         }
 
@@ -379,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++)
             {
-                if (_cpUniformBuffers.Buffers[i].Address != 0)
+                if (!_cpUniformBuffers.Buffers[i].IsUnmapped)
                 {
                     mask |= 1u << i;
                 }
@@ -399,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++)
             {
-                if (_gpUniformBuffers[stage].Buffers[i].Address != 0)
+                if (!_gpUniformBuffers[stage].Buffers[i].IsUnmapped)
                 {
                     mask |= 1u << i;
                 }
@@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
         public ulong GetComputeUniformBufferAddress(int index)
         {
-            return _cpUniformBuffers.Buffers[index].Address;
+            return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
         }
 
         /// <summary>
@@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
         public ulong GetGraphicsUniformBufferAddress(int stage, int index)
         {
-            return _gpUniformBuffers[stage].Buffers[index].Address;
+            return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
         }
 
         /// <summary>
@@ -477,7 +477,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 foreach (var binding in _bufferTextures)
                 {
                     var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
-                    var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
+                    var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Range, isStore);
                     binding.Texture.SetStorage(range);
 
                     // The texture must be rebound to use the new storage if it was updated.
@@ -511,16 +511,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 {
                     _indexBufferDirty = false;
 
-                    if (_indexBuffer.Address != 0)
+                    if (!_indexBuffer.Range.IsUnmapped)
                     {
-                        BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
+                        BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range);
 
                         _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
                     }
                 }
-                else if (_indexBuffer.Address != 0)
+                else if (!_indexBuffer.Range.IsUnmapped)
                 {
-                    bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
+                    bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
                 }
             }
             else if (_rebind)
@@ -540,12 +540,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 {
                     VertexBuffer vb = _vertexBuffers[index];
 
-                    if (vb.Address == 0)
+                    if (vb.Range.IsUnmapped)
                     {
                         continue;
                     }
 
-                    BufferRange buffer = bufferCache.GetBufferRange(vb.Address, vb.Size);
+                    BufferRange buffer = bufferCache.GetBufferRange(vb.Range);
 
                     vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
                 }
@@ -558,12 +558,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 {
                     VertexBuffer vb = _vertexBuffers[index];
 
-                    if (vb.Address == 0)
+                    if (vb.Range.IsUnmapped)
                     {
                         continue;
                     }
 
-                    bufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
+                    bufferCache.SynchronizeBufferRange(vb.Range);
                 }
             }
 
@@ -579,13 +579,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
                     {
                         BufferBounds tfb = _transformFeedbackBuffers[index];
 
-                        if (tfb.Address == 0)
+                        if (tfb.IsUnmapped)
                         {
                             tfbs[index] = BufferRange.Empty;
                             continue;
                         }
 
-                        tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size, write: true);
+                        tfbs[index] = bufferCache.GetBufferRange(tfb.Range, write: true);
                     }
 
                     _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -600,21 +600,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
                     {
                         BufferBounds tfb = _transformFeedbackBuffers[index];
 
-                        if (tfb.Address == 0)
+                        if (tfb.IsUnmapped)
                         {
                             buffers[index] = new BufferAssignment(index, BufferRange.Empty);
                         }
                         else
                         {
-                            ulong endAddress = tfb.Address + tfb.Size;
-                            ulong address = BitUtils.AlignDown(tfb.Address, (ulong)alignment);
-                            ulong size = endAddress - address;
+                            MultiRange range = tfb.Range;
+                            ulong address0 = range.GetSubRange(0).Address;
+                            ulong address = BitUtils.AlignDown(address0, (ulong)alignment);
+
+                            if (range.Count == 1)
+                            {
+                                range = new MultiRange(address, range.GetSubRange(0).Size + (address0 - address));
+                            }
+                            else
+                            {
+                                MemoryRange[] subRanges = new MemoryRange[range.Count];
+
+                                subRanges[0] = new MemoryRange(address, range.GetSubRange(0).Size + (address0 - address));
+
+                                for (int i = 1; i < range.Count; i++)
+                                {
+                                    subRanges[i] = range.GetSubRange(i);
+                                }
+
+                                range = new MultiRange(subRanges);
+                            }
 
-                            int tfeOffset = ((int)tfb.Address & (alignment - 1)) / 4;
+                            int tfeOffset = ((int)address0 & (alignment - 1)) / 4;
 
                             _context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
 
-                            buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(address, size, write: true));
+                            buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, write: true));
                         }
                     }
 
@@ -627,12 +645,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
                 {
                     BufferBounds tfb = _transformFeedbackBuffers[index];
 
-                    if (tfb.Address == 0)
+                    if (tfb.IsUnmapped)
                     {
                         continue;
                     }
 
-                    bufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
+                    bufferCache.SynchronizeBufferRange(tfb.Range);
                 }
             }
 
@@ -688,12 +706,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
                     BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
 
-                    if (bounds.Address != 0)
+                    if (!bounds.IsUnmapped)
                     {
                         var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
                         var range = isStorage
-                            ? bufferCache.GetBufferRangeAligned(bounds.Address, bounds.Size, isWrite)
-                            : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
+                            ? bufferCache.GetBufferRangeAligned(bounds.Range, isWrite)
+                            : bufferCache.GetBufferRange(bounds.Range);
 
                         ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
                     }
@@ -725,12 +743,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
                 BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
 
-                if (bounds.Address != 0)
+                if (!bounds.IsUnmapped)
                 {
                     var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
                     var range = isStorage
-                        ? bufferCache.GetBufferRangeAligned(bounds.Address, bounds.Size, isWrite)
-                        : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
+                        ? bufferCache.GetBufferRangeAligned(bounds.Range, isWrite)
+                        : bufferCache.GetBufferRange(bounds.Range);
 
                     ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
                 }
@@ -778,12 +796,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
                     BufferBounds bounds = buffers.Buffers[binding.Slot];
 
-                    if (bounds.Address == 0)
+                    if (bounds.IsUnmapped)
                     {
                         continue;
                     }
 
-                    _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
+                    _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range);
                 }
             }
         }
@@ -793,23 +811,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// </summary>
         /// <param name="stage">Shader stage accessing the texture</param>
         /// <param name="texture">Buffer texture</param>
-        /// <param name="address">Address of the buffer in memory</param>
-        /// <param name="size">Size of the buffer in bytes</param>
+        /// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
         /// <param name="bindingInfo">Binding info for the buffer texture</param>
         /// <param name="format">Format of the buffer texture</param>
         /// <param name="isImage">Whether the binding is for an image or a sampler</param>
         public void SetBufferTextureStorage(
             ShaderStage stage,
             ITexture texture,
-            ulong address,
-            ulong size,
+            MultiRange range,
             TextureBindingInfo bindingInfo,
             Format format,
             bool isImage)
         {
-            _channel.MemoryManager.Physical.BufferCache.CreateBuffer(address, size);
+            _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range);
 
-            _bufferTextures.Add(new BufferTextureBinding(stage, texture, address, size, bindingInfo, format, isImage));
+            _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage));
         }
 
         /// <summary>
-- 
cgit v1.2.3-70-g09d2