using System; using System.Runtime.InteropServices; using RefreshCS; namespace MoonWorks.Graphics { /// /// Buffers are generic data containers that can be used by the GPU. /// public class Buffer : RefreshResource { protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer; /// /// Size in bytes. /// public uint Size { get; } /// /// Creates a buffer of appropriate size given a type and element count. /// /// The type that the buffer will contain. /// The GraphicsDevice. /// Specifies how the buffer will be used. /// How many elements of type T the buffer will contain. /// public unsafe static Buffer Create( GraphicsDevice device, BufferUsageFlags usageFlags, uint elementCount ) where T : unmanaged { return new Buffer( device, usageFlags, (uint) Marshal.SizeOf() * elementCount ); } /// /// Creates a buffer. /// /// An initialized GraphicsDevice. /// Specifies how the buffer will be used. /// The length of the array. Cannot be resized. public Buffer( GraphicsDevice device, BufferUsageFlags usageFlags, uint sizeInBytes ) : base(device) { Handle = Refresh.Refresh_CreateBuffer( device.Handle, (Refresh.BufferUsageFlags) usageFlags, sizeInBytes ); Size = sizeInBytes; } /// /// Reads data out of a buffer and into a span. /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. /// /// The span that data will be copied to. /// The length of the data to read. public unsafe void GetData( Span data, uint dataLengthInBytes ) where T : unmanaged { #if DEBUG if (dataLengthInBytes > Size) { Logger.LogWarn("Requested too many bytes from buffer!"); } if (dataLengthInBytes > data.Length * Marshal.SizeOf()) { Logger.LogWarn("Data length is larger than the provided Span!"); } #endif fixed (T* ptr = data) { Refresh.Refresh_GetBufferData( Device.Handle, Handle, (IntPtr) ptr, dataLengthInBytes ); } } /// /// Reads data out of a buffer and into an array. /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. /// /// The span that data will be copied to. /// The length of the data to read. public unsafe void GetData( T[] data, uint dataLengthInBytes ) where T : unmanaged { GetData(new Span(data), dataLengthInBytes); } /// /// Reads data out of a buffer and into a span. /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. /// Fills the span with as much data from the buffer as it can. /// /// The span that data will be copied to. public unsafe void GetData( Span data ) where T : unmanaged { var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf(), Size); GetData(data, (uint) lengthInBytes); } /// /// Reads data out of a buffer and into an array. /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. /// Fills the array with as much data from the buffer as it can. /// /// The span that data will be copied to. public unsafe void GetData( T[] data ) where T : unmanaged { var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf(), Size); GetData(new Span(data), (uint) lengthInBytes); } public static implicit operator BufferBinding(Buffer b) { return new BufferBinding(b, 0); } } }