* GPU: Migrate buffers on GPU project, pre-emptively flush device local mappings Essentially retreading #4540, but it's on the GPU project now instead of the backend. This allows us to have a lot more control + knowledge of where the buffer backing has been changed and allows us to pre-emptively flush pages to host memory for quicker readback. It will allow us to do other stuff in the future, but we'll get there when we get there. Performance greatly improved in Hyrule Warriors: Age of Calamity. Performance notably improved in TOTK (average). Performance for BOTW restored to how it was before #4911, perhaps a bit better. - Rewrites a bunch of buffer migration stuff. Might want to tighten up how dispose stuff works. - Fixed an issue where the copy for texture pre-flush would happen _after_ the syncpoint. TODO: remove a page from pre-flush if it isn't flushed after a certain number of copies. * Add copy deactivation * Fix dependent virtual buffers * Remove logging * Fix format issues (maybe) * Vulkan: Remove backing swap * Add explicit memory access types for most buffers * Fix typo * Add device local force expiry, change buffer inheritance behaviour * General cleanup, OGL fix * BufferPreFlush comments * BufferBackingState comments * Add an extra precaution to BufferMigration This is very unlikely, but it's important to cover loose ends like this. * Address some feedback * Docs
651 lines
25 KiB
C#
651 lines
25 KiB
C#
using Ryujinx.Common;
|
|
using Ryujinx.Graphics.GAL;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|
{
|
|
/// <summary>
|
|
/// Vertex, tessellation and geometry as compute shader context.
|
|
/// </summary>
|
|
class VtgAsComputeContext : IDisposable
|
|
{
|
|
private const int DummyBufferSize = 16;
|
|
|
|
private readonly GpuContext _context;
|
|
|
|
/// <summary>
|
|
/// Cache of buffer textures used for vertex and index buffers.
|
|
/// </summary>
|
|
private class BufferTextureCache : IDisposable
|
|
{
|
|
private readonly Dictionary<Format, ITexture> _cache;
|
|
|
|
/// <summary>
|
|
/// Creates a new instance of the buffer texture cache.
|
|
/// </summary>
|
|
public BufferTextureCache()
|
|
{
|
|
_cache = new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a cached or creates and caches a buffer texture with the specified format.
|
|
/// </summary>
|
|
/// <param name="renderer">Renderer where the texture will be used</param>
|
|
/// <param name="format">Format of the buffer texture</param>
|
|
/// <returns>Buffer texture</returns>
|
|
public ITexture Get(IRenderer renderer, Format format)
|
|
{
|
|
if (!_cache.TryGetValue(format, out ITexture bufferTexture))
|
|
{
|
|
bufferTexture = renderer.CreateTexture(new TextureCreateInfo(
|
|
1,
|
|
1,
|
|
1,
|
|
1,
|
|
1,
|
|
1,
|
|
1,
|
|
1,
|
|
format,
|
|
DepthStencilMode.Depth,
|
|
Target.TextureBuffer,
|
|
SwizzleComponent.Red,
|
|
SwizzleComponent.Green,
|
|
SwizzleComponent.Blue,
|
|
SwizzleComponent.Alpha));
|
|
|
|
_cache.Add(format, bufferTexture);
|
|
}
|
|
|
|
return bufferTexture;
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
foreach (var texture in _cache.Values)
|
|
{
|
|
texture.Release();
|
|
}
|
|
|
|
_cache.Clear();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Buffer state.
|
|
/// </summary>
|
|
private struct Buffer
|
|
{
|
|
/// <summary>
|
|
/// Buffer handle.
|
|
/// </summary>
|
|
public BufferHandle Handle;
|
|
|
|
/// <summary>
|
|
/// Current free buffer offset.
|
|
/// </summary>
|
|
public int Offset;
|
|
|
|
/// <summary>
|
|
/// Total buffer size in bytes.
|
|
/// </summary>
|
|
public int Size;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Index buffer state.
|
|
/// </summary>
|
|
private readonly struct IndexBuffer
|
|
{
|
|
/// <summary>
|
|
/// Buffer handle.
|
|
/// </summary>
|
|
public BufferHandle Handle { get; }
|
|
|
|
/// <summary>
|
|
/// Index count.
|
|
/// </summary>
|
|
public int Count { get; }
|
|
|
|
/// <summary>
|
|
/// Size in bytes.
|
|
/// </summary>
|
|
public int Size { get; }
|
|
|
|
/// <summary>
|
|
/// Creates a new index buffer state.
|
|
/// </summary>
|
|
/// <param name="handle">Buffer handle</param>
|
|
/// <param name="count">Index count</param>
|
|
/// <param name="size">Size in bytes</param>
|
|
public IndexBuffer(BufferHandle handle, int count, int size)
|
|
{
|
|
Handle = handle;
|
|
Count = count;
|
|
Size = size;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a full range starting from the beggining of the buffer.
|
|
/// </summary>
|
|
/// <returns>Range</returns>
|
|
public readonly BufferRange ToRange()
|
|
{
|
|
return new BufferRange(Handle, 0, Size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a range starting from the beggining of the buffer, with the specified size.
|
|
/// </summary>
|
|
/// <param name="size">Size in bytes of the range</param>
|
|
/// <returns>Range</returns>
|
|
public readonly BufferRange ToRange(int size)
|
|
{
|
|
return new BufferRange(Handle, 0, size);
|
|
}
|
|
}
|
|
|
|
private readonly BufferTextureCache[] _bufferTextures;
|
|
private BufferHandle _dummyBuffer;
|
|
private Buffer _vertexDataBuffer;
|
|
private Buffer _geometryVertexDataBuffer;
|
|
private Buffer _geometryIndexDataBuffer;
|
|
private BufferHandle _sequentialIndexBuffer;
|
|
private int _sequentialIndexBufferCount;
|
|
|
|
private readonly Dictionary<PrimitiveTopology, IndexBuffer> _topologyRemapBuffers;
|
|
|
|
/// <summary>
|
|
/// Vertex information buffer updater.
|
|
/// </summary>
|
|
public VertexInfoBufferUpdater VertexInfoBufferUpdater { get; }
|
|
|
|
/// <summary>
|
|
/// Creates a new instance of the vertex, tessellation and geometry as compute shader context.
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
public VtgAsComputeContext(GpuContext context)
|
|
{
|
|
_context = context;
|
|
_bufferTextures = new BufferTextureCache[Constants.TotalVertexBuffers + 2];
|
|
_topologyRemapBuffers = new();
|
|
VertexInfoBufferUpdater = new(context.Renderer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of complete primitives that can be formed with a given vertex count, for a given topology.
|
|
/// </summary>
|
|
/// <param name="primitiveType">Topology</param>
|
|
/// <param name="count">Vertex count</param>
|
|
/// <returns>Total of complete primitives</returns>
|
|
public static int GetPrimitivesCount(PrimitiveTopology primitiveType, int count)
|
|
{
|
|
return primitiveType switch
|
|
{
|
|
PrimitiveTopology.Lines => count / 2,
|
|
PrimitiveTopology.LinesAdjacency => count / 4,
|
|
PrimitiveTopology.LineLoop => count > 1 ? count : 0,
|
|
PrimitiveTopology.LineStrip => Math.Max(count - 1, 0),
|
|
PrimitiveTopology.LineStripAdjacency => Math.Max(count - 3, 0),
|
|
PrimitiveTopology.Triangles => count / 3,
|
|
PrimitiveTopology.TrianglesAdjacency => count / 6,
|
|
PrimitiveTopology.TriangleStrip or
|
|
PrimitiveTopology.TriangleFan or
|
|
PrimitiveTopology.Polygon => Math.Max(count - 2, 0),
|
|
PrimitiveTopology.TriangleStripAdjacency => Math.Max(count - 2, 0) / 2,
|
|
PrimitiveTopology.Quads => (count / 4) * 2, // In triangles.
|
|
PrimitiveTopology.QuadStrip => Math.Max((count - 2) / 2, 0) * 2, // In triangles.
|
|
_ => count,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the total of vertices that a single primitive has, for the specified topology.
|
|
/// </summary>
|
|
/// <param name="primitiveType">Topology</param>
|
|
/// <returns>Vertex count</returns>
|
|
private static int GetVerticesPerPrimitive(PrimitiveTopology primitiveType)
|
|
{
|
|
return primitiveType switch
|
|
{
|
|
PrimitiveTopology.Lines or
|
|
PrimitiveTopology.LineLoop or
|
|
PrimitiveTopology.LineStrip => 2,
|
|
PrimitiveTopology.LinesAdjacency or
|
|
PrimitiveTopology.LineStripAdjacency => 4,
|
|
PrimitiveTopology.Triangles or
|
|
PrimitiveTopology.TriangleStrip or
|
|
PrimitiveTopology.TriangleFan or
|
|
PrimitiveTopology.Polygon => 3,
|
|
PrimitiveTopology.TrianglesAdjacency or
|
|
PrimitiveTopology.TriangleStripAdjacency => 6,
|
|
PrimitiveTopology.Quads or
|
|
PrimitiveTopology.QuadStrip => 3, // 2 triangles.
|
|
_ => 1,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a cached or creates a new buffer that can be used to map linear indices to ones
|
|
/// of a specified topology, and build complete primitives.
|
|
/// </summary>
|
|
/// <param name="topology">Topology</param>
|
|
/// <param name="count">Number of input vertices that needs to be mapped using that buffer</param>
|
|
/// <returns>Remap buffer range</returns>
|
|
public BufferRange GetOrCreateTopologyRemapBuffer(PrimitiveTopology topology, int count)
|
|
{
|
|
if (!_topologyRemapBuffers.TryGetValue(topology, out IndexBuffer buffer) || buffer.Count < count)
|
|
{
|
|
if (buffer.Handle != BufferHandle.Null)
|
|
{
|
|
_context.Renderer.DeleteBuffer(buffer.Handle);
|
|
}
|
|
|
|
buffer = CreateTopologyRemapBuffer(topology, count);
|
|
_topologyRemapBuffers[topology] = buffer;
|
|
|
|
return buffer.ToRange();
|
|
}
|
|
|
|
return buffer.ToRange(Math.Max(GetPrimitivesCount(topology, count) * GetVerticesPerPrimitive(topology), 1) * sizeof(uint));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new topology remap buffer.
|
|
/// </summary>
|
|
/// <param name="topology">Topology</param>
|
|
/// <param name="count">Maximum of vertices that will be accessed</param>
|
|
/// <returns>Remap buffer range</returns>
|
|
private IndexBuffer CreateTopologyRemapBuffer(PrimitiveTopology topology, int count)
|
|
{
|
|
// Size can't be zero as creating zero sized buffers is invalid.
|
|
Span<int> data = new int[Math.Max(GetPrimitivesCount(topology, count) * GetVerticesPerPrimitive(topology), 1)];
|
|
|
|
switch (topology)
|
|
{
|
|
case PrimitiveTopology.Points:
|
|
case PrimitiveTopology.Lines:
|
|
case PrimitiveTopology.LinesAdjacency:
|
|
case PrimitiveTopology.Triangles:
|
|
case PrimitiveTopology.TrianglesAdjacency:
|
|
case PrimitiveTopology.Patches:
|
|
for (int index = 0; index < data.Length; index++)
|
|
{
|
|
data[index] = index;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.LineLoop:
|
|
data[^1] = 0;
|
|
|
|
for (int index = 0; index < ((data.Length - 1) & ~1); index += 2)
|
|
{
|
|
data[index] = index >> 1;
|
|
data[index + 1] = (index >> 1) + 1;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.LineStrip:
|
|
for (int index = 0; index < ((data.Length - 1) & ~1); index += 2)
|
|
{
|
|
data[index] = index >> 1;
|
|
data[index + 1] = (index >> 1) + 1;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.TriangleStrip:
|
|
int tsTrianglesCount = data.Length / 3;
|
|
int tsOutIndex = 3;
|
|
|
|
if (tsTrianglesCount > 0)
|
|
{
|
|
data[0] = 0;
|
|
data[1] = 1;
|
|
data[2] = 2;
|
|
}
|
|
|
|
for (int tri = 1; tri < tsTrianglesCount; tri++)
|
|
{
|
|
int baseIndex = tri * 3;
|
|
|
|
if ((tri & 1) != 0)
|
|
{
|
|
data[baseIndex] = tsOutIndex - 1;
|
|
data[baseIndex + 1] = tsOutIndex - 2;
|
|
data[baseIndex + 2] = tsOutIndex++;
|
|
}
|
|
else
|
|
{
|
|
data[baseIndex] = tsOutIndex - 2;
|
|
data[baseIndex + 1] = tsOutIndex - 1;
|
|
data[baseIndex + 2] = tsOutIndex++;
|
|
}
|
|
}
|
|
break;
|
|
case PrimitiveTopology.TriangleFan:
|
|
case PrimitiveTopology.Polygon:
|
|
int tfTrianglesCount = data.Length / 3;
|
|
int tfOutIndex = 1;
|
|
|
|
for (int index = 0; index < tfTrianglesCount * 3; index += 3)
|
|
{
|
|
data[index] = 0;
|
|
data[index + 1] = tfOutIndex;
|
|
data[index + 2] = ++tfOutIndex;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.Quads:
|
|
int qQuadsCount = data.Length / 6;
|
|
|
|
for (int quad = 0; quad < qQuadsCount; quad++)
|
|
{
|
|
int index = quad * 6;
|
|
int qIndex = quad * 4;
|
|
|
|
data[index] = qIndex;
|
|
data[index + 1] = qIndex + 1;
|
|
data[index + 2] = qIndex + 2;
|
|
data[index + 3] = qIndex;
|
|
data[index + 4] = qIndex + 2;
|
|
data[index + 5] = qIndex + 3;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.QuadStrip:
|
|
int qsQuadsCount = data.Length / 6;
|
|
|
|
if (qsQuadsCount > 0)
|
|
{
|
|
data[0] = 0;
|
|
data[1] = 1;
|
|
data[2] = 2;
|
|
data[3] = 0;
|
|
data[4] = 2;
|
|
data[5] = 3;
|
|
}
|
|
|
|
for (int quad = 1; quad < qsQuadsCount; quad++)
|
|
{
|
|
int index = quad * 6;
|
|
int qIndex = quad * 2;
|
|
|
|
data[index] = qIndex + 1;
|
|
data[index + 1] = qIndex;
|
|
data[index + 2] = qIndex + 2;
|
|
data[index + 3] = qIndex + 1;
|
|
data[index + 4] = qIndex + 2;
|
|
data[index + 5] = qIndex + 3;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.LineStripAdjacency:
|
|
for (int index = 0; index < ((data.Length - 3) & ~3); index += 4)
|
|
{
|
|
int lIndex = index >> 2;
|
|
|
|
data[index] = lIndex;
|
|
data[index + 1] = lIndex + 1;
|
|
data[index + 2] = lIndex + 2;
|
|
data[index + 3] = lIndex + 3;
|
|
}
|
|
break;
|
|
case PrimitiveTopology.TriangleStripAdjacency:
|
|
int tsaTrianglesCount = data.Length / 6;
|
|
int tsaOutIndex = 6;
|
|
|
|
if (tsaTrianglesCount > 0)
|
|
{
|
|
data[0] = 0;
|
|
data[1] = 1;
|
|
data[2] = 2;
|
|
data[3] = 3;
|
|
data[4] = 4;
|
|
data[5] = 5;
|
|
}
|
|
|
|
for (int tri = 1; tri < tsaTrianglesCount; tri++)
|
|
{
|
|
int baseIndex = tri * 6;
|
|
|
|
if ((tri & 1) != 0)
|
|
{
|
|
data[baseIndex] = tsaOutIndex - 2;
|
|
data[baseIndex + 1] = tsaOutIndex - 1;
|
|
data[baseIndex + 2] = tsaOutIndex - 4;
|
|
data[baseIndex + 3] = tsaOutIndex - 3;
|
|
data[baseIndex + 4] = tsaOutIndex++;
|
|
data[baseIndex + 5] = tsaOutIndex++;
|
|
}
|
|
else
|
|
{
|
|
data[baseIndex] = tsaOutIndex - 4;
|
|
data[baseIndex + 1] = tsaOutIndex - 3;
|
|
data[baseIndex + 2] = tsaOutIndex - 2;
|
|
data[baseIndex + 3] = tsaOutIndex - 1;
|
|
data[baseIndex + 4] = tsaOutIndex++;
|
|
data[baseIndex + 5] = tsaOutIndex++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ReadOnlySpan<byte> dataBytes = MemoryMarshal.Cast<int, byte>(data);
|
|
|
|
BufferHandle buffer = _context.Renderer.CreateBuffer(dataBytes.Length, BufferAccess.DeviceMemory);
|
|
_context.Renderer.SetBufferData(buffer, 0, dataBytes);
|
|
|
|
return new IndexBuffer(buffer, count, dataBytes.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a buffer texture with a given format, for the given index.
|
|
/// </summary>
|
|
/// <param name="index">Index of the buffer texture</param>
|
|
/// <param name="format">Format of the buffer texture</param>
|
|
/// <returns>Buffer texture</returns>
|
|
public ITexture EnsureBufferTexture(int index, Format format)
|
|
{
|
|
return (_bufferTextures[index] ??= new()).Get(_context.Renderer, format);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the offset and size of usable storage on the output vertex buffer.
|
|
/// </summary>
|
|
/// <param name="size">Size in bytes that will be used</param>
|
|
/// <returns>Usable offset and size on the buffer</returns>
|
|
public (int, int) GetVertexDataBuffer(int size)
|
|
{
|
|
return EnsureBuffer(ref _vertexDataBuffer, size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the offset and size of usable storage on the output geometry shader vertex buffer.
|
|
/// </summary>
|
|
/// <param name="size">Size in bytes that will be used</param>
|
|
/// <returns>Usable offset and size on the buffer</returns>
|
|
public (int, int) GetGeometryVertexDataBuffer(int size)
|
|
{
|
|
return EnsureBuffer(ref _geometryVertexDataBuffer, size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the offset and size of usable storage on the output geometry shader index buffer.
|
|
/// </summary>
|
|
/// <param name="size">Size in bytes that will be used</param>
|
|
/// <returns>Usable offset and size on the buffer</returns>
|
|
public (int, int) GetGeometryIndexDataBuffer(int size)
|
|
{
|
|
return EnsureBuffer(ref _geometryIndexDataBuffer, size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a range of the output vertex buffer for binding.
|
|
/// </summary>
|
|
/// <param name="offset">Offset of the range</param>
|
|
/// <param name="size">Size of the range in bytes</param>
|
|
/// <param name="write">Indicates if the buffer contents will be modified</param>
|
|
/// <returns>Range</returns>
|
|
public BufferRange GetVertexDataBufferRange(int offset, int size, bool write)
|
|
{
|
|
return new BufferRange(_vertexDataBuffer.Handle, offset, size, write);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a range of the output geometry shader vertex buffer for binding.
|
|
/// </summary>
|
|
/// <param name="offset">Offset of the range</param>
|
|
/// <param name="size">Size of the range in bytes</param>
|
|
/// <param name="write">Indicates if the buffer contents will be modified</param>
|
|
/// <returns>Range</returns>
|
|
public BufferRange GetGeometryVertexDataBufferRange(int offset, int size, bool write)
|
|
{
|
|
return new BufferRange(_geometryVertexDataBuffer.Handle, offset, size, write);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a range of the output geometry shader index buffer for binding.
|
|
/// </summary>
|
|
/// <param name="offset">Offset of the range</param>
|
|
/// <param name="size">Size of the range in bytes</param>
|
|
/// <param name="write">Indicates if the buffer contents will be modified</param>
|
|
/// <returns>Range</returns>
|
|
public BufferRange GetGeometryIndexDataBufferRange(int offset, int size, bool write)
|
|
{
|
|
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the range for a dummy 16 bytes buffer, filled with zeros.
|
|
/// </summary>
|
|
/// <returns>Dummy buffer range</returns>
|
|
public BufferRange GetDummyBufferRange()
|
|
{
|
|
if (_dummyBuffer == BufferHandle.Null)
|
|
{
|
|
_dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory);
|
|
_context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0);
|
|
}
|
|
|
|
return new BufferRange(_dummyBuffer, 0, DummyBufferSize);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
|
/// </summary>
|
|
/// <param name="count">Minimum number of indices that the buffer should have</param>
|
|
/// <returns>Buffer handle</returns>
|
|
public BufferHandle GetSequentialIndexBuffer(int count)
|
|
{
|
|
if (_sequentialIndexBufferCount < count)
|
|
{
|
|
if (_sequentialIndexBuffer != BufferHandle.Null)
|
|
{
|
|
_context.Renderer.DeleteBuffer(_sequentialIndexBuffer);
|
|
}
|
|
|
|
_sequentialIndexBuffer = _context.Renderer.CreateBuffer(count * sizeof(uint), BufferAccess.DeviceMemory);
|
|
_sequentialIndexBufferCount = count;
|
|
|
|
Span<int> data = new int[count];
|
|
|
|
for (int index = 0; index < count; index++)
|
|
{
|
|
data[index] = index;
|
|
}
|
|
|
|
_context.Renderer.SetBufferData(_sequentialIndexBuffer, 0, MemoryMarshal.Cast<int, byte>(data));
|
|
}
|
|
|
|
return _sequentialIndexBuffer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensure that a buffer exists, is large enough, and allocates a sub-region of the specified size inside the buffer.
|
|
/// </summary>
|
|
/// <param name="buffer">Buffer state</param>
|
|
/// <param name="size">Required size in bytes</param>
|
|
/// <returns>Allocated offset and size</returns>
|
|
private (int, int) EnsureBuffer(ref Buffer buffer, int size)
|
|
{
|
|
int newSize = buffer.Offset + size;
|
|
|
|
if (buffer.Size < newSize)
|
|
{
|
|
if (buffer.Handle != BufferHandle.Null)
|
|
{
|
|
_context.Renderer.DeleteBuffer(buffer.Handle);
|
|
}
|
|
|
|
buffer.Handle = _context.Renderer.CreateBuffer(newSize, BufferAccess.DeviceMemory);
|
|
buffer.Size = newSize;
|
|
}
|
|
|
|
int offset = buffer.Offset;
|
|
|
|
buffer.Offset = BitUtils.AlignUp(newSize, _context.Capabilities.StorageBufferOffsetAlignment);
|
|
|
|
return (offset, size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Frees all buffer sub-regions that were previously allocated.
|
|
/// </summary>
|
|
public void FreeBuffers()
|
|
{
|
|
_vertexDataBuffer.Offset = 0;
|
|
_geometryVertexDataBuffer.Offset = 0;
|
|
_geometryIndexDataBuffer.Offset = 0;
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
for (int index = 0; index < _bufferTextures.Length; index++)
|
|
{
|
|
_bufferTextures[index]?.Dispose();
|
|
_bufferTextures[index] = null;
|
|
}
|
|
|
|
DestroyIfNotNull(ref _dummyBuffer);
|
|
DestroyIfNotNull(ref _vertexDataBuffer.Handle);
|
|
DestroyIfNotNull(ref _geometryVertexDataBuffer.Handle);
|
|
DestroyIfNotNull(ref _geometryIndexDataBuffer.Handle);
|
|
DestroyIfNotNull(ref _sequentialIndexBuffer);
|
|
|
|
foreach (var indexBuffer in _topologyRemapBuffers.Values)
|
|
{
|
|
_context.Renderer.DeleteBuffer(indexBuffer.Handle);
|
|
}
|
|
|
|
_topologyRemapBuffers.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a buffer if the handle is valid (not null), then sets the handle to null.
|
|
/// </summary>
|
|
/// <param name="handle">Buffer handle</param>
|
|
private void DestroyIfNotNull(ref BufferHandle handle)
|
|
{
|
|
if (handle != BufferHandle.Null)
|
|
{
|
|
_context.Renderer.DeleteBuffer(handle);
|
|
handle = BufferHandle.Null;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
}
|