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
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;
}
/// <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
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;
}
/// <summary>
/// Binds a buffer to be used in the compute shader.
/// </summary>
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
);
}
/// <summary>
/// Binds buffers to be used in the compute shader.
/// </summary>
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;
}
/// <summary>
/// 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()
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
);
}

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 CommandBufferPool CommandBufferPool;
internal RenderPassPool RenderPassPool;
private FencePool FencePool;
internal RenderPassPool RenderPassPool;
internal ComputePassPool ComputePassPool;
internal unsafe GraphicsDevice(
BackendFlags preferredBackends,

View File

@ -9,7 +9,7 @@ namespace MoonWorks.Graphics;
/// </summary>
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<T>(
in T uniforms
in T uniforms,
uint slot = 0
) where T : unmanaged
{
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>(
in T uniforms
in T uniforms,
uint slot = 0
) where T : unmanaged
{
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;
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>();
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);
}
}

View File

@ -1,4 +1,4 @@
using RefreshCS;
using SDL2_gpuCS;
using System;
namespace MoonWorks.Graphics
@ -8,34 +8,33 @@ namespace MoonWorks.Graphics
/// </summary>
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(
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;
}
}
}

View File

@ -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!");

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>
/// All of the information that is used to create a GraphicsPipeline.
/// </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
};
}
}
}