From 97dee2a17088d15d214b334aad5170571d39bfeb Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 4 Jun 2024 17:24:25 -0700 Subject: [PATCH] refactor ComputePass --- lib/SDL2_gpuCS | 2 +- src/Graphics/Bindings/BufferBinding.cs | 19 - src/Graphics/Bindings/ComputeBufferBinding.cs | 34 -- .../Bindings/ComputeTextureBinding.cs | 35 -- .../Bindings/TextureSamplerBinding.cs | 21 - src/Graphics/CommandBuffer.cs | 413 +++--------------- src/Graphics/ComputePass.cs | 213 +++++++++ src/Graphics/ComputePassPool.cs | 25 ++ src/Graphics/GraphicsDevice.cs | 3 +- src/Graphics/RenderPass.cs | 12 +- src/Graphics/RenderPassPool.cs | 31 +- src/Graphics/Resources/ComputePipeline.cs | 25 +- src/Graphics/Resources/Texture.cs | 18 +- src/Graphics/SDL_GpuTypes.cs | 82 ++++ src/Graphics/State/ComputeShaderInfo.cs | 50 --- 15 files changed, 425 insertions(+), 558 deletions(-) delete mode 100644 src/Graphics/Bindings/BufferBinding.cs delete mode 100644 src/Graphics/Bindings/ComputeBufferBinding.cs delete mode 100644 src/Graphics/Bindings/ComputeTextureBinding.cs delete mode 100644 src/Graphics/Bindings/TextureSamplerBinding.cs create mode 100644 src/Graphics/ComputePass.cs create mode 100644 src/Graphics/ComputePassPool.cs delete mode 100644 src/Graphics/State/ComputeShaderInfo.cs diff --git a/lib/SDL2_gpuCS b/lib/SDL2_gpuCS index 60e2c21..70925aa 160000 --- a/lib/SDL2_gpuCS +++ b/lib/SDL2_gpuCS @@ -1 +1 @@ -Subproject commit 60e2c21a7b224dc6aa6a457dbb71d798a4e66241 +Subproject commit 70925aa394c0f24388f3cfb97f1c6ee1f91baf52 diff --git a/src/Graphics/Bindings/BufferBinding.cs b/src/Graphics/Bindings/BufferBinding.cs deleted file mode 100644 index caadfb0..0000000 --- a/src/Graphics/Bindings/BufferBinding.cs +++ /dev/null @@ -1,19 +0,0 @@ -using SDL2_gpuCS; -namespace MoonWorks.Graphics; - -/// -/// A buffer-offset pair to be used when binding buffers. -/// -public readonly record struct BufferBinding( - GpuBuffer Buffer, - uint Offset -) { - public SDL_Gpu.BufferBinding ToSDL() - { - return new SDL_Gpu.BufferBinding - { - Buffer = Buffer.Handle, - Offset = Offset - }; - } -} diff --git a/src/Graphics/Bindings/ComputeBufferBinding.cs b/src/Graphics/Bindings/ComputeBufferBinding.cs deleted file mode 100644 index 012dd44..0000000 --- a/src/Graphics/Bindings/ComputeBufferBinding.cs +++ /dev/null @@ -1,34 +0,0 @@ -using SDL2_gpuCS; - -namespace MoonWorks.Graphics; - -/// -/// Binding specification to be used when binding buffers for compute shaders. -/// -/// The GpuBuffer to bind. -/// -/// Specifies data dependency behavior when this buffer is written to in the shader.
-/// -/// 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.
-/// -/// SafeOverwrite: -/// Overwrites the data safely using a GPU memory barrier. -/// -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 - }; - } -} diff --git a/src/Graphics/Bindings/ComputeTextureBinding.cs b/src/Graphics/Bindings/ComputeTextureBinding.cs deleted file mode 100644 index 4baa158..0000000 --- a/src/Graphics/Bindings/ComputeTextureBinding.cs +++ /dev/null @@ -1,35 +0,0 @@ -using RefreshCS; - -namespace MoonWorks.Graphics -{ - /// - /// Binding specification used for binding texture slices for compute shaders. - /// - /// The TextureSlice to bind. - /// - /// Specifies data dependency behavior when this texture is written to in the shader.
- /// - /// 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.
- /// - /// SafeOverwrite: - /// Overwrites the data safely using a GPU memory barrier. - /// - public readonly record struct ComputeTextureBinding( - TextureSlice TextureSlice, - WriteOptions WriteOption - ) { - public Refresh.ComputeTextureBinding ToRefresh() - { - return new Refresh.ComputeTextureBinding - { - textureSlice = TextureSlice.ToRefreshTextureSlice(), - writeOption = (Refresh.WriteOptions) WriteOption - }; - } - } -} diff --git a/src/Graphics/Bindings/TextureSamplerBinding.cs b/src/Graphics/Bindings/TextureSamplerBinding.cs deleted file mode 100644 index 1a122dc..0000000 --- a/src/Graphics/Bindings/TextureSamplerBinding.cs +++ /dev/null @@ -1,21 +0,0 @@ -using SDL2_gpuCS; - -namespace MoonWorks.Graphics -{ - /// - /// A texture-sampler pair to be used when binding samplers. - /// - 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 - }; - } - } -} diff --git a/src/Graphics/CommandBuffer.cs b/src/Graphics/CommandBuffer.cs index 74b8719..229abf2 100644 --- a/src/Graphics/CommandBuffer.cs +++ b/src/Graphics/CommandBuffer.cs @@ -641,401 +641,106 @@ public class CommandBuffer ); } - public void BeginComputePass() - { + public unsafe ComputePass BeginComputePass( + in StorageTextureReadWriteBinding readWriteTextureBinding + ) { #if DEBUG AssertNotSubmitted(); AssertNotInPass("Cannot begin compute pass while in another pass!"); computePassActive = true; #endif - Refresh.Refresh_BeginComputePass( - Device.Handle, - Handle + var sdlTextureBinding = readWriteTextureBinding.ToSDL(); + + 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; } - /// - /// Binds a compute pipeline so that compute work may be dispatched. - /// - /// The compute pipeline to bind. - public void BindComputePipeline( - ComputePipeline computePipeline + public unsafe ComputePass BeginComputePass( + in StorageBufferReadWriteBinding readWriteBufferBinding ) { #if DEBUG AssertNotSubmitted(); - AssertInComputePass("Cannot bind compute pipeline outside of compute pass!"); + AssertNotInPass("Cannot begin compute pass while in another pass!"); + computePassActive = true; #endif - Refresh.Refresh_BindComputePipeline( - Device.Handle, + var sdlBufferBinding = readWriteBufferBinding.ToSDL(); + + var computePassHandle = SDL_Gpu.SDL_GpuBeginComputePass( Handle, - computePipeline.Handle + (SDL_Gpu.StorageTextureReadWriteBinding*) nint.Zero, + 0, + &sdlBufferBinding, + 1 ); + var computePass = Device.ComputePassPool.Obtain(); + computePass.SetHandle(computePassHandle); + #if DEBUG - currentComputePipeline = computePipeline; + computePass.active = true; #endif + + return computePass; } - /// - /// Binds a buffer to be used in the compute shader. - /// - public unsafe void BindComputeBuffers( - ComputeBufferBinding binding + public unsafe ComputePass BeginComputePass( + in StorageTextureReadWriteBinding readWriteTextureBinding, + in StorageBufferReadWriteBinding readWriteBufferBinding ) { #if DEBUG AssertNotSubmitted(); - AssertInComputePass("Cannot bind compute buffers outside of compute pass!"); - AssertComputePipelineBound(); - AssertComputeBufferCount(1); + AssertNotInPass("Cannot begin compute pass while in another pass!"); + computePassActive = true; #endif - var bindingArray = stackalloc Refresh.ComputeBufferBinding[1]; - bindingArray[0] = binding.ToRefresh(); + var sdlTextureBinding = readWriteTextureBinding.ToSDL(); + var sdlBufferBinding = readWriteBufferBinding.ToSDL(); - Refresh.Refresh_BindComputeBuffers( - Device.Handle, + var computePassHandle = SDL_Gpu.SDL_GpuBeginComputePass( Handle, - bindingArray + &sdlTextureBinding, + 1, + &sdlBufferBinding, + 1 ); - } - /// - /// Binds buffers to be used in the compute shader. - /// - public unsafe void BindComputeBuffers( - ComputeBufferBinding bindingOne, - ComputeBufferBinding bindingTwo - ) { + var computePass = Device.ComputePassPool.Obtain(); + computePass.SetHandle(computePassHandle); + #if DEBUG - AssertNotSubmitted(); - AssertInComputePass("Cannot bind compute buffers outside of compute pass!"); - AssertComputePipelineBound(); - AssertComputeBufferCount(2); + computePass.active = true; #endif - var bindingArray = stackalloc Refresh.ComputeBufferBinding[2]; - bindingArray[0] = bindingOne.ToRefresh(); - bindingArray[1] = bindingTwo.ToRefresh(); - - Refresh.Refresh_BindComputeBuffers( - Device.Handle, - Handle, - bindingArray - ); + return computePass; } - /// - /// Binds buffers to be used in the compute shader. - /// - 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 - ); - } - - /// - /// Binds buffers to be used in the compute shader. - /// - 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 - ); - } - - /// - /// Binds buffers to be used in the compute shader. - /// - /// A Span of buffers to bind. - public unsafe void BindComputeBuffers( - in Span 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() * 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); - } - - /// - /// Binds a texture slice to be used in the compute shader. - /// - 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 - ); - } - - /// - /// Binds textures to be used in the compute shader. - /// - 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 - ); - } - - /// - /// Binds textures to be used in the compute shader. - /// - 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 - ); - } - - /// - /// Binds textures to be used in the compute shader. - /// - 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 - ); - } - - /// - /// Binds textures to be used in the compute shader. - /// - public unsafe void BindComputeTextures( - in Span 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() * 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); - } - - /// - /// Pushes compute shader uniforms to the device. - /// - /// A starting offset to be used with dispatch calls. - 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 - ); - } - - /// - /// Pushes compute shader uniforms to the device. - /// - /// A starting offset to be used with dispatch calls. - public unsafe void PushComputeShaderUniforms( - in T uniforms - ) where T : unmanaged - { - fixed (T* uniformsPtr = &uniforms) - { - PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); - } - } - - /// - /// Dispatches compute work. - /// - /// - /// - /// - /// - 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() + public void EndComputePass(ComputePass computePass) { #if DEBUG + AssertNotSubmitted(); AssertInComputePass("Cannot end compute pass while not in a compute pass!"); computePassActive = false; + computePass.active = false; #endif - Refresh.Refresh_EndComputePass( - Device.Handle, - Handle + SDL_Gpu.SDL_GpuEndComputePass( + computePass.Handle ); } diff --git a/src/Graphics/ComputePass.cs b/src/Graphics/ComputePass.cs new file mode 100644 index 0000000..ad8fd97 --- /dev/null +++ b/src/Graphics/ComputePass.cs @@ -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 + + /// + /// Binds a compute pipeline so that compute work may be dispatched. + /// + /// The compute pipeline to bind. + public void BindComputePipeline( + ComputePipeline computePipeline + ) { +#if DEBUG + AssertComputePassActive(); + + // TODO: validate formats? +#endif + + SDL_Gpu.SDL_GpuBindComputePipeline( + Handle, + computePipeline.Handle + ); + +#if DEBUG + currentComputePipeline = computePipeline; +#endif + } + + /// + /// Binds a texture to be used in the compute shader. + /// This texture must have been created with the ComputeShaderRead usage flag. + /// + 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 + ); + } + + /// + /// Binds a buffer to be used in the compute shader. + /// This buffer must have been created with the ComputeShaderRead usage flag. + /// + 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 + ); + } + + /// + /// Pushes compute shader uniform data. + /// Subsequent draw calls will use this uniform data. + /// + 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 + ); + } + + /// + /// Pushes compute shader uniform data. + /// Subsequent draw calls will use this uniform data. + /// + public unsafe void PushUniformData( + in T uniforms, + uint slot = 0 + ) where T : unmanaged + { + fixed (T* uniformsPtr = &uniforms) + { + PushUniformData(uniformsPtr, (uint) Marshal.SizeOf(), slot); + } + } + + + /// + /// Dispatches compute work. + /// + 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 +} diff --git a/src/Graphics/ComputePassPool.cs b/src/Graphics/ComputePassPool.cs new file mode 100644 index 0000000..da3a366 --- /dev/null +++ b/src/Graphics/ComputePassPool.cs @@ -0,0 +1,25 @@ +using System.Collections.Concurrent; + +namespace MoonWorks.Graphics; + +internal class ComputePassPool +{ + private ConcurrentQueue ComputePasses = new ConcurrentQueue(); + + public ComputePass Obtain() + { + if (ComputePasses.TryDequeue(out var computePass)) + { + return computePass; + } + else + { + return new ComputePass(); + } + } + + public void Return(ComputePass computePass) + { + ComputePasses.Enqueue(computePass); + } +} diff --git a/src/Graphics/GraphicsDevice.cs b/src/Graphics/GraphicsDevice.cs index 3a863eb..92abfd7 100644 --- a/src/Graphics/GraphicsDevice.cs +++ b/src/Graphics/GraphicsDevice.cs @@ -38,8 +38,9 @@ namespace MoonWorks.Graphics private readonly HashSet resources = new HashSet(); private CommandBufferPool CommandBufferPool; - internal RenderPassPool RenderPassPool; private FencePool FencePool; + internal RenderPassPool RenderPassPool; + internal ComputePassPool ComputePassPool; internal unsafe GraphicsDevice( BackendFlags preferredBackends, diff --git a/src/Graphics/RenderPass.cs b/src/Graphics/RenderPass.cs index faa8cb5..e9aa9ca 100644 --- a/src/Graphics/RenderPass.cs +++ b/src/Graphics/RenderPass.cs @@ -9,7 +9,7 @@ namespace MoonWorks.Graphics; /// public class RenderPass { - public nint Handle { get; internal set; } + public nint Handle { get; private set; } #if DEBUG internal bool active; @@ -296,12 +296,13 @@ public class RenderPass } public unsafe void PushVertexUniformData( - in T uniforms + in T uniforms, + uint slot = 0 ) where T : unmanaged { fixed (T* uniformsPtr = &uniforms) { - PushVertexUniformData(uniformsPtr, (uint) Marshal.SizeOf()); + PushVertexUniformData(uniformsPtr, (uint) Marshal.SizeOf(), slot); } } @@ -328,12 +329,13 @@ public class RenderPass } public unsafe void PushFragmentUniformData( - in T uniforms + in T uniforms, + uint slot = 0 ) where T : unmanaged { fixed (T* uniformsPtr = &uniforms) { - PushFragmentUniformData(uniformsPtr, (uint) Marshal.SizeOf()); + PushFragmentUniformData(uniformsPtr, (uint) Marshal.SizeOf(), slot); } } diff --git a/src/Graphics/RenderPassPool.cs b/src/Graphics/RenderPassPool.cs index d0bf0f3..b851069 100644 --- a/src/Graphics/RenderPassPool.cs +++ b/src/Graphics/RenderPassPool.cs @@ -1,26 +1,25 @@ using System.Collections.Concurrent; -namespace MoonWorks.Graphics +namespace MoonWorks.Graphics; + +internal class RenderPassPool { - internal class RenderPassPool + private ConcurrentQueue RenderPasses = new ConcurrentQueue(); + + public RenderPass Obtain() { - private ConcurrentQueue RenderPasses = new ConcurrentQueue(); - - public RenderPass Obtain() + if (RenderPasses.TryDequeue(out var renderPass)) { - if (RenderPasses.TryDequeue(out var renderPass)) - { - return renderPass; - } - else - { - return new RenderPass(); - } + return renderPass; } - - public void Return(RenderPass renderPass) + else { - RenderPasses.Enqueue(renderPass); + return new RenderPass(); } } + + public void Return(RenderPass renderPass) + { + RenderPasses.Enqueue(renderPass); + } } diff --git a/src/Graphics/Resources/ComputePipeline.cs b/src/Graphics/Resources/ComputePipeline.cs index 89da094..5020565 100644 --- a/src/Graphics/Resources/ComputePipeline.cs +++ b/src/Graphics/Resources/ComputePipeline.cs @@ -1,4 +1,4 @@ -using RefreshCS; +using SDL2_gpuCS; using System; namespace MoonWorks.Graphics @@ -8,34 +8,33 @@ namespace MoonWorks.Graphics /// public class ComputePipeline : SDL_GpuResource { - protected override Action ReleaseFunction => Refresh.Refresh_QueueDestroyComputePipeline; + protected override Action ReleaseFunction => SDL_Gpu.SDL_GpuReleaseComputePipeline; - public ComputeShaderInfo ComputeShaderInfo { get; } + public ComputePipelineResourceInfo ResourceInfo { get; } public unsafe ComputePipeline( GraphicsDevice device, - ComputeShaderInfo computeShaderInfo + Shader computeShader, + ComputePipelineResourceInfo resourceInfo ) : base(device) { - var refreshComputeShaderInfo = new Refresh.ComputeShaderInfo + var sdlComputePipelineCreateInfo = new SDL_Gpu.ComputePipelineCreateInfo { - entryPointName = computeShaderInfo.EntryPointName, - shaderModule = computeShaderInfo.ShaderModule.Handle, - uniformBufferSize = computeShaderInfo.UniformBufferSize, - bufferBindingCount = computeShaderInfo.BufferBindingCount, - imageBindingCount = computeShaderInfo.ImageBindingCount + ComputeShader = computeShader.Handle, + PipelineResourceInfo = resourceInfo.ToSDL() }; - Handle = Refresh.Refresh_CreateComputePipeline( + Handle = SDL_Gpu.SDL_GpuCreateComputePipeline( device.Handle, - refreshComputeShaderInfo + sdlComputePipelineCreateInfo ); + if (Handle == IntPtr.Zero) { throw new Exception("Could not create compute pipeline!"); } - ComputeShaderInfo = computeShaderInfo; + ResourceInfo = resourceInfo; } } } diff --git a/src/Graphics/Resources/Texture.cs b/src/Graphics/Resources/Texture.cs index f601e73..81f71e0 100644 --- a/src/Graphics/Resources/Texture.cs +++ b/src/Graphics/Resources/Texture.cs @@ -230,10 +230,8 @@ namespace MoonWorks.Graphics case TextureFormat.R8G8_SNORM: case TextureFormat.R8G8_UINT: case TextureFormat.R16_UINT: - case TextureFormat.D16: + case TextureFormat.D16_UNORM: return 2; - case TextureFormat.D16S8: - return 3; case TextureFormat.R8G8B8A8: case TextureFormat.B8G8R8A8: case TextureFormat.R32_SFLOAT: @@ -243,9 +241,10 @@ namespace MoonWorks.Graphics case TextureFormat.A2R10G10B10: case TextureFormat.R8G8B8A8_UINT: case TextureFormat.R16G16_UINT: - case TextureFormat.D32: + case TextureFormat.D24_UNORM_S8_UINT: + case TextureFormat.D32_SFLOAT: return 4; - case TextureFormat.D32S8: + case TextureFormat.D32_SFLOAT_S8_UINT: return 5; case TextureFormat.R16G16B16A16_SFLOAT: case TextureFormat.R16G16B16A16: @@ -311,10 +310,11 @@ namespace MoonWorks.Graphics case TextureFormat.R16_UINT: case TextureFormat.R16G16_UINT: case TextureFormat.R16G16B16A16_UINT: - case TextureFormat.D16: - case TextureFormat.D32: - case TextureFormat.D16S8: - case TextureFormat.D32S8: + case TextureFormat.D16_UNORM: + case TextureFormat.D24_UNORM: + case TextureFormat.D24_UNORM_S8_UINT: + case TextureFormat.D32_SFLOAT: + case TextureFormat.D32_SFLOAT_S8_UINT: return 1; default: Logger.LogError("Texture format not recognized!"); diff --git a/src/Graphics/SDL_GpuTypes.cs b/src/Graphics/SDL_GpuTypes.cs index 34eac9d..d5ac12d 100644 --- a/src/Graphics/SDL_GpuTypes.cs +++ b/src/Graphics/SDL_GpuTypes.cs @@ -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 + }; + } +} + +/// +/// A buffer-offset pair to be used when binding buffers. +/// +public readonly record struct BufferBinding( + GpuBuffer Buffer, + uint Offset +) { + public SDL_Gpu.BufferBinding ToSDL() + { + return new SDL_Gpu.BufferBinding + { + Buffer = Buffer.Handle, + Offset = Offset + }; + } +} + +/// +/// A texture-sampler pair to be used when binding samplers. +/// +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) + }; + } +} + /// /// All of the information that is used to create a GraphicsPipeline. /// diff --git a/src/Graphics/State/ComputeShaderInfo.cs b/src/Graphics/State/ComputeShaderInfo.cs deleted file mode 100644 index 72fbbb7..0000000 --- a/src/Graphics/State/ComputeShaderInfo.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Runtime.InteropServices; - -namespace MoonWorks.Graphics -{ - /// - /// Information that the compute pipeline needs about a compute shader. - /// - public struct ComputeShaderInfo - { - public ShaderModule ShaderModule; - public string EntryPointName; - public uint UniformBufferSize; - public uint BufferBindingCount; - public uint ImageBindingCount; - - public unsafe static ComputeShaderInfo Create( - ShaderModule shaderModule, - string entryPointName, - uint bufferBindingCount, - uint imageBindingCount - ) where T : unmanaged - { - return new ComputeShaderInfo - { - ShaderModule = shaderModule, - EntryPointName = entryPointName, - UniformBufferSize = (uint) Marshal.SizeOf(), - 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 - }; - } - } -}