using System;
using System.Runtime.InteropServices;
using RefreshCS;

namespace MoonWorks.Graphics
{
	/// <summary>
	/// 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.
	/// </summary>
	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;

		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;

			Submitted = false;
		}
#endif

		/// <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="colorAttachmentInfo">The color attachment to use in the render pass.</param>
		public unsafe void BeginRenderPass(
			in ColorAttachmentInfo colorAttachmentInfo
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertTextureNotNull(colorAttachmentInfo);
			AssertColorTarget(colorAttachmentInfo);
#endif

			var refreshColorAttachmentInfos = stackalloc Refresh.ColorAttachmentInfo[1];
			refreshColorAttachmentInfos[0] = colorAttachmentInfo.ToRefresh();

			Refresh.Refresh_BeginRenderPass(
				Device.Handle,
				Handle,
				(IntPtr) refreshColorAttachmentInfos,
				1,
				IntPtr.Zero
			);

#if DEBUG
			renderPassActive = true;
			hasDepthStencilAttachment = false;
			colorAttachmentSampleCount = colorAttachmentInfo.Texture.SampleCount;
			colorAttachmentCount = 1;
			colorFormatOne = colorAttachmentInfo.Texture.Format;
#endif
		}

		/// <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 void BeginRenderPass(
			in ColorAttachmentInfo colorAttachmentInfoOne,
			in ColorAttachmentInfo colorAttachmentInfoTwo
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertTextureNotNull(colorAttachmentInfoOne);
			AssertColorTarget(colorAttachmentInfoOne);

			AssertTextureNotNull(colorAttachmentInfoTwo);
			AssertColorTarget(colorAttachmentInfoTwo);

			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoTwo.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.Texture.SampleCount;
			colorAttachmentCount = 2;
			colorFormatOne = colorAttachmentInfoOne.Texture.Format;
			colorFormatTwo = colorAttachmentInfoTwo.Texture.Format;
#endif
		}

		/// <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 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.Texture, colorAttachmentInfoTwo.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoThree.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.Texture.SampleCount;
			colorAttachmentCount = 3;
			colorFormatOne = colorAttachmentInfoOne.Texture.Format;
			colorFormatTwo = colorAttachmentInfoTwo.Texture.Format;
			colorFormatThree = colorAttachmentInfoThree.Texture.Format;
#endif
		}

		/// <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 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.Texture, colorAttachmentInfoTwo.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoThree.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoFour.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.Texture.SampleCount;
			colorAttachmentCount = 4;
			colorFormatOne = colorAttachmentInfoOne.Texture.Format;
			colorFormatTwo = colorAttachmentInfoTwo.Texture.Format;
			colorFormatThree = colorAttachmentInfoThree.Texture.Format;
			colorFormatFour = colorAttachmentInfoFour.Texture.Format;
#endif
		}

		/// <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 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.Texture.SampleCount;
			depthStencilFormat = depthStencilAttachmentInfo.Texture.Format;
#endif
		}

		/// <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 void BeginRenderPass(
			in DepthStencilAttachmentInfo depthStencilAttachmentInfo,
			in ColorAttachmentInfo colorAttachmentInfo
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertValidDepthAttachment(depthStencilAttachmentInfo);

			AssertTextureNotNull(colorAttachmentInfo);
			AssertColorTarget(colorAttachmentInfo);
			AssertSameSampleCount(colorAttachmentInfo.Texture, depthStencilAttachmentInfo.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.Texture.SampleCount;
			colorAttachmentCount = 1;
			depthStencilAttachmentSampleCount = depthStencilAttachmentInfo.Texture.SampleCount;
			colorFormatOne = colorAttachmentInfo.Texture.Format;
			depthStencilFormat = depthStencilAttachmentInfo.Texture.Format;
#endif
		}

		/// <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 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.Texture, colorAttachmentInfoTwo.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, depthStencilAttachmentInfo.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.Texture.SampleCount;
			colorAttachmentCount = 2;
			colorFormatOne = colorAttachmentInfoOne.Texture.Format;
			colorFormatTwo = colorAttachmentInfoTwo.Texture.Format;
			depthStencilFormat = depthStencilAttachmentInfo.Texture.Format;
#endif
		}

		/// <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 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.Texture, colorAttachmentInfoTwo.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoTwo.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, depthStencilAttachmentInfo.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.Texture.SampleCount;
			colorAttachmentCount = 3;
			colorFormatOne = colorAttachmentInfoOne.Texture.Format;
			colorFormatTwo = colorAttachmentInfoTwo.Texture.Format;
			colorFormatThree = colorAttachmentInfoThree.Texture.Format;
			depthStencilFormat = depthStencilAttachmentInfo.Texture.Format;
#endif
		}

		/// <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 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.Texture, colorAttachmentInfoTwo.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoThree.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, colorAttachmentInfoFour.Texture);
			AssertSameSampleCount(colorAttachmentInfoOne.Texture, depthStencilAttachmentInfo.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.Texture.SampleCount;
			colorAttachmentCount = 4;
			colorFormatOne = colorAttachmentInfoOne.Texture.Format;
			colorFormatTwo = colorAttachmentInfoTwo.Texture.Format;
			colorFormatThree = colorAttachmentInfoThree.Texture.Format;
			colorFormatFour = colorAttachmentInfoFour.Texture.Format;
			depthStencilFormat = depthStencilAttachmentInfo.Texture.Format;
#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
			AssertNotSubmitted();
#endif

			Refresh.Refresh_BindComputePipeline(
				Device.Handle,
				Handle,
				computePipeline.Handle
			);

#if DEBUG
			currentComputePipeline = computePipeline;
#endif
		}

		/// <summary>
		/// Binds a buffer to be used in the compute shader.
		/// </summary>
		/// <param name="buffer">A buffer to bind.</param>
		public unsafe void BindComputeBuffers(
			Buffer buffer
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeBufferCount(1);
#endif

			var bufferPtrs = stackalloc IntPtr[1];
			bufferPtrs[0] = buffer.Handle;

			Refresh.Refresh_BindComputeBuffers(
				Device.Handle,
				Handle,
				(IntPtr) bufferPtrs
			);
		}

		/// <summary>
		/// Binds buffers to be used in the compute shader.
		/// </summary>
		/// <param name="bufferOne">A buffer to bind.</param>
		/// <param name="bufferTwo">A buffer to bind.</param>
		public unsafe void BindComputeBuffers(
			Buffer bufferOne,
			Buffer bufferTwo
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeBufferCount(2);
#endif

			var bufferPtrs = stackalloc IntPtr[2];
			bufferPtrs[0] = bufferOne.Handle;
			bufferPtrs[1] = bufferTwo.Handle;

			Refresh.Refresh_BindComputeBuffers(
				Device.Handle,
				Handle,
				(IntPtr) bufferPtrs
			);
		}

		/// <summary>
		/// Binds buffers to be used in the compute shader.
		/// </summary>
		/// <param name="bufferOne">A buffer to bind.</param>
		/// <param name="bufferTwo">A buffer to bind.</param>
		/// <param name="bufferThree">A buffer to bind.</param>
		public unsafe void BindComputeBuffers(
			Buffer bufferOne,
			Buffer bufferTwo,
			Buffer bufferThree
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeBufferCount(3);
#endif

			var bufferPtrs = stackalloc IntPtr[3];
			bufferPtrs[0] = bufferOne.Handle;
			bufferPtrs[1] = bufferTwo.Handle;
			bufferPtrs[2] = bufferThree.Handle;

			Refresh.Refresh_BindComputeBuffers(
				Device.Handle,
				Handle,
				(IntPtr) bufferPtrs
			);
		}

		/// <summary>
		/// Binds buffers to be used in the compute shader.
		/// </summary>
		/// <param name="bufferOne">A buffer to bind.</param>
		/// <param name="bufferTwo">A buffer to bind.</param>
		/// <param name="bufferThree">A buffer to bind.</param>
		/// <param name="bufferFour">A buffer to bind.</param>
		public unsafe void BindComputeBuffers(
			Buffer bufferOne,
			Buffer bufferTwo,
			Buffer bufferThree,
			Buffer bufferFour
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeBufferCount(4);
#endif

			var bufferPtrs = stackalloc IntPtr[4];
			bufferPtrs[0] = bufferOne.Handle;
			bufferPtrs[1] = bufferTwo.Handle;
			bufferPtrs[2] = bufferThree.Handle;
			bufferPtrs[3] = bufferFour.Handle;

			Refresh.Refresh_BindComputeBuffers(
				Device.Handle,
				Handle,
				(IntPtr) bufferPtrs
			);
		}

		/// <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<Buffer> buffers
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeBufferCount(buffers.Length);
#endif

			var bufferPtrs = stackalloc IntPtr[buffers.Length];

			for (var i = 0; i < buffers.Length; i += 1)
			{
				bufferPtrs[i] = buffers[i].Handle;
			}

			Refresh.Refresh_BindComputeBuffers(
				Device.Handle,
				Handle,
				(IntPtr) bufferPtrs
			);
		}

		/// <summary>
		/// Binds a texture to be used in the compute shader.
		/// </summary>
		/// <param name="texture">A texture to bind.</param>
		public unsafe void BindComputeTextures(
			Texture texture
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeTextureCount(1);
#endif

			var texturePtrs = stackalloc IntPtr[1];
			texturePtrs[0] = texture.Handle;

			Refresh.Refresh_BindComputeTextures(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs
			);
		}

		/// <summary>
		/// Binds textures to be used in the compute shader.
		/// </summary>
		/// <param name="textureOne">A texture to bind.</param>
		/// <param name="textureTwo">A texture to bind.</param>
		public unsafe void BindComputeTextures(
			Texture textureOne,
			Texture textureTwo
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeTextureCount(2);
#endif

			var texturePtrs = stackalloc IntPtr[2];
			texturePtrs[0] = textureOne.Handle;
			texturePtrs[1] = textureTwo.Handle;

			Refresh.Refresh_BindComputeTextures(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs
			);
		}

		/// <summary>
		/// Binds textures to be used in the compute shader.
		/// </summary>
		/// <param name="textureOne">A texture to bind.</param>
		/// <param name="textureTwo">A texture to bind.</param>
		/// <param name="textureThree">A texture to bind.</param>
		public unsafe void BindComputeTextures(
			Texture textureOne,
			Texture textureTwo,
			Texture textureThree
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeTextureCount(3);
#endif

			var texturePtrs = stackalloc IntPtr[3];
			texturePtrs[0] = textureOne.Handle;
			texturePtrs[1] = textureTwo.Handle;
			texturePtrs[2] = textureThree.Handle;

			Refresh.Refresh_BindComputeTextures(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs
			);
		}

		/// <summary>
		/// Binds textures to be used in the compute shader.
		/// </summary>
		/// <param name="textureOne">A texture to bind.</param>
		/// <param name="textureTwo">A texture to bind.</param>
		/// <param name="textureThree">A texture to bind.</param>
		/// <param name="textureFour">A texture to bind.</param>
		public unsafe void BindComputeTextures(
			Texture textureOne,
			Texture textureTwo,
			Texture textureThree,
			Texture textureFour
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeTextureCount(4);
#endif

			var texturePtrs = stackalloc IntPtr[4];
			texturePtrs[0] = textureOne.Handle;
			texturePtrs[1] = textureTwo.Handle;
			texturePtrs[2] = textureThree.Handle;
			texturePtrs[3] = textureFour.Handle;

			Refresh.Refresh_BindComputeTextures(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs
			);
		}

		/// <summary>
		/// Binds textures to be used in the compute shader.
		/// </summary>
		/// <param name="textures">A set of textures to bind.</param>
		public unsafe void BindComputeTextures(
			in Span<Texture> textures
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertComputePipelineBound();
			AssertComputeTextureCount(textures.Length);
#endif

			var texturePtrs = stackalloc IntPtr[textures.Length];

			for (var i = 0; i < textures.Length; i += 1)
			{
				texturePtrs[i] = textures[i].Handle;
			}

			Refresh.Refresh_BindComputeTextures(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs
			);
		}

		/// <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,
			uint computeParamOffset
		) {
#if DEBUG
			AssertNotSubmitted();
			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,
				computeParamOffset
			);
		}

		/// <summary>
		/// Binds a graphics pipeline so that rendering work may be performed.
		/// </summary>
		/// <param name="graphicsPipeline">The graphics pipeline to bind.</param>
		public void BindGraphicsPipeline(
			GraphicsPipeline graphicsPipeline
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassActive();
			AssertRenderPassPipelineFormatMatch(graphicsPipeline);

			if (colorAttachmentCount > 0)
			{
				if (graphicsPipeline.SampleCount != colorAttachmentSampleCount)
				{
					throw new System.InvalidOperationException($"Sample count mismatch! Graphics pipeline sample count: {graphicsPipeline.SampleCount}, Color attachment sample count: {colorAttachmentSampleCount}");
				}
			}

			if (hasDepthStencilAttachment)
			{
				if (graphicsPipeline.SampleCount != depthStencilAttachmentSampleCount)
				{
					throw new System.InvalidOperationException($"Sample count mismatch! Graphics pipeline sample count: {graphicsPipeline.SampleCount}, Depth stencil attachment sample count: {depthStencilAttachmentSampleCount}");
				}
			}
#endif

			Refresh.Refresh_BindGraphicsPipeline(
				Device.Handle,
				Handle,
				graphicsPipeline.Handle
			);

#if DEBUG
			currentGraphicsPipeline = graphicsPipeline;
#endif
		}

		/// <summary>
		/// Sets the viewport. Only valid during a render pass.
		/// </summary>
		public void SetViewport(in Viewport viewport)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassActive();
#endif

			Refresh.Refresh_SetViewport(
				Device.Handle,
				Handle,
				viewport.ToRefresh()
			);
		}

		/// <summary>
		/// Sets the scissor area. Only valid during a render pass.
		/// </summary>
		public void SetScissor(in Rect scissor)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassActive();

			if (scissor.X < 0 || scissor.Y < 0 || scissor.W <= 0 || scissor.H <= 0)
			{
				throw new System.ArgumentOutOfRangeException("Scissor position cannot be negative and dimensions must be positive!");
			}
#endif

			Refresh.Refresh_SetScissor(
				Device.Handle,
				Handle,
				scissor.ToRefresh()
			);
		}

		/// <summary>
		/// Binds vertex buffers to be used by subsequent draw calls.
		/// </summary>
		/// <param name="bufferBinding">Buffer to bind and associated offset.</param>
		/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param>
		public unsafe void BindVertexBuffers(
			in BufferBinding bufferBinding,
			uint firstBinding = 0
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			var bufferPtrs = stackalloc IntPtr[1];
			var offsets = stackalloc ulong[1];

			bufferPtrs[0] = bufferBinding.Buffer.Handle;
			offsets[0] = bufferBinding.Offset;

			Refresh.Refresh_BindVertexBuffers(
				Device.Handle,
				Handle,
				firstBinding,
				1,
				(IntPtr) bufferPtrs,
				(IntPtr) offsets
			);
		}

		/// <summary>
		/// Binds vertex buffers to be used by subsequent draw calls.
		/// </summary>
		/// <param name="bufferBindingOne">Buffer to bind and associated offset.</param>
		/// <param name="bufferBindingTwo">Buffer to bind and associated offset.</param>
		/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param>
		public unsafe void BindVertexBuffers(
			in BufferBinding bufferBindingOne,
			in BufferBinding bufferBindingTwo,
			uint firstBinding = 0
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			var bufferPtrs = stackalloc IntPtr[2];
			var offsets = stackalloc ulong[2];

			bufferPtrs[0] = bufferBindingOne.Buffer.Handle;
			bufferPtrs[1] = bufferBindingTwo.Buffer.Handle;

			offsets[0] = bufferBindingOne.Offset;
			offsets[1] = bufferBindingTwo.Offset;

			Refresh.Refresh_BindVertexBuffers(
				Device.Handle,
				Handle,
				firstBinding,
				2,
				(IntPtr) bufferPtrs,
				(IntPtr) offsets
			);
		}

		/// <summary>
		/// Binds vertex buffers to be used by subsequent draw calls.
		/// </summary>
		/// <param name="bufferBindingOne">Buffer to bind and associated offset.</param>
		/// <param name="bufferBindingTwo">Buffer to bind and associated offset.</param>
		/// <param name="bufferBindingThree">Buffer to bind and associated offset.</param>
		/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param>
		public unsafe void BindVertexBuffers(
			in BufferBinding bufferBindingOne,
			in BufferBinding bufferBindingTwo,
			in BufferBinding bufferBindingThree,
			uint firstBinding = 0
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			var bufferPtrs = stackalloc IntPtr[3];
			var offsets = stackalloc ulong[3];

			bufferPtrs[0] = bufferBindingOne.Buffer.Handle;
			bufferPtrs[1] = bufferBindingTwo.Buffer.Handle;
			bufferPtrs[2] = bufferBindingThree.Buffer.Handle;

			offsets[0] = bufferBindingOne.Offset;
			offsets[1] = bufferBindingTwo.Offset;
			offsets[2] = bufferBindingThree.Offset;

			Refresh.Refresh_BindVertexBuffers(
				Device.Handle,
				Handle,
				firstBinding,
				3,
				(IntPtr) bufferPtrs,
				(IntPtr) offsets
			);
		}

		/// <summary>
		/// Binds vertex buffers to be used by subsequent draw calls.
		/// </summary>
		/// <param name="bufferBindingOne">Buffer to bind and associated offset.</param>
		/// <param name="bufferBindingTwo">Buffer to bind and associated offset.</param>
		/// <param name="bufferBindingThree">Buffer to bind and associated offset.</param>
		/// <param name="bufferBindingFour">Buffer to bind and associated offset.</param>
		/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param>
		public unsafe void BindVertexBuffers(
			in BufferBinding bufferBindingOne,
			in BufferBinding bufferBindingTwo,
			in BufferBinding bufferBindingThree,
			in BufferBinding bufferBindingFour,
			uint firstBinding = 0
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			var bufferPtrs = stackalloc IntPtr[4];
			var offsets = stackalloc ulong[4];

			bufferPtrs[0] = bufferBindingOne.Buffer.Handle;
			bufferPtrs[1] = bufferBindingTwo.Buffer.Handle;
			bufferPtrs[2] = bufferBindingThree.Buffer.Handle;
			bufferPtrs[3] = bufferBindingFour.Buffer.Handle;

			offsets[0] = bufferBindingOne.Offset;
			offsets[1] = bufferBindingTwo.Offset;
			offsets[2] = bufferBindingThree.Offset;
			offsets[3] = bufferBindingFour.Offset;

			Refresh.Refresh_BindVertexBuffers(
				Device.Handle,
				Handle,
				firstBinding,
				4,
				(IntPtr) bufferPtrs,
				(IntPtr) offsets
			);
		}

		/// <summary>
		/// Binds vertex buffers to be used by subsequent draw calls.
		/// </summary>
		/// <param name="bufferBindings">Spawn of buffers to bind and their associated offsets.</param>
		/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param>
		public unsafe void BindVertexBuffers(
			in Span<BufferBinding> bufferBindings,
			uint firstBinding = 0
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			var bufferPtrs = stackalloc IntPtr[bufferBindings.Length];
			var offsets = stackalloc ulong[bufferBindings.Length];

			for (var i = 0; i < bufferBindings.Length; i += 1)
			{
				bufferPtrs[i] = bufferBindings[i].Buffer.Handle;
				offsets[i] = bufferBindings[i].Offset;
			}

			Refresh.Refresh_BindVertexBuffers(
				Device.Handle,
				Handle,
				firstBinding,
				(uint) bufferBindings.Length,
				(IntPtr) bufferPtrs,
				(IntPtr) offsets
			);
		}

		/// <summary>
		/// Binds an index buffer to be used by subsequent draw calls.
		/// </summary>
		/// <param name="indexBuffer">The index buffer to bind.</param>
		/// <param name="indexElementSize">The size in bytes of the index buffer elements.</param>
		/// <param name="offset">The offset index for the buffer.</param>
		public void BindIndexBuffer(
			Buffer indexBuffer,
			IndexElementSize indexElementSize,
			uint offset = 0
		)
		{
#if DEBUG
			AssertNotSubmitted();
#endif

			Refresh.Refresh_BindIndexBuffer(
				Device.Handle,
				Handle,
				indexBuffer.Handle,
				offset,
				(Refresh.IndexElementSize) indexElementSize
			);
		}

		/// <summary>
		/// Binds samplers to be used by the vertex shader.
		/// </summary>
		/// <param name="textureSamplerBindings">The texture-sampler to bind.</param>
		public unsafe void BindVertexSamplers(
			in TextureSamplerBinding textureSamplerBinding
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertVertexSamplerCount(1);
			AssertTextureSamplerBindingNonNull(textureSamplerBinding);
			AssertTextureBindingUsageFlags(textureSamplerBinding.Texture);
#endif

			var texturePtrs = stackalloc IntPtr[1];
			var samplerPtrs = stackalloc IntPtr[1];

			texturePtrs[0] = textureSamplerBinding.Texture.Handle;
			samplerPtrs[0] = textureSamplerBinding.Sampler.Handle;

			Refresh.Refresh_BindVertexSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the vertex shader.
		/// </summary>
		/// <param name="textureSamplerBindingOne">The first texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingTwo">The second texture-sampler to bind.</param>
		public unsafe void BindVertexSamplers(
			in TextureSamplerBinding textureSamplerBindingOne,
			in TextureSamplerBinding textureSamplerBindingTwo
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertVertexSamplerCount(2);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingOne);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingTwo);
			AssertTextureBindingUsageFlags(textureSamplerBindingOne.Texture);
			AssertTextureBindingUsageFlags(textureSamplerBindingTwo.Texture);
#endif

			var texturePtrs = stackalloc IntPtr[2];
			var samplerPtrs = stackalloc IntPtr[2];

			texturePtrs[0] = textureSamplerBindingOne.Texture.Handle;
			texturePtrs[1] = textureSamplerBindingTwo.Texture.Handle;

			samplerPtrs[0] = textureSamplerBindingOne.Sampler.Handle;
			samplerPtrs[1] = textureSamplerBindingTwo.Sampler.Handle;

			Refresh.Refresh_BindVertexSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the vertex shader.
		/// </summary>
		/// <param name="textureSamplerBindingOne">The first texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingTwo">The second texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingThree">The third texture-sampler to bind.</param>
		public unsafe void BindVertexSamplers(
			in TextureSamplerBinding textureSamplerBindingOne,
			in TextureSamplerBinding textureSamplerBindingTwo,
			in TextureSamplerBinding textureSamplerBindingThree
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertVertexSamplerCount(3);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingOne);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingTwo);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingThree);
			AssertTextureBindingUsageFlags(textureSamplerBindingOne.Texture);
			AssertTextureBindingUsageFlags(textureSamplerBindingTwo.Texture);
			AssertTextureBindingUsageFlags(textureSamplerBindingThree.Texture);
#endif

			var texturePtrs = stackalloc IntPtr[3];
			var samplerPtrs = stackalloc IntPtr[3];

			texturePtrs[0] = textureSamplerBindingOne.Texture.Handle;
			texturePtrs[1] = textureSamplerBindingTwo.Texture.Handle;
			texturePtrs[2] = textureSamplerBindingThree.Texture.Handle;

			samplerPtrs[0] = textureSamplerBindingOne.Sampler.Handle;
			samplerPtrs[1] = textureSamplerBindingTwo.Sampler.Handle;
			samplerPtrs[2] = textureSamplerBindingThree.Sampler.Handle;

			Refresh.Refresh_BindVertexSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the vertex shader.
		/// </summary>
		/// <param name="textureSamplerBindingOne">The first texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingTwo">The second texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingThree">The third texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingThree">The fourth texture-sampler to bind.</param>
		public unsafe void BindVertexSamplers(
			in TextureSamplerBinding textureSamplerBindingOne,
			in TextureSamplerBinding textureSamplerBindingTwo,
			in TextureSamplerBinding textureSamplerBindingThree,
			in TextureSamplerBinding textureSamplerBindingFour
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertVertexSamplerCount(4);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingOne);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingTwo);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingThree);
			AssertTextureSamplerBindingNonNull(textureSamplerBindingFour);
			AssertTextureBindingUsageFlags(textureSamplerBindingOne.Texture);
			AssertTextureBindingUsageFlags(textureSamplerBindingTwo.Texture);
			AssertTextureBindingUsageFlags(textureSamplerBindingThree.Texture);
			AssertTextureBindingUsageFlags(textureSamplerBindingFour.Texture);
#endif

			var texturePtrs = stackalloc IntPtr[4];
			var samplerPtrs = stackalloc IntPtr[4];

			texturePtrs[0] = textureSamplerBindingOne.Texture.Handle;
			texturePtrs[1] = textureSamplerBindingTwo.Texture.Handle;
			texturePtrs[2] = textureSamplerBindingThree.Texture.Handle;
			texturePtrs[3] = textureSamplerBindingFour.Texture.Handle;

			samplerPtrs[0] = textureSamplerBindingOne.Sampler.Handle;
			samplerPtrs[1] = textureSamplerBindingTwo.Sampler.Handle;
			samplerPtrs[2] = textureSamplerBindingThree.Sampler.Handle;
			samplerPtrs[3] = textureSamplerBindingFour.Sampler.Handle;

			Refresh.Refresh_BindVertexSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the vertex shader.
		/// </summary>
		/// <param name="textureSamplerBindings">The texture-sampler pairs to bind.</param>
		public unsafe void BindVertexSamplers(
			in Span<TextureSamplerBinding> textureSamplerBindings
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertVertexSamplerCount(textureSamplerBindings.Length);
#endif

			var texturePtrs = stackalloc IntPtr[textureSamplerBindings.Length];
			var samplerPtrs = stackalloc IntPtr[textureSamplerBindings.Length];

			for (var i = 0; i < textureSamplerBindings.Length; i += 1)
			{
#if DEBUG
				AssertTextureSamplerBindingNonNull(textureSamplerBindings[i]);
				AssertTextureBindingUsageFlags(textureSamplerBindings[i].Texture);
#endif

				texturePtrs[i] = textureSamplerBindings[i].Texture.Handle;
				samplerPtrs[i] = textureSamplerBindings[i].Sampler.Handle;
			}

			Refresh.Refresh_BindVertexSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the fragment shader.
		/// </summary>
		/// <param name="textureSamplerBinding">The texture-sampler to bind.</param>
		public unsafe void BindFragmentSamplers(
			in TextureSamplerBinding textureSamplerBinding
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertFragmentSamplerCount(1);
			AssertTextureSamplerBindingNonNull(textureSamplerBinding);
			AssertTextureBindingUsageFlags(textureSamplerBinding.Texture);
#endif

			var texturePtrs = stackalloc IntPtr[1];
			var samplerPtrs = stackalloc IntPtr[1];

			texturePtrs[0] = textureSamplerBinding.Texture.Handle;
			samplerPtrs[0] = textureSamplerBinding.Sampler.Handle;

			Refresh.Refresh_BindFragmentSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the fragment shader.
		/// </summary>
		/// <param name="textureSamplerBindingOne">The first texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingTwo">The second texture-sampler to bind.</param>
		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 texturePtrs = stackalloc IntPtr[2];
			var samplerPtrs = stackalloc IntPtr[2];

			texturePtrs[0] = textureSamplerBindingOne.Texture.Handle;
			texturePtrs[1] = textureSamplerBindingTwo.Texture.Handle;

			samplerPtrs[0] = textureSamplerBindingOne.Sampler.Handle;
			samplerPtrs[1] = textureSamplerBindingTwo.Sampler.Handle;

			Refresh.Refresh_BindFragmentSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the fragment shader.
		/// </summary>
		/// <param name="textureSamplerBindingOne">The first texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingTwo">The second texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingThree">The third texture-sampler to bind.</param>
		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 texturePtrs = stackalloc IntPtr[3];
			var samplerPtrs = stackalloc IntPtr[3];

			texturePtrs[0] = textureSamplerBindingOne.Texture.Handle;
			texturePtrs[1] = textureSamplerBindingTwo.Texture.Handle;
			texturePtrs[2] = textureSamplerBindingThree.Texture.Handle;

			samplerPtrs[0] = textureSamplerBindingOne.Sampler.Handle;
			samplerPtrs[1] = textureSamplerBindingTwo.Sampler.Handle;
			samplerPtrs[2] = textureSamplerBindingThree.Sampler.Handle;

			Refresh.Refresh_BindFragmentSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the fragment shader.
		/// </summary>
		/// <param name="textureSamplerBindingOne">The first texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingTwo">The second texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingThree">The third texture-sampler to bind.</param>
		/// <param name="textureSamplerBindingFour">The fourth texture-sampler to bind.</param>
		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 texturePtrs = stackalloc IntPtr[4];
			var samplerPtrs = stackalloc IntPtr[4];

			texturePtrs[0] = textureSamplerBindingOne.Texture.Handle;
			texturePtrs[1] = textureSamplerBindingTwo.Texture.Handle;
			texturePtrs[2] = textureSamplerBindingThree.Texture.Handle;
			texturePtrs[3] = textureSamplerBindingFour.Texture.Handle;

			samplerPtrs[0] = textureSamplerBindingOne.Sampler.Handle;
			samplerPtrs[1] = textureSamplerBindingTwo.Sampler.Handle;
			samplerPtrs[2] = textureSamplerBindingThree.Sampler.Handle;
			samplerPtrs[3] = textureSamplerBindingFour.Sampler.Handle;

			Refresh.Refresh_BindFragmentSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Binds samplers to be used by the fragment shader.
		/// </summary>
		/// <param name="textureSamplerBindings">The texture-sampler pairs to bind.</param>
		public unsafe void BindFragmentSamplers(
			in Span<TextureSamplerBinding> textureSamplerBindings
		) {
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
			AssertFragmentSamplerCount(textureSamplerBindings.Length);
#endif

			var texturePtrs = stackalloc IntPtr[textureSamplerBindings.Length];
			var samplerPtrs = stackalloc IntPtr[textureSamplerBindings.Length];

			for (var i = 0; i < textureSamplerBindings.Length; i += 1)
			{
#if DEBUG
				AssertTextureSamplerBindingNonNull(textureSamplerBindings[i]);
				AssertTextureBindingUsageFlags(textureSamplerBindings[i].Texture);
#endif

				texturePtrs[i] = textureSamplerBindings[i].Texture.Handle;
				samplerPtrs[i] = textureSamplerBindings[i].Sampler.Handle;
			}

			Refresh.Refresh_BindFragmentSamplers(
				Device.Handle,
				Handle,
				(IntPtr) texturePtrs,
				(IntPtr) samplerPtrs
			);
		}

		/// <summary>
		/// Pushes vertex shader uniforms to the device.
		/// </summary>
		/// <returns>A starting offset value to be used with draw calls.</returns>
		public unsafe uint 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!");
			}
#endif

			return Refresh.Refresh_PushVertexShaderUniforms(
				Device.Handle,
				Handle,
				(IntPtr) uniformsPtr,
				size
			);
		}

		/// <summary>
		/// Pushes vertex shader uniforms to the device.
		/// </summary>
		/// <returns>A starting offset value to be used with draw calls.</returns>
		public unsafe uint PushVertexShaderUniforms<T>(
			in T uniforms
		) where T : unmanaged
		{
			fixed (T* uniformsPtr = &uniforms)
			{
				return PushVertexShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf<T>());
			}
		}

		/// <summary>
		/// Pushes fragment shader uniforms to the device.
		/// </summary>
		/// <returns>A starting offset to be used with draw calls.</returns>
		public unsafe uint 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!");
			}
#endif

			return Refresh.Refresh_PushFragmentShaderUniforms(
				Device.Handle,
				Handle,
				(IntPtr) uniformsPtr,
				size
			);
		}

		/// <summary>
		/// Pushes fragment shader uniforms to the device.
		/// </summary>
		/// <returns>A starting offset to be used with draw calls.</returns>
		public unsafe uint PushFragmentShaderUniforms<T>(
			in T uniforms
		) where T : unmanaged
		{
			fixed (T* uniformsPtr = &uniforms)
			{
				return PushFragmentShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf<T>());
			}
		}

		/// <summary>
		/// Pushes compute shader uniforms to the device.
		/// </summary>
		/// <returns>A starting offset to be used with dispatch calls.</returns>
		public unsafe uint 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!");
			}
#endif

			return 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 uint PushComputeShaderUniforms<T>(
			in T uniforms
		) where T : unmanaged
		{
			fixed (T* uniformsPtr = &uniforms)
			{
				return PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf<T>());
			}
		}

		/// <summary>
		/// Draws using instanced rendering.
		/// </summary>
		/// <param name="baseVertex">The starting index offset for the vertex buffer.</param>
		/// <param name="startIndex">The starting index offset for the index buffer.</param>
		/// <param name="primitiveCount">The number of primitives to draw.</param>
		/// <param name="instanceCount">The number of instances to draw.</param>
		/// <param name="vertexParamOffset">An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.</param>
		/// <param name="fragmentParamOffset">An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.</param>
		public void DrawInstancedPrimitives(
			uint baseVertex,
			uint startIndex,
			uint primitiveCount,
			uint instanceCount,
			uint vertexParamOffset,
			uint fragmentParamOffset
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			Refresh.Refresh_DrawInstancedPrimitives(
				Device.Handle,
				Handle,
				baseVertex,
				startIndex,
				primitiveCount,
				instanceCount,
				vertexParamOffset,
				fragmentParamOffset
			);
		}

		/// <summary>
		/// Draws using a vertex buffer and an index buffer.
		/// </summary>
		/// <param name="baseVertex">The starting index offset for the vertex buffer.</param>
		/// <param name="startIndex">The starting index offset for the index buffer.</param>
		/// <param name="primitiveCount">The number of primitives to draw.</param>
		/// <param name="vertexParamOffset">An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.</param>
		/// <param name="fragmentParamOffset">An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.</param>
		public void DrawIndexedPrimitives(
			uint baseVertex,
			uint startIndex,
			uint primitiveCount,
			uint vertexParamOffset,
			uint fragmentParamOffset
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			Refresh.Refresh_DrawIndexedPrimitives(
				Device.Handle,
				Handle,
				baseVertex,
				startIndex,
				primitiveCount,
				vertexParamOffset,
				fragmentParamOffset
			);
		}

		/// <summary>
		/// Draws using a vertex buffer.
		/// </summary>
		/// <param name="vertexStart"></param>
		/// <param name="primitiveCount"></param>
		/// <param name="vertexParamOffset"></param>
		/// <param name="fragmentParamOffset"></param>
		public void DrawPrimitives(
			uint vertexStart,
			uint primitiveCount,
			uint vertexParamOffset,
			uint fragmentParamOffset
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			Refresh.Refresh_DrawPrimitives(
				Device.Handle,
				Handle,
				vertexStart,
				primitiveCount,
				vertexParamOffset,
				fragmentParamOffset
			);
		}

		/// <summary>
		/// Similar to DrawPrimitives, but parameters are set from a buffer.
		/// </summary>
		/// <param name="buffer">The draw parameters buffer.</param>
		/// <param name="offsetInBytes">The offset to start reading from the draw parameters buffer.</param>
		/// <param name="drawCount">The number of draw parameter sets that should be read from the buffer.</param>
		/// <param name="stride">The byte stride between sets of draw parameters.</param>
		/// <param name="vertexParamOffset">An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.</param>
		/// <param name="fragmentParamOffset">An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.</param>
		public void DrawPrimitivesIndirect(
			Buffer buffer,
			uint offsetInBytes,
			uint drawCount,
			uint stride,
			uint vertexParamOffset,
			uint fragmentParamOffset
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertGraphicsPipelineBound();
#endif

			Refresh.Refresh_DrawPrimitivesIndirect(
				Device.Handle,
				Handle,
				buffer.Handle,
				offsetInBytes,
				drawCount,
				stride,
				vertexParamOffset,
				fragmentParamOffset
			);
		}

		/// <summary>
		/// Ends the current render pass.
		/// This must be called before beginning another render pass or submitting the command buffer.
		/// </summary>
		public void EndRenderPass()
		{
#if DEBUG
			AssertNotSubmitted();
#endif

			Refresh.Refresh_EndRenderPass(
				Device.Handle,
				Handle
			);

#if DEBUG
			currentGraphicsPipeline = null;
			renderPassActive = 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

			var texturePtr = Refresh.Refresh_AcquireSwapchainTexture(
				Device.Handle,
				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;
		}

		/// <summary>
		/// Copies array data into a buffer.
		/// </summary>
		/// <param name="buffer">The buffer to copy to.</param>
		/// <param name="data">The array to copy from.</param>
		/// <param name="bufferOffsetInBytes">Specifies where in the buffer to start copying.</param>
		/// <param name="setDataOption">Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.</param>
		public unsafe void SetBufferData<T>(
			Buffer buffer,
			Span<T> data,
			uint bufferOffsetInBytes = 0
		) where T : unmanaged
		{
			SetBufferData(
				buffer,
				data,
				bufferOffsetInBytes,
				0,
				(uint) data.Length
			);
		}

		/// <summary>
		/// Copies array data into a buffer.
		/// </summary>
		/// <param name="buffer">The buffer to copy to.</param>
		/// <param name="data">The array to copy from.</param>
		/// <param name="bufferOffsetInBytes">Specifies where in the buffer to start copying.</param>
		/// <param name="setDataOption">Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.</param>
		public unsafe void SetBufferData<T>(
			Buffer buffer,
			T[] data,
			uint bufferOffsetInBytes = 0
		) where T : unmanaged
		{
			SetBufferData(
				buffer,
				new Span<T>(data),
				bufferOffsetInBytes,
				0,
				(uint) data.Length
			);
		}

		/// <summary>
		/// Copies arbitrary data into a buffer.
		/// </summary>
		/// <param name="buffer">The buffer to copy into.</param>
		/// <param name="dataPtr">Pointer to the data to copy into the buffer.</param>
		/// <param name="bufferOffsetInBytes">Specifies where in the buffer to copy data.</param>
		/// <param name="dataLengthInBytes">The length of data that should be copied.</param>
		/// <param name="setDataOption">Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.</param>
		public void SetBufferData(
			Buffer buffer,
			IntPtr dataPtr,
			uint bufferOffsetInBytes,
			uint dataLengthInBytes
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
			AssertNonEmptyCopy(dataLengthInBytes);
			AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes);
#endif

			Refresh.Refresh_SetBufferData(
				Device.Handle,
				Handle,
				buffer.Handle,
				bufferOffsetInBytes,
				dataPtr,
				dataLengthInBytes
			);
		}

		/// <summary>
		/// Copies array data into a buffer.
		/// </summary>
		/// <param name="buffer">The buffer to copy to.</param>
		/// <param name="data">The span to copy from.</param>
		/// <param name="bufferOffsetInBytes">Specifies where in the buffer to start copying.</param>
		/// <param name="startElement">The index of the first element to copy from the array.</param>
		/// <param name="numElements">How many elements to copy.</param>
		/// <param name="setDataOption">Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.</param>
		public unsafe void SetBufferData<T>(
			Buffer buffer,
			Span<T> data,
			uint bufferOffsetInBytes,
			uint startElement,
			uint numElements
		) where T : unmanaged
		{
			var elementSize = Marshal.SizeOf<T>();
			var dataLengthInBytes = (uint) (elementSize * numElements);
			var dataOffsetInBytes = (int) startElement * elementSize;

#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
			AssertNonEmptyCopy(dataLengthInBytes);
			AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes);
#endif

			fixed (T* ptr = data)
			{
				Refresh.Refresh_SetBufferData(
					Device.Handle,
					Handle,
					buffer.Handle,
					bufferOffsetInBytes,
					(IntPtr) ptr + dataOffsetInBytes,
					dataLengthInBytes
				);
			}
		}

		/// <summary>
		/// Copies array data into a buffer.
		/// </summary>
		/// <param name="buffer">The buffer to copy to.</param>
		/// <param name="data">The span to copy from.</param>
		/// <param name="bufferOffsetInBytes">Specifies where in the buffer to start copying.</param>
		/// <param name="startElement">The index of the first element to copy from the array.</param>
		/// <param name="numElements">How many elements to copy.</param>
		/// <param name="setDataOption">Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.</param>
		public unsafe void SetBufferData<T>(
			Buffer buffer,
			T[] data,
			uint bufferOffsetInBytes,
			uint startElement,
			uint numElements
		) where T : unmanaged
		{
			SetBufferData<T>(buffer, new Span<T>(data), bufferOffsetInBytes, startElement, numElements);
		}

		public unsafe void SetBufferData<T>(
			Buffer buffer,
			IntPtr dataPtr,
			uint bufferOffsetInElements,
			uint numElements
		) where T : unmanaged
		{
			var elementSize = Marshal.SizeOf<T>();
			var dataLengthInBytes = (uint) (elementSize * numElements);
			var offsetLengthInBytes = (uint) elementSize * bufferOffsetInElements;

#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
			AssertNonEmptyCopy((uint) (elementSize * numElements));
			AssertBufferBoundsCheck(buffer.Size, offsetLengthInBytes, dataLengthInBytes);
#endif

			Refresh.Refresh_SetBufferData(
				Device.Handle,
				Handle,
				buffer.Handle,
				offsetLengthInBytes,
				dataPtr,
				dataLengthInBytes
			);
		}

		/// <summary>
		/// Asynchronously copies data into a texture.
		/// </summary>
		/// <param name="data">A span of data to copy into the texture.</param>
		public unsafe void SetTextureData<T>(Texture texture, Span<T> data) where T : unmanaged
		{
			SetTextureData(new TextureSlice(texture), data);
		}

		/// <summary>
		/// Asynchronously copies data into a texture.
		/// </summary>
		/// <param name="data">An array of data to copy into the texture.</param>
		public unsafe void SetTextureData<T>(Texture texture, T[] data) where T : unmanaged
		{
			SetTextureData(new TextureSlice(texture), new Span<T>(data));
		}

		/// <summary>
		/// Asynchronously copies data into a texture slice.
		/// </summary>
		/// <param name="textureSlice">The texture slice to copy into.</param>
		/// <param name="data">A span of data to copy into the texture.</param>
		public unsafe void SetTextureData<T>(in TextureSlice textureSlice, Span<T> data) where T : unmanaged
		{
			var dataLengthInBytes = (uint) (data.Length * Marshal.SizeOf<T>());

#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
			AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes);
#endif

			fixed (T* ptr = data)
			{
				Refresh.Refresh_SetTextureData(
					Device.Handle,
					Handle,
					textureSlice.ToRefreshTextureSlice(),
					(IntPtr) ptr,
					dataLengthInBytes
				);
			}
		}

		/// <summary>
		/// Asynchronously copies data into a texture slice.
		/// </summary>
		/// <param name="textureSlice">The texture slice to copy into.</param>
		/// <param name="data">An array of data to copy into the texture.</param>
		public unsafe void SetTextureData<T>(in TextureSlice textureSlice, T[] data) where T : unmanaged
		{
			SetTextureData(textureSlice, new Span<T>(data));
		}

		/// <summary>
		/// Asynchronously copies data into a texture slice.
		/// </summary>
		/// <param name="textureSlice">The texture slice to copy into.</param>
		/// <param name="dataPtr">A pointer to an array of data to copy from.</param>
		/// <param name="dataLengthInBytes">The amount of data to copy from the array.</param>
		public void SetTextureData(in TextureSlice textureSlice, IntPtr dataPtr, uint dataLengthInBytes)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
			AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes);
#endif

			Refresh.Refresh_SetTextureData(
				Device.Handle,
				Handle,
				textureSlice.ToRefreshTextureSlice(),
				dataPtr,
				dataLengthInBytes
			);
		}

		/// <summary>
		/// Asynchronously copies data into a texture.
		/// </summary>
		/// <param name="dataPtr">A pointer to an array of data to copy from.</param>
		/// <param name="dataLengthInBytes">The amount of data to copy from the array.</param>
		public void SetTextureData(Texture texture, IntPtr dataPtr, uint dataLengthInBytes)
		{
			SetTextureData(new TextureSlice(texture), dataPtr, dataLengthInBytes);
		}

		/// <summary>
		/// Asynchronously copies YUV data into three textures. Use with compressed video.
		/// </summary>
		public void SetTextureDataYUV(
			Texture yTexture,
			Texture uTexture,
			Texture vTexture,
			IntPtr yDataPtr,
			IntPtr uDataPtr,
			IntPtr vDataPtr,
			uint yDataLengthInBytes,
			uint uvDataLengthInBytes,
			uint yStride,
			uint uvStride)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
#endif

			Refresh.Refresh_SetTextureDataYUV(
				Device.Handle,
				Handle,
				yTexture.Handle,
				uTexture.Handle,
				vTexture.Handle,
				yTexture.Width,
				yTexture.Height,
				uTexture.Width,
				uTexture.Height,
				yDataPtr,
				uDataPtr,
				vDataPtr,
				yDataLengthInBytes,
				uvDataLengthInBytes,
				yStride,
				uvStride
			);
		}

		/// <summary>
		/// Performs an asynchronous texture-to-texture copy on the GPU.
		/// </summary>
		/// <param name="sourceTextureSlice">The texture slice to copy from.</param>
		/// <param name="destinationTextureSlice">The texture slice to copy to.</param>
		/// <param name="filter">The filter to use if the sizes of the texture slices differ.</param>
		public void CopyTextureToTexture(
			in TextureSlice sourceTextureSlice,
			in TextureSlice destinationTextureSlice,
			Filter filter
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
#endif

			var sourceRefreshTextureSlice = sourceTextureSlice.ToRefreshTextureSlice();
			var destRefreshTextureSlice = destinationTextureSlice.ToRefreshTextureSlice();

			Refresh.Refresh_CopyTextureToTexture(
				Device.Handle,
				Handle,
				sourceRefreshTextureSlice,
				destRefreshTextureSlice,
				(Refresh.Filter) filter
			);
		}

		/// <summary>
		/// Performs an asynchronous texture-to-buffer copy.
		/// Note that the buffer is not guaranteed to be filled until you call GraphicsDevice.Wait()
		/// </summary>
		/// <param name="textureSlice"></param>
		/// <param name="buffer"></param>
		public void CopyTextureToBuffer(
			in TextureSlice textureSlice,
			Buffer buffer
		)
		{
#if DEBUG
			AssertNotSubmitted();
			AssertRenderPassInactive("Cannot copy during render pass!");
			AssertBufferBoundsCheck(buffer.Size, 0, textureSlice.Size);
#endif

			var refreshTextureSlice = textureSlice.ToRefreshTextureSlice();

			Refresh.Refresh_CopyTextureToBuffer(
				Device.Handle,
				Handle,
				refreshTextureSlice,
				buffer.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 AssertRenderPassPipelineFormatMatch(GraphicsPipeline graphicsPipeline)
		{
			for (var i = 0; i < graphicsPipeline.AttachmentInfo.ColorAttachmentDescriptions.Length; i += 1)
			{
				TextureFormat format;
				if (i == 0)
				{
					format = colorFormatOne;
				}
				else if (i == 1)
				{
					format = colorFormatTwo;
				}
				else if (i == 2)
				{
					format = colorFormatThree;
				}
				else
				{
					format = colorFormatFour;
				}

				var pipelineFormat = graphicsPipeline.AttachmentInfo.ColorAttachmentDescriptions[i].Format;
				if (pipelineFormat != format)
				{
					throw new System.InvalidOperationException($"Color texture format mismatch! Pipeline expects {pipelineFormat}, render pass attachment is {format}");
				}
			}

			if (graphicsPipeline.AttachmentInfo.HasDepthStencilAttachment)
			{
				var pipelineDepthFormat = graphicsPipeline.AttachmentInfo.DepthStencilFormat;

				if (!hasDepthStencilAttachment)
				{
					throw new System.InvalidOperationException("Pipeline expects depth attachment!");
				}

				if (pipelineDepthFormat != depthStencilFormat)
				{
					throw new System.InvalidOperationException($"Depth texture format mismatch! Pipeline expects {pipelineDepthFormat}, render pass attachment is {depthStencilFormat}");
				}
			}
		}

		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.Texture == null || colorAttachmentInfo.Texture.Handle == IntPtr.Zero)
			{
				throw new System.ArgumentException("Render pass color attachment Texture cannot be null!");
			}
		}

		private void AssertColorTarget(ColorAttachmentInfo colorAttachmentInfo)
		{
			if ((colorAttachmentInfo.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.Texture == null ||
				depthStencilAttachmentInfo.Texture.Handle == IntPtr.Zero)
			{
				throw new System.ArgumentException("Render pass depth stencil attachment Texture cannot be null!");
			}

			if ((depthStencilAttachmentInfo.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 AssertNotSubmitted()
		{
			if (Submitted)
			{
				throw new System.InvalidOperationException("Cannot add commands to a submitted command buffer!");
			}
		}
#endif
	}
}