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 }