Restride vertex buffer when stride causes attributes to misalign in Vulkan. (#3679)

* Vertex Buffer Alignment part 1

* Update CacheByRange

* Add Stride Change compute shader, fix storage buffers in helpers

* An AMD exclusive

* Reword

* Change rules - stride conversion when attrs misalign

* Fix stupid mistake

* Fix background pipeline compile

* Improve a few things.

* Fix some feedback

* Address Feedback

(the shader binary didn't change when i changed the source to use the subgroup size)

* Fix bug where rewritten buffer would be disposed instantly.
This commit is contained in:
riperiperi 2022-09-09 00:30:19 +01:00 committed by GitHub
parent ee1825219b
commit c6d82209ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1069 additions and 120 deletions

View file

@ -3,29 +3,110 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan
{
struct CacheByRange<T> where T : IDisposable
interface ICacheKey : IDisposable
{
private Dictionary<ulong, T> _ranges;
bool KeyEqual(ICacheKey other);
}
public void Add(int offset, int size, T value)
struct I8ToI16CacheKey : ICacheKey
{
public I8ToI16CacheKey() { }
public bool KeyEqual(ICacheKey other)
{
EnsureInitialized();
_ranges.Add(PackRange(offset, size), value);
return other is I8ToI16CacheKey;
}
public bool TryGetValue(int offset, int size, out T value)
public void Dispose() { }
}
struct AlignedVertexBufferCacheKey : ICacheKey
{
private readonly int _stride;
private readonly int _alignment;
// Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public AlignedVertexBufferCacheKey(VulkanRenderer gd, int stride, int alignment)
{
EnsureInitialized();
return _ranges.TryGetValue(PackRange(offset, size), out value);
_gd = gd;
_stride = stride;
_alignment = alignment;
_buffer = null;
}
public bool KeyEqual(ICacheKey other)
{
return other is AlignedVertexBufferCacheKey entry &&
entry._stride == _stride &&
entry._alignment == _alignment;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
_buffer = buffer;
}
public void Dispose()
{
_gd.PipelineInternal.DirtyVertexBuffer(_buffer);
}
}
struct CacheByRange<T> where T : IDisposable
{
private struct Entry
{
public ICacheKey Key;
public T Value;
public Entry(ICacheKey key, T value)
{
Key = key;
Value = value;
}
}
private Dictionary<ulong, List<Entry>> _ranges;
public void Add(int offset, int size, ICacheKey key, T value)
{
List<Entry> entries = GetEntries(offset, size);
entries.Add(new Entry(key, value));
}
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
{
List<Entry> entries = GetEntries(offset, size);
foreach (Entry entry in entries)
{
if (entry.Key.KeyEqual(key))
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
public void Clear()
{
if (_ranges != null)
{
foreach (T value in _ranges.Values)
foreach (List<Entry> entries in _ranges.Values)
{
value.Dispose();
foreach (Entry entry in entries)
{
entry.Key.Dispose();
entry.Value.Dispose();
}
}
_ranges.Clear();
@ -33,12 +114,23 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void EnsureInitialized()
private List<Entry> GetEntries(int offset, int size)
{
if (_ranges == null)
{
_ranges = new Dictionary<ulong, T>();
_ranges = new Dictionary<ulong, List<Entry>>();
}
ulong key = PackRange(offset, size);
List<Entry> value;
if (!_ranges.TryGetValue(key, out value))
{
value = new List<Entry>();
_ranges.Add(key, value);
}
return value;
}
private static ulong PackRange(int offset, int size)