refactor ComputePass
parent
fe6734d6db
commit
97dee2a170
|
@ -1 +1 @@
|
|||
Subproject commit 60e2c21a7b224dc6aa6a457dbb71d798a4e66241
|
||||
Subproject commit 70925aa394c0f24388f3cfb97f1c6ee1f91baf52
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Concurrent;
|
||||
|
||||
namespace MoonWorks.Graphics
|
||||
{
|
||||
namespace MoonWorks.Graphics;
|
||||
|
||||
internal class RenderPassPool
|
||||
{
|
||||
private ConcurrentQueue<RenderPass> RenderPasses = new ConcurrentQueue<RenderPass>();
|
||||
|
@ -23,4 +23,3 @@ namespace MoonWorks.Graphics
|
|||
RenderPasses.Enqueue(renderPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue