Setup entry point + integrated moonworks stuff
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using RefreshCS;
|
||||
|
||||
namespace Nerfed.Runtime.Graphics;
|
||||
|
||||
/// <summary>
|
||||
/// A data container that can efficiently transfer data to and from the GPU.
|
||||
/// </summary>
|
||||
public unsafe class TransferBuffer : RefreshResource
|
||||
{
|
||||
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseTransferBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Size in bytes.
|
||||
/// </summary>
|
||||
public uint Size { get; }
|
||||
|
||||
#if DEBUG
|
||||
public bool Mapped { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer of requested size given a type and element count.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that the buffer will contain.</typeparam>
|
||||
/// <param name="device">The GraphicsDevice.</param>
|
||||
/// <param name="elementCount">How many elements of type T the buffer will contain.</param>
|
||||
/// <returns></returns>
|
||||
public unsafe static TransferBuffer Create<T>(
|
||||
GraphicsDevice device,
|
||||
TransferBufferUsage usage,
|
||||
uint elementCount
|
||||
) where T : unmanaged
|
||||
{
|
||||
return new TransferBuffer(
|
||||
device,
|
||||
usage,
|
||||
(uint) Marshal.SizeOf<T>() * elementCount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a TransferBuffer.
|
||||
/// </summary>
|
||||
/// <param name="device">An initialized GraphicsDevice.</param>
|
||||
/// <param name="usage">Whether this will be used to upload buffers or textures.</param>
|
||||
/// <param name="sizeInBytes">The length of the buffer. Cannot be resized.</param>
|
||||
public TransferBuffer(
|
||||
GraphicsDevice device,
|
||||
TransferBufferUsage usage,
|
||||
uint sizeInBytes
|
||||
) : base(device)
|
||||
{
|
||||
Handle = Refresh.Refresh_CreateTransferBuffer(
|
||||
device.Handle,
|
||||
(Refresh.TransferBufferUsage) usage,
|
||||
sizeInBytes
|
||||
);
|
||||
Size = sizeInBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately copies data from a Span to the TransferBuffer.
|
||||
/// Returns the length of the copy in bytes.
|
||||
///
|
||||
/// If cycle is set to true and this TransferBuffer was used in an Upload command,
|
||||
/// that command will still use the correct data at the cost of increased memory usage.
|
||||
///
|
||||
/// If cycle is set to false, the data will be overwritten immediately,
|
||||
/// which could cause a data race.
|
||||
/// </summary>
|
||||
public unsafe uint SetData<T>(
|
||||
Span<T> source,
|
||||
uint bufferOffsetInBytes,
|
||||
bool cycle
|
||||
) where T : unmanaged
|
||||
{
|
||||
int elementSize = Marshal.SizeOf<T>();
|
||||
uint dataLengthInBytes = (uint) (elementSize * source.Length);
|
||||
|
||||
#if DEBUG
|
||||
AssertBufferBoundsCheck(Size, bufferOffsetInBytes, dataLengthInBytes);
|
||||
AssertNotMapped();
|
||||
#endif
|
||||
|
||||
fixed (T* dataPtr = source)
|
||||
{
|
||||
Refresh.Refresh_SetTransferData(
|
||||
Device.Handle,
|
||||
(nint) dataPtr,
|
||||
new Refresh.TransferBufferRegion
|
||||
{
|
||||
TransferBuffer = Handle,
|
||||
Offset = bufferOffsetInBytes,
|
||||
Size = dataLengthInBytes
|
||||
},
|
||||
Conversions.BoolToInt(cycle)
|
||||
);
|
||||
}
|
||||
|
||||
return dataLengthInBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately copies data from a Span to the TransferBuffer.
|
||||
/// Returns the length of the copy in bytes.
|
||||
///
|
||||
/// If cycle is set to true and this TransferBuffer was used in an Upload command,
|
||||
/// that command will still use the corret data at the cost of increased memory usage.
|
||||
///
|
||||
/// If cycle is set to false, the data will be overwritten immediately,
|
||||
/// which could cause a data race.
|
||||
/// </summary>
|
||||
public unsafe uint SetData<T>(
|
||||
Span<T> source,
|
||||
bool cycle
|
||||
) where T : unmanaged
|
||||
{
|
||||
return SetData(source, 0, cycle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately copies data from the TransferBuffer into a Span.
|
||||
/// </summary>
|
||||
public unsafe void GetData<T>(
|
||||
Span<T> destination,
|
||||
uint bufferOffsetInBytes = 0
|
||||
) where T : unmanaged
|
||||
{
|
||||
int elementSize = Marshal.SizeOf<T>();
|
||||
uint dataLengthInBytes = (uint) (elementSize * destination.Length);
|
||||
|
||||
#if DEBUG
|
||||
AssertBufferBoundsCheck(Size, bufferOffsetInBytes, dataLengthInBytes);
|
||||
AssertNotMapped();
|
||||
#endif
|
||||
|
||||
fixed (T* dataPtr = destination)
|
||||
{
|
||||
Refresh.Refresh_GetTransferData(
|
||||
Device.Handle,
|
||||
new Refresh.TransferBufferRegion
|
||||
{
|
||||
TransferBuffer = Handle,
|
||||
Offset = bufferOffsetInBytes,
|
||||
Size = dataLengthInBytes
|
||||
},
|
||||
(nint) dataPtr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the transfer buffer into application address space.
|
||||
/// You must call Unmap before encoding transfer commands.
|
||||
/// </summary>
|
||||
public unsafe void Map(bool cycle, out byte* data)
|
||||
{
|
||||
#if DEBUG
|
||||
AssertNotMapped();
|
||||
#endif
|
||||
|
||||
Refresh.Refresh_MapTransferBuffer(
|
||||
Device.Handle,
|
||||
Handle,
|
||||
Conversions.BoolToInt(cycle),
|
||||
out data
|
||||
);
|
||||
|
||||
#if DEBUG
|
||||
Mapped = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmaps the transfer buffer.
|
||||
/// The pointer given by Map is no longer valid.
|
||||
/// </summary>
|
||||
public void Unmap()
|
||||
{
|
||||
Refresh.Refresh_UnmapTransferBuffer(
|
||||
Device.Handle,
|
||||
Handle
|
||||
);
|
||||
|
||||
#if DEBUG
|
||||
Mapped = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes)
|
||||
{
|
||||
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
|
||||
{
|
||||
throw new InvalidOperationException($"Data overflow! Transfer buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertNotMapped()
|
||||
{
|
||||
if (Mapped)
|
||||
{
|
||||
throw new InvalidOperationException("Transfer buffer must not be mapped!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user