Vulkan: Migrate buffers between memory types to improve GPU performance (#4540)
* Initial implementation of migration between memory heaps - Missing OOM handling - Missing `_map` data safety when remapping - Copy may not have completed yet (needs some kind of fence) - Map may be unmapped before it is done being used. (needs scoped access) - SSBO accesses are all "writes" - maybe pass info in another way. - Missing keeping map type when resizing buffers (should this be done?) * Ensure migrated data is in place before flushing. * Fix issue where old waitable would be signalled. - There is a real issue where existing Auto<> references need to be replaced. * Swap bound Auto<> instances when swapping buffer backing * Fix conversion buffers * Don't try move buffers if the host has shared memory. * Make GPU methods return PinnedSpan with scope * Storage Hint * Fix stupidity * Fix rebase * Tweak rules Attempt to sidestep BOTW slowdown * Remove line * Migrate only when command buffers flush * Change backing swap log to debug * Address some feedback * Disallow backing swap when the flush lock is held by the current thread * Make PinnedSpan from ReadOnlySpan explicitly unsafe * Fix some small issues - Index buffer swap fixed - Allocate DeviceLocal buffers using a separate block list to images. * Remove alternative flags * Address feedback
This commit is contained in:
parent
67b4e63cff
commit
9f1cf6458c
35 changed files with 660 additions and 167 deletions
|
@ -15,7 +15,12 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
void BackgroundContextAction(Action action, bool alwaysBackground = false);
|
void BackgroundContextAction(Action action, bool alwaysBackground = false);
|
||||||
|
|
||||||
BufferHandle CreateBuffer(int size);
|
BufferHandle CreateBuffer(int size, BufferHandle storageHint);
|
||||||
|
|
||||||
|
BufferHandle CreateBuffer(int size)
|
||||||
|
{
|
||||||
|
return CreateBuffer(size, BufferHandle.Null);
|
||||||
|
}
|
||||||
|
|
||||||
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);
|
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);
|
||||||
|
|
||||||
|
@ -26,7 +31,7 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
void DeleteBuffer(BufferHandle buffer);
|
void DeleteBuffer(BufferHandle buffer);
|
||||||
|
|
||||||
ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
||||||
|
|
||||||
Capabilities GetCapabilities();
|
Capabilities GetCapabilities();
|
||||||
ulong GetCurrentSync();
|
ulong GetCurrentSync();
|
||||||
|
|
|
@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);
|
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);
|
||||||
|
|
||||||
ReadOnlySpan<byte> GetData();
|
PinnedSpan<byte> GetData();
|
||||||
ReadOnlySpan<byte> GetData(int layer, int level);
|
PinnedSpan<byte> GetData(int layer, int level);
|
||||||
|
|
||||||
void SetData(SpanOrArray<byte> data);
|
void SetData(SpanOrArray<byte> data);
|
||||||
void SetData(SpanOrArray<byte> data, int layer, int level);
|
void SetData(SpanOrArray<byte> data, int layer, int level);
|
||||||
|
|
|
@ -21,9 +21,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer
|
||||||
|
|
||||||
public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref BufferGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size);
|
PinnedSpan<byte> result = renderer.GetBufferData(threaded.Buffers.MapBuffer(command._buffer), command._offset, command._size);
|
||||||
|
|
||||||
command._result.Get(threaded).Result = new PinnedSpan<byte>(result);
|
command._result.Get(threaded).Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,25 @@
|
||||||
public CommandType CommandType => CommandType.CreateBuffer;
|
public CommandType CommandType => CommandType.CreateBuffer;
|
||||||
private BufferHandle _threadedHandle;
|
private BufferHandle _threadedHandle;
|
||||||
private int _size;
|
private int _size;
|
||||||
|
private BufferHandle _storageHint;
|
||||||
|
|
||||||
public void Set(BufferHandle threadedHandle, int size)
|
public void Set(BufferHandle threadedHandle, int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
_threadedHandle = threadedHandle;
|
_threadedHandle = threadedHandle;
|
||||||
_size = size;
|
_size = size;
|
||||||
|
_storageHint = storageHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref CreateBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size));
|
BufferHandle hint = BufferHandle.Null;
|
||||||
|
|
||||||
|
if (command._storageHint != BufferHandle.Null)
|
||||||
|
{
|
||||||
|
hint = threaded.Buffers.MapBuffer(command._storageHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, hint));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
|
|
||||||
public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref TextureGetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData();
|
PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData();
|
||||||
|
|
||||||
command._result.Get(threaded).Result = new PinnedSpan<byte>(result);
|
command._result.Get(threaded).Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
|
|
||||||
public static void Run(ref TextureGetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref TextureGetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level);
|
PinnedSpan<byte> result = command._texture.Get(threaded).Base.GetData(command._layer, command._level);
|
||||||
|
|
||||||
command._result.Get(threaded).Result = new PinnedSpan<byte>(result);
|
command._result.Get(threaded).Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Model
|
|
||||||
{
|
|
||||||
unsafe struct PinnedSpan<T> where T : unmanaged
|
|
||||||
{
|
|
||||||
private void* _ptr;
|
|
||||||
private int _size;
|
|
||||||
|
|
||||||
public PinnedSpan(ReadOnlySpan<T> span)
|
|
||||||
{
|
|
||||||
_ptr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
|
|
||||||
_size = span.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<T> Get()
|
|
||||||
{
|
|
||||||
return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
return newTex;
|
return newTex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
if (_renderer.IsGpuThread())
|
if (_renderer.IsGpuThread())
|
||||||
{
|
{
|
||||||
|
@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
_renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box));
|
_renderer.New<TextureGetDataCommand>().Set(Ref(this), Ref(box));
|
||||||
_renderer.InvokeCommand();
|
_renderer.InvokeCommand();
|
||||||
|
|
||||||
return box.Result.Get();
|
return box.Result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
if (_renderer.IsGpuThread())
|
if (_renderer.IsGpuThread())
|
||||||
{
|
{
|
||||||
|
@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
_renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level);
|
_renderer.New<TextureGetDataSliceCommand>().Set(Ref(this), Ref(box), layer, level);
|
||||||
_renderer.InvokeCommand();
|
_renderer.InvokeCommand();
|
||||||
|
|
||||||
return box.Result.Get();
|
return box.Result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -265,10 +265,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
BufferHandle handle = Buffers.CreateBufferHandle();
|
BufferHandle handle = Buffers.CreateBufferHandle();
|
||||||
New<CreateBufferCommand>().Set(handle, size);
|
New<CreateBufferCommand>().Set(handle, size, storageHint);
|
||||||
QueueCommand();
|
QueueCommand();
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
|
@ -329,7 +329,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
QueueCommand();
|
QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
if (IsGpuThread())
|
if (IsGpuThread())
|
||||||
{
|
{
|
||||||
|
@ -337,7 +337,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box));
|
New<BufferGetDataCommand>().Set(buffer, offset, size, Ref(box));
|
||||||
InvokeCommand();
|
InvokeCommand();
|
||||||
|
|
||||||
return box.Result.Get();
|
return box.Result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
53
Ryujinx.Graphics.GAL/PinnedSpan.cs
Normal file
53
Ryujinx.Graphics.GAL/PinnedSpan.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public unsafe struct PinnedSpan<T> : IDisposable where T : unmanaged
|
||||||
|
{
|
||||||
|
private void* _ptr;
|
||||||
|
private int _size;
|
||||||
|
private Action _disposeAction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new PinnedSpan from an existing ReadOnlySpan. The span *must* be pinned in memory.
|
||||||
|
/// The data must be guaranteed to live until disposeAction is called.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">Existing span</param>
|
||||||
|
/// <param name="disposeAction">Action to call on dispose</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a dispose action is not provided, it is safe to assume the resource will be available until the next call.
|
||||||
|
/// </remarks>
|
||||||
|
public static PinnedSpan<T> UnsafeFromSpan(ReadOnlySpan<T> span, Action disposeAction = null)
|
||||||
|
{
|
||||||
|
return new PinnedSpan<T>(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)), span.Length, disposeAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new PinnedSpan from an existing unsafe region. The data must be guaranteed to live until disposeAction is called.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">Pointer to the region</param>
|
||||||
|
/// <param name="size">The total items of T the region contains</param>
|
||||||
|
/// <param name="disposeAction">Action to call on dispose</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a dispose action is not provided, it is safe to assume the resource will be available until the next call.
|
||||||
|
/// </remarks>
|
||||||
|
public PinnedSpan(void* ptr, int size, Action disposeAction = null)
|
||||||
|
{
|
||||||
|
_ptr = ptr;
|
||||||
|
_size = size;
|
||||||
|
_disposeAction = disposeAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<T> Get()
|
||||||
|
{
|
||||||
|
return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_disposeAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1022,13 +1022,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// This method should be used to retrieve data that was modified by the host GPU.
|
/// This method should be used to retrieve data that was modified by the host GPU.
|
||||||
/// This is not cheap, avoid doing that unless strictly needed.
|
/// This is not cheap, avoid doing that unless strictly needed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="output">An output span to place the texture data into. If empty, one is generated</param>
|
/// <param name="output">An output span to place the texture data into</param>
|
||||||
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
||||||
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
||||||
/// <returns>The span containing the texture data</returns>
|
private void GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
|
||||||
private ReadOnlySpan<byte> GetTextureDataFromGpu(Span<byte> output, bool blacklist, ITexture texture = null)
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data;
|
PinnedSpan<byte> data;
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
|
@ -1054,9 +1053,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = ConvertFromHostCompatibleFormat(output, data);
|
ConvertFromHostCompatibleFormat(output, data.Get());
|
||||||
|
|
||||||
return data;
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1071,10 +1070,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="level">The level of the texture to flush</param>
|
/// <param name="level">The level of the texture to flush</param>
|
||||||
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
/// <param name="blacklist">True if the texture should be blacklisted, false otherwise</param>
|
||||||
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
/// <param name="texture">The specific host texture to flush. Defaults to this texture</param>
|
||||||
/// <returns>The span containing the texture data</returns>
|
public void GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
|
||||||
public ReadOnlySpan<byte> GetTextureDataSliceFromGpu(Span<byte> output, int layer, int level, bool blacklist, ITexture texture = null)
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data;
|
PinnedSpan<byte> data;
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
|
@ -1100,9 +1098,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = ConvertFromHostCompatibleFormat(output, data, level, true);
|
ConvertFromHostCompatibleFormat(output, data.Get(), level, true);
|
||||||
|
|
||||||
return data;
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
||||||
Handle = context.Renderer.CreateBuffer((int)size);
|
Handle = context.Renderer.CreateBuffer((int)size, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null);
|
||||||
|
|
||||||
_useGranular = size > GranularBufferThreshold;
|
_useGranular = size > GranularBufferThreshold;
|
||||||
|
|
||||||
|
@ -415,10 +415,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
{
|
{
|
||||||
int offset = (int)(address - Address);
|
int offset = (int)(address - Address);
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
||||||
|
|
||||||
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
||||||
_physicalMemory.WriteUntracked(address, data);
|
_physicalMemory.WriteUntracked(address, data.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -55,11 +55,14 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
(IntPtr)size);
|
(IntPtr)size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe ReadOnlySpan<byte> GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size)
|
public static unsafe PinnedSpan<byte> GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
|
// Data in the persistent buffer and host array is guaranteed to be available
|
||||||
|
// until the next time the host thread requests data.
|
||||||
|
|
||||||
if (HwCapabilities.UsePersistentBufferForFlush)
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
||||||
{
|
{
|
||||||
return renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size);
|
return PinnedSpan<byte>.UnsafeFromSpan(renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -69,7 +72,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, target);
|
GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, target);
|
||||||
|
|
||||||
return new ReadOnlySpan<byte>(target.ToPointer(), size);
|
return new PinnedSpan<byte>(target.ToPointer(), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,12 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
return Buffer.GetData(_renderer, _buffer, _bufferOffset, _bufferSize);
|
return Buffer.GetData(_renderer, _buffer, _bufferOffset, _bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
return GetData();
|
return GetData();
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
_renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData()
|
public unsafe PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
int levels = Info.GetLevelsClamped();
|
int levels = Info.GetLevelsClamped();
|
||||||
|
@ -196,16 +196,16 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
data = FormatConverter.ConvertD24S8ToS8D24(data);
|
data = FormatConverter.ConvertD24S8ToS8D24(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return PinnedSpan<byte>.UnsafeFromSpan(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData(int layer, int level)
|
public unsafe PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
int size = Info.GetMipSize(level);
|
int size = Info.GetMipSize(level);
|
||||||
|
|
||||||
if (HwCapabilities.UsePersistentBufferForFlush)
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
||||||
{
|
{
|
||||||
return _renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level);
|
return PinnedSpan<byte>.UnsafeFromSpan(_renderer.PersistentBuffers.Default.GetTextureData(this, size, layer, level));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -213,7 +213,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
|
||||||
int offset = WriteTo2D(target, layer, level);
|
int offset = WriteTo2D(target, layer, level);
|
||||||
|
|
||||||
return new ReadOnlySpan<byte>(target.ToPointer(), size).Slice(offset);
|
return new PinnedSpan<byte>((byte*)target.ToPointer() + offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
ResourcePool = new ResourcePool();
|
ResourcePool = new ResourcePool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
BufferCount++;
|
BufferCount++;
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
return new HardwareInfo(GpuVendor, GpuRenderer);
|
return new HardwareInfo(GpuVendor, GpuRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
return Buffer.GetData(this, buffer, offset, size);
|
return Buffer.GetData(this, buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
|
12
Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
Normal file
12
Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
internal enum BufferAllocationType
|
||||||
|
{
|
||||||
|
Auto = 0,
|
||||||
|
|
||||||
|
HostMappedNoCache,
|
||||||
|
HostMapped,
|
||||||
|
DeviceLocal,
|
||||||
|
DeviceLocalMapped
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
|
||||||
|
@ -11,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
private const int MaxUpdateBufferSize = 0x10000;
|
private const int MaxUpdateBufferSize = 0x10000;
|
||||||
|
|
||||||
|
private const int SetCountThreshold = 100;
|
||||||
|
private const int WriteCountThreshold = 50;
|
||||||
|
private const int FlushCountThreshold = 5;
|
||||||
|
|
||||||
|
public const int DeviceLocalSizeThreshold = 256 * 1024; // 256kb
|
||||||
|
|
||||||
public const AccessFlags DefaultAccessFlags =
|
public const AccessFlags DefaultAccessFlags =
|
||||||
AccessFlags.IndirectCommandReadBit |
|
AccessFlags.IndirectCommandReadBit |
|
||||||
AccessFlags.ShaderReadBit |
|
AccessFlags.ShaderReadBit |
|
||||||
|
@ -21,10 +30,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly MemoryAllocation _allocation;
|
private MemoryAllocation _allocation;
|
||||||
private readonly Auto<DisposableBuffer> _buffer;
|
private Auto<DisposableBuffer> _buffer;
|
||||||
private readonly Auto<MemoryAllocation> _allocationAuto;
|
private Auto<MemoryAllocation> _allocationAuto;
|
||||||
private readonly ulong _bufferHandle;
|
private ulong _bufferHandle;
|
||||||
|
|
||||||
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
|
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
|
||||||
|
|
||||||
|
@ -32,11 +41,28 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private IntPtr _map;
|
private IntPtr _map;
|
||||||
|
|
||||||
private readonly MultiFenceHolder _waitable;
|
private MultiFenceHolder _waitable;
|
||||||
|
|
||||||
private bool _lastAccessIsWrite;
|
private bool _lastAccessIsWrite;
|
||||||
|
|
||||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size)
|
private BufferAllocationType _baseType;
|
||||||
|
private BufferAllocationType _currentType;
|
||||||
|
private bool _swapQueued;
|
||||||
|
|
||||||
|
public BufferAllocationType DesiredType { get; private set; }
|
||||||
|
|
||||||
|
private int _setCount;
|
||||||
|
private int _writeCount;
|
||||||
|
private int _flushCount;
|
||||||
|
private int _flushTemp;
|
||||||
|
|
||||||
|
private ReaderWriterLock _flushLock;
|
||||||
|
private FenceHolder _flushFence;
|
||||||
|
private int _flushWaiting;
|
||||||
|
|
||||||
|
private List<Action> _swapActions;
|
||||||
|
|
||||||
|
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
|
@ -47,9 +73,153 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_bufferHandle = buffer.Handle;
|
_bufferHandle = buffer.Handle;
|
||||||
Size = size;
|
Size = size;
|
||||||
_map = allocation.HostPointer;
|
_map = allocation.HostPointer;
|
||||||
|
|
||||||
|
_baseType = type;
|
||||||
|
_currentType = currentType;
|
||||||
|
DesiredType = currentType;
|
||||||
|
|
||||||
|
_flushLock = new ReaderWriterLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size)
|
public bool TryBackingSwap(ref CommandBufferScoped? cbs)
|
||||||
|
{
|
||||||
|
if (_swapQueued && DesiredType != _currentType)
|
||||||
|
{
|
||||||
|
// Only swap if the buffer is not used in any queued command buffer.
|
||||||
|
bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool);
|
||||||
|
|
||||||
|
if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld)
|
||||||
|
{
|
||||||
|
var currentAllocation = _allocationAuto;
|
||||||
|
var currentBuffer = _buffer;
|
||||||
|
IntPtr currentMap = _map;
|
||||||
|
|
||||||
|
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, _currentType);
|
||||||
|
|
||||||
|
if (buffer.Handle != 0)
|
||||||
|
{
|
||||||
|
_flushLock.AcquireWriterLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
ClearFlushFence();
|
||||||
|
|
||||||
|
_waitable = new MultiFenceHolder(Size);
|
||||||
|
|
||||||
|
_allocation = allocation;
|
||||||
|
_allocationAuto = new Auto<MemoryAllocation>(allocation);
|
||||||
|
_buffer = new Auto<DisposableBuffer>(new DisposableBuffer(_gd.Api, _device, buffer), _waitable, _allocationAuto);
|
||||||
|
_bufferHandle = buffer.Handle;
|
||||||
|
_map = allocation.HostPointer;
|
||||||
|
|
||||||
|
if (_map != IntPtr.Zero && currentMap != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
// Copy data directly. Readbacks don't have to wait if this is done.
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
new Span<byte>((void*)currentMap, Size).CopyTo(new Span<byte>((void*)_map, Size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cbs == null)
|
||||||
|
{
|
||||||
|
cbs = _gd.CommandBufferPool.Rent();
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBufferScoped cbsV = cbs.Value;
|
||||||
|
|
||||||
|
Copy(_gd, cbsV, currentBuffer, _buffer, 0, 0, Size);
|
||||||
|
|
||||||
|
// Need to wait for the data to reach the new buffer before data can be flushed.
|
||||||
|
|
||||||
|
_flushFence = _gd.CommandBufferPool.GetFence(cbsV.CommandBufferIndex);
|
||||||
|
_flushFence.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug?.PrintMsg(LogClass.Gpu, $"Converted {Size} buffer {_currentType} to {resultType}");
|
||||||
|
|
||||||
|
_currentType = resultType;
|
||||||
|
|
||||||
|
if (_swapActions != null)
|
||||||
|
{
|
||||||
|
foreach (var action in _swapActions)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapActions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBuffer.Dispose();
|
||||||
|
currentAllocation.Dispose();
|
||||||
|
|
||||||
|
_gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer);
|
||||||
|
|
||||||
|
_flushLock.ReleaseWriterLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapQueued = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_swapQueued = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConsiderBackingSwap()
|
||||||
|
{
|
||||||
|
if (_baseType == BufferAllocationType.Auto)
|
||||||
|
{
|
||||||
|
if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold)
|
||||||
|
{
|
||||||
|
if (_flushCount > 0 || _flushTemp-- > 0)
|
||||||
|
{
|
||||||
|
// Buffers that flush should ideally be mapped in host address space for easy copies.
|
||||||
|
// If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages).
|
||||||
|
// If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached.
|
||||||
|
DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
|
||||||
|
|
||||||
|
// It's harder for a buffer that is flushed to revert to another type of mapping.
|
||||||
|
if (_flushCount > 0)
|
||||||
|
{
|
||||||
|
_flushTemp = 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_writeCount >= WriteCountThreshold)
|
||||||
|
{
|
||||||
|
// Buffers that are written often should ideally be in the device local heap. (Storage buffers)
|
||||||
|
DesiredType = BufferAllocationType.DeviceLocal;
|
||||||
|
}
|
||||||
|
else if (_setCount > SetCountThreshold)
|
||||||
|
{
|
||||||
|
// Buffers that have their data set often should ideally be host mapped. (Constant buffers)
|
||||||
|
DesiredType = BufferAllocationType.HostMapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushCount = 0;
|
||||||
|
_writeCount = 0;
|
||||||
|
_setCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_swapQueued && DesiredType != _currentType)
|
||||||
|
{
|
||||||
|
_swapQueued = true;
|
||||||
|
|
||||||
|
_gd.PipelineInternal.AddBackingSwap(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView)
|
||||||
{
|
{
|
||||||
var bufferViewCreateInfo = new BufferViewCreateInfo()
|
var bufferViewCreateInfo = new BufferViewCreateInfo()
|
||||||
{
|
{
|
||||||
|
@ -62,9 +232,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
||||||
|
|
||||||
|
(_swapActions ??= new List<Action>()).Add(invalidateView);
|
||||||
|
|
||||||
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer);
|
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InheritMetrics(BufferHolder other)
|
||||||
|
{
|
||||||
|
_setCount = other._setCount;
|
||||||
|
_writeCount = other._writeCount;
|
||||||
|
_flushCount = other._flushCount;
|
||||||
|
_flushTemp = other._flushTemp;
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite)
|
public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite)
|
||||||
{
|
{
|
||||||
// If the last access is write, we always need a barrier to be sure we will read or modify
|
// If the last access is write, we always need a barrier to be sure we will read or modify
|
||||||
|
@ -104,12 +284,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false)
|
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false, bool isSSBO = false)
|
||||||
{
|
{
|
||||||
if (isWrite)
|
if (isWrite)
|
||||||
{
|
{
|
||||||
|
_writeCount++;
|
||||||
|
|
||||||
SignalWrite(0, Size);
|
SignalWrite(0, Size);
|
||||||
}
|
}
|
||||||
|
else if (isSSBO)
|
||||||
|
{
|
||||||
|
// Always consider SSBO access for swapping to device local memory.
|
||||||
|
|
||||||
|
_writeCount++;
|
||||||
|
|
||||||
|
ConsiderBackingSwap();
|
||||||
|
}
|
||||||
|
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +308,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (isWrite)
|
if (isWrite)
|
||||||
{
|
{
|
||||||
|
_writeCount++;
|
||||||
|
|
||||||
SignalWrite(offset, size);
|
SignalWrite(offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +318,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void SignalWrite(int offset, int size)
|
public void SignalWrite(int offset, int size)
|
||||||
{
|
{
|
||||||
|
ConsiderBackingSwap();
|
||||||
|
|
||||||
if (offset == 0 && size == Size)
|
if (offset == 0 && size == Size)
|
||||||
{
|
{
|
||||||
_cachedConvertedBuffers.Clear();
|
_cachedConvertedBuffers.Clear();
|
||||||
|
@ -147,11 +341,76 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return _map;
|
return _map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData(int offset, int size)
|
private void ClearFlushFence()
|
||||||
{
|
{
|
||||||
|
// Asusmes _flushLock is held as writer.
|
||||||
|
|
||||||
|
if (_flushFence != null)
|
||||||
|
{
|
||||||
|
if (_flushWaiting == 0)
|
||||||
|
{
|
||||||
|
_flushFence.Put();
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushFence = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WaitForFlushFence()
|
||||||
|
{
|
||||||
|
// Assumes the _flushLock is held as reader, returns in same state.
|
||||||
|
|
||||||
|
if (_flushFence != null)
|
||||||
|
{
|
||||||
|
// If storage has changed, make sure the fence has been reached so that the data is in place.
|
||||||
|
|
||||||
|
var cookie = _flushLock.UpgradeToWriterLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
if (_flushFence != null)
|
||||||
|
{
|
||||||
|
var fence = _flushFence;
|
||||||
|
Interlocked.Increment(ref _flushWaiting);
|
||||||
|
|
||||||
|
// Don't wait in the lock.
|
||||||
|
|
||||||
|
var restoreCookie = _flushLock.ReleaseLock();
|
||||||
|
|
||||||
|
fence.Wait();
|
||||||
|
|
||||||
|
_flushLock.RestoreLock(ref restoreCookie);
|
||||||
|
|
||||||
|
if (Interlocked.Decrement(ref _flushWaiting) == 0)
|
||||||
|
{
|
||||||
|
fence.Put();
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushFence = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushLock.DowngradeFromWriterLock(ref cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe PinnedSpan<byte> GetData(int offset, int size)
|
||||||
|
{
|
||||||
|
_flushLock.AcquireReaderLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
WaitForFlushFence();
|
||||||
|
|
||||||
|
_flushCount++;
|
||||||
|
|
||||||
|
Span<byte> result;
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
if (_map != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
return GetDataStorage(offset, size);
|
result = GetDataStorage(offset, size);
|
||||||
|
|
||||||
|
// Need to be careful here, the buffer can't be unmapped while the data is being used.
|
||||||
|
_buffer.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_flushLock.ReleaseReaderLock();
|
||||||
|
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -161,12 +420,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
|
result = resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
|
result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_flushLock.ReleaseReaderLock();
|
||||||
|
|
||||||
|
// Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses.
|
||||||
|
return PinnedSpan<byte>.UnsafeFromSpan(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +454,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setCount++;
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
if (_map != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
// If persistently mapped, set the data directly if the buffer is not currently in use.
|
// If persistently mapped, set the data directly if the buffer is not currently in use.
|
||||||
|
@ -268,6 +534,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value;
|
var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value;
|
||||||
|
|
||||||
|
_writeCount--;
|
||||||
|
|
||||||
InsertBufferBarrier(
|
InsertBufferBarrier(
|
||||||
_gd,
|
_gd,
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
|
@ -502,11 +770,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_swapQueued = false;
|
||||||
|
|
||||||
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
|
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
|
||||||
|
|
||||||
_buffer.Dispose();
|
_buffer.Dispose();
|
||||||
_allocationAuto.Dispose();
|
_allocationAuto.Dispose();
|
||||||
_cachedConvertedBuffers.Dispose();
|
_cachedConvertedBuffers.Dispose();
|
||||||
|
|
||||||
|
_flushLock.AcquireWriterLock(Timeout.Infinite);
|
||||||
|
|
||||||
|
ClearFlushFence();
|
||||||
|
|
||||||
|
_flushLock.ReleaseWriterLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -16,17 +17,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
// Some drivers don't expose a "HostCached" memory type,
|
// Some drivers don't expose a "HostCached" memory type,
|
||||||
// so we need those alternative flags for the allocation to succeed there.
|
// so we need those alternative flags for the allocation to succeed there.
|
||||||
private const MemoryPropertyFlags DefaultBufferMemoryAltFlags =
|
private const MemoryPropertyFlags DefaultBufferMemoryNoCacheFlags =
|
||||||
MemoryPropertyFlags.HostVisibleBit |
|
MemoryPropertyFlags.HostVisibleBit |
|
||||||
MemoryPropertyFlags.HostCoherentBit;
|
MemoryPropertyFlags.HostCoherentBit;
|
||||||
|
|
||||||
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
||||||
MemoryPropertyFlags.DeviceLocalBit;
|
MemoryPropertyFlags.DeviceLocalBit;
|
||||||
|
|
||||||
private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags =
|
private const MemoryPropertyFlags DeviceLocalMappedBufferMemoryFlags =
|
||||||
|
MemoryPropertyFlags.DeviceLocalBit |
|
||||||
MemoryPropertyFlags.HostVisibleBit |
|
MemoryPropertyFlags.HostVisibleBit |
|
||||||
MemoryPropertyFlags.HostCoherentBit |
|
MemoryPropertyFlags.HostCoherentBit;
|
||||||
MemoryPropertyFlags.DeviceLocalBit;
|
|
||||||
|
|
||||||
private const BufferUsageFlags DefaultBufferUsageFlags =
|
private const BufferUsageFlags DefaultBufferUsageFlags =
|
||||||
BufferUsageFlags.TransferSrcBit |
|
BufferUsageFlags.TransferSrcBit |
|
||||||
|
@ -54,14 +55,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
StagingBuffer = new StagingBuffer(gd, this);
|
StagingBuffer = new StagingBuffer(gd, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal)
|
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
|
||||||
{
|
{
|
||||||
return CreateWithHandle(gd, size, deviceLocal, out _);
|
return CreateWithHandle(gd, size, out _, baseType, storageHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal, out BufferHolder holder)
|
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
|
||||||
{
|
{
|
||||||
holder = Create(gd, size, deviceLocal: deviceLocal);
|
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
|
||||||
if (holder == null)
|
if (holder == null)
|
||||||
{
|
{
|
||||||
return BufferHandle.Null;
|
return BufferHandle.Null;
|
||||||
|
@ -74,7 +75,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return Unsafe.As<ulong, BufferHandle>(ref handle64);
|
return Unsafe.As<ulong, BufferHandle>(ref handle64);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe BufferHolder Create(VulkanRenderer gd, int size, bool forConditionalRendering = false, bool deviceLocal = false)
|
public unsafe (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) CreateBacking(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
int size,
|
||||||
|
BufferAllocationType type,
|
||||||
|
bool forConditionalRendering = false,
|
||||||
|
BufferAllocationType fallbackType = BufferAllocationType.Auto)
|
||||||
{
|
{
|
||||||
var usage = DefaultBufferUsageFlags;
|
var usage = DefaultBufferUsageFlags;
|
||||||
|
|
||||||
|
@ -98,48 +104,106 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
||||||
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
||||||
|
|
||||||
MemoryPropertyFlags allocateFlags;
|
MemoryAllocation allocation;
|
||||||
MemoryPropertyFlags allocateFlagsAlt;
|
|
||||||
|
|
||||||
if (deviceLocal)
|
do
|
||||||
{
|
{
|
||||||
allocateFlags = DeviceLocalBufferMemoryFlags;
|
var allocateFlags = type switch
|
||||||
allocateFlagsAlt = DeviceLocalBufferMemoryFlags;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
allocateFlags = DefaultBufferMemoryFlags;
|
BufferAllocationType.HostMappedNoCache => DefaultBufferMemoryNoCacheFlags,
|
||||||
allocateFlagsAlt = DefaultBufferMemoryAltFlags;
|
BufferAllocationType.HostMapped => DefaultBufferMemoryFlags,
|
||||||
}
|
BufferAllocationType.DeviceLocal => DeviceLocalBufferMemoryFlags,
|
||||||
|
BufferAllocationType.DeviceLocalMapped => DeviceLocalMappedBufferMemoryFlags,
|
||||||
|
_ => DefaultBufferMemoryFlags
|
||||||
|
};
|
||||||
|
|
||||||
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
|
// If an allocation with this memory type fails, fall back to the previous one.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, true);
|
||||||
|
}
|
||||||
|
catch (VulkanException)
|
||||||
|
{
|
||||||
|
allocation = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (allocation.Memory.Handle == 0 && (--type != fallbackType));
|
||||||
|
|
||||||
if (allocation.Memory.Handle == 0UL)
|
if (allocation.Memory.Handle == 0UL)
|
||||||
{
|
{
|
||||||
gd.Api.DestroyBuffer(_device, buffer, null);
|
gd.Api.DestroyBuffer(_device, buffer, null);
|
||||||
return null;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
|
gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
|
||||||
|
|
||||||
return new BufferHolder(gd, _device, buffer, allocation, size);
|
return (buffer, allocation, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size)
|
public unsafe BufferHolder Create(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
int size,
|
||||||
|
bool forConditionalRendering = false,
|
||||||
|
BufferAllocationType baseType = BufferAllocationType.HostMapped,
|
||||||
|
BufferHandle storageHint = default)
|
||||||
{
|
{
|
||||||
if (TryGetBuffer(handle, out var holder))
|
BufferAllocationType type = baseType;
|
||||||
|
BufferHolder storageHintHolder = null;
|
||||||
|
|
||||||
|
if (baseType == BufferAllocationType.Auto)
|
||||||
{
|
{
|
||||||
return holder.CreateView(format, offset, size);
|
if (gd.IsSharedMemory)
|
||||||
|
{
|
||||||
|
baseType = BufferAllocationType.HostMapped;
|
||||||
|
type = baseType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = size >= BufferHolder.DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocal : BufferAllocationType.HostMapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageHint != BufferHandle.Null)
|
||||||
|
{
|
||||||
|
if (TryGetBuffer(storageHint, out storageHintHolder))
|
||||||
|
{
|
||||||
|
type = storageHintHolder.DesiredType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) =
|
||||||
|
CreateBacking(gd, size, type, forConditionalRendering);
|
||||||
|
|
||||||
|
if (buffer.Handle != 0)
|
||||||
|
{
|
||||||
|
var holder = new BufferHolder(gd, _device, buffer, allocation, size, baseType, resultType);
|
||||||
|
|
||||||
|
if (storageHintHolder != null)
|
||||||
|
{
|
||||||
|
holder.InheritMetrics(storageHintHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite)
|
public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size, Action invalidateView)
|
||||||
{
|
{
|
||||||
if (TryGetBuffer(handle, out var holder))
|
if (TryGetBuffer(handle, out var holder))
|
||||||
{
|
{
|
||||||
return holder.GetBuffer(commandBuffer, isWrite);
|
return holder.CreateView(format, offset, size, invalidateView);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, bool isSSBO = false)
|
||||||
|
{
|
||||||
|
if (TryGetBuffer(handle, out var holder))
|
||||||
|
{
|
||||||
|
return holder.GetBuffer(commandBuffer, isWrite, isSSBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -332,14 +396,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(BufferHandle handle, int offset, int size)
|
public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
|
||||||
{
|
{
|
||||||
if (TryGetBuffer(handle, out var holder))
|
if (TryGetBuffer(handle, out var holder))
|
||||||
{
|
{
|
||||||
return holder.GetData(offset, size);
|
return holder.GetData(offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReadOnlySpan<byte>.Empty;
|
return new PinnedSpan<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
readonly struct BufferState : IDisposable
|
struct BufferState : IDisposable
|
||||||
{
|
{
|
||||||
public static BufferState Null => new BufferState(null, 0, 0);
|
public static BufferState Null => new BufferState(null, 0, 0);
|
||||||
|
|
||||||
private readonly int _offset;
|
private readonly int _offset;
|
||||||
private readonly int _size;
|
private readonly int _size;
|
||||||
|
|
||||||
private readonly Auto<DisposableBuffer> _buffer;
|
private Auto<DisposableBuffer> _buffer;
|
||||||
|
|
||||||
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
|
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
if (_buffer == from)
|
||||||
|
{
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
to.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_buffer = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_buffer?.DecrementReferenceCount();
|
_buffer?.DecrementReferenceCount();
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings.
|
// If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings.
|
||||||
_dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, deviceLocal: true);
|
_dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, baseType: BufferAllocationType.DeviceLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
_dummyTexture = gd.CreateTextureView(new TextureCreateInfo(
|
_dummyTexture = gd.CreateTextureView(new TextureCreateInfo(
|
||||||
|
@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
var buffer = assignment.Range;
|
var buffer = assignment.Range;
|
||||||
int index = assignment.Binding;
|
int index = assignment.Binding;
|
||||||
|
|
||||||
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false, isSSBO: true);
|
||||||
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
|
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
|
||||||
|
|
||||||
DescriptorBufferInfo info = new DescriptorBufferInfo()
|
DescriptorBufferInfo info = new DescriptorBufferInfo()
|
||||||
|
@ -640,6 +640,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Array.Clear(_storageSet);
|
Array.Clear(_storageSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.Length; i++)
|
||||||
|
{
|
||||||
|
if (list[i] == from)
|
||||||
|
{
|
||||||
|
list[i] = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
SwapBuffer(_uniformBufferRefs, from, to);
|
||||||
|
SwapBuffer(_storageBufferRefs, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
|
|
|
@ -156,11 +156,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
};
|
};
|
||||||
|
|
||||||
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
||||||
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
|
||||||
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer);
|
||||||
|
|
||||||
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)};
|
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)};
|
||||||
var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false);
|
var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float));
|
||||||
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer);
|
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer);
|
||||||
|
|
||||||
int threadGroupWorkRegionDim = 16;
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
|
|
@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
|
||||||
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
||||||
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
||||||
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
|
||||||
|
|
||||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
|
|
||||||
|
|
|
@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
|
||||||
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
||||||
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
||||||
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
|
||||||
|
|
||||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
|
|
|
@ -394,7 +394,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
(region[2], region[3]) = (region[3], region[2]);
|
(region[2], region[3]) = (region[3], region[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
|
@ -495,7 +495,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
(region[2], region[3]) = (region[3], region[2]);
|
(region[2], region[3]) = (region[3], region[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
|
@ -649,7 +649,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
||||||
|
|
||||||
|
@ -726,7 +726,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
(region[2], region[3]) = (region[3], region[2]);
|
(region[2], region[3]) = (region[3], region[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
|
@ -802,7 +802,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
shaderParams[2] = size;
|
shaderParams[2] = size;
|
||||||
shaderParams[3] = srcOffset;
|
shaderParams[3] = srcOffset;
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
|
@ -958,7 +958,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
shaderParams[0] = BitOperations.Log2((uint)ratio);
|
shaderParams[0] = BitOperations.Log2((uint)ratio);
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
|
@ -1050,7 +1050,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
||||||
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
|
@ -1133,7 +1133,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
||||||
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||||
|
|
||||||
|
@ -1407,7 +1407,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
|
pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
|
||||||
|
|
||||||
var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer);
|
var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer);
|
||||||
var patternBufferAuto = patternBuffer.GetBuffer();
|
var patternBufferAuto = patternBuffer.GetBuffer();
|
||||||
|
|
||||||
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams);
|
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams);
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand the repeating pattern to the number of requested primitives.
|
// Expand the repeating pattern to the number of requested primitives.
|
||||||
BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
|
BufferHandle newBuffer = _gd.BufferManager.CreateWithHandle(_gd, expectedSize * sizeof(int));
|
||||||
|
|
||||||
// Copy the old data to the new one.
|
// Copy the old data to the new one.
|
||||||
if (_repeatingBuffer != BufferHandle.Null)
|
if (_repeatingBuffer != BufferHandle.Null)
|
||||||
|
|
|
@ -146,5 +146,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
return _buffer == buffer;
|
return _buffer == buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
if (_buffer == from)
|
||||||
|
{
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
to.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_buffer = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,32 +28,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
MemoryRequirements requirements,
|
MemoryRequirements requirements,
|
||||||
MemoryPropertyFlags flags = 0)
|
MemoryPropertyFlags flags = 0,
|
||||||
|
bool isBuffer = false)
|
||||||
{
|
{
|
||||||
return AllocateDeviceMemory(requirements, flags, flags);
|
int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags);
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
|
||||||
MemoryRequirements requirements,
|
|
||||||
MemoryPropertyFlags flags,
|
|
||||||
MemoryPropertyFlags alternativeFlags)
|
|
||||||
{
|
|
||||||
int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
|
|
||||||
if (memoryTypeIndex < 0)
|
if (memoryTypeIndex < 0)
|
||||||
{
|
{
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
|
bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
|
||||||
return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map);
|
return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map, isBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map)
|
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _blockLists.Count; i++)
|
for (int i = 0; i < _blockLists.Count; i++)
|
||||||
{
|
{
|
||||||
var bl = _blockLists[i];
|
var bl = _blockLists[i];
|
||||||
if (bl.MemoryTypeIndex == memoryTypeIndex)
|
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||||
{
|
{
|
||||||
lock (bl)
|
lock (bl)
|
||||||
{
|
{
|
||||||
|
@ -62,18 +55,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment);
|
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
||||||
_blockLists.Add(newBl);
|
_blockLists.Add(newBl);
|
||||||
return newBl.Allocate(size, alignment, map);
|
return newBl.Allocate(size, alignment, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int FindSuitableMemoryTypeIndex(
|
private int FindSuitableMemoryTypeIndex(
|
||||||
uint memoryTypeBits,
|
uint memoryTypeBits,
|
||||||
MemoryPropertyFlags flags,
|
MemoryPropertyFlags flags)
|
||||||
MemoryPropertyFlags alternativeFlags)
|
|
||||||
{
|
{
|
||||||
int bestCandidateIndex = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
||||||
{
|
{
|
||||||
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
|
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
|
||||||
|
@ -84,14 +74,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
else if (type.PropertyFlags.HasFlag(alternativeFlags))
|
|
||||||
{
|
|
||||||
bestCandidateIndex = i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestCandidateIndex;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice)
|
||||||
|
{
|
||||||
|
// The device is regarded as having shared memory if all heaps have the device local bit.
|
||||||
|
|
||||||
|
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
|
||||||
|
|
||||||
|
for (int i = 0; i < properties.MemoryHeapCount; i++)
|
||||||
|
{
|
||||||
|
if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -162,15 +162,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
|
|
||||||
public int MemoryTypeIndex { get; }
|
public int MemoryTypeIndex { get; }
|
||||||
|
public bool ForBuffer { get; }
|
||||||
|
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
|
||||||
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment)
|
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
||||||
{
|
{
|
||||||
_blocks = new List<Block>();
|
_blocks = new List<Block>();
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
MemoryTypeIndex = memoryTypeIndex;
|
MemoryTypeIndex = memoryTypeIndex;
|
||||||
|
ForBuffer = forBuffer;
|
||||||
_blockAlignment = blockAlignment;
|
_blockAlignment = blockAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1297,6 +1297,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
_indexBuffer.Swap(from, to);
|
||||||
|
|
||||||
|
for (int i = 0; i < _vertexBuffers.Length; i++)
|
||||||
|
{
|
||||||
|
_vertexBuffers[i].Swap(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _transformFeedbackBuffers.Length; i++)
|
||||||
|
{
|
||||||
|
_transformFeedbackBuffers[i].Swap(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
_descriptorSetUpdater.SwapBuffer(from, to);
|
||||||
|
|
||||||
|
SignalCommandBufferChange();
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe void TextureBarrier()
|
public unsafe void TextureBarrier()
|
||||||
{
|
{
|
||||||
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||||
|
|
|
@ -17,10 +17,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private ulong _byteWeight;
|
private ulong _byteWeight;
|
||||||
|
|
||||||
|
private List<BufferHolder> _backingSwaps;
|
||||||
|
|
||||||
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
||||||
{
|
{
|
||||||
_activeQueries = new List<(QueryPool, bool)>();
|
_activeQueries = new List<(QueryPool, bool)>();
|
||||||
_pendingQueryCopies = new();
|
_pendingQueryCopies = new();
|
||||||
|
_backingSwaps = new();
|
||||||
|
|
||||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +188,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TryBackingSwaps()
|
||||||
|
{
|
||||||
|
CommandBufferScoped? cbs = null;
|
||||||
|
|
||||||
|
_backingSwaps.RemoveAll((holder) => holder.TryBackingSwap(ref cbs));
|
||||||
|
|
||||||
|
cbs?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddBackingSwap(BufferHolder holder)
|
||||||
|
{
|
||||||
|
_backingSwaps.Add(holder);
|
||||||
|
}
|
||||||
|
|
||||||
public void Restore()
|
public void Restore()
|
||||||
{
|
{
|
||||||
if (Pipeline != null)
|
if (Pipeline != null)
|
||||||
|
@ -230,6 +247,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
Gd.ResetCounterPool();
|
Gd.ResetCounterPool();
|
||||||
|
|
||||||
|
TryBackingSwaps();
|
||||||
|
|
||||||
Restore();
|
Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
return _gd.GetBufferData(_bufferHandle, _offset, _size);
|
return _gd.GetBufferData(_bufferHandle, _offset, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
return GetData();
|
return GetData();
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (_bufferView == null)
|
if (_bufferView == null)
|
||||||
{
|
{
|
||||||
_bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size);
|
_bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _bufferView?.Get(cbs, _offset, _size).Value ?? default;
|
return _bufferView?.Get(cbs, _offset, _size).Value ?? default;
|
||||||
|
@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return bufferView.Get(cbs, _offset, _size).Value;
|
return bufferView.Get(cbs, _offset, _size).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size);
|
bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl);
|
||||||
|
|
||||||
if (bufferView != null)
|
if (bufferView != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -531,7 +531,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public PinnedSpan<byte> GetData()
|
||||||
{
|
{
|
||||||
BackgroundResource resources = _gd.BackgroundResources.Get();
|
BackgroundResource resources = _gd.BackgroundResources.Get();
|
||||||
|
|
||||||
|
@ -539,15 +539,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer());
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetData(resources.GetPool(), resources.GetFlushBuffer());
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData(int layer, int level)
|
public PinnedSpan<byte> GetData(int layer, int level)
|
||||||
{
|
{
|
||||||
BackgroundResource resources = _gd.BackgroundResources.Get();
|
BackgroundResource resources = _gd.BackgroundResources.Get();
|
||||||
|
|
||||||
|
@ -555,11 +555,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level);
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level);
|
return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return _buffer == buffer;
|
return _buffer == buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||||
|
{
|
||||||
|
if (_buffer == from)
|
||||||
|
{
|
||||||
|
_buffer.DecrementReferenceCount();
|
||||||
|
to.IncrementReferenceCount();
|
||||||
|
|
||||||
|
_buffer = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// Only dispose if this buffer is not refetched on each bind.
|
// Only dispose if this buffer is not refetched on each bind.
|
||||||
|
|
|
@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
internal bool IsAmdGcn { get; private set; }
|
internal bool IsAmdGcn { get; private set; }
|
||||||
internal bool IsMoltenVk { get; private set; }
|
internal bool IsMoltenVk { get; private set; }
|
||||||
internal bool IsTBDR { get; private set; }
|
internal bool IsTBDR { get; private set; }
|
||||||
|
internal bool IsSharedMemory { get; private set; }
|
||||||
public string GpuVendor { get; private set; }
|
public string GpuVendor { get; private set; }
|
||||||
public string GpuRenderer { get; private set; }
|
public string GpuRenderer { get; private set; }
|
||||||
public string GpuVersion { get; private set; }
|
public string GpuVersion { get; private set; }
|
||||||
|
@ -313,6 +314,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
portabilityFlags,
|
portabilityFlags,
|
||||||
vertexBufferAlignment);
|
vertexBufferAlignment);
|
||||||
|
|
||||||
|
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice);
|
||||||
|
|
||||||
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
||||||
|
|
||||||
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||||
|
@ -373,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
|
||||||
{
|
{
|
||||||
return BufferManager.CreateWithHandle(this, size, false);
|
return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
|
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
|
||||||
|
@ -439,7 +442,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_syncManager.RegisterFlush();
|
_syncManager.RegisterFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||||
{
|
{
|
||||||
return BufferManager.GetData(buffer, offset, size);
|
return BufferManager.GetData(buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue