Optimize RangeList by not doing an allocation on every call to the Find methods

This commit is contained in:
gdk 2019-11-24 21:29:37 -03:00 committed by Thog
parent e0c95b18eb
commit d0c7732fe2
3 changed files with 92 additions and 28 deletions

View file

@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
class TextureManager class TextureManager
{ {
private const int OverlapsBufferInitialCapacity = 10;
private const int OverlapsBufferMaxCapacity = 10000;
private GpuContext _context; private GpuContext _context;
private TextureBindingsManager _cpBindingsManager; private TextureBindingsManager _cpBindingsManager;
@ -24,6 +27,8 @@ namespace Ryujinx.Graphics.Gpu.Image
private RangeList<Texture> _textures; private RangeList<Texture> _textures;
private Texture[] _textureOverlaps;
private AutoDeleteCache _cache; private AutoDeleteCache _cache;
public TextureManager(GpuContext context) public TextureManager(GpuContext context)
@ -41,6 +46,8 @@ namespace Ryujinx.Graphics.Gpu.Image
_textures = new RangeList<Texture>(); _textures = new RangeList<Texture>();
_textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
_cache = new AutoDeleteCache(); _cache = new AutoDeleteCache();
} }
@ -321,10 +328,12 @@ namespace Ryujinx.Graphics.Gpu.Image
bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0; bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0;
// Try to find a perfect texture match, with the same address and parameters. // Try to find a perfect texture match, with the same address and parameters.
Texture[] sameAddressOverlaps = _textures.FindOverlaps(info.Address); int sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);
foreach (Texture overlap in sameAddressOverlaps) for (int index = 0; index < sameAddressOverlapsCount; index++)
{ {
Texture overlap = _textureOverlaps[index];
if (overlap.IsPerfectMatch(info, flags)) if (overlap.IsPerfectMatch(info, flags))
{ {
if (!isSamplerTexture) if (!isSamplerTexture)
@ -376,12 +385,14 @@ namespace Ryujinx.Graphics.Gpu.Image
// Find view compatible matches. // Find view compatible matches.
ulong size = (ulong)sizeInfo.TotalSize; ulong size = (ulong)sizeInfo.TotalSize;
Texture[] overlaps = _textures.FindOverlaps(info.Address, size); int overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);
Texture texture = null; Texture texture = null;
foreach (Texture overlap in overlaps) for (int index = 0; index < overlapsCount; index++)
{ {
Texture overlap = _textureOverlaps[index];
if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel)) if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
{ {
if (!isSamplerTexture) if (!isSamplerTexture)
@ -412,8 +423,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// otherwise the copied data would be overwritten by a future synchronization. // otherwise the copied data would be overwritten by a future synchronization.
texture.SynchronizeMemory(); texture.SynchronizeMemory();
foreach (Texture overlap in overlaps) for (int index = 0; index < overlapsCount; index++)
{ {
Texture overlap = _textureOverlaps[index];
if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel)) if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
{ {
TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel); TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);
@ -432,8 +445,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// of the 3D texture to the newly created 3D texture. // of the 3D texture to the newly created 3D texture.
if (info.Target == Target.Texture3D) if (info.Target == Target.Texture3D)
{ {
foreach (Texture overlap in overlaps) for (int index = 0; index < overlapsCount; index++)
{ {
Texture overlap = _textureOverlaps[index];
if (texture.IsViewCompatible( if (texture.IsViewCompatible(
overlap.Info, overlap.Info,
overlap.Size, overlap.Size,
@ -456,9 +471,19 @@ namespace Ryujinx.Graphics.Gpu.Image
_textures.Add(texture); _textures.Add(texture);
ShrinkOverlapsBufferIfNeeded();
return texture; return texture;
} }
private void ShrinkOverlapsBufferIfNeeded()
{
if (_textureOverlaps.Length > OverlapsBufferMaxCapacity)
{
Array.Resize(ref _textureOverlaps, OverlapsBufferMaxCapacity);
}
}
private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel) private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
{ {
// When the texture is used as view of another texture, we must // When the texture is used as view of another texture, we must

View file

@ -8,6 +8,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
class BufferManager class BufferManager
{ {
private const int OverlapsBufferInitialCapacity = 10;
private const int OverlapsBufferMaxCapacity = 10000;
private const ulong BufferAlignmentSize = 0x1000; private const ulong BufferAlignmentSize = 0x1000;
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
@ -15,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private RangeList<Buffer> _buffers; private RangeList<Buffer> _buffers;
private Buffer[] _bufferOverlaps;
private IndexBuffer _indexBuffer; private IndexBuffer _indexBuffer;
private VertexBuffer[] _vertexBuffers; private VertexBuffer[] _vertexBuffers;
@ -57,6 +62,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_buffers = new RangeList<Buffer>(); _buffers = new RangeList<Buffer>();
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers]; _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
_cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers); _cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
@ -207,9 +214,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
private void CreateBuffer(ulong address, ulong size) private void CreateBuffer(ulong address, ulong size)
{ {
Buffer[] overlaps = _buffers.FindOverlapsNonOverlapping(address, size); int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
if (overlaps.Length != 0) if (overlapsCount != 0)
{ {
// The buffer already exists. We can just return the existing buffer // The buffer already exists. We can just return the existing buffer
// if the buffer we need is fully contained inside the overlapping buffer. // if the buffer we need is fully contained inside the overlapping buffer.
@ -218,10 +225,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
// old buffer(s) to the new buffer. // old buffer(s) to the new buffer.
ulong endAddress = address + size; ulong endAddress = address + size;
if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress) if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
{ {
foreach (Buffer buffer in overlaps) for (int index = 0; index < overlapsCount; index++)
{ {
Buffer buffer = _bufferOverlaps[index];
address = Math.Min(address, buffer.Address); address = Math.Min(address, buffer.Address);
endAddress = Math.Max(endAddress, buffer.EndAddress); endAddress = Math.Max(endAddress, buffer.EndAddress);
@ -234,8 +243,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
_buffers.Add(newBuffer); _buffers.Add(newBuffer);
foreach (Buffer buffer in overlaps) for (int index = 0; index < overlapsCount; index++)
{ {
Buffer buffer = _bufferOverlaps[index];
int dstOffset = (int)(buffer.Address - newBuffer.Address); int dstOffset = (int)(buffer.Address - newBuffer.Address);
buffer.CopyTo(newBuffer, dstOffset); buffer.CopyTo(newBuffer, dstOffset);
@ -253,6 +264,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
_buffers.Add(buffer); _buffers.Add(buffer);
} }
ShrinkOverlapsBufferIfNeeded();
}
private void ShrinkOverlapsBufferIfNeeded()
{
if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity)
{
Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity);
}
} }
public ulong GetComputeUniformBufferAddress(int index) public ulong GetComputeUniformBufferAddress(int index)

View file

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
{ {
class RangeList<T> where T : IRange<T> class RangeList<T> where T : IRange<T>
{ {
private const int ArrayGrowthSize = 32;
private List<T> _items; private List<T> _items;
public RangeList() public RangeList()
@ -72,14 +75,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
return _items[index]; return _items[index];
} }
public T[] FindOverlaps(T item) public int FindOverlaps(T item, ref T[] output)
{ {
return FindOverlaps(item.Address, item.Size); return FindOverlaps(item.Address, item.Size, ref output);
} }
public T[] FindOverlaps(ulong address, ulong size) public int FindOverlaps(ulong address, ulong size, ref T[] output)
{ {
List<T> overlapsList = new List<T>(); int outputIndex = 0;
ulong endAddress = address + size; ulong endAddress = address + size;
@ -94,24 +97,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (item.OverlapsWith(address, size)) if (item.OverlapsWith(address, size))
{ {
overlapsList.Add(item); if (outputIndex == output.Length)
}
}
}
return overlapsList.ToArray();
}
public T[] FindOverlapsNonOverlapping(T item)
{ {
return FindOverlapsNonOverlapping(item.Address, item.Size); Array.Resize(ref output, outputIndex + ArrayGrowthSize);
} }
public T[] FindOverlapsNonOverlapping(ulong address, ulong size) output[outputIndex++] = item;
}
}
}
return outputIndex;
}
public int FindOverlapsNonOverlapping(T item, ref T[] output)
{
return FindOverlapsNonOverlapping(item.Address, item.Size, ref output);
}
public int FindOverlapsNonOverlapping(ulong address, ulong size, ref T[] output)
{ {
// This is a bit faster than FindOverlaps, but only works // This is a bit faster than FindOverlaps, but only works
// when none of the items on the list overlaps with each other. // when none of the items on the list overlaps with each other.
List<T> overlapsList = new List<T>(); int outputIndex = 0;
ulong endAddress = address + size; ulong endAddress = address + size;
@ -126,20 +134,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
do do
{ {
overlapsList.Add(_items[index++]); if (outputIndex == output.Length)
{
Array.Resize(ref output, outputIndex + ArrayGrowthSize);
}
output[outputIndex++] = _items[index++];
} }
while (index < _items.Count && _items[index].OverlapsWith(address, size)); while (index < _items.Count && _items[index].OverlapsWith(address, size));
} }
return overlapsList.ToArray(); return outputIndex;
} }
public T[] FindOverlaps(ulong address) public int FindOverlaps(ulong address, ref T[] output)
{ {
List<T> overlapsList = new List<T>();
int index = BinarySearch(address); int index = BinarySearch(address);
int outputIndex = 0;
if (index >= 0) if (index >= 0)
{ {
while (index > 0 && _items[index - 1].Address == address) while (index > 0 && _items[index - 1].Address == address)
@ -156,11 +169,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
break; break;
} }
overlapsList.Add(overlap); if (outputIndex == output.Length)
{
Array.Resize(ref output, outputIndex + ArrayGrowthSize);
}
output[outputIndex++] = overlap;
} }
} }
return overlapsList.ToArray(); return outputIndex;
} }
private int BinarySearch(ulong address) private int BinarySearch(ulong address)