Nerfed/Nerfed.Runtime/Graphics/CommandBuffer.cs

1043 lines
35 KiB
C#

using System;
using System.Runtime.InteropServices;
using RefreshCS;
namespace Nerfed.Runtime.Graphics;
/// <summary>
/// A structure that buffers commands to be submitted to the GPU.
/// These commands include operations like copying data, draw calls, and compute dispatches.
/// Commands only begin executing once the command buffer is submitted.
/// If you need to know when the command buffer is done, use GraphicsDevice.SubmitAndAcquireFence
/// </summary>
public class CommandBuffer
{
public GraphicsDevice Device { get; }
public IntPtr Handle { get; internal set; }
#if DEBUG
bool swapchainTextureAcquired;
ComputePipeline currentComputePipeline;
bool renderPassActive;
bool copyPassActive;
bool computePassActive;
internal bool Submitted;
#endif
// called from CommandBufferPool
internal CommandBuffer(GraphicsDevice device)
{
Device = device;
Handle = IntPtr.Zero;
#if DEBUG
ResetStateTracking();
#endif
}
internal void SetHandle(nint handle)
{
Handle = handle;
}
#if DEBUG
internal void ResetStateTracking()
{
swapchainTextureAcquired = false;
currentComputePipeline = null;
renderPassActive = false;
copyPassActive = false;
computePassActive = false;
Submitted = false;
}
#endif
/// <summary>
/// Acquires a swapchain texture.
/// This texture will be presented to the given window when the command buffer is submitted.
/// Can return null if the swapchain is unavailable. The user should ALWAYS handle the case where this occurs.
/// If null is returned, presentation will not occur.
/// It is an error to acquire two swapchain textures from the same window in one command buffer.
/// It is an error to dispose the swapchain texture. If you do this your game WILL crash. DO NOT DO THIS.
/// </summary>
public Texture AcquireSwapchainTexture(
Window window
) {
#if DEBUG
AssertNotSubmitted();
if (!window.Claimed)
{
throw new System.InvalidOperationException("Cannot acquire swapchain texture, window has not been claimed!");
}
if (swapchainTextureAcquired)
{
throw new System.InvalidOperationException("Cannot acquire two swapchain textures on the same command buffer!");
}
#endif
IntPtr texturePtr = Refresh.Refresh_AcquireSwapchainTexture(
Handle,
window.Handle,
out uint width,
out uint height
);
if (texturePtr == IntPtr.Zero)
{
return null;
}
// Override the texture properties to avoid allocating a new texture instance!
window.SwapchainTexture.Handle = texturePtr;
window.SwapchainTexture.Width = width;
window.SwapchainTexture.Height = height;
window.SwapchainTexture.Format = window.SwapchainFormat;
#if DEBUG
swapchainTextureAcquired = true;
#endif
return window.SwapchainTexture;
}
/// <summary>
/// Pushes data to a vertex uniform slot on the command buffer.
/// Subsequent draw calls will use this uniform data.
/// It is legal to push uniforms during a render or compute pass.
/// </summary>
public unsafe void PushVertexUniformData(
void* uniformsPtr,
uint size,
uint slot = 0
) {
Refresh.Refresh_PushVertexUniformData(
Handle,
slot,
(nint) uniformsPtr,
size
);
}
/// <summary>
/// Pushes data to a vertex uniform slot on the command buffer.
/// Subsequent draw calls will use this uniform data.
/// It is legal to push uniforms during a render or compute pass.
/// </summary>
public unsafe void PushVertexUniformData<T>(
in T uniforms,
uint slot = 0
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
PushVertexUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot);
}
}
/// <summary>
/// Pushes data to a fragment uniform slot on the command buffer.
/// Subsequent draw calls will use this uniform data.
/// It is legal to push uniforms during a pass.
/// </summary>
public unsafe void PushFragmentUniformData(
void* uniformsPtr,
uint size,
uint slot = 0
) {
Refresh.Refresh_PushFragmentUniformData(
Handle,
slot,
(nint) uniformsPtr,
size
);
}
/// <summary>
/// Pushes data to a fragment uniform slot on the command buffer.
/// Subsequent draw calls will use this uniform data.
/// It is legal to push uniforms during a pass.
/// </summary>
public unsafe void PushFragmentUniformData<T>(
in T uniforms,
uint slot = 0
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
PushFragmentUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot);
}
}
/// <summary>
/// Pushes data to a compute uniform slot on the command buffer.
/// Subsequent draw calls will use this uniform data.
/// It is legal to push uniforms during a pass.
/// </summary>
public unsafe void PushComputeUniformData(
void* uniformsPtr,
uint size,
uint slot = 0
) {
Refresh.Refresh_PushComputeUniformData(
Handle,
slot,
(nint) uniformsPtr,
size
);
}
/// <summary>
/// Pushes data to a compute uniform slot on the command buffer.
/// Subsequent draw calls will use this uniform data.
/// It is legal to push uniforms during a pass.
/// </summary>
public unsafe void PushComputeUniformData<T>(
in T uniforms,
uint slot = 0
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
PushComputeUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot);
}
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this during any kind of pass.
/// </summary>
/// <param name="colorAttachmentInfo">The color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in ColorAttachmentInfo colorAttachmentInfo
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin a render pass inside another pass!");
AssertTextureNotNull(colorAttachmentInfo);
AssertColorTarget(colorAttachmentInfo);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[1];
refreshColorAttachmentInfos[0] = colorAttachmentInfo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
1,
(Refresh.DepthStencilAttachmentInfo*) nint.Zero
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.colorAttachmentCount = 1;
renderPass.colorAttachmentSampleCount = colorAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfo.TextureSlice.Texture.Format;
renderPass.hasDepthStencilAttachment = false;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="colorAttachmentInfoOne">The first color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoTwo">The second color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in ColorAttachmentInfo colorAttachmentInfoOne,
in ColorAttachmentInfo colorAttachmentInfoTwo
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin a render pass inside another pass!");
AssertTextureNotNull(colorAttachmentInfoOne);
AssertColorTarget(colorAttachmentInfoOne);
AssertTextureNotNull(colorAttachmentInfoTwo);
AssertColorTarget(colorAttachmentInfoTwo);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[2];
refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
2,
(Refresh.DepthStencilAttachmentInfo*) nint.Zero
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.colorAttachmentCount = 2;
renderPass.colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
renderPass.colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
renderPass.hasDepthStencilAttachment = false;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="colorAttachmentInfoOne">The first color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoTwo">The second color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoThree">The third color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in ColorAttachmentInfo colorAttachmentInfoOne,
in ColorAttachmentInfo colorAttachmentInfoTwo,
in ColorAttachmentInfo colorAttachmentInfoThree
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin a render pass inside another pass!");
AssertTextureNotNull(colorAttachmentInfoOne);
AssertColorTarget(colorAttachmentInfoOne);
AssertTextureNotNull(colorAttachmentInfoTwo);
AssertColorTarget(colorAttachmentInfoTwo);
AssertTextureNotNull(colorAttachmentInfoThree);
AssertColorTarget(colorAttachmentInfoThree);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoThree.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[3];
refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
3,
(Refresh.DepthStencilAttachmentInfo*) nint.Zero
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.colorAttachmentCount = 3;
renderPass.colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
renderPass.colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
renderPass.colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
renderPass.hasDepthStencilAttachment = false;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="colorAttachmentInfoOne">The first color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoTwo">The second color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoThree">The third color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoFour">The four color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in ColorAttachmentInfo colorAttachmentInfoOne,
in ColorAttachmentInfo colorAttachmentInfoTwo,
in ColorAttachmentInfo colorAttachmentInfoThree,
in ColorAttachmentInfo colorAttachmentInfoFour
) {
#if DEBUG
AssertNotSubmitted();
AssertTextureNotNull(colorAttachmentInfoOne);
AssertColorTarget(colorAttachmentInfoOne);
AssertTextureNotNull(colorAttachmentInfoTwo);
AssertColorTarget(colorAttachmentInfoTwo);
AssertTextureNotNull(colorAttachmentInfoThree);
AssertColorTarget(colorAttachmentInfoThree);
AssertTextureNotNull(colorAttachmentInfoFour);
AssertColorTarget(colorAttachmentInfoFour);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoThree.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoFour.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[4];
refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
refreshColorAttachmentInfos[3] = colorAttachmentInfoFour.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
4,
(Refresh.DepthStencilAttachmentInfo*) nint.Zero
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.colorAttachmentCount = 3;
renderPass.colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
renderPass.colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
renderPass.colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
renderPass.colorFormatFour = colorAttachmentInfoFour.TextureSlice.Texture.Format;
renderPass.hasDepthStencilAttachment = false;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="depthStencilAttachmentInfo">The depth stencil attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in DepthStencilAttachmentInfo depthStencilAttachmentInfo
) {
#if DEBUG
AssertNotSubmitted();
AssertValidDepthAttachment(depthStencilAttachmentInfo);
#endif
Refresh.DepthStencilAttachmentInfo refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
(Refresh.ColorAttachmentInfo*) nint.Zero,
0,
&refreshDepthStencilAttachmentInfo
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.hasDepthStencilAttachment = true;
renderPass.depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="depthStencilAttachmentInfo">The depth stencil attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfo">The color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in DepthStencilAttachmentInfo depthStencilAttachmentInfo,
in ColorAttachmentInfo colorAttachmentInfo
) {
#if DEBUG
AssertNotSubmitted();
AssertValidDepthAttachment(depthStencilAttachmentInfo);
AssertTextureNotNull(colorAttachmentInfo);
AssertColorTarget(colorAttachmentInfo);
AssertSameSampleCount(colorAttachmentInfo.TextureSlice.Texture, depthStencilAttachmentInfo.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[1];
refreshColorAttachmentInfos[0] = colorAttachmentInfo.ToRefresh();
Refresh.DepthStencilAttachmentInfo refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
1,
&refreshDepthStencilAttachmentInfo
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.hasDepthStencilAttachment = true;
renderPass.colorAttachmentCount = 1;
renderPass.colorAttachmentSampleCount = colorAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfo.TextureSlice.Texture.Format;
renderPass.depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="depthStencilAttachmentInfo">The depth stencil attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoOne">The first color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoTwo">The second color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in DepthStencilAttachmentInfo depthStencilAttachmentInfo,
in ColorAttachmentInfo colorAttachmentInfoOne,
in ColorAttachmentInfo colorAttachmentInfoTwo
) {
#if DEBUG
AssertNotSubmitted();
AssertValidDepthAttachment(depthStencilAttachmentInfo);
AssertTextureNotNull(colorAttachmentInfoOne);
AssertColorTarget(colorAttachmentInfoOne);
AssertTextureNotNull(colorAttachmentInfoTwo);
AssertColorTarget(colorAttachmentInfoTwo);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, depthStencilAttachmentInfo.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[2];
refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
Refresh.DepthStencilAttachmentInfo refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
2,
&refreshDepthStencilAttachmentInfo
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.hasDepthStencilAttachment = true;
renderPass.colorAttachmentCount = 2;
renderPass.colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
renderPass.colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
renderPass.colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
renderPass.depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="depthStencilAttachmentInfo">The depth stencil attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoOne">The first color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoTwo">The second color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoThree">The third color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in DepthStencilAttachmentInfo depthStencilAttachmentInfo,
in ColorAttachmentInfo colorAttachmentInfoOne,
in ColorAttachmentInfo colorAttachmentInfoTwo,
in ColorAttachmentInfo colorAttachmentInfoThree
) {
#if DEBUG
AssertNotSubmitted();
AssertValidDepthAttachment(depthStencilAttachmentInfo);
AssertTextureNotNull(colorAttachmentInfoOne);
AssertColorTarget(colorAttachmentInfoOne);
AssertTextureNotNull(colorAttachmentInfoTwo);
AssertColorTarget(colorAttachmentInfoTwo);
AssertTextureNotNull(colorAttachmentInfoThree);
AssertColorTarget(colorAttachmentInfoThree);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoThree.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, depthStencilAttachmentInfo.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[3];
refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
Refresh.DepthStencilAttachmentInfo refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
3,
&refreshDepthStencilAttachmentInfo
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.hasDepthStencilAttachment = true;
renderPass.colorAttachmentCount = 3;
renderPass.colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
renderPass.colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
renderPass.colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
renderPass.depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
return renderPass;
}
/// <summary>
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
/// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
/// </summary>
/// <param name="depthStencilAttachmentInfo">The depth stencil attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoOne">The first color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoTwo">The second color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoThree">The third color attachment to use in the render pass.</param>
/// <param name="colorAttachmentInfoFour">The four color attachment to use in the render pass.</param>
public unsafe RenderPass BeginRenderPass(
in DepthStencilAttachmentInfo depthStencilAttachmentInfo,
in ColorAttachmentInfo colorAttachmentInfoOne,
in ColorAttachmentInfo colorAttachmentInfoTwo,
in ColorAttachmentInfo colorAttachmentInfoThree,
in ColorAttachmentInfo colorAttachmentInfoFour
) {
#if DEBUG
AssertNotSubmitted();
AssertValidDepthAttachment(depthStencilAttachmentInfo);
AssertTextureNotNull(colorAttachmentInfoOne);
AssertColorTarget(colorAttachmentInfoOne);
AssertTextureNotNull(colorAttachmentInfoTwo);
AssertColorTarget(colorAttachmentInfoTwo);
AssertTextureNotNull(colorAttachmentInfoThree);
AssertColorTarget(colorAttachmentInfoThree);
AssertTextureNotNull(colorAttachmentInfoFour);
AssertColorTarget(colorAttachmentInfoFour);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoThree.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoFour.TextureSlice.Texture);
AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, depthStencilAttachmentInfo.TextureSlice.Texture);
#endif
Refresh.ColorAttachmentInfo* refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[4];
refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
refreshColorAttachmentInfos[3] = colorAttachmentInfoFour.ToRefresh();
Refresh.DepthStencilAttachmentInfo refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
IntPtr renderPassHandle = Refresh.Refresh_BeginRenderPass(
Handle,
refreshColorAttachmentInfos,
4,
&refreshDepthStencilAttachmentInfo
);
RenderPass renderPass = Device.RenderPassPool.Obtain();
renderPass.SetHandle(renderPassHandle);
#if DEBUG
renderPassActive = true;
renderPass.hasDepthStencilAttachment = true;
renderPass.colorAttachmentCount = 4;
renderPass.colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
renderPass.colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
renderPass.colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
renderPass.colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
renderPass.colorFormatFour = colorAttachmentInfoFour.TextureSlice.Texture.Format;
renderPass.depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
renderPass.depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
return renderPass;
}
/// <summary>
/// Ends the current render pass.
/// This must be called before beginning another render pass or submitting the command buffer.
/// </summary>
public void EndRenderPass(RenderPass renderPass)
{
#if DEBUG
AssertNotSubmitted();
AssertRenderPassActive();
renderPassActive = false;
#endif
Refresh.Refresh_EndRenderPass(
renderPass.Handle
);
renderPass.SetHandle(nint.Zero);
Device.RenderPassPool.Return(renderPass);
}
/// <summary>
/// Blits a texture to another texture with the specified filter.
///
/// This operation cannot be performed inside any pass.
/// </summary>
/// <param name="cycle">If true, the destination texture will cycle if bound.</param>
public void Blit(
in TextureRegion source,
in TextureRegion destination,
Filter filter,
bool cycle
) {
Refresh.Refresh_Blit(
Handle,
source.ToRefresh(),
destination.ToRefresh(),
(Refresh.Filter) filter,
Conversions.BoolToInt(cycle)
);
}
public unsafe ComputePass BeginComputePass(
in StorageTextureReadWriteBinding readWriteTextureBinding
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true;
#endif
Refresh.StorageTextureReadWriteBinding refreshTextureBinding = readWriteTextureBinding.ToRefresh();
IntPtr computePassHandle = Refresh.Refresh_BeginComputePass(
Handle,
&refreshTextureBinding,
1,
(Refresh.StorageBufferReadWriteBinding*) nint.Size,
0
);
ComputePass computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG
computePass.active = true;
#endif
return computePass;
}
public unsafe ComputePass BeginComputePass(
in StorageBufferReadWriteBinding readWriteBufferBinding
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true;
#endif
Refresh.StorageBufferReadWriteBinding refreshBufferBinding = readWriteBufferBinding.ToRefresh();
IntPtr computePassHandle = Refresh.Refresh_BeginComputePass(
Handle,
(Refresh.StorageTextureReadWriteBinding*) nint.Zero,
0,
&refreshBufferBinding,
1
);
ComputePass computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG
computePass.active = true;
#endif
return computePass;
}
public unsafe ComputePass BeginComputePass(
in StorageTextureReadWriteBinding readWriteTextureBinding,
in StorageBufferReadWriteBinding readWriteBufferBinding
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true;
#endif
Refresh.StorageTextureReadWriteBinding refreshTextureBinding = readWriteTextureBinding.ToRefresh();
Refresh.StorageBufferReadWriteBinding refreshBufferBinding = readWriteBufferBinding.ToRefresh();
IntPtr computePassHandle = Refresh.Refresh_BeginComputePass(
Handle,
&refreshTextureBinding,
1,
&refreshBufferBinding,
1
);
ComputePass computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG
computePass.active = true;
#endif
return computePass;
}
public unsafe ComputePass BeginComputePass(
Span<StorageTextureReadWriteBinding> readWriteTextureBindings,
Span<StorageBufferReadWriteBinding> readWriteBufferBindings
) {
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin compute pass while in another pass!");
computePassActive = true;
#endif
void* refreshTextureBindings = NativeMemory.Alloc(
(nuint) (readWriteTextureBindings.Length * Marshal.SizeOf<StorageTextureReadWriteBinding>())
);
void* refreshBufferBindings = NativeMemory.Alloc(
(nuint) (readWriteBufferBindings.Length * Marshal.SizeOf<StorageBufferReadWriteBinding>())
);
IntPtr computePassHandle = Refresh.Refresh_BeginComputePass(
Handle,
(Refresh.StorageTextureReadWriteBinding*) refreshTextureBindings,
(uint) readWriteTextureBindings.Length,
(Refresh.StorageBufferReadWriteBinding*) refreshBufferBindings,
(uint) readWriteBufferBindings.Length
);
ComputePass computePass = Device.ComputePassPool.Obtain();
computePass.SetHandle(computePassHandle);
#if DEBUG
computePass.active = true;
#endif
NativeMemory.Free(refreshTextureBindings);
NativeMemory.Free(refreshBufferBindings);
return computePass;
}
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(
computePass.Handle
);
computePass.SetHandle(nint.Zero);
Device.ComputePassPool.Return(computePass);
}
// Copy Pass
/// <summary>
/// Begins a copy pass.
/// All copy commands must be made within a copy pass.
/// It is an error to call this during any kind of pass.
/// </summary>
public CopyPass BeginCopyPass()
{
#if DEBUG
AssertNotSubmitted();
AssertNotInPass("Cannot begin copy pass while in another pass!");
copyPassActive = true;
#endif
IntPtr copyPassHandle = Refresh.Refresh_BeginCopyPass(Handle);
CopyPass copyPass = Device.CopyPassPool.Obtain();
copyPass.SetHandle(copyPassHandle);
return copyPass;
}
public void EndCopyPass(CopyPass copyPass)
{
#if DEBUG
AssertNotSubmitted();
AssertInCopyPass("Cannot end copy pass while not in a copy pass!");
copyPassActive = false;
#endif
Refresh.Refresh_EndCopyPass(
copyPass.Handle
);
copyPass.SetHandle(nint.Zero);
Device.CopyPassPool.Return(copyPass);
}
#if DEBUG
private void AssertRenderPassActive(string message = "No active render pass!")
{
if (!renderPassActive)
{
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 AssertTextureNotNull(ColorAttachmentInfo colorAttachmentInfo)
{
if (colorAttachmentInfo.TextureSlice.Texture == null || colorAttachmentInfo.TextureSlice.Texture.Handle == IntPtr.Zero)
{
throw new System.ArgumentException("Render pass color attachment Texture cannot be null!");
}
}
private void AssertColorTarget(ColorAttachmentInfo colorAttachmentInfo)
{
if ((colorAttachmentInfo.TextureSlice.Texture.UsageFlags & TextureUsageFlags.ColorTarget) == 0)
{
throw new System.ArgumentException("Render pass color attachment UsageFlags must include TextureUsageFlags.ColorTarget!");
}
}
private void AssertSameSampleCount(Texture a, Texture b)
{
if (a.SampleCount != b.SampleCount)
{
throw new System.ArgumentException("All attachments in a render pass must have the same SampleCount!");
}
}
private void AssertValidDepthAttachment(DepthStencilAttachmentInfo depthStencilAttachmentInfo)
{
if (depthStencilAttachmentInfo.TextureSlice.Texture == null ||
depthStencilAttachmentInfo.TextureSlice.Texture.Handle == IntPtr.Zero)
{
throw new System.ArgumentException("Render pass depth stencil attachment Texture cannot be null!");
}
if ((depthStencilAttachmentInfo.TextureSlice.Texture.UsageFlags & TextureUsageFlags.DepthStencil) == 0)
{
throw new System.ArgumentException("Render pass depth stencil attachment UsageFlags must include TextureUsageFlags.DepthStencilTarget!");
}
}
private void AssertNonEmptyCopy(uint dataLengthInBytes)
{
if (dataLengthInBytes == 0)
{
throw new System.InvalidOperationException("SetBufferData must have a length greater than 0 bytes!");
}
}
private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes)
{
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
{
throw new System.InvalidOperationException($"SetBufferData overflow! buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
}
}
private void AssertTextureBoundsCheck(uint textureSizeInBytes, uint dataLengthInBytes)
{
if (dataLengthInBytes > textureSizeInBytes)
{
throw new System.InvalidOperationException($"SetTextureData overflow! texture size {textureSizeInBytes}, data size {dataLengthInBytes}");
}
}
private void AssertNotInPass(string message)
{
if (renderPassActive || copyPassActive || computePassActive)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertInCopyPass(string message)
{
if (!copyPassActive)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertInComputePass(string message)
{
if (!computePassActive)
{
throw new System.InvalidOperationException(message);
}
}
private void AssertNotSubmitted()
{
if (Submitted)
{
throw new System.InvalidOperationException("Cannot add commands to a submitted command buffer!");
}
}
#endif
}