using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
public class CopyPass
{
public nint Handle { get; private set; }
internal void SetHandle(nint handle)
{
Handle = handle;
}
///
/// Uploads data from a TransferBuffer to a TextureSlice.
/// This copy occurs on the GPU timeline.
///
/// Overwriting the contents of the TransferBuffer before the command buffer
/// has finished execution will cause undefined behavior.
///
/// You MAY assume that the copy has finished for subsequent commands.
///
/// If true, cycles the texture if the given slice is bound.
public void UploadToTexture(
in TextureTransferInfo source,
in TextureRegion destination,
bool cycle
) {
#if DEBUG
AssertTransferBufferNotMapped(source.TransferBuffer);
#endif
Refresh.Refresh_UploadToTexture(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
Conversions.BoolToInt(cycle)
);
}
///
/// Uploads the contents of an entire buffer to a 2D texture with no mips.
///
public void UploadToTexture(
TransferBuffer source,
Texture destination,
bool cycle
) {
UploadToTexture(
new TextureTransferInfo(source),
new TextureRegion(destination),
cycle
);
}
///
/// Uploads data from a TransferBuffer to a Buffer.
/// This copy occurs on the GPU timeline.
///
/// Overwriting the contents of the TransferBuffer before the command buffer
/// has finished execution will cause undefined behavior.
///
/// You MAY assume that the copy has finished for subsequent commands.
///
/// If true, cycles the buffer if it is bound.
public void UploadToBuffer(
in TransferBufferLocation source,
in BufferRegion destination,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(source.TransferBuffer.Size, source.Offset, destination.Size);
AssertBufferBoundsCheck(destination.Buffer.Size, destination.Offset, destination.Size);
AssertTransferBufferNotMapped(source.TransferBuffer);
#endif
Refresh.Refresh_UploadToBuffer(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
Conversions.BoolToInt(cycle)
);
}
///
/// Copies the entire contents of a TransferBuffer to a Buffer.
///
public void UploadToBuffer(
TransferBuffer source,
Buffer destination,
bool cycle
) {
UploadToBuffer(
new TransferBufferLocation(source),
new BufferRegion(destination, 0, destination.Size),
cycle
);
}
///
/// Copies data element-wise into from a TransferBuffer to a Buffer.
///
public void UploadToBuffer(
TransferBuffer source,
Buffer destination,
uint sourceStartElement,
uint destinationStartElement,
uint numElements,
bool cycle
) where T : unmanaged
{
int elementSize = Marshal.SizeOf();
uint dataLengthInBytes = (uint) (elementSize * numElements);
uint srcOffsetInBytes = (uint) (elementSize * sourceStartElement);
uint dstOffsetInBytes = (uint) (elementSize * destinationStartElement);
UploadToBuffer(
new TransferBufferLocation(source, srcOffsetInBytes),
new BufferRegion(destination, dstOffsetInBytes, dataLengthInBytes),
cycle
);
}
///
/// Copies the contents of a TextureLocation to another TextureLocation.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
///
public void CopyTextureToTexture(
in TextureLocation source,
in TextureLocation destination,
uint w,
uint h,
uint d,
bool cycle
) {
#if DEBUG
AssertTextureBoundsCheck(source, w, h, d);
AssertTextureBoundsCheck(destination, w, h, d);
#endif
Refresh.Refresh_CopyTextureToTexture(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
w,
h,
d,
Conversions.BoolToInt(cycle)
);
}
///
/// Copies data from a Buffer to another Buffer.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
///
public void CopyBufferToBuffer(
in BufferLocation source,
in BufferLocation destination,
uint size,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(source.Buffer.Size, source.Offset, size);
AssertBufferBoundsCheck(destination.Buffer.Size, destination.Offset, size);
#endif
Refresh.Refresh_CopyBufferToBuffer(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
size,
Conversions.BoolToInt(cycle)
);
}
public void DownloadFromBuffer(
in BufferRegion source,
in TransferBufferLocation destination
) {
#if DEBUG
AssertBufferBoundsCheck(source.Buffer.Size, source.Offset, source.Size);
AssertBufferBoundsCheck(destination.TransferBuffer.Size, destination.Offset, source.Size);
AssertTransferBufferNotMapped(destination.TransferBuffer);
#endif
Refresh.Refresh_DownloadFromBuffer(
Handle,
source.ToRefresh(),
destination.ToRefresh()
);
}
public void DownloadFromTexture(
in TextureRegion source,
in TextureTransferInfo destination
) {
#if DEBUG
AssertTransferBufferNotMapped(destination.TransferBuffer);
#endif
Refresh.Refresh_DownloadFromTexture(
Handle,
source.ToRefresh(),
destination.ToRefresh()
);
}
#if DEBUG
private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes)
{
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
{
throw new System.InvalidOperationException($"SetBufferData overflow! buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
}
}
private void AssertTextureBoundsCheck(in TextureLocation textureLocation, uint w, uint h, uint d)
{
if (
textureLocation.X + w > textureLocation.TextureSlice.Texture.Width ||
textureLocation.Y + h > textureLocation.TextureSlice.Texture.Height ||
textureLocation.Z + d > textureLocation.TextureSlice.Texture.Depth
) {
throw new System.InvalidOperationException($"Texture data is out of bounds!");
}
}
private void AssertTransferBufferNotMapped(TransferBuffer transferBuffer)
{
if (transferBuffer.Mapped)
{
throw new System.InvalidOperationException("Transfer buffer must not be mapped!");
}
}
#endif
}