using System;
using System.Runtime.InteropServices;
using SDL2_gpuCS;
namespace MoonWorks.Graphics;
/// 
/// Command buffers are used to apply render state and issue draw calls.
/// NOTE: it is not recommended to hold references to command buffers long term.
/// 
public class CommandBuffer
{
	public GraphicsDevice Device { get; }
	public IntPtr Handle { get; internal set; }
#if DEBUG
	bool swapchainTextureAcquired;
	GraphicsPipeline currentGraphicsPipeline;
	ComputePipeline currentComputePipeline;
	bool renderPassActive;
	SampleCount colorAttachmentSampleCount;
	uint colorAttachmentCount;
	TextureFormat colorFormatOne;
	TextureFormat colorFormatTwo;
	TextureFormat colorFormatThree;
	TextureFormat colorFormatFour;
	bool hasDepthStencilAttachment;
	SampleCount depthStencilAttachmentSampleCount;
	TextureFormat depthStencilFormat;
	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;
		currentGraphicsPipeline = null;
		currentComputePipeline = null;
		renderPassActive = false;
		colorAttachmentSampleCount = SampleCount.One;
		depthStencilAttachmentSampleCount = SampleCount.One;
		colorAttachmentCount = 0;
		colorFormatOne = TextureFormat.R8G8B8A8;
		colorFormatTwo = TextureFormat.R8G8B8A8;
		colorFormatThree = TextureFormat.R8G8B8A8;
		colorFormatFour = TextureFormat.R8G8B8A8;
		depthStencilFormat = TextureFormat.D16;
		copyPassActive = false;
		computePassActive = false;
		Submitted = false;
	}
#endif
	/// 
	/// 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.
	/// 
	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
		var texturePtr = SDL_Gpu.SDL_GpuAcquireSwapchainTexture(
			Handle,
			window.Handle,
			out var width,
			out var 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;
	}
	/// 
	/// 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.
	/// 
	/// The color attachment to use in the render pass.
	public unsafe void BeginRenderPass(
		in ColorAttachmentInfo colorAttachmentInfo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertNotInPass("Cannot begin a render pass inside another pass!");
		AssertTextureNotNull(colorAttachmentInfo);
		AssertColorTarget(colorAttachmentInfo);
#endif
		var refreshColorAttachmentInfos = stackalloc SDL_Gpu.ColorAttachmentInfo[1];
		refreshColorAttachmentInfos[0] = colorAttachmentInfo.ToRefresh();
		SDL_Gpu.SDL_GpuBeginRenderPass(
			Handle,
			(IntPtr) refreshColorAttachmentInfos,
			1,
			IntPtr.Zero
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = false;
		colorAttachmentSampleCount = colorAttachmentInfo.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 1;
		colorFormatOne = colorAttachmentInfo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The first color attachment to use in the render pass.
	/// The second color attachment to use in the render pass.
	public unsafe void BeginRenderPass(
		in ColorAttachmentInfo colorAttachmentInfoOne,
		in ColorAttachmentInfo colorAttachmentInfoTwo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertTextureNotNull(colorAttachmentInfoOne);
		AssertColorTarget(colorAttachmentInfoOne);
		AssertTextureNotNull(colorAttachmentInfoTwo);
		AssertColorTarget(colorAttachmentInfoTwo);
		AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, colorAttachmentInfoTwo.TextureSlice.Texture);
#endif
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[2];
		refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
		refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			(IntPtr) refreshColorAttachmentInfos,
			2,
			IntPtr.Zero
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = false;
		colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 2;
		colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
		colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The first color attachment to use in the render pass.
	/// The second color attachment to use in the render pass.
	/// The third color attachment to use in the render pass.
	public unsafe void BeginRenderPass(
		in ColorAttachmentInfo colorAttachmentInfoOne,
		in ColorAttachmentInfo colorAttachmentInfoTwo,
		in ColorAttachmentInfo colorAttachmentInfoThree
	) {
#if DEBUG
		AssertNotSubmitted();
		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
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[3];
		refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
		refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
		refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			(IntPtr) refreshColorAttachmentInfos,
			3,
			IntPtr.Zero
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = false;
		colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 3;
		colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
		colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
		colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The first color attachment to use in the render pass.
	/// The second color attachment to use in the render pass.
	/// The third color attachment to use in the render pass.
	/// The four color attachment to use in the render pass.
	public unsafe void 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
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[4];
		refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
		refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
		refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
		refreshColorAttachmentInfos[3] = colorAttachmentInfoFour.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			(IntPtr) refreshColorAttachmentInfos,
			4,
			IntPtr.Zero
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = false;
		colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 4;
		colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
		colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
		colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
		colorFormatFour = colorAttachmentInfoFour.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The depth stencil attachment to use in the render pass.
	public unsafe void BeginRenderPass(
		in DepthStencilAttachmentInfo depthStencilAttachmentInfo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertValidDepthAttachment(depthStencilAttachmentInfo);
#endif
		var refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			(Refresh.ColorAttachmentInfo*) IntPtr.Zero,
			0,
			&refreshDepthStencilAttachmentInfo
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = true;
		depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
		depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The depth stencil attachment to use in the render pass.
	/// The color attachment to use in the render pass.
	public unsafe void BeginRenderPass(
		in DepthStencilAttachmentInfo depthStencilAttachmentInfo,
		in ColorAttachmentInfo colorAttachmentInfo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertValidDepthAttachment(depthStencilAttachmentInfo);
		AssertTextureNotNull(colorAttachmentInfo);
		AssertColorTarget(colorAttachmentInfo);
		AssertSameSampleCount(colorAttachmentInfo.TextureSlice.Texture, depthStencilAttachmentInfo.TextureSlice.Texture);
#endif
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[1];
		refreshColorAttachmentInfos[0] = colorAttachmentInfo.ToRefresh();
		var refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			refreshColorAttachmentInfos,
			1,
			&refreshDepthStencilAttachmentInfo
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = true;
		colorAttachmentSampleCount = colorAttachmentInfo.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 1;
		depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.TextureSlice.Texture.SampleCount;
		colorFormatOne = colorAttachmentInfo.TextureSlice.Texture.Format;
		depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The depth stencil attachment to use in the render pass.
	/// The first color attachment to use in the render pass.
	/// The second color attachment to use in the render pass.
	public unsafe void 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
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[2];
		refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
		refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
		var refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			refreshColorAttachmentInfos,
			2,
			&refreshDepthStencilAttachmentInfo
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = true;
		colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 2;
		colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
		colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
		depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The depth stencil attachment to use in the render pass.
	/// The first color attachment to use in the render pass.
	/// The second color attachment to use in the render pass.
	/// The third color attachment to use in the render pass.
	public unsafe void 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, colorAttachmentInfoTwo.TextureSlice.Texture);
		AssertSameSampleCount(colorAttachmentInfoOne.TextureSlice.Texture, depthStencilAttachmentInfo.TextureSlice.Texture);
#endif
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[3];
		refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
		refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
		refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
		var refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			refreshColorAttachmentInfos,
			3,
			&refreshDepthStencilAttachmentInfo
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = true;
		colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 3;
		colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
		colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
		colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
		depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// 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.
	/// 
	/// The depth stencil attachment to use in the render pass.
	/// The first color attachment to use in the render pass.
	/// The second color attachment to use in the render pass.
	/// The third color attachment to use in the render pass.
	/// The four color attachment to use in the render pass.
	public unsafe void 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
		var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[4];
		refreshColorAttachmentInfos[0] = colorAttachmentInfoOne.ToRefresh();
		refreshColorAttachmentInfos[1] = colorAttachmentInfoTwo.ToRefresh();
		refreshColorAttachmentInfos[2] = colorAttachmentInfoThree.ToRefresh();
		refreshColorAttachmentInfos[3] = colorAttachmentInfoFour.ToRefresh();
		var refreshDepthStencilAttachmentInfo = depthStencilAttachmentInfo.ToRefresh();
		Refresh.Refresh_BeginRenderPass(
			Device.Handle,
			Handle,
			refreshColorAttachmentInfos,
			4,
			&refreshDepthStencilAttachmentInfo
		);
#if DEBUG
		renderPassActive = true;
		hasDepthStencilAttachment = true;
		colorAttachmentSampleCount = colorAttachmentInfoOne.TextureSlice.Texture.SampleCount;
		colorAttachmentCount = 4;
		colorFormatOne = colorAttachmentInfoOne.TextureSlice.Texture.Format;
		colorFormatTwo = colorAttachmentInfoTwo.TextureSlice.Texture.Format;
		colorFormatThree = colorAttachmentInfoThree.TextureSlice.Texture.Format;
		colorFormatFour = colorAttachmentInfoFour.TextureSlice.Texture.Format;
		depthStencilFormat = depthStencilAttachmentInfo.TextureSlice.Texture.Format;
#endif
	}
	/// 
	/// Binds samplers to be used by the fragment shader.
	/// 
	/// The texture-sampler to bind.
	public unsafe void BindFragmentSamplers(
		in TextureSamplerBinding textureSamplerBinding
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		AssertFragmentSamplerCount(1);
		AssertTextureSamplerBindingNonNull(textureSamplerBinding);
		AssertTextureBindingUsageFlags(textureSamplerBinding.Texture);
#endif
		var bindingArray = stackalloc Refresh.TextureSamplerBinding[1];
		bindingArray[0] = textureSamplerBinding.ToRefresh();
		Refresh.Refresh_BindFragmentSamplers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds samplers to be used by the fragment shader.
	/// 
	/// The first texture-sampler to bind.
	/// The second texture-sampler to bind.
	public unsafe void BindFragmentSamplers(
		in TextureSamplerBinding textureSamplerBindingOne,
		in TextureSamplerBinding textureSamplerBindingTwo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		AssertFragmentSamplerCount(2);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingOne);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingTwo);
		AssertTextureBindingUsageFlags(textureSamplerBindingOne.Texture);
		AssertTextureBindingUsageFlags(textureSamplerBindingTwo.Texture);
#endif
		var bindingArray = stackalloc Refresh.TextureSamplerBinding[2];
		bindingArray[0] = textureSamplerBindingOne.ToRefresh();
		bindingArray[1] = textureSamplerBindingTwo.ToRefresh();
		Refresh.Refresh_BindFragmentSamplers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds samplers to be used by the fragment shader.
	/// 
	/// The first texture-sampler to bind.
	/// The second texture-sampler to bind.
	/// The third texture-sampler to bind.
	public unsafe void BindFragmentSamplers(
		in TextureSamplerBinding textureSamplerBindingOne,
		in TextureSamplerBinding textureSamplerBindingTwo,
		in TextureSamplerBinding textureSamplerBindingThree
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		AssertFragmentSamplerCount(3);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingOne);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingTwo);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingThree);
		AssertTextureBindingUsageFlags(textureSamplerBindingOne.Texture);
		AssertTextureBindingUsageFlags(textureSamplerBindingTwo.Texture);
		AssertTextureBindingUsageFlags(textureSamplerBindingThree.Texture);
#endif
		var bindingArray = stackalloc Refresh.TextureSamplerBinding[3];
		bindingArray[0] = textureSamplerBindingOne.ToRefresh();
		bindingArray[1] = textureSamplerBindingTwo.ToRefresh();
		bindingArray[2] = textureSamplerBindingThree.ToRefresh();
		Refresh.Refresh_BindFragmentSamplers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds samplers to be used by the fragment shader.
	/// 
	/// The first texture-sampler to bind.
	/// The second texture-sampler to bind.
	/// The third texture-sampler to bind.
	/// The fourth texture-sampler to bind.
	public unsafe void BindFragmentSamplers(
		in TextureSamplerBinding textureSamplerBindingOne,
		in TextureSamplerBinding textureSamplerBindingTwo,
		in TextureSamplerBinding textureSamplerBindingThree,
		in TextureSamplerBinding textureSamplerBindingFour
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		AssertFragmentSamplerCount(4);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingOne);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingTwo);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingThree);
		AssertTextureSamplerBindingNonNull(textureSamplerBindingFour);
		AssertTextureBindingUsageFlags(textureSamplerBindingOne.Texture);
		AssertTextureBindingUsageFlags(textureSamplerBindingTwo.Texture);
		AssertTextureBindingUsageFlags(textureSamplerBindingThree.Texture);
		AssertTextureBindingUsageFlags(textureSamplerBindingFour.Texture);
#endif
		var bindingArray = stackalloc Refresh.TextureSamplerBinding[4];
		bindingArray[0] = textureSamplerBindingOne.ToRefresh();
		bindingArray[1] = textureSamplerBindingTwo.ToRefresh();
		bindingArray[2] = textureSamplerBindingThree.ToRefresh();
		bindingArray[3] = textureSamplerBindingFour.ToRefresh();
		Refresh.Refresh_BindFragmentSamplers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds samplers to be used by the fragment shader.
	/// 
	/// The texture-sampler pairs to bind.
	public unsafe void BindFragmentSamplers(
		in Span textureSamplerBindings
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		AssertFragmentSamplerCount(textureSamplerBindings.Length);
#endif
		Refresh.TextureSamplerBinding* bindingArray = (Refresh.TextureSamplerBinding*) NativeMemory.Alloc((nuint) (Marshal.SizeOf() * textureSamplerBindings.Length));
		for (var i = 0; i < textureSamplerBindings.Length; i += 1)
		{
#if DEBUG
			AssertTextureSamplerBindingNonNull(textureSamplerBindings[i]);
			AssertTextureBindingUsageFlags(textureSamplerBindings[i].Texture);
#endif
			bindingArray[i] = textureSamplerBindings[i].ToRefresh();
		}
		Refresh.Refresh_BindFragmentSamplers(
			Device.Handle,
			Handle,
			bindingArray
		);
		NativeMemory.Free(bindingArray);
	}
	/// 
	/// Pushes vertex shader uniforms to the device.
	/// 
	/// A starting offset value to be used with draw calls.
	public unsafe void PushVertexShaderUniforms(
		void* uniformsPtr,
		uint size
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		if (currentGraphicsPipeline.VertexShaderInfo.UniformBufferSize == 0)
		{
			throw new InvalidOperationException("The current vertex shader does not take a uniform buffer!");
		}
		if (currentGraphicsPipeline.VertexShaderInfo.UniformBufferSize != size)
		{
			throw new InvalidOperationException("Vertex uniform data size mismatch!");
		}
#endif
		Refresh.Refresh_PushVertexShaderUniforms(
			Device.Handle,
			Handle,
			(IntPtr) uniformsPtr,
			size
		);
	}
	/// 
	/// Pushes vertex shader uniforms to the device.
	/// 
	/// A starting offset value to be used with draw calls.
	public unsafe void PushVertexShaderUniforms(
		in T uniforms
	) where T : unmanaged
	{
		fixed (T* uniformsPtr = &uniforms)
		{
			PushVertexShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
		}
	}
	/// 
	/// Pushes fragment shader uniforms to the device.
	/// 
	/// A starting offset to be used with draw calls.
	public unsafe void PushFragmentShaderUniforms(
		void* uniformsPtr,
		uint size
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
		if (currentGraphicsPipeline.FragmentShaderInfo.UniformBufferSize == 0)
		{
			throw new InvalidOperationException("The current fragment shader does not take a uniform buffer!");
		}
		if (currentGraphicsPipeline.FragmentShaderInfo.UniformBufferSize != size)
		{
			throw new InvalidOperationException("Fragment uniform data size mismatch!");
		}
#endif
		Refresh.Refresh_PushFragmentShaderUniforms(
			Device.Handle,
			Handle,
			(IntPtr) uniformsPtr,
			size
		);
	}
	/// 
	/// Pushes fragment shader uniforms to the device.
	/// 
	/// A starting offset to be used with draw calls.
	public unsafe void PushFragmentShaderUniforms(
		in T uniforms
	) where T : unmanaged
	{
		fixed (T* uniformsPtr = &uniforms)
		{
			PushFragmentShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
		}
	}
	/// 
	/// Draws using instanced rendering.
	/// 
	/// The starting index offset for the vertex buffer.
	/// The starting index offset for the index buffer.
	/// The number of primitives to draw.
	/// The number of instances to draw.
	public void DrawInstancedPrimitives(
		uint baseVertex,
		uint startIndex,
		uint primitiveCount,
		uint instanceCount
	)
	{
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
#endif
		Refresh.Refresh_DrawInstancedPrimitives(
			Device.Handle,
			Handle,
			baseVertex,
			startIndex,
			primitiveCount,
			instanceCount
		);
	}
	/// 
	/// Draws using a vertex buffer and an index buffer.
	/// 
	/// The starting index offset for the vertex buffer.
	/// The starting index offset for the index buffer.
	/// The number of primitives to draw.
	public void DrawIndexedPrimitives(
		uint baseVertex,
		uint startIndex,
		uint primitiveCount
	)
	{
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
#endif
		Refresh.Refresh_DrawInstancedPrimitives(
			Device.Handle,
			Handle,
			baseVertex,
			startIndex,
			primitiveCount,
			1
		);
	}
	/// 
	/// Draws using a vertex buffer.
	/// 
	/// 
	/// 
	public void DrawPrimitives(
		uint vertexStart,
		uint primitiveCount
	)
	{
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
#endif
		Refresh.Refresh_DrawPrimitives(
			Device.Handle,
			Handle,
			vertexStart,
			primitiveCount
		);
	}
	/// 
	/// Similar to DrawPrimitives, but parameters are set from a buffer.
	/// 
	/// The draw parameters buffer.
	/// The offset to start reading from the draw parameters buffer.
	/// The number of draw parameter sets that should be read from the buffer.
	/// The byte stride between sets of draw parameters.
	/// An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.
	/// An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.
	public void DrawPrimitivesIndirect(
		GpuBuffer buffer,
		uint offsetInBytes,
		uint drawCount,
		uint stride
	)
	{
#if DEBUG
		AssertNotSubmitted();
		AssertGraphicsPipelineBound();
#endif
		Refresh.Refresh_DrawPrimitivesIndirect(
			Device.Handle,
			Handle,
			buffer.Handle,
			offsetInBytes,
			drawCount,
			stride
		);
	}
	/// 
	/// Ends the current render pass.
	/// This must be called before beginning another render pass or submitting the command buffer.
	/// 
	public void EndRenderPass()
	{
#if DEBUG
		AssertNotSubmitted();
#endif
		Refresh.Refresh_EndRenderPass(
			Device.Handle,
			Handle
		);
#if DEBUG
		currentGraphicsPipeline = null;
		renderPassActive = false;
#endif
	}
	/// 
	/// Blits a texture to another texture with the specified filter.
	///
	/// This operation cannot be performed inside any pass.
	/// 
	/// Specifies data dependency behavior.
	public void Blit(
		TextureRegion source,
		TextureRegion destination,
		Filter filter,
		WriteOptions writeOption
	) {
		var sampler = filter == Filter.Linear ? Device.LinearSampler : Device.PointSampler;
		// FIXME: this will break with non-2D textures
		// FIXME: the source texture region does nothing right now
		BeginRenderPass(new ColorAttachmentInfo(destination.TextureSlice, writeOption));
		SetViewport(new Viewport(destination.X, destination.Y, destination.Width, destination.Height));
		BindGraphicsPipeline(Device.BlitPipeline);
		BindFragmentSamplers(new TextureSamplerBinding(source.TextureSlice.Texture, sampler));
		DrawPrimitives(0, 2);
		EndRenderPass();
	}
	public void BeginComputePass()
	{
#if DEBUG
		AssertNotSubmitted();
		AssertNotInPass("Cannot begin compute pass while in another pass!");
		computePassActive = true;
#endif
		Refresh.Refresh_BeginComputePass(
			Device.Handle,
			Handle
		);
	}
	/// 
	/// Binds a compute pipeline so that compute work may be dispatched.
	/// 
	/// The compute pipeline to bind.
	public void BindComputePipeline(
		ComputePipeline computePipeline
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute pipeline outside of compute pass!");
#endif
		Refresh.Refresh_BindComputePipeline(
			Device.Handle,
			Handle,
			computePipeline.Handle
		);
#if DEBUG
		currentComputePipeline = computePipeline;
#endif
	}
	/// 
	/// Binds a buffer to be used in the compute shader.
	/// 
	public unsafe void BindComputeBuffers(
		ComputeBufferBinding binding
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeBufferCount(1);
#endif
		var bindingArray = stackalloc Refresh.ComputeBufferBinding[1];
		bindingArray[0] = binding.ToRefresh();
		Refresh.Refresh_BindComputeBuffers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds buffers to be used in the compute shader.
	/// 
	public unsafe void BindComputeBuffers(
		ComputeBufferBinding bindingOne,
		ComputeBufferBinding bindingTwo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeBufferCount(2);
#endif
		var bindingArray = stackalloc Refresh.ComputeBufferBinding[2];
		bindingArray[0] = bindingOne.ToRefresh();
		bindingArray[1] = bindingTwo.ToRefresh();
		Refresh.Refresh_BindComputeBuffers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds buffers to be used in the compute shader.
	/// 
	public unsafe void BindComputeBuffers(
		ComputeBufferBinding bindingOne,
		ComputeBufferBinding bindingTwo,
		ComputeBufferBinding bindingThree
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeBufferCount(3);
#endif
		var bindingArray = stackalloc Refresh.ComputeBufferBinding[3];
		bindingArray[0] = bindingOne.ToRefresh();
		bindingArray[1] = bindingTwo.ToRefresh();
		bindingArray[2] = bindingThree.ToRefresh();
		Refresh.Refresh_BindComputeBuffers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds buffers to be used in the compute shader.
	/// 
	public unsafe void BindComputeBuffers(
		ComputeBufferBinding bindingOne,
		ComputeBufferBinding bindingTwo,
		ComputeBufferBinding bindingThree,
		ComputeBufferBinding bindingFour
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeBufferCount(4);
#endif
		var bindingArray = stackalloc Refresh.ComputeBufferBinding[4];
		bindingArray[0] = bindingOne.ToRefresh();
		bindingArray[1] = bindingTwo.ToRefresh();
		bindingArray[2] = bindingThree.ToRefresh();
		bindingArray[3] = bindingFour.ToRefresh();
		Refresh.Refresh_BindComputeBuffers(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds buffers to be used in the compute shader.
	/// 
	/// A Span of buffers to bind.
	public unsafe void BindComputeBuffers(
		in Span bindings
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute buffers outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeBufferCount(bindings.Length);
#endif
		Refresh.ComputeBufferBinding* bindingArray = (Refresh.ComputeBufferBinding*) NativeMemory.Alloc(
			(nuint) (Marshal.SizeOf() * bindings.Length)
		);
		for (var i = 0; i < bindings.Length; i += 1)
		{
			bindingArray[i] = bindings[i].ToRefresh();
		}
		Refresh.Refresh_BindComputeBuffers(
			Device.Handle,
			Handle,
			bindingArray
		);
		NativeMemory.Free(bindingArray);
	}
	/// 
	/// Binds a texture slice to be used in the compute shader.
	/// 
	public unsafe void BindComputeTextures(
		ComputeTextureBinding binding
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute textures outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeTextureCount(1);
#endif
		var bindingArray = stackalloc Refresh.ComputeTextureBinding[1];
		bindingArray[0] = binding.ToRefresh();
		Refresh.Refresh_BindComputeTextures(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds textures to be used in the compute shader.
	/// 
	public unsafe void BindComputeTextures(
		ComputeTextureBinding bindingOne,
		ComputeTextureBinding bindingTwo
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute textures outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeTextureCount(2);
#endif
		var bindingArray = stackalloc Refresh.ComputeTextureBinding[2];
		bindingArray[0] = bindingOne.ToRefresh();
		bindingArray[1] = bindingTwo.ToRefresh();
		Refresh.Refresh_BindComputeTextures(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds textures to be used in the compute shader.
	/// 
	public unsafe void BindComputeTextures(
		ComputeTextureBinding bindingOne,
		ComputeTextureBinding bindingTwo,
		ComputeTextureBinding bindingThree
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute textures outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeTextureCount(3);
#endif
		var bindingArray = stackalloc Refresh.ComputeTextureBinding[3];
		bindingArray[0] = bindingOne.ToRefresh();
		bindingArray[1] = bindingTwo.ToRefresh();
		bindingArray[2] = bindingThree.ToRefresh();
		Refresh.Refresh_BindComputeTextures(
			Device.Handle,
			Handle,
			bindingArray
		);
	}
	/// 
	/// Binds textures to be used in the compute shader.
	/// 
	public unsafe void BindComputeTextures(
		ComputeTextureBinding bindingOne,
		ComputeTextureBinding bindingTwo,
		ComputeTextureBinding bindingThree,
		ComputeTextureBinding bindingFour
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute textures outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeTextureCount(4);
#endif
		var textureSlicePtrs = stackalloc Refresh.ComputeTextureBinding[4];
		textureSlicePtrs[0] = bindingOne.ToRefresh();
		textureSlicePtrs[1] = bindingTwo.ToRefresh();
		textureSlicePtrs[2] = bindingThree.ToRefresh();
		textureSlicePtrs[3] = bindingFour.ToRefresh();
		Refresh.Refresh_BindComputeTextures(
			Device.Handle,
			Handle,
			textureSlicePtrs
		);
	}
	/// 
	/// Binds textures to be used in the compute shader.
	/// 
	public unsafe void BindComputeTextures(
		in Span bindings
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot bind compute textures outside of compute pass!");
		AssertComputePipelineBound();
		AssertComputeTextureCount(bindings.Length);
#endif
		Refresh.ComputeTextureBinding* bindingArray = (Refresh.ComputeTextureBinding*) NativeMemory.Alloc(
			(nuint) (Marshal.SizeOf() * bindings.Length)
		);
		for (var i = 0; i < bindings.Length; i += 1)
		{
			bindingArray[i] = bindings[i].ToRefresh();
		}
		Refresh.Refresh_BindComputeTextures(
			Device.Handle,
			Handle,
			bindingArray
		);
		NativeMemory.Free(bindingArray);
	}
	/// 
	/// Pushes compute shader uniforms to the device.
	/// 
	/// A starting offset to be used with dispatch calls.
	public unsafe void PushComputeShaderUniforms(
		void* uniformsPtr,
		uint size
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertComputePipelineBound();
		if (currentComputePipeline.ComputeShaderInfo.UniformBufferSize == 0)
		{
			throw new System.InvalidOperationException("The current compute shader does not take a uniform buffer!");
		}
		if (currentComputePipeline.ComputeShaderInfo.UniformBufferSize != size)
		{
			throw new InvalidOperationException("Compute uniform data size mismatch!");
		}
#endif
		Refresh.Refresh_PushComputeShaderUniforms(
			Device.Handle,
			Handle,
			(IntPtr) uniformsPtr,
			size
		);
	}
	/// 
	/// Pushes compute shader uniforms to the device.
	/// 
	/// A starting offset to be used with dispatch calls.
	public unsafe void PushComputeShaderUniforms(
		in T uniforms
	) where T : unmanaged
	{
		fixed (T* uniformsPtr = &uniforms)
		{
			PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
		}
	}
	/// 
	/// Dispatches compute work.
	/// 
	/// 
	/// 
	/// 
	/// 
	public void DispatchCompute(
		uint groupCountX,
		uint groupCountY,
		uint groupCountZ
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInComputePass("Cannot dispatch compute outside of compute pass!");
		AssertComputePipelineBound();
		if (groupCountX < 1 || groupCountY < 1 || groupCountZ < 1)
		{
			throw new ArgumentException("All dimensions for the compute work group must be >= 1!");
		}
#endif
		Refresh.Refresh_DispatchCompute(
			Device.Handle,
			Handle,
			groupCountX,
			groupCountY,
			groupCountZ
		);
	}
	public void EndComputePass()
	{
#if DEBUG
		AssertInComputePass("Cannot end compute pass while not in a compute pass!");
		computePassActive = false;
#endif
		Refresh.Refresh_EndComputePass(
			Device.Handle,
			Handle
		);
	}
	// Copy Pass
	/// 
	/// 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.
	/// 
	public void BeginCopyPass()
	{
#if DEBUG
		AssertNotSubmitted();
		AssertNotInPass("Cannot begin copy pass while in another pass!");
		copyPassActive = true;
#endif
		Refresh.Refresh_BeginCopyPass(
			Device.Handle,
			Handle
		);
	}
	/// 
	/// Uploads data from a TransferBuffer to a TextureSlice.
	/// This copy occurs on the GPU timeline.
	///
	/// Overwriting the contents of the TransferBuffer before the command buffer
	/// has finished execution will cause undefined behavior.
	///
	/// You MAY assume that the copy has finished for subsequent commands.
	/// 
	/// Specifies data dependency behavior.
	public void UploadToTexture(
		TransferBuffer transferBuffer,
		in TextureRegion textureRegion,
		in BufferImageCopy copyParams,
		WriteOptions writeOption
	)
	{
#if DEBUG
		AssertNotSubmitted();
		AssertInCopyPass("Cannot upload to texture outside of copy pass!");
		AssertBufferBoundsCheck(transferBuffer.Size, copyParams.BufferOffset, textureRegion.Size);
#endif
		Refresh.Refresh_UploadToTexture(
			Device.Handle,
			Handle,
			transferBuffer.Handle,
			textureRegion.ToRefreshTextureRegion(),
			copyParams.ToRefresh(),
			(Refresh.WriteOptions) writeOption
		);
	}
	/// 
	/// Uploads the contents of an entire buffer to a texture with no mips.
	/// 
	public void UploadToTexture(
		TransferBuffer transferBuffer,
		Texture texture,
		WriteOptions writeOption
	) {
		UploadToTexture(
			transferBuffer,
			new TextureRegion(texture),
			new BufferImageCopy(0, 0, 0),
			writeOption
		);
	}
	/// 
	/// Uploads data from a TransferBuffer to a GpuBuffer.
	/// This copy occurs on the GPU timeline.
	///
	/// Overwriting the contents of the TransferBuffer before the command buffer
	/// has finished execution will cause undefined behavior.
	///
	/// You MAY assume that the copy has finished for subsequent commands.
	/// 
	public void UploadToBuffer(
		TransferBuffer transferBuffer,
		GpuBuffer gpuBuffer,
		in BufferCopy copyParams,
		WriteOptions option
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInCopyPass("Cannot upload to texture outside of copy pass!");
		AssertBufferBoundsCheck(transferBuffer.Size, copyParams.SrcOffset, copyParams.Size);
		AssertBufferBoundsCheck(gpuBuffer.Size, copyParams.DstOffset, copyParams.Size);
#endif
		Refresh.Refresh_UploadToBuffer(
			Device.Handle,
			Handle,
			transferBuffer.Handle,
			gpuBuffer.Handle,
			copyParams.ToRefresh(),
			(Refresh.WriteOptions) option
		);
	}
	/// 
	/// Copies the entire contents of a TransferBuffer to a GpuBuffer.
	/// 
	public void UploadToBuffer(
		TransferBuffer transferBuffer,
		GpuBuffer gpuBuffer,
		WriteOptions option
	) {
		UploadToBuffer(
			transferBuffer,
			gpuBuffer,
			new BufferCopy(0, 0, transferBuffer.Size),
			option
		);
	}
	/// 
	/// Copies data element-wise into from a TransferBuffer to a GpuBuffer.
	/// 
	public void UploadToBuffer(
		TransferBuffer transferBuffer,
		GpuBuffer gpuBuffer,
		uint sourceStartElement,
		uint destinationStartElement,
		uint numElements,
		WriteOptions option
	) where T : unmanaged
	{
		var elementSize = Marshal.SizeOf();
		var dataLengthInBytes = (uint) (elementSize * numElements);
		var srcOffsetInBytes = (uint) (elementSize * sourceStartElement);
		var dstOffsetInBytes = (uint) (elementSize * destinationStartElement);
		UploadToBuffer(
			transferBuffer,
			gpuBuffer,
			new BufferCopy(
				srcOffsetInBytes,
				dstOffsetInBytes,
				dataLengthInBytes
			),
			option
		);
	}
	/// 
	/// Copies the contents of a TextureSlice to another TextureSlice.
	/// The slices must have the same dimensions.
	/// This copy occurs on the GPU timeline.
	///
	/// You MAY assume that the copy has finished in subsequent commands.
	/// 
	public void CopyTextureToTexture(
		in TextureRegion source,
		in TextureRegion destination,
		WriteOptions option
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInCopyPass("Cannot download from texture outside of copy pass!");
		AssertTextureBoundsCheck(destination.Size, source.Size);
#endif
		Refresh.Refresh_CopyTextureToTexture(
			Device.Handle,
			Handle,
			source.ToRefreshTextureRegion(),
			destination.ToRefreshTextureRegion(),
			(Refresh.WriteOptions) option
		);
	}
	/// 
	/// Copies the contents of an entire Texture with no mips to another Texture with no mips.
	/// The textures must have the same dimensions.
	/// 
	public void CopyTextureToTexture(
		Texture source,
		Texture destination,
		WriteOptions option
	) {
		CopyTextureToTexture(
			new TextureRegion(source),
			new TextureRegion(destination),
			option
		);
	}
	/// 
	/// Copies data from a GpuBuffer to another GpuBuffer.
	/// This copy occurs on the GPU timeline.
	///
	/// You MAY assume that the copy has finished in subsequent commands.
	/// 
	public void CopyBufferToBuffer(
		GpuBuffer source,
		GpuBuffer destination,
		in BufferCopy copyParams,
		WriteOptions option
	) {
#if DEBUG
		AssertNotSubmitted();
		AssertInCopyPass("Cannot download from texture outside of copy pass!");
		AssertBufferBoundsCheck(source.Size, copyParams.SrcOffset, copyParams.Size);
		AssertBufferBoundsCheck(destination.Size, copyParams.DstOffset, copyParams.Size);
#endif
		Refresh.Refresh_CopyBufferToBuffer(
			Device.Handle,
			Handle,
			source.Handle,
			destination.Handle,
			copyParams.ToRefresh(),
			(Refresh.WriteOptions) option
		);
	}
	/// 
	/// Copies the entire contents of a GpuBuffer to another GpuBuffer.
	/// 
	public void CopyBufferToBuffer(
		GpuBuffer source,
		GpuBuffer destination,
		WriteOptions option
	) {
		CopyBufferToBuffer(
			source,
			destination,
			new BufferCopy(0, 0, source.Size),
			option
		);
	}
	public void EndCopyPass()
	{
#if DEBUG
		AssertNotSubmitted();
		AssertInCopyPass("Cannot end copy pass while not in a copy pass!");
		copyPassActive = false;
#endif
		Refresh.Refresh_EndCopyPass(
			Device.Handle,
			Handle
		);
	}
#if DEBUG
	private void AssertRenderPassActive(string message = "No active render pass!")
	{
		if (!renderPassActive)
		{
			throw new System.InvalidOperationException(message);
		}
	}
	private void AssertRenderPassInactive(string message = "Render pass is active!")
	{
		if (renderPassActive)
		{
			throw new System.InvalidCastException(message);
		}
	}
	private void AssertGraphicsPipelineBound(string message = "No graphics pipeline is bound!")
	{
		if (currentGraphicsPipeline == null)
		{
			throw new System.InvalidOperationException(message);
		}
	}
	private void AssertVertexSamplerCount(int count)
	{
		if (currentGraphicsPipeline.VertexShaderInfo.SamplerBindingCount != count)
		{
			throw new System.InvalidOperationException($"Vertex sampler expected {currentGraphicsPipeline.VertexShaderInfo.SamplerBindingCount} samplers, but received {count}");
		}
	}
	private void AssertFragmentSamplerCount(int count)
	{
		if (currentGraphicsPipeline.FragmentShaderInfo.SamplerBindingCount != count)
		{
			throw new System.InvalidOperationException($"Fragment sampler expected {currentGraphicsPipeline.FragmentShaderInfo.SamplerBindingCount} samplers, but received {count}");
		}
	}
	private void AssertComputePipelineBound(string message = "No compute pipeline is bound!")
	{
		if (currentComputePipeline == null)
		{
			throw new System.InvalidOperationException(message);
		}
	}
	private void AssertComputeBufferCount(int count)
	{
		if (currentComputePipeline.ComputeShaderInfo.BufferBindingCount != count)
		{
			throw new System.InvalidOperationException($"Compute pipeline expects {currentComputePipeline.ComputeShaderInfo.BufferBindingCount} buffers, but received {count}");
		}
	}
	private void AssertComputeTextureCount(int count)
	{
		if (currentComputePipeline.ComputeShaderInfo.ImageBindingCount != count)
		{
			throw new System.InvalidOperationException($"Compute pipeline expects {currentComputePipeline.ComputeShaderInfo.ImageBindingCount} textures, but received {count}");
		}
	}
	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.DepthStencilTarget) == 0)
		{
			throw new System.ArgumentException("Render pass depth stencil attachment UsageFlags must include TextureUsageFlags.DepthStencilTarget!");
		}
	}
	private void AssertTextureSamplerBindingNonNull(in TextureSamplerBinding binding)
	{
		if (binding.Texture == null || binding.Texture.Handle == IntPtr.Zero)
		{
			throw new NullReferenceException("Texture binding must not be null!");
		}
		if (binding.Sampler == null || binding.Sampler.Handle == IntPtr.Zero)
		{
			throw new NullReferenceException("Sampler binding must not be null!");
		}
	}
	private void AssertTextureBindingUsageFlags(Texture texture)
	{
		if ((texture.UsageFlags & TextureUsageFlags.Sampler) == 0)
		{
			throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.Sampler!");
		}
	}
	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 AssertInRenderPass(string message)
	{
		if (!renderPassActive)
		{
			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
}