| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- 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);
- _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>
- /// <returns>Range</returns>
- public BufferRange GetVertexDataBufferRange(int offset, int size)
- {
- return new BufferRange(_vertexDataBuffer.Handle, offset, size);
- }
- /// <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>
- /// <returns>Range</returns>
- public BufferRange GetGeometryVertexDataBufferRange(int offset, int size)
- {
- return new BufferRange(_geometryVertexDataBuffer.Handle, offset, size);
- }
- /// <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>
- /// <returns>Range</returns>
- public BufferRange GetGeometryIndexDataBufferRange(int offset, int size)
- {
- return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size);
- }
- /// <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);
- _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));
- _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);
- 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);
- }
- }
- }
|