refactor CopyPass

sdl2_gpu
cosmonaut 2024-06-04 19:35:17 -07:00
parent 97dee2a170
commit 3cfb43438c
13 changed files with 381 additions and 344 deletions

View File

@ -48,12 +48,13 @@ namespace MoonWorks
/// </summary> /// </summary>
/// <param name="windowCreateInfo">The parameters that will be used to create the MainWindow.</param> /// <param name="windowCreateInfo">The parameters that will be used to create the MainWindow.</param>
/// <param name="frameLimiterSettings">The frame limiter settings.</param> /// <param name="frameLimiterSettings">The frame limiter settings.</param>
/// <param name="preferredBackends">Bitflags of which GPU backends to attempt to initialize.</param>
/// <param name="targetTimestep">How often Game.Update will run in terms of ticks per second.</param> /// <param name="targetTimestep">How often Game.Update will run in terms of ticks per second.</param>
/// <param name="debugMode">If true, enables extra debug checks. Should be turned off for release builds.</param> /// <param name="debugMode">If true, enables extra debug checks. Should be turned off for release builds.</param>
public Game( public Game(
WindowCreateInfo windowCreateInfo, WindowCreateInfo windowCreateInfo,
FrameLimiterSettings frameLimiterSettings, FrameLimiterSettings frameLimiterSettings,
Span<Backend> preferredBackends, BackendFlags preferredBackends,
int targetTimestep = 60, int targetTimestep = 60,
bool debugMode = false bool debugMode = false
) { ) {
@ -75,8 +76,6 @@ namespace MoonWorks
return; return;
} }
Logger.Initialize();
Logger.LogInfo("Initializing input..."); Logger.LogInfo("Initializing input...");
Inputs = new Inputs(); Inputs = new Inputs();
@ -89,7 +88,7 @@ namespace MoonWorks
Logger.LogInfo("Initializing main window..."); Logger.LogInfo("Initializing main window...");
MainWindow = new Window(windowCreateInfo, GraphicsDevice.WindowFlags | SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN); MainWindow = new Window(windowCreateInfo, GraphicsDevice.WindowFlags | SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN);
if (!GraphicsDevice.ClaimWindow(MainWindow, windowCreateInfo.PresentMode)) if (!GraphicsDevice.ClaimWindow(MainWindow, windowCreateInfo.SwapchainComposition, windowCreateInfo.PresentMode))
{ {
throw new System.SystemException("Could not claim window!"); throw new System.SystemException("Could not claim window!");
} }

View File

@ -617,6 +617,7 @@ public class CommandBuffer
renderPass.Handle renderPass.Handle
); );
renderPass.SetHandle(nint.Zero);
Device.RenderPassPool.Return(renderPass); Device.RenderPassPool.Return(renderPass);
} }
@ -730,6 +731,45 @@ public class CommandBuffer
return computePass; return computePass;
} }
public unsafe ComputePass BeginComputePass(
Span<StorageTextureReadWriteBinding> readWriteTextureBindings,
Span<StorageBufferReadWriteBinding> readWriteBufferBindings
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true;
#endif
var sdlTextureBindings = NativeMemory.Alloc(
(nuint) (readWriteTextureBindings.Length * Marshal.SizeOf<StorageTextureReadWriteBinding>())
);
var sdlBufferBindings = NativeMemory.Alloc(
(nuint) (readWriteBufferBindings.Length * Marshal.SizeOf<StorageBufferReadWriteBinding>())
);
var computePassHandle = SDL_Gpu.SDL_GpuBeginComputePass(
Handle,
(SDL_Gpu.StorageTextureReadWriteBinding*) sdlTextureBindings,
(uint) readWriteTextureBindings.Length,
(SDL_Gpu.StorageBufferReadWriteBinding*) sdlBufferBindings,
(uint) readWriteBufferBindings.Length
);
var computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG
computePass.active = true;
#endif
NativeMemory.Free(sdlTextureBindings);
NativeMemory.Free(sdlBufferBindings);
return computePass;
}
public void EndComputePass(ComputePass computePass) public void EndComputePass(ComputePass computePass)
{ {
#if DEBUG #if DEBUG
@ -742,6 +782,9 @@ public class CommandBuffer
SDL_Gpu.SDL_GpuEndComputePass( SDL_Gpu.SDL_GpuEndComputePass(
computePass.Handle computePass.Handle
); );
computePass.SetHandle(nint.Zero);
Device.ComputePassPool.Return(computePass);
} }
// Copy Pass // Copy Pass
@ -751,7 +794,7 @@ public class CommandBuffer
/// All copy commands must be made within a copy pass. /// All copy commands must be made within a copy pass.
/// It is an error to call this during any kind of pass. /// It is an error to call this during any kind of pass.
/// </summary> /// </summary>
public void BeginCopyPass() public CopyPass BeginCopyPass()
{ {
#if DEBUG #if DEBUG
AssertNotSubmitted(); AssertNotSubmitted();
@ -759,228 +802,15 @@ public class CommandBuffer
copyPassActive = true; copyPassActive = true;
#endif #endif
Refresh.Refresh_BeginCopyPass( var copyPassHandle = SDL_Gpu.SDL_GpuBeginCopyPass(Handle);
Device.Handle,
Handle var copyPass = Device.CopyPassPool.Obtain();
); copyPass.SetHandle(copyPassHandle);
return copyPass;
} }
public void EndCopyPass(CopyPass copyPass)
/// <summary>
/// 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.
/// </summary>
/// <param name="writeOption">Specifies data dependency behavior.</param>
public void UploadToTexture(
TransferBuffer transferBuffer,
in TextureRegion textureRegion,
in BufferImageCopy copyParams,
WriteOptions writeOption
)
{
#if DEBUG
AssertNotSubmitted();
AssertInCopyPass("Cannot upload to texture outside of copy pass!");
AssertBufferBoundsCheck(transferBuffer.Size, copyParams.BufferOffset, textureRegion.Size);
#endif
Refresh.Refresh_UploadToTexture(
Device.Handle,
Handle,
transferBuffer.Handle,
textureRegion.ToRefreshTextureRegion(),
copyParams.ToRefresh(),
(Refresh.WriteOptions) writeOption
);
}
/// <summary>
/// Uploads the contents of an entire buffer to a texture with no mips.
/// </summary>
public void UploadToTexture(
TransferBuffer transferBuffer,
Texture texture,
WriteOptions writeOption
) {
UploadToTexture(
transferBuffer,
new TextureRegion(texture),
new BufferImageCopy(0, 0, 0),
writeOption
);
}
/// <summary>
/// Uploads data from a TransferBuffer to a GpuBuffer.
/// 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.
/// </summary>
public void UploadToBuffer(
TransferBuffer transferBuffer,
GpuBuffer gpuBuffer,
in BufferCopy copyParams,
WriteOptions option
) {
#if DEBUG
AssertNotSubmitted();
AssertInCopyPass("Cannot upload to texture outside of copy pass!");
AssertBufferBoundsCheck(transferBuffer.Size, copyParams.SrcOffset, copyParams.Size);
AssertBufferBoundsCheck(gpuBuffer.Size, copyParams.DstOffset, copyParams.Size);
#endif
Refresh.Refresh_UploadToBuffer(
Device.Handle,
Handle,
transferBuffer.Handle,
gpuBuffer.Handle,
copyParams.ToRefresh(),
(Refresh.WriteOptions) option
);
}
/// <summary>
/// Copies the entire contents of a TransferBuffer to a GpuBuffer.
/// </summary>
public void UploadToBuffer(
TransferBuffer transferBuffer,
GpuBuffer gpuBuffer,
WriteOptions option
) {
UploadToBuffer(
transferBuffer,
gpuBuffer,
new BufferCopy(0, 0, transferBuffer.Size),
option
);
}
/// <summary>
/// Copies data element-wise into from a TransferBuffer to a GpuBuffer.
/// </summary>
public void UploadToBuffer<T>(
TransferBuffer transferBuffer,
GpuBuffer gpuBuffer,
uint sourceStartElement,
uint destinationStartElement,
uint numElements,
WriteOptions option
) where T : unmanaged
{
var elementSize = Marshal.SizeOf<T>();
var dataLengthInBytes = (uint) (elementSize * numElements);
var srcOffsetInBytes = (uint) (elementSize * sourceStartElement);
var dstOffsetInBytes = (uint) (elementSize * destinationStartElement);
UploadToBuffer(
transferBuffer,
gpuBuffer,
new BufferCopy(
srcOffsetInBytes,
dstOffsetInBytes,
dataLengthInBytes
),
option
);
}
/// <summary>
/// Copies the contents of a TextureSlice to another TextureSlice.
/// The slices must have the same dimensions.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
/// </summary>
public void CopyTextureToTexture(
in TextureRegion source,
in TextureRegion destination,
WriteOptions option
) {
#if DEBUG
AssertNotSubmitted();
AssertInCopyPass("Cannot download from texture outside of copy pass!");
AssertTextureBoundsCheck(destination.Size, source.Size);
#endif
Refresh.Refresh_CopyTextureToTexture(
Device.Handle,
Handle,
source.ToRefreshTextureRegion(),
destination.ToRefreshTextureRegion(),
(Refresh.WriteOptions) option
);
}
/// <summary>
/// Copies the contents of an entire Texture with no mips to another Texture with no mips.
/// The textures must have the same dimensions.
/// </summary>
public void CopyTextureToTexture(
Texture source,
Texture destination,
WriteOptions option
) {
CopyTextureToTexture(
new TextureRegion(source),
new TextureRegion(destination),
option
);
}
/// <summary>
/// Copies data from a GpuBuffer to another GpuBuffer.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
/// </summary>
public void CopyBufferToBuffer(
GpuBuffer source,
GpuBuffer destination,
in BufferCopy copyParams,
WriteOptions option
) {
#if DEBUG
AssertNotSubmitted();
AssertInCopyPass("Cannot download from texture outside of copy pass!");
AssertBufferBoundsCheck(source.Size, copyParams.SrcOffset, copyParams.Size);
AssertBufferBoundsCheck(destination.Size, copyParams.DstOffset, copyParams.Size);
#endif
Refresh.Refresh_CopyBufferToBuffer(
Device.Handle,
Handle,
source.Handle,
destination.Handle,
copyParams.ToRefresh(),
(Refresh.WriteOptions) option
);
}
/// <summary>
/// Copies the entire contents of a GpuBuffer to another GpuBuffer.
/// </summary>
public void CopyBufferToBuffer(
GpuBuffer source,
GpuBuffer destination,
WriteOptions option
) {
CopyBufferToBuffer(
source,
destination,
new BufferCopy(0, 0, source.Size),
option
);
}
public void EndCopyPass()
{ {
#if DEBUG #if DEBUG
AssertNotSubmitted(); AssertNotSubmitted();
@ -988,10 +818,12 @@ public class CommandBuffer
copyPassActive = false; copyPassActive = false;
#endif #endif
Refresh.Refresh_EndCopyPass( SDL_Gpu.SDL_GpuEndCopyPass(
Device.Handle, copyPass.Handle
Handle
); );
copyPass.SetHandle(nint.Zero);
Device.CopyPassPool.Return(copyPass);
} }
#if DEBUG #if DEBUG
@ -1011,22 +843,6 @@ public class CommandBuffer
} }
} }
private void AssertComputeBufferCount(int count)
{
if (currentComputePipeline.ComputeShaderInfo.BufferBindingCount != count)
{
throw new System.InvalidOperationException($"Compute pipeline expects {currentComputePipeline.ComputeShaderInfo.BufferBindingCount} buffers, but received {count}");
}
}
private void AssertComputeTextureCount(int count)
{
if (currentComputePipeline.ComputeShaderInfo.ImageBindingCount != count)
{
throw new System.InvalidOperationException($"Compute pipeline expects {currentComputePipeline.ComputeShaderInfo.ImageBindingCount} textures, but received {count}");
}
}
private void AssertTextureNotNull(ColorAttachmentInfo colorAttachmentInfo) private void AssertTextureNotNull(ColorAttachmentInfo colorAttachmentInfo)
{ {
if (colorAttachmentInfo.TextureSlice.Texture == null || colorAttachmentInfo.TextureSlice.Texture.Handle == IntPtr.Zero) if (colorAttachmentInfo.TextureSlice.Texture == null || colorAttachmentInfo.TextureSlice.Texture.Handle == IntPtr.Zero)
@ -1059,7 +875,7 @@ public class CommandBuffer
throw new System.ArgumentException("Render pass depth stencil attachment Texture cannot be null!"); throw new System.ArgumentException("Render pass depth stencil attachment Texture cannot be null!");
} }
if ((depthStencilAttachmentInfo.TextureSlice.Texture.UsageFlags & TextureUsageFlags.DepthStencilTarget) == 0) if ((depthStencilAttachmentInfo.TextureSlice.Texture.UsageFlags & TextureUsageFlags.DepthStencil) == 0)
{ {
throw new System.ArgumentException("Render pass depth stencil attachment UsageFlags must include TextureUsageFlags.DepthStencilTarget!"); throw new System.ArgumentException("Render pass depth stencil attachment UsageFlags must include TextureUsageFlags.DepthStencilTarget!");
} }

203
src/Graphics/CopyPass.cs Normal file
View File

@ -0,0 +1,203 @@
using System.Runtime.InteropServices;
using SDL2_gpuCS;
namespace MoonWorks.Graphics;
public class CopyPass
{
public nint Handle { get; private set; }
internal void SetHandle(nint handle)
{
Handle = handle;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="cycle">If true, cycles the texture if the given slice is bound.</param>
public void UploadToTexture(
TransferBuffer transferBuffer,
in TextureRegion textureRegion,
in BufferImageCopy copyParams,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(transferBuffer.Size, copyParams.BufferOffset, textureRegion.Size);
#endif
SDL_Gpu.SDL_GpuUploadToTexture(
Handle,
transferBuffer.Handle,
textureRegion.ToSDL(),
copyParams.ToSDL(),
Conversions.BoolToInt(cycle)
);
}
/// <summary>
/// Uploads the contents of an entire buffer to a 2D texture with no mips.
/// </summary>
public void UploadToTexture(
TransferBuffer transferBuffer,
Texture texture,
bool cycle
) {
UploadToTexture(
transferBuffer,
new TextureRegion(texture),
new BufferImageCopy(0, 0, 0),
cycle
);
}
/// <summary>
/// Uploads data from a TransferBuffer to a GpuBuffer.
/// 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.
/// </summary>
/// <param name="cycle">If true, cycles the buffer if it is bound.</param>
public void UploadToBuffer(
TransferBuffer transferBuffer,
GpuBuffer buffer,
in BufferCopy copyParams,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(transferBuffer.Size, copyParams.SrcOffset, copyParams.Size);
AssertBufferBoundsCheck(buffer.Size, copyParams.DstOffset, copyParams.Size);
#endif
SDL_Gpu.SDL_GpuUploadToBuffer(
Handle,
transferBuffer.Handle,
buffer.Handle,
copyParams.ToSDL(),
Conversions.BoolToInt(cycle)
);
}
/// <summary>
/// Copies the entire contents of a TransferBuffer to a GpuBuffer.
/// </summary>
public void UploadToBuffer(
TransferBuffer transferBuffer,
GpuBuffer buffer,
bool cycle
) {
UploadToBuffer(
transferBuffer,
buffer,
new BufferCopy(0, 0, transferBuffer.Size),
cycle
);
}
/// <summary>
/// Copies data element-wise into from a TransferBuffer to a GpuBuffer.
/// </summary>
public void UploadToBuffer<T>(
TransferBuffer transferBuffer,
GpuBuffer buffer,
uint sourceStartElement,
uint destinationStartElement,
uint numElements,
bool cycle
) where T : unmanaged
{
var elementSize = Marshal.SizeOf<T>();
var dataLengthInBytes = (uint) (elementSize * numElements);
var srcOffsetInBytes = (uint) (elementSize * sourceStartElement);
var dstOffsetInBytes = (uint) (elementSize * destinationStartElement);
UploadToBuffer(
transferBuffer,
buffer,
new BufferCopy(srcOffsetInBytes, dstOffsetInBytes, dataLengthInBytes),
cycle
);
}
/// <summary>
/// Copies the contents of a TextureRegion to another TextureRegion.
/// The regions must have the same dimensions.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
/// </summary>
public void CopyTextureToTexture(
in TextureRegion source,
in TextureRegion destination,
bool cycle
) {
#if DEBUG
AssertTextureBoundsCheck(destination.Size, source.Size);
if (source.Width != destination.Width || source.Height != destination.Height || source.Depth != destination.Depth)
{
throw new System.InvalidOperationException("Texture copy must have the same dimensions!");
}
#endif
SDL_Gpu.SDL_GpuCopyTextureToTexture(
Handle,
source.ToSDL(),
destination.ToSDL(),
Conversions.BoolToInt(cycle)
);
}
/// <summary>
/// Copies data from a GpuBuffer to another GpuBuffer.
/// This copy occurs on the GPU timeline.
///
/// You MAY assume that the copy has finished in subsequent commands.
/// </summary>
public void CopyBufferToBuffer(
GpuBuffer source,
GpuBuffer destination,
in BufferCopy copyParams,
bool cycle
) {
#if DEBUG
AssertBufferBoundsCheck(source.Size, copyParams.SrcOffset, copyParams.Size);
AssertBufferBoundsCheck(destination.Size, copyParams.DstOffset, copyParams.Size);
#endif
SDL_Gpu.SDL_GpuCopyBufferToBuffer(
Handle,
source.Handle,
destination.Handle,
copyParams.ToSDL(),
Conversions.BoolToInt(cycle)
);
}
#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(uint textureSizeInBytes, uint dataLengthInBytes)
{
if (dataLengthInBytes > textureSizeInBytes)
{
throw new System.InvalidOperationException($"SetTextureData overflow! texture size {textureSizeInBytes}, data size {dataLengthInBytes}");
}
}
#endif
}

View File

@ -0,0 +1,25 @@
using System.Collections.Concurrent;
namespace MoonWorks.Graphics;
internal class CopyPassPool
{
private ConcurrentQueue<CopyPass> CopyPasses = new ConcurrentQueue<CopyPass>();
public CopyPass Obtain()
{
if (CopyPasses.TryDequeue(out var copyPass))
{
return copyPass;
}
else
{
return new CopyPass();
}
}
public void Return(CopyPass copyPass)
{
CopyPasses.Enqueue(copyPass);
}
}

View File

@ -41,6 +41,7 @@ namespace MoonWorks.Graphics
private FencePool FencePool; private FencePool FencePool;
internal RenderPassPool RenderPassPool; internal RenderPassPool RenderPassPool;
internal ComputePassPool ComputePassPool; internal ComputePassPool ComputePassPool;
internal CopyPassPool CopyPassPool;
internal unsafe GraphicsDevice( internal unsafe GraphicsDevice(
BackendFlags preferredBackends, BackendFlags preferredBackends,

View File

@ -2,6 +2,7 @@ using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using RefreshCS; using RefreshCS;
using SDL2;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
{ {

View File

@ -26,8 +26,8 @@ namespace MoonWorks.Graphics
uint textureDataOffset = 0; uint textureDataOffset = 0;
uint textureDataSize = 1024; uint textureDataSize = 1024;
List<(GpuBuffer, BufferCopy, WriteOptions)> BufferUploads = new List<(GpuBuffer, BufferCopy, WriteOptions)>(); List<(GpuBuffer, BufferCopy, bool)> BufferUploads = new List<(GpuBuffer, BufferCopy, bool)>();
List<(TextureRegion, uint, WriteOptions)> TextureUploads = new List<(TextureRegion, uint, WriteOptions)>(); List<(TextureRegion, uint, bool)> TextureUploads = new List<(TextureRegion, uint, bool)>();
public ResourceUploader(GraphicsDevice device) : base(device) public ResourceUploader(GraphicsDevice device) : base(device)
{ {
@ -45,7 +45,7 @@ namespace MoonWorks.Graphics
var lengthInBytes = (uint) (Marshal.SizeOf<T>() * data.Length); var lengthInBytes = (uint) (Marshal.SizeOf<T>() * data.Length);
var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes); var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes);
SetBufferData(gpuBuffer, 0, data, WriteOptions.Unsafe); SetBufferData(gpuBuffer, 0, data, false);
return gpuBuffer; return gpuBuffer;
} }
@ -53,7 +53,7 @@ namespace MoonWorks.Graphics
/// <summary> /// <summary>
/// Prepares upload of data into a GpuBuffer. /// Prepares upload of data into a GpuBuffer.
/// </summary> /// </summary>
public void SetBufferData<T>(GpuBuffer buffer, uint bufferOffsetInElements, Span<T> data, WriteOptions option) where T : unmanaged public void SetBufferData<T>(GpuBuffer buffer, uint bufferOffsetInElements, Span<T> data, bool cycle) where T : unmanaged
{ {
uint elementSize = (uint) Marshal.SizeOf<T>(); uint elementSize = (uint) Marshal.SizeOf<T>();
uint offsetInBytes = elementSize * bufferOffsetInElements; uint offsetInBytes = elementSize * bufferOffsetInElements;
@ -66,7 +66,7 @@ namespace MoonWorks.Graphics
} }
var bufferCopyParams = new BufferCopy(resourceOffset, offsetInBytes, lengthInBytes); var bufferCopyParams = new BufferCopy(resourceOffset, offsetInBytes, lengthInBytes);
BufferUploads.Add((buffer, bufferCopyParams, option)); BufferUploads.Add((buffer, bufferCopyParams, cycle));
} }
// Textures // Textures
@ -74,7 +74,7 @@ namespace MoonWorks.Graphics
public Texture CreateTexture2D<T>(Span<T> pixelData, uint width, uint height) where T : unmanaged public Texture CreateTexture2D<T>(Span<T> pixelData, uint width, uint height) where T : unmanaged
{ {
var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
SetTextureData(texture, pixelData, WriteOptions.Unsafe); SetTextureData(texture, pixelData, false);
return texture; return texture;
} }
@ -164,7 +164,7 @@ namespace MoonWorks.Graphics
Depth = 1 Depth = 1
}; };
SetTextureData(textureRegion, byteSpan, WriteOptions.Unsafe); SetTextureData(textureRegion, byteSpan, false);
NativeMemory.Free(byteBuffer); NativeMemory.Free(byteBuffer);
} }
@ -187,7 +187,7 @@ namespace MoonWorks.Graphics
var pixelData = ImageUtils.GetPixelDataFromBytes(compressedImageData, out var _, out var _, out var sizeInBytes); var pixelData = ImageUtils.GetPixelDataFromBytes(compressedImageData, out var _, out var _, out var sizeInBytes);
var pixelSpan = new Span<byte>((void*) pixelData, (int) sizeInBytes); var pixelSpan = new Span<byte>((void*) pixelData, (int) sizeInBytes);
SetTextureData(textureRegion, pixelSpan, WriteOptions.Unsafe); SetTextureData(textureRegion, pixelSpan, false);
ImageUtils.FreePixelData(pixelData); ImageUtils.FreePixelData(pixelData);
} }
@ -211,7 +211,7 @@ namespace MoonWorks.Graphics
/// <summary> /// <summary>
/// Prepares upload of pixel data into a TextureSlice. /// Prepares upload of pixel data into a TextureSlice.
/// </summary> /// </summary>
public void SetTextureData<T>(TextureRegion textureRegion, Span<T> data, WriteOptions option) where T : unmanaged public void SetTextureData<T>(TextureRegion textureRegion, Span<T> data, bool cycle) where T : unmanaged
{ {
var elementSize = Marshal.SizeOf<T>(); var elementSize = Marshal.SizeOf<T>();
var dataLengthInBytes = (uint) (elementSize * data.Length); var dataLengthInBytes = (uint) (elementSize * data.Length);
@ -222,7 +222,7 @@ namespace MoonWorks.Graphics
resourceOffset = CopyTextureData(dataPtr, dataLengthInBytes, Texture.BytesPerPixel(textureRegion.TextureSlice.Texture.Format)); resourceOffset = CopyTextureData(dataPtr, dataLengthInBytes, Texture.BytesPerPixel(textureRegion.TextureSlice.Texture.Format));
} }
TextureUploads.Add((textureRegion, resourceOffset, option)); TextureUploads.Add((textureRegion, resourceOffset, cycle));
} }
// Upload // Upload
@ -263,11 +263,11 @@ namespace MoonWorks.Graphics
if (BufferTransferBuffer == null || BufferTransferBuffer.Size < bufferDataSize) if (BufferTransferBuffer == null || BufferTransferBuffer.Size < bufferDataSize)
{ {
BufferTransferBuffer?.Dispose(); BufferTransferBuffer?.Dispose();
BufferTransferBuffer = new TransferBuffer(Device, TransferUsage.Buffer, bufferDataSize); BufferTransferBuffer = new TransferBuffer(Device, TransferUsage.Buffer, TransferBufferMapFlags.Write, bufferDataSize);
} }
var dataSpan = new Span<byte>(bufferData, (int) bufferDataSize); var dataSpan = new Span<byte>(bufferData, (int) bufferDataSize);
BufferTransferBuffer.SetData(dataSpan, TransferOptions.Cycle); BufferTransferBuffer.SetData(dataSpan, true);
} }
@ -276,21 +276,21 @@ namespace MoonWorks.Graphics
if (TextureTransferBuffer == null || TextureTransferBuffer.Size < textureDataSize) if (TextureTransferBuffer == null || TextureTransferBuffer.Size < textureDataSize)
{ {
TextureTransferBuffer?.Dispose(); TextureTransferBuffer?.Dispose();
TextureTransferBuffer = new TransferBuffer(Device, TransferUsage.Texture, textureDataSize); TextureTransferBuffer = new TransferBuffer(Device, TransferUsage.Texture, TransferBufferMapFlags.Write, textureDataSize);
} }
var dataSpan = new Span<byte>(textureData, (int) textureDataSize); var dataSpan = new Span<byte>(textureData, (int) textureDataSize);
TextureTransferBuffer.SetData(dataSpan, TransferOptions.Cycle); TextureTransferBuffer.SetData(dataSpan, true);
} }
} }
private void RecordUploadCommands(CommandBuffer commandBuffer) private void RecordUploadCommands(CommandBuffer commandBuffer)
{ {
commandBuffer.BeginCopyPass(); var copyPass = commandBuffer.BeginCopyPass();
foreach (var (gpuBuffer, bufferCopyParams, option) in BufferUploads) foreach (var (gpuBuffer, bufferCopyParams, option) in BufferUploads)
{ {
commandBuffer.UploadToBuffer( copyPass.UploadToBuffer(
BufferTransferBuffer, BufferTransferBuffer,
gpuBuffer, gpuBuffer,
bufferCopyParams, bufferCopyParams,
@ -300,7 +300,7 @@ namespace MoonWorks.Graphics
foreach (var (textureRegion, offset, option) in TextureUploads) foreach (var (textureRegion, offset, option) in TextureUploads)
{ {
commandBuffer.UploadToTexture( copyPass.UploadToTexture(
TextureTransferBuffer, TextureTransferBuffer,
textureRegion, textureRegion,
new BufferImageCopy( new BufferImageCopy(
@ -312,7 +312,7 @@ namespace MoonWorks.Graphics
); );
} }
commandBuffer.EndCopyPass(); commandBuffer.EndCopyPass(copyPass);
BufferUploads.Clear(); BufferUploads.Clear();
TextureUploads.Clear(); TextureUploads.Clear();

View File

@ -1,18 +1,18 @@
using System; using System;
using RefreshCS; using SDL2_gpuCS;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics;
/// <summary>
/// Fences allow you to track the status of a submitted command buffer. <br/>
/// You should only acquire a Fence if you will need to track the command buffer. <br/>
/// You should make sure to call GraphicsDevice.ReleaseFence when done with a Fence to avoid memory growth. <br/>
/// The Fence object itself is basically just a wrapper for the Refresh_Fence. <br/>
/// The internal handle is replaced so that we can pool Fence objects to manage garbage.
/// </summary>
public class Fence : SDL_GpuResource
{ {
/// <summary> protected override Action<nint, nint> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseFence;
/// Fences allow you to track the status of a submitted command buffer. <br/>
/// You should only acquire a Fence if you will need to track the command buffer. <br/>
/// You should make sure to call GraphicsDevice.ReleaseFence when done with a Fence to avoid memory growth. <br/>
/// The Fence object itself is basically just a wrapper for the Refresh_Fence. <br/>
/// The internal handle is replaced so that we can pool Fence objects to manage garbage.
/// </summary>
public class Fence : SDL_GpuResource
{
protected override Action<nint, nint> ReleaseFunction => Refresh.Refresh_ReleaseFence;
internal Fence(GraphicsDevice device) : base(device) internal Fence(GraphicsDevice device) : base(device)
{ {
@ -22,5 +22,4 @@ namespace MoonWorks.Graphics
{ {
Handle = handle; Handle = handle;
} }
}
} }

View File

@ -1,24 +1,23 @@
using System; using System;
using RefreshCS; using SDL2_gpuCS;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics;
/// <summary>
/// A sampler specifies how a texture will be sampled in a shader.
/// </summary>
public class Sampler : SDL_GpuResource
{ {
/// <summary> protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseSampler;
/// A sampler specifies how a texture will be sampled in a shader.
/// </summary>
public class Sampler : SDL_GpuResource
{
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroySampler;
public Sampler( public Sampler(
GraphicsDevice device, GraphicsDevice device,
in SamplerCreateInfo samplerCreateInfo in SamplerCreateInfo samplerCreateInfo
) : base(device) ) : base(device)
{ {
Handle = Refresh.Refresh_CreateSampler( Handle = SDL_Gpu.SDL_GpuCreateSampler(
device.Handle, device.Handle,
samplerCreateInfo.ToRefreshSamplerStateCreateInfo() samplerCreateInfo.ToSDL()
); );
} }
}
} }

View File

@ -1,12 +1,12 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using RefreshCS; using SDL2_gpuCS;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
{ {
public unsafe class TransferBuffer : SDL_GpuResource public unsafe class TransferBuffer : SDL_GpuResource
{ {
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyTransferBuffer; protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseTransferBuffer;
/// <summary> /// <summary>
/// Size in bytes. /// Size in bytes.
@ -23,12 +23,14 @@ namespace MoonWorks.Graphics
public unsafe static TransferBuffer Create<T>( public unsafe static TransferBuffer Create<T>(
GraphicsDevice device, GraphicsDevice device,
TransferUsage usage, TransferUsage usage,
TransferBufferMapFlags mapFlags,
uint elementCount uint elementCount
) where T : unmanaged ) where T : unmanaged
{ {
return new TransferBuffer( return new TransferBuffer(
device, device,
usage, usage,
mapFlags,
(uint) Marshal.SizeOf<T>() * elementCount (uint) Marshal.SizeOf<T>() * elementCount
); );
} }
@ -42,12 +44,14 @@ namespace MoonWorks.Graphics
public TransferBuffer( public TransferBuffer(
GraphicsDevice device, GraphicsDevice device,
TransferUsage usage, TransferUsage usage,
TransferBufferMapFlags mapFlags,
uint sizeInBytes uint sizeInBytes
) : base(device) ) : base(device)
{ {
Handle = Refresh.Refresh_CreateTransferBuffer( Handle = SDL_Gpu.SDL_GpuCreateTransferBuffer(
device.Handle, device.Handle,
(Refresh.TransferUsage) usage, (SDL_Gpu.TransferUsage) usage,
(SDL_Gpu.TransferBufferMapFlags) mapFlags,
sizeInBytes sizeInBytes
); );
Size = sizeInBytes; Size = sizeInBytes;
@ -57,16 +61,16 @@ namespace MoonWorks.Graphics
/// Immediately copies data from a Span to the TransferBuffer. /// Immediately copies data from a Span to the TransferBuffer.
/// Returns the length of the copy in bytes. /// Returns the length of the copy in bytes.
/// ///
/// If setDataOption is DISCARD and this TransferBuffer was used in an Upload command, /// 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. /// that command will still use the corret data at the cost of increased memory usage.
/// ///
/// If setDataOption is OVERWRITE and this TransferBuffer was used in an Upload command, /// If cycle is set to false, the data will be overwritten immediately,
/// the data will be overwritten immediately, which could cause a data race. /// which could cause a data race.
/// </summary> /// </summary>
public unsafe uint SetData<T>( public unsafe uint SetData<T>(
Span<T> data, Span<T> data,
uint bufferOffsetInBytes, uint bufferOffsetInBytes,
TransferOptions setDataOption bool cycle
) where T : unmanaged ) where T : unmanaged
{ {
var elementSize = Marshal.SizeOf<T>(); var elementSize = Marshal.SizeOf<T>();
@ -78,17 +82,17 @@ namespace MoonWorks.Graphics
fixed (T* dataPtr = data) fixed (T* dataPtr = data)
{ {
Refresh.Refresh_SetTransferData( SDL_Gpu.SDL_GpuSetTransferData(
Device.Handle, Device.Handle,
(nint) dataPtr, (nint) dataPtr,
Handle, Handle,
new Refresh.BufferCopy new SDL_Gpu.BufferCopy
{ {
srcOffset = 0, SourceOffset = 0,
dstOffset = bufferOffsetInBytes, DestinationOffset = bufferOffsetInBytes,
size = dataLengthInBytes Size = dataLengthInBytes
}, },
(Refresh.TransferOptions) setDataOption Conversions.BoolToInt(cycle)
); );
} }
@ -99,18 +103,18 @@ namespace MoonWorks.Graphics
/// Immediately copies data from a Span to the TransferBuffer. /// Immediately copies data from a Span to the TransferBuffer.
/// Returns the length of the copy in bytes. /// Returns the length of the copy in bytes.
/// ///
/// If setDataOption is DISCARD and this TransferBuffer was used in an Upload command, /// 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. /// that command will still use the corret data at the cost of increased memory usage.
/// ///
/// If setDataOption is OVERWRITE and this TransferBuffer was used in an Upload command, /// If cycle is set to false, the data will be overwritten immediately,
/// the data will be overwritten immediately, which could cause a data race. /// which could cause a data race.
/// </summary> /// </summary>
public unsafe uint SetData<T>( public unsafe uint SetData<T>(
Span<T> data, Span<T> data,
TransferOptions setDataOption bool cycle
) where T : unmanaged ) where T : unmanaged
{ {
return SetData(data, 0, setDataOption); return SetData(data, 0, cycle);
} }
/// <summary> /// <summary>
@ -130,15 +134,15 @@ namespace MoonWorks.Graphics
fixed (T* dataPtr = data) fixed (T* dataPtr = data)
{ {
Refresh.Refresh_GetTransferData( SDL_Gpu.SDL_GpuGetTransferData(
Device.Handle, Device.Handle,
Handle, Handle,
(nint) dataPtr, (nint) dataPtr,
new Refresh.BufferCopy new SDL_Gpu.BufferCopy
{ {
srcOffset = bufferOffsetInBytes, SourceOffset = bufferOffsetInBytes,
dstOffset = 0, DestinationOffset = 0,
size = dataLengthInBytes Size = dataLengthInBytes
} }
); );
} }

View File

@ -788,7 +788,7 @@ public struct BufferCopy
Size = size; Size = size;
} }
public SDL_Gpu.BufferCopy ToRefresh() public SDL_Gpu.BufferCopy ToSDL()
{ {
return new SDL_Gpu.BufferCopy return new SDL_Gpu.BufferCopy
{ {
@ -811,7 +811,7 @@ public readonly record struct BufferImageCopy(
uint BufferStride, uint BufferStride,
uint BufferImageHeight uint BufferImageHeight
) { ) {
public SDL_Gpu.BufferImageCopy ToRefresh() public SDL_Gpu.BufferImageCopy ToSDL()
{ {
return new SDL_Gpu.BufferImageCopy return new SDL_Gpu.BufferImageCopy
{ {

View File

@ -1,5 +1,4 @@
using System; using System;
using RefreshCS;
namespace MoonWorks namespace MoonWorks
{ {
@ -9,19 +8,6 @@ namespace MoonWorks
public static Action<string> LogWarn = LogWarnDefault; public static Action<string> LogWarn = LogWarnDefault;
public static Action<string> LogError = LogErrorDefault; public static Action<string> LogError = LogErrorDefault;
private static RefreshCS.Refresh.Refresh_LogFunc LogInfoFunc = RefreshLogInfo;
private static RefreshCS.Refresh.Refresh_LogFunc LogWarnFunc = RefreshLogWarn;
private static RefreshCS.Refresh.Refresh_LogFunc LogErrorFunc = RefreshLogError;
internal static void Initialize()
{
Refresh.Refresh_HookLogFunctions(
LogInfoFunc,
LogWarnFunc,
LogErrorFunc
);
}
private static void LogInfoDefault(string str) private static void LogInfoDefault(string str)
{ {
Console.ForegroundColor = ConsoleColor.Green; Console.ForegroundColor = ConsoleColor.Green;

View File

@ -22,9 +22,13 @@
/// </summary> /// </summary>
public ScreenMode ScreenMode; public ScreenMode ScreenMode;
/// <summary> /// <summary>
/// Specifies the swapchain composition. Use SDR unless you know what you're doing.
/// </summary>
public Graphics.SwapchainComposition SwapchainComposition;
/// <summary>
/// Specifies the presentation mode for the window. Roughly equivalent to V-Sync. /// Specifies the presentation mode for the window. Roughly equivalent to V-Sync.
/// </summary> /// </summary>
public PresentMode PresentMode; public Graphics.PresentMode PresentMode;
/// <summary> /// <summary>
/// Whether the window can be resized using the operating system's window dragging feature. /// Whether the window can be resized using the operating system's window dragging feature.
/// </summary> /// </summary>
@ -39,7 +43,7 @@
uint windowWidth, uint windowWidth,
uint windowHeight, uint windowHeight,
ScreenMode screenMode, ScreenMode screenMode,
PresentMode presentMode, Graphics.PresentMode presentMode,
bool systemResizable = false, bool systemResizable = false,
bool startMaximized = false bool startMaximized = false
) { ) {