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 }