refactor ComputePass

sdl2_gpu
cosmonaut 2024-06-04 17:24:25 -07:00
parent fe6734d6db
commit 97dee2a170
15 changed files with 425 additions and 558 deletions

@ -1 +1 @@
Subproject commit 60e2c21a7b224dc6aa6a457dbb71d798a4e66241 Subproject commit 70925aa394c0f24388f3cfb97f1c6ee1f91baf52

View File

@ -1,19 +0,0 @@
using SDL2_gpuCS;
namespace MoonWorks.Graphics;
/// <summary>
/// A buffer-offset pair to be used when binding buffers.
/// </summary>
public readonly record struct BufferBinding(
GpuBuffer Buffer,
uint Offset
) {
public SDL_Gpu.BufferBinding ToSDL()
{
return new SDL_Gpu.BufferBinding
{
Buffer = Buffer.Handle,
Offset = Offset
};
}
}

View File

@ -1,34 +0,0 @@
using SDL2_gpuCS;
namespace MoonWorks.Graphics;
/// <summary>
/// Binding specification to be used when binding buffers for compute shaders.
/// </summary>
/// <param name="GpuBuffer">The GpuBuffer to bind.</param>
/// <param name="WriteOption">
/// Specifies data dependency behavior when this buffer is written to in the shader. <br/>
///
/// Cycle:
/// If this buffer has been used in commands that have not finished,
/// the implementation may choose to prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous data is retained.
/// This may prevent stalls when frequently updating a resource. <br />
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </param>
public readonly record struct ComputeBufferBinding(
GpuBuffer GpuBuffer,
WriteOptions WriteOption
) {
public SDL_Gpu.BufferBinding ToRefresh()
{
return new Refresh.ComputeBufferBinding
{
gpuBuffer = GpuBuffer.Handle,
writeOption = (Refresh.WriteOptions) WriteOption
};
}
}

View File

@ -1,35 +0,0 @@
using RefreshCS;
namespace MoonWorks.Graphics
{
/// <summary>
/// Binding specification used for binding texture slices for compute shaders.
/// </summary>
/// <param name="TextureSlice">The TextureSlice to bind.</param>
/// <param name="WriteOption">
/// Specifies data dependency behavior when this texture is written to in the shader. <br/>
///
/// Cycle:
/// If this buffer has been used in commands that have not finished,
/// the implementation may choose to prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous data is retained.
/// This may prevent stalls when frequently updating a resource. <br />
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </param>
public readonly record struct ComputeTextureBinding(
TextureSlice TextureSlice,
WriteOptions WriteOption
) {
public Refresh.ComputeTextureBinding ToRefresh()
{
return new Refresh.ComputeTextureBinding
{
textureSlice = TextureSlice.ToRefreshTextureSlice(),
writeOption = (Refresh.WriteOptions) WriteOption
};
}
}
}

View File

@ -1,21 +0,0 @@
using SDL2_gpuCS;
namespace MoonWorks.Graphics
{
/// <summary>
/// A texture-sampler pair to be used when binding samplers.
/// </summary>
public readonly record struct TextureSamplerBinding(
Texture Texture,
Sampler Sampler
) {
public SDL_Gpu.TextureSamplerBinding ToSDL()
{
return new SDL_Gpu.TextureSamplerBinding
{
Texture = Texture.Handle,
Sampler = Sampler.Handle
};
}
}
}

View File

@ -641,401 +641,106 @@ public class CommandBuffer
); );
} }
public void BeginComputePass() public unsafe ComputePass BeginComputePass(
{ in StorageTextureReadWriteBinding readWriteTextureBinding
) {
#if DEBUG #if DEBUG
AssertNotSubmitted(); AssertNotSubmitted();
AssertNotInPass("Cannot begin compute pass while in another pass!"); AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true; computePassActive = true;
#endif #endif
Refresh.Refresh_BeginComputePass( var sdlTextureBinding = readWriteTextureBinding.ToSDL();
Device.Handle,
Handle var computePassHandle = SDL_Gpu.SDL_GpuBeginComputePass(
Handle,
&sdlTextureBinding,
1,
(SDL_Gpu.StorageBufferReadWriteBinding*) nint.Size,
0
); );
var computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG
computePass.active = true;
#endif
return computePass;
} }
/// <summary> public unsafe ComputePass BeginComputePass(
/// Binds a compute pipeline so that compute work may be dispatched. in StorageBufferReadWriteBinding readWriteBufferBinding
/// </summary>
/// <param name="computePipeline">The compute pipeline to bind.</param>
public void BindComputePipeline(
ComputePipeline computePipeline
) { ) {
#if DEBUG #if DEBUG
AssertNotSubmitted(); AssertNotSubmitted();
AssertInComputePass("Cannot bind compute pipeline outside of compute pass!"); AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true;
#endif #endif
Refresh.Refresh_BindComputePipeline( var sdlBufferBinding = readWriteBufferBinding.ToSDL();
Device.Handle,
var computePassHandle = SDL_Gpu.SDL_GpuBeginComputePass(
Handle, Handle,
computePipeline.Handle (SDL_Gpu.StorageTextureReadWriteBinding*) nint.Zero,
0,
&sdlBufferBinding,
1
); );
var computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG #if DEBUG
currentComputePipeline = computePipeline; computePass.active = true;
#endif #endif
return computePass;
} }
/// <summary> public unsafe ComputePass BeginComputePass(
/// Binds a buffer to be used in the compute shader. in StorageTextureReadWriteBinding readWriteTextureBinding,
/// </summary> in StorageBufferReadWriteBinding readWriteBufferBinding
public unsafe void BindComputeBuffers(
ComputeBufferBinding binding
) { ) {
#if DEBUG #if DEBUG
AssertNotSubmitted(); AssertNotSubmitted();
AssertInComputePass("Cannot bind compute buffers outside of compute pass!"); AssertNotInPass("Cannot begin compute pass while in another pass!");
AssertComputePipelineBound(); computePassActive = true;
AssertComputeBufferCount(1);
#endif #endif
var bindingArray = stackalloc Refresh.ComputeBufferBinding[1]; var sdlTextureBinding = readWriteTextureBinding.ToSDL();
bindingArray[0] = binding.ToRefresh(); var sdlBufferBinding = readWriteBufferBinding.ToSDL();
Refresh.Refresh_BindComputeBuffers( var computePassHandle = SDL_Gpu.SDL_GpuBeginComputePass(
Device.Handle,
Handle, Handle,
bindingArray &sdlTextureBinding,
1,
&sdlBufferBinding,
1
); );
}
/// <summary> var computePass = Device.ComputePassPool.Obtain();
/// Binds buffers to be used in the compute shader. computePass.SetHandle(computePassHandle);
/// </summary>
public unsafe void BindComputeBuffers(
ComputeBufferBinding bindingOne,
ComputeBufferBinding bindingTwo
) {
#if DEBUG #if DEBUG
AssertNotSubmitted(); computePass.active = true;
AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
AssertComputePipelineBound();
AssertComputeBufferCount(2);
#endif #endif
var bindingArray = stackalloc Refresh.ComputeBufferBinding[2]; return computePass;
bindingArray[0] = bindingOne.ToRefresh();
bindingArray[1] = bindingTwo.ToRefresh();
Refresh.Refresh_BindComputeBuffers(
Device.Handle,
Handle,
bindingArray
);
} }
/// <summary> public void EndComputePass(ComputePass computePass)
/// Binds buffers to be used in the compute shader.
/// </summary>
public unsafe void BindComputeBuffers(
ComputeBufferBinding bindingOne,
ComputeBufferBinding bindingTwo,
ComputeBufferBinding bindingThree
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
AssertComputePipelineBound();
AssertComputeBufferCount(3);
#endif
var bindingArray = stackalloc Refresh.ComputeBufferBinding[3];
bindingArray[0] = bindingOne.ToRefresh();
bindingArray[1] = bindingTwo.ToRefresh();
bindingArray[2] = bindingThree.ToRefresh();
Refresh.Refresh_BindComputeBuffers(
Device.Handle,
Handle,
bindingArray
);
}
/// <summary>
/// Binds buffers to be used in the compute shader.
/// </summary>
public unsafe void BindComputeBuffers(
ComputeBufferBinding bindingOne,
ComputeBufferBinding bindingTwo,
ComputeBufferBinding bindingThree,
ComputeBufferBinding bindingFour
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
AssertComputePipelineBound();
AssertComputeBufferCount(4);
#endif
var bindingArray = stackalloc Refresh.ComputeBufferBinding[4];
bindingArray[0] = bindingOne.ToRefresh();
bindingArray[1] = bindingTwo.ToRefresh();
bindingArray[2] = bindingThree.ToRefresh();
bindingArray[3] = bindingFour.ToRefresh();
Refresh.Refresh_BindComputeBuffers(
Device.Handle,
Handle,
bindingArray
);
}
/// <summary>
/// Binds buffers to be used in the compute shader.
/// </summary>
/// <param name="buffers">A Span of buffers to bind.</param>
public unsafe void BindComputeBuffers(
in Span<ComputeBufferBinding> bindings
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
AssertComputePipelineBound();
AssertComputeBufferCount(bindings.Length);
#endif
Refresh.ComputeBufferBinding* bindingArray = (Refresh.ComputeBufferBinding*) NativeMemory.Alloc(
(nuint) (Marshal.SizeOf<ComputeBufferBinding>() * bindings.Length)
);
for (var i = 0; i < bindings.Length; i += 1)
{
bindingArray[i] = bindings[i].ToRefresh();
}
Refresh.Refresh_BindComputeBuffers(
Device.Handle,
Handle,
bindingArray
);
NativeMemory.Free(bindingArray);
}
/// <summary>
/// Binds a texture slice to be used in the compute shader.
/// </summary>
public unsafe void BindComputeTextures(
ComputeTextureBinding binding
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute textures outside of compute pass!");
AssertComputePipelineBound();
AssertComputeTextureCount(1);
#endif
var bindingArray = stackalloc Refresh.ComputeTextureBinding[1];
bindingArray[0] = binding.ToRefresh();
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
bindingArray
);
}
/// <summary>
/// Binds textures to be used in the compute shader.
/// </summary>
public unsafe void BindComputeTextures(
ComputeTextureBinding bindingOne,
ComputeTextureBinding bindingTwo
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute textures outside of compute pass!");
AssertComputePipelineBound();
AssertComputeTextureCount(2);
#endif
var bindingArray = stackalloc Refresh.ComputeTextureBinding[2];
bindingArray[0] = bindingOne.ToRefresh();
bindingArray[1] = bindingTwo.ToRefresh();
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
bindingArray
);
}
/// <summary>
/// Binds textures to be used in the compute shader.
/// </summary>
public unsafe void BindComputeTextures(
ComputeTextureBinding bindingOne,
ComputeTextureBinding bindingTwo,
ComputeTextureBinding bindingThree
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute textures outside of compute pass!");
AssertComputePipelineBound();
AssertComputeTextureCount(3);
#endif
var bindingArray = stackalloc Refresh.ComputeTextureBinding[3];
bindingArray[0] = bindingOne.ToRefresh();
bindingArray[1] = bindingTwo.ToRefresh();
bindingArray[2] = bindingThree.ToRefresh();
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
bindingArray
);
}
/// <summary>
/// Binds textures to be used in the compute shader.
/// </summary>
public unsafe void BindComputeTextures(
ComputeTextureBinding bindingOne,
ComputeTextureBinding bindingTwo,
ComputeTextureBinding bindingThree,
ComputeTextureBinding bindingFour
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute textures outside of compute pass!");
AssertComputePipelineBound();
AssertComputeTextureCount(4);
#endif
var textureSlicePtrs = stackalloc Refresh.ComputeTextureBinding[4];
textureSlicePtrs[0] = bindingOne.ToRefresh();
textureSlicePtrs[1] = bindingTwo.ToRefresh();
textureSlicePtrs[2] = bindingThree.ToRefresh();
textureSlicePtrs[3] = bindingFour.ToRefresh();
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
textureSlicePtrs
);
}
/// <summary>
/// Binds textures to be used in the compute shader.
/// </summary>
public unsafe void BindComputeTextures(
in Span<ComputeTextureBinding> bindings
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot bind compute textures outside of compute pass!");
AssertComputePipelineBound();
AssertComputeTextureCount(bindings.Length);
#endif
Refresh.ComputeTextureBinding* bindingArray = (Refresh.ComputeTextureBinding*) NativeMemory.Alloc(
(nuint) (Marshal.SizeOf<Refresh.TextureSlice>() * bindings.Length)
);
for (var i = 0; i < bindings.Length; i += 1)
{
bindingArray[i] = bindings[i].ToRefresh();
}
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
bindingArray
);
NativeMemory.Free(bindingArray);
}
/// <summary>
/// Pushes compute shader uniforms to the device.
/// </summary>
/// <returns>A starting offset to be used with dispatch calls.</returns>
public unsafe void PushComputeShaderUniforms(
void* uniformsPtr,
uint size
) {
#if DEBUG
AssertNotSubmitted();
AssertComputePipelineBound();
if (currentComputePipeline.ComputeShaderInfo.UniformBufferSize == 0)
{
throw new System.InvalidOperationException("The current compute shader does not take a uniform buffer!");
}
if (currentComputePipeline.ComputeShaderInfo.UniformBufferSize != size)
{
throw new InvalidOperationException("Compute uniform data size mismatch!");
}
#endif
Refresh.Refresh_PushComputeShaderUniforms(
Device.Handle,
Handle,
(IntPtr) uniformsPtr,
size
);
}
/// <summary>
/// Pushes compute shader uniforms to the device.
/// </summary>
/// <returns>A starting offset to be used with dispatch calls.</returns>
public unsafe void PushComputeShaderUniforms<T>(
in T uniforms
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf<T>());
}
}
/// <summary>
/// Dispatches compute work.
/// </summary>
/// <param name="groupCountX"></param>
/// <param name="groupCountY"></param>
/// <param name="groupCountZ"></param>
/// <param name="computeParamOffset"></param>
public void DispatchCompute(
uint groupCountX,
uint groupCountY,
uint groupCountZ
) {
#if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot dispatch compute outside of compute pass!");
AssertComputePipelineBound();
if (groupCountX < 1 || groupCountY < 1 || groupCountZ < 1)
{
throw new ArgumentException("All dimensions for the compute work group must be >= 1!");
}
#endif
Refresh.Refresh_DispatchCompute(
Device.Handle,
Handle,
groupCountX,
groupCountY,
groupCountZ
);
}
public void EndComputePass()
{ {
#if DEBUG #if DEBUG
AssertNotSubmitted();
AssertInComputePass("Cannot end compute pass while not in a compute pass!"); AssertInComputePass("Cannot end compute pass while not in a compute pass!");
computePassActive = false; computePassActive = false;
computePass.active = false;
#endif #endif
Refresh.Refresh_EndComputePass( SDL_Gpu.SDL_GpuEndComputePass(
Device.Handle, computePass.Handle
Handle
); );
} }

213
src/Graphics/ComputePass.cs Normal file
View File

@ -0,0 +1,213 @@
using System.Runtime.InteropServices;
using SDL2;
using SDL2_gpuCS;
namespace MoonWorks.Graphics;
public class ComputePass
{
public nint Handle { get; private set; }
internal void SetHandle(nint handle)
{
Handle = handle;
}
#if DEBUG
internal bool active;
ComputePipeline currentComputePipeline;
#endif
/// <summary>
/// Binds a compute pipeline so that compute work may be dispatched.
/// </summary>
/// <param name="computePipeline">The compute pipeline to bind.</param>
public void BindComputePipeline(
ComputePipeline computePipeline
) {
#if DEBUG
AssertComputePassActive();
// TODO: validate formats?
#endif
SDL_Gpu.SDL_GpuBindComputePipeline(
Handle,
computePipeline.Handle
);
#if DEBUG
currentComputePipeline = computePipeline;
#endif
}
/// <summary>
/// Binds a texture to be used in the compute shader.
/// This texture must have been created with the ComputeShaderRead usage flag.
/// </summary>
public unsafe void BindStorageTexture(
in TextureSlice textureSlice,
uint slot = 0
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
AssertTextureNonNull(textureSlice.Texture);
AssertTextureHasComputeStorageReadFlag(textureSlice.Texture);
#endif
var sdlTextureSlice = textureSlice.ToSDL();
SDL_Gpu.SDL_GpuBindComputeStorageTextures(
Handle,
slot,
&sdlTextureSlice,
1
);
}
/// <summary>
/// Binds a buffer to be used in the compute shader.
/// This buffer must have been created with the ComputeShaderRead usage flag.
/// </summary>
public unsafe void BindStorageBuffer(
GpuBuffer buffer,
uint slot = 0
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
AssertBufferNonNull(buffer);
AssertBufferHasComputeStorageReadFlag(buffer);
#endif
var bufferHandle = buffer.Handle;
SDL_Gpu.SDL_GpuBindComputeStorageBuffers(
Handle,
slot,
&bufferHandle,
1
);
}
/// <summary>
/// Pushes compute shader uniform data.
/// Subsequent draw calls will use this uniform data.
/// </summary>
public unsafe void PushUniformData(
void* uniformsPtr,
uint size,
uint slot = 0
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
if (slot >= currentComputePipeline.ResourceInfo.UniformBufferCount)
{
throw new System.ArgumentException($"Slot {slot} given, but {currentComputePipeline.ResourceInfo.UniformBufferCount} uniform buffers are used on the shader!");
}
#endif
SDL_Gpu.SDL_GpuPushComputeUniformData(
Handle,
slot,
(nint) uniformsPtr,
size
);
}
/// <summary>
/// Pushes compute shader uniform data.
/// Subsequent draw calls will use this uniform data.
/// </summary>
public unsafe void PushUniformData<T>(
in T uniforms,
uint slot = 0
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
PushUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot);
}
}
/// <summary>
/// Dispatches compute work.
/// </summary>
public void Dispatch(
uint groupCountX,
uint groupCountY,
uint groupCountZ
) {
#if DEBUG
AssertComputePassActive();
AssertComputePipelineBound();
if (groupCountX < 1 || groupCountY < 1 || groupCountZ < 1)
{
throw new System.ArgumentException("All dimensions for the compute work groups must be >= 1!");
}
#endif
SDL_Gpu.SDL_GpuDispatchCompute(
Handle,
groupCountX,
groupCountY,
groupCountZ
);
}
#if DEBUG
private void AssertComputePassActive(string message = "Render pass is not active!")
{
if (!active)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertComputePipelineBound(string message = "No compute pipeline is bound!")
{
if (currentComputePipeline == null)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertTextureNonNull(in TextureSlice textureSlice)
{
if (textureSlice.Texture == null || textureSlice.Texture.Handle == nint.Zero)
{
throw new System.NullReferenceException("Texture must not be null!");
}
}
private void AssertTextureHasComputeStorageReadFlag(Texture texture)
{
if ((texture.UsageFlags & TextureUsageFlags.ComputeStorageRead) == 0)
{
throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.ComputeStorageRead!");
}
}
private void AssertBufferNonNull(GpuBuffer buffer)
{
if (buffer == null || buffer.Handle == nint.Zero)
{
throw new System.NullReferenceException("Buffer must not be null!");
}
}
private void AssertBufferHasComputeStorageReadFlag(GpuBuffer buffer)
{
if ((buffer.UsageFlags & BufferUsageFlags.ComputeStorageRead) == 0)
{
throw new System.ArgumentException("The bound Buffer's UsageFlags must include BufferUsageFlag.ComputeStorageRead!");
}
}
#endif
}

View File

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

View File

@ -38,8 +38,9 @@ namespace MoonWorks.Graphics
private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>(); private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>();
private CommandBufferPool CommandBufferPool; private CommandBufferPool CommandBufferPool;
internal RenderPassPool RenderPassPool;
private FencePool FencePool; private FencePool FencePool;
internal RenderPassPool RenderPassPool;
internal ComputePassPool ComputePassPool;
internal unsafe GraphicsDevice( internal unsafe GraphicsDevice(
BackendFlags preferredBackends, BackendFlags preferredBackends,

View File

@ -9,7 +9,7 @@ namespace MoonWorks.Graphics;
/// </summary> /// </summary>
public class RenderPass public class RenderPass
{ {
public nint Handle { get; internal set; } public nint Handle { get; private set; }
#if DEBUG #if DEBUG
internal bool active; internal bool active;
@ -296,12 +296,13 @@ public class RenderPass
} }
public unsafe void PushVertexUniformData<T>( public unsafe void PushVertexUniformData<T>(
in T uniforms in T uniforms,
uint slot = 0
) where T : unmanaged ) where T : unmanaged
{ {
fixed (T* uniformsPtr = &uniforms) fixed (T* uniformsPtr = &uniforms)
{ {
PushVertexUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>()); PushVertexUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot);
} }
} }
@ -328,12 +329,13 @@ public class RenderPass
} }
public unsafe void PushFragmentUniformData<T>( public unsafe void PushFragmentUniformData<T>(
in T uniforms in T uniforms,
uint slot = 0
) where T : unmanaged ) where T : unmanaged
{ {
fixed (T* uniformsPtr = &uniforms) fixed (T* uniformsPtr = &uniforms)
{ {
PushFragmentUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>()); PushFragmentUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot);
} }
} }

View File

@ -1,26 +1,25 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics;
internal class RenderPassPool
{ {
internal class RenderPassPool private ConcurrentQueue<RenderPass> RenderPasses = new ConcurrentQueue<RenderPass>();
public RenderPass Obtain()
{ {
private ConcurrentQueue<RenderPass> RenderPasses = new ConcurrentQueue<RenderPass>(); if (RenderPasses.TryDequeue(out var renderPass))
public RenderPass Obtain()
{ {
if (RenderPasses.TryDequeue(out var renderPass)) return renderPass;
{
return renderPass;
}
else
{
return new RenderPass();
}
} }
else
public void Return(RenderPass renderPass)
{ {
RenderPasses.Enqueue(renderPass); return new RenderPass();
} }
} }
public void Return(RenderPass renderPass)
{
RenderPasses.Enqueue(renderPass);
}
} }

View File

@ -1,4 +1,4 @@
using RefreshCS; using SDL2_gpuCS;
using System; using System;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
@ -8,34 +8,33 @@ namespace MoonWorks.Graphics
/// </summary> /// </summary>
public class ComputePipeline : SDL_GpuResource public class ComputePipeline : SDL_GpuResource
{ {
protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_QueueDestroyComputePipeline; protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseComputePipeline;
public ComputeShaderInfo ComputeShaderInfo { get; } public ComputePipelineResourceInfo ResourceInfo { get; }
public unsafe ComputePipeline( public unsafe ComputePipeline(
GraphicsDevice device, GraphicsDevice device,
ComputeShaderInfo computeShaderInfo Shader computeShader,
ComputePipelineResourceInfo resourceInfo
) : base(device) ) : base(device)
{ {
var refreshComputeShaderInfo = new Refresh.ComputeShaderInfo var sdlComputePipelineCreateInfo = new SDL_Gpu.ComputePipelineCreateInfo
{ {
entryPointName = computeShaderInfo.EntryPointName, ComputeShader = computeShader.Handle,
shaderModule = computeShaderInfo.ShaderModule.Handle, PipelineResourceInfo = resourceInfo.ToSDL()
uniformBufferSize = computeShaderInfo.UniformBufferSize,
bufferBindingCount = computeShaderInfo.BufferBindingCount,
imageBindingCount = computeShaderInfo.ImageBindingCount
}; };
Handle = Refresh.Refresh_CreateComputePipeline( Handle = SDL_Gpu.SDL_GpuCreateComputePipeline(
device.Handle, device.Handle,
refreshComputeShaderInfo sdlComputePipelineCreateInfo
); );
if (Handle == IntPtr.Zero) if (Handle == IntPtr.Zero)
{ {
throw new Exception("Could not create compute pipeline!"); throw new Exception("Could not create compute pipeline!");
} }
ComputeShaderInfo = computeShaderInfo; ResourceInfo = resourceInfo;
} }
} }
} }

View File

@ -230,10 +230,8 @@ namespace MoonWorks.Graphics
case TextureFormat.R8G8_SNORM: case TextureFormat.R8G8_SNORM:
case TextureFormat.R8G8_UINT: case TextureFormat.R8G8_UINT:
case TextureFormat.R16_UINT: case TextureFormat.R16_UINT:
case TextureFormat.D16: case TextureFormat.D16_UNORM:
return 2; return 2;
case TextureFormat.D16S8:
return 3;
case TextureFormat.R8G8B8A8: case TextureFormat.R8G8B8A8:
case TextureFormat.B8G8R8A8: case TextureFormat.B8G8R8A8:
case TextureFormat.R32_SFLOAT: case TextureFormat.R32_SFLOAT:
@ -243,9 +241,10 @@ namespace MoonWorks.Graphics
case TextureFormat.A2R10G10B10: case TextureFormat.A2R10G10B10:
case TextureFormat.R8G8B8A8_UINT: case TextureFormat.R8G8B8A8_UINT:
case TextureFormat.R16G16_UINT: case TextureFormat.R16G16_UINT:
case TextureFormat.D32: case TextureFormat.D24_UNORM_S8_UINT:
case TextureFormat.D32_SFLOAT:
return 4; return 4;
case TextureFormat.D32S8: case TextureFormat.D32_SFLOAT_S8_UINT:
return 5; return 5;
case TextureFormat.R16G16B16A16_SFLOAT: case TextureFormat.R16G16B16A16_SFLOAT:
case TextureFormat.R16G16B16A16: case TextureFormat.R16G16B16A16:
@ -311,10 +310,11 @@ namespace MoonWorks.Graphics
case TextureFormat.R16_UINT: case TextureFormat.R16_UINT:
case TextureFormat.R16G16_UINT: case TextureFormat.R16G16_UINT:
case TextureFormat.R16G16B16A16_UINT: case TextureFormat.R16G16B16A16_UINT:
case TextureFormat.D16: case TextureFormat.D16_UNORM:
case TextureFormat.D32: case TextureFormat.D24_UNORM:
case TextureFormat.D16S8: case TextureFormat.D24_UNORM_S8_UINT:
case TextureFormat.D32S8: case TextureFormat.D32_SFLOAT:
case TextureFormat.D32_SFLOAT_S8_UINT:
return 1; return 1;
default: default:
Logger.LogError("Texture format not recognized!"); Logger.LogError("Texture format not recognized!");

View File

@ -840,6 +840,88 @@ public readonly record struct GraphicsPipelineResourceInfo(
} }
} }
public readonly record struct ComputePipelineResourceInfo(
uint ReadOnlyStorageTextureCount,
uint ReadOnlyStorageBufferCount,
uint ReadWriteStorageTextureCount,
uint ReadWriteStorageBufferCount,
uint UniformBufferCount
) {
public SDL_Gpu.ComputePipelineResourceInfo ToSDL()
{
return new SDL_Gpu.ComputePipelineResourceInfo
{
ReadOnlyStorageTextureCount = ReadOnlyStorageTextureCount,
ReadOnlyStorageBufferCount = ReadOnlyStorageBufferCount,
ReadWriteStorageTextureCount = ReadWriteStorageTextureCount,
ReadWriteStorageBufferCount = ReadWriteStorageBufferCount,
UniformBufferCount = UniformBufferCount
};
}
}
/// <summary>
/// A buffer-offset pair to be used when binding buffers.
/// </summary>
public readonly record struct BufferBinding(
GpuBuffer Buffer,
uint Offset
) {
public SDL_Gpu.BufferBinding ToSDL()
{
return new SDL_Gpu.BufferBinding
{
Buffer = Buffer.Handle,
Offset = Offset
};
}
}
/// <summary>
/// A texture-sampler pair to be used when binding samplers.
/// </summary>
public readonly record struct TextureSamplerBinding(
Texture Texture,
Sampler Sampler
) {
public SDL_Gpu.TextureSamplerBinding ToSDL()
{
return new SDL_Gpu.TextureSamplerBinding
{
Texture = Texture.Handle,
Sampler = Sampler.Handle
};
}
}
public readonly record struct StorageBufferReadWriteBinding(
GpuBuffer Buffer,
bool cycle
) {
public SDL_Gpu.StorageBufferReadWriteBinding ToSDL()
{
return new SDL_Gpu.StorageBufferReadWriteBinding
{
Buffer = Buffer.Handle,
Cycle = Conversions.BoolToInt(cycle)
};
}
}
public readonly record struct StorageTextureReadWriteBinding(
in TextureSlice TextureSlice,
bool cycle
) {
public SDL_Gpu.StorageTextureReadWriteBinding ToSDL()
{
return new SDL_Gpu.StorageTextureReadWriteBinding
{
TextureSlice = TextureSlice.ToSDL(),
Cycle = Conversions.BoolToInt(cycle)
};
}
}
/// <summary> /// <summary>
/// All of the information that is used to create a GraphicsPipeline. /// All of the information that is used to create a GraphicsPipeline.
/// </summary> /// </summary>

View File

@ -1,50 +0,0 @@
using System.Runtime.InteropServices;
namespace MoonWorks.Graphics
{
/// <summary>
/// Information that the compute pipeline needs about a compute shader.
/// </summary>
public struct ComputeShaderInfo
{
public ShaderModule ShaderModule;
public string EntryPointName;
public uint UniformBufferSize;
public uint BufferBindingCount;
public uint ImageBindingCount;
public unsafe static ComputeShaderInfo Create<T>(
ShaderModule shaderModule,
string entryPointName,
uint bufferBindingCount,
uint imageBindingCount
) where T : unmanaged
{
return new ComputeShaderInfo
{
ShaderModule = shaderModule,
EntryPointName = entryPointName,
UniformBufferSize = (uint) Marshal.SizeOf<T>(),
BufferBindingCount = bufferBindingCount,
ImageBindingCount = imageBindingCount
};
}
public static ComputeShaderInfo Create(
ShaderModule shaderModule,
string entryPointName,
uint bufferBindingCount,
uint imageBindingCount
)
{
return new ComputeShaderInfo
{
ShaderModule = shaderModule,
EntryPointName = entryPointName,
UniformBufferSize = 0,
BufferBindingCount = bufferBindingCount,
ImageBindingCount = imageBindingCount
};
}
}
}