using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using System; namespace Ryujinx.Graphics.Gpu.Memory { /// /// Buffer data updater. /// class BufferUpdater : IDisposable { private BufferHandle _handle; /// /// Handle of the buffer. /// public BufferHandle Handle => _handle; private readonly IRenderer _renderer; private int _startOffset = -1; private int _endOffset = -1; /// /// Creates a new instance of the buffer updater. /// /// Renderer that the buffer will be used with public BufferUpdater(IRenderer renderer) { _renderer = renderer; } /// /// Mark a region of the buffer as modified and needing to be sent to the GPU. /// /// Start offset of the region in bytes /// Size of the region in bytes protected void MarkDirty(int startOffset, int byteSize) { int endOffset = startOffset + byteSize; if (_startOffset == -1) { _startOffset = startOffset; _endOffset = endOffset; } else { if (startOffset < _startOffset) { _startOffset = startOffset; } if (endOffset > _endOffset) { _endOffset = endOffset; } } } /// /// Submits all pending buffer updates to the GPU. /// /// All data that should be sent to the GPU. Only the modified regions will be updated /// Optional binding to bind the buffer if a new buffer was created protected void Commit(ReadOnlySpan data, int binding = -1) { if (_startOffset != -1) { if (_handle == BufferHandle.Null) { _handle = _renderer.CreateBuffer(data.Length, BufferAccess.Stream); _renderer.Pipeline.ClearBuffer(_handle, 0, data.Length, 0); if (binding >= 0) { var range = new BufferRange(_handle, 0, data.Length); _renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, range) }); } }; _renderer.SetBufferData(_handle, _startOffset, data[_startOffset.._endOffset]); _startOffset = -1; _endOffset = -1; } } /// /// Gets a reference to a given element of a vector. /// /// Vector to get the element reference from /// Element index /// Reference to the specified element protected static ref T GetElementRef(ref Vector4 vector, int elementIndex) { switch (elementIndex) { case 0: return ref vector.X; case 1: return ref vector.Y; case 2: return ref vector.Z; case 3: return ref vector.W; default: throw new ArgumentOutOfRangeException(nameof(elementIndex)); } } /// /// Destroys the buffer. /// public void Dispose() { if (_handle != BufferHandle.Null) { _renderer.DeleteBuffer(_handle); _handle = BufferHandle.Null; } } } }