From 39496c37ea5e2c84ebb362c4ad6c93f654bd7b27 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 23 Feb 2024 00:06:04 -0800 Subject: [PATCH] Refresh 2 changes --- lib/RefreshCS | 2 +- src/Graphics/Bindings/BufferBinding.cs | 4 +- src/Graphics/Bindings/TextureLevelBinding.cs | 17 + src/Graphics/CommandBuffer.cs | 769 ++++++++++--------- src/Graphics/Font/Font.cs | 20 +- src/Graphics/Font/TextBatch.cs | 42 +- src/Graphics/ImageUtils.cs | 233 ++++++ src/Graphics/RefreshEnums.cs | 6 + src/Graphics/RefreshStructs.cs | 56 ++ src/Graphics/ResourceInitializer.cs | 171 +++++ src/Graphics/Resources/Buffer.cs | 141 ---- src/Graphics/Resources/CpuBuffer.cs | 158 ++++ src/Graphics/Resources/GpuBuffer.cs | 65 ++ src/Graphics/Resources/GraphicsPipeline.cs | 3 +- src/Graphics/Resources/Texture.cs | 216 ------ src/Graphics/State/DepthStencilState.cs | 9 +- src/Graphics/TextureSlice.cs | 58 +- src/Video/VideoPlayer.cs | 62 +- 18 files changed, 1251 insertions(+), 781 deletions(-) create mode 100644 src/Graphics/Bindings/TextureLevelBinding.cs create mode 100644 src/Graphics/ImageUtils.cs create mode 100644 src/Graphics/ResourceInitializer.cs delete mode 100644 src/Graphics/Resources/Buffer.cs create mode 100644 src/Graphics/Resources/CpuBuffer.cs create mode 100644 src/Graphics/Resources/GpuBuffer.cs diff --git a/lib/RefreshCS b/lib/RefreshCS index b5325e6..ad0b168 160000 --- a/lib/RefreshCS +++ b/lib/RefreshCS @@ -1 +1 @@ -Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28 +Subproject commit ad0b168c4794ee0377c825b0149a0d21bd856e43 diff --git a/src/Graphics/Bindings/BufferBinding.cs b/src/Graphics/Bindings/BufferBinding.cs index 4e318b6..c64f767 100644 --- a/src/Graphics/Bindings/BufferBinding.cs +++ b/src/Graphics/Bindings/BufferBinding.cs @@ -5,10 +5,10 @@ /// public struct BufferBinding { - public Buffer Buffer; + public GpuBuffer Buffer; public ulong Offset; - public BufferBinding(Buffer buffer, ulong offset) + public BufferBinding(GpuBuffer buffer, ulong offset) { Buffer = buffer; Offset = offset; diff --git a/src/Graphics/Bindings/TextureLevelBinding.cs b/src/Graphics/Bindings/TextureLevelBinding.cs new file mode 100644 index 0000000..82f8b87 --- /dev/null +++ b/src/Graphics/Bindings/TextureLevelBinding.cs @@ -0,0 +1,17 @@ +namespace MoonWorks.Graphics +{ + /// + /// A texture-level pair to be used when binding compute textures. + /// + public struct TextureLevelBinding + { + public Texture Texture; + public uint MipLevel; + + public TextureLevelBinding(Texture texture, uint mipLevel = 0) + { + Texture = texture; + MipLevel = mipLevel; + } + } +} diff --git a/src/Graphics/CommandBuffer.cs b/src/Graphics/CommandBuffer.cs index 8378ebb..cf1f534 100644 --- a/src/Graphics/CommandBuffer.cs +++ b/src/Graphics/CommandBuffer.cs @@ -29,6 +29,10 @@ namespace MoonWorks.Graphics SampleCount depthStencilAttachmentSampleCount; TextureFormat depthStencilFormat; + bool copyPassActive; + + bool computePassActive; + internal bool Submitted; #endif @@ -65,6 +69,9 @@ namespace MoonWorks.Graphics colorFormatFour = TextureFormat.R8G8B8A8; depthStencilFormat = TextureFormat.D16; + copyPassActive = false; + computePassActive = false; + Submitted = false; } #endif @@ -72,7 +79,7 @@ namespace MoonWorks.Graphics /// /// 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. + /// 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( @@ -545,7 +552,7 @@ namespace MoonWorks.Graphics /// /// A buffer to bind. public unsafe void BindComputeBuffers( - Buffer buffer + GpuBuffer buffer ) { #if DEBUG AssertNotSubmitted(); @@ -569,8 +576,8 @@ namespace MoonWorks.Graphics /// A buffer to bind. /// A buffer to bind. public unsafe void BindComputeBuffers( - Buffer bufferOne, - Buffer bufferTwo + GpuBuffer bufferOne, + GpuBuffer bufferTwo ) { #if DEBUG AssertNotSubmitted(); @@ -596,9 +603,9 @@ namespace MoonWorks.Graphics /// A buffer to bind. /// A buffer to bind. public unsafe void BindComputeBuffers( - Buffer bufferOne, - Buffer bufferTwo, - Buffer bufferThree + GpuBuffer bufferOne, + GpuBuffer bufferTwo, + GpuBuffer bufferThree ) { #if DEBUG AssertNotSubmitted(); @@ -626,10 +633,10 @@ namespace MoonWorks.Graphics /// A buffer to bind. /// A buffer to bind. public unsafe void BindComputeBuffers( - Buffer bufferOne, - Buffer bufferTwo, - Buffer bufferThree, - Buffer bufferFour + GpuBuffer bufferOne, + GpuBuffer bufferTwo, + GpuBuffer bufferThree, + GpuBuffer bufferFour ) { #if DEBUG AssertNotSubmitted(); @@ -655,7 +662,7 @@ namespace MoonWorks.Graphics /// /// A Span of buffers to bind. public unsafe void BindComputeBuffers( - in Span buffers + in Span buffers ) { #if DEBUG AssertNotSubmitted(); @@ -680,9 +687,9 @@ namespace MoonWorks.Graphics /// /// Binds a texture to be used in the compute shader. /// - /// A texture to bind. + /// A texture-level pair to bind. public unsafe void BindComputeTextures( - Texture texture + TextureLevelBinding binding ) { #if DEBUG AssertNotSubmitted(); @@ -691,23 +698,27 @@ namespace MoonWorks.Graphics #endif var texturePtrs = stackalloc IntPtr[1]; - texturePtrs[0] = texture.Handle; + texturePtrs[0] = binding.Texture.Handle; + + var mipLevels = stackalloc uint[1]; + mipLevels[0] = binding.MipLevel; Refresh.Refresh_BindComputeTextures( Device.Handle, Handle, - (IntPtr) texturePtrs + (IntPtr) texturePtrs, + (IntPtr) mipLevels ); } /// /// Binds textures to be used in the compute shader. /// - /// A texture to bind. - /// A texture to bind. + /// A texture-level pair to bind. + /// A texture-level pair to bind. public unsafe void BindComputeTextures( - Texture textureOne, - Texture textureTwo + TextureLevelBinding bindingOne, + TextureLevelBinding bindingTwo ) { #if DEBUG AssertNotSubmitted(); @@ -716,26 +727,31 @@ namespace MoonWorks.Graphics #endif var texturePtrs = stackalloc IntPtr[2]; - texturePtrs[0] = textureOne.Handle; - texturePtrs[1] = textureTwo.Handle; + texturePtrs[0] = bindingOne.Texture.Handle; + texturePtrs[1] = bindingTwo.Texture.Handle; + + var mipLevels = stackalloc uint[2]; + mipLevels[0] = bindingOne.MipLevel; + mipLevels[1] = bindingTwo.MipLevel; Refresh.Refresh_BindComputeTextures( Device.Handle, Handle, - (IntPtr) texturePtrs + (IntPtr) texturePtrs, + (IntPtr) mipLevels ); } /// /// Binds textures to be used in the compute shader. /// - /// A texture to bind. - /// A texture to bind. - /// A texture to bind. + /// A texture-level pair to bind. + /// A texture-level pair to bind. + /// A texture-level pair to bind. public unsafe void BindComputeTextures( - Texture textureOne, - Texture textureTwo, - Texture textureThree + TextureLevelBinding bindingOne, + TextureLevelBinding bindingTwo, + TextureLevelBinding bindingThree ) { #if DEBUG AssertNotSubmitted(); @@ -744,29 +760,35 @@ namespace MoonWorks.Graphics #endif var texturePtrs = stackalloc IntPtr[3]; - texturePtrs[0] = textureOne.Handle; - texturePtrs[1] = textureTwo.Handle; - texturePtrs[2] = textureThree.Handle; + texturePtrs[0] = bindingOne.Texture.Handle; + texturePtrs[1] = bindingTwo.Texture.Handle; + texturePtrs[2] = bindingThree.Texture.Handle; + + var mipLevels = stackalloc uint[3]; + mipLevels[0] = bindingOne.MipLevel; + mipLevels[1] = bindingTwo.MipLevel; + mipLevels[2] = bindingThree.MipLevel; Refresh.Refresh_BindComputeTextures( Device.Handle, Handle, - (IntPtr) texturePtrs + (IntPtr) texturePtrs, + (IntPtr) mipLevels ); } /// /// Binds textures to be used in the compute shader. /// - /// A texture to bind. - /// A texture to bind. - /// A texture to bind. - /// A texture to bind. + /// A texture-level pair to bind. + /// A texture-level pair to bind. + /// A texture-level pair to bind. + /// A texture-level pair to bind. public unsafe void BindComputeTextures( - Texture textureOne, - Texture textureTwo, - Texture textureThree, - Texture textureFour + TextureLevelBinding bindingOne, + TextureLevelBinding bindingTwo, + TextureLevelBinding bindingThree, + TextureLevelBinding bindingFour ) { #if DEBUG AssertNotSubmitted(); @@ -775,42 +797,52 @@ namespace MoonWorks.Graphics #endif var texturePtrs = stackalloc IntPtr[4]; - texturePtrs[0] = textureOne.Handle; - texturePtrs[1] = textureTwo.Handle; - texturePtrs[2] = textureThree.Handle; - texturePtrs[3] = textureFour.Handle; + texturePtrs[0] = bindingOne.Texture.Handle; + texturePtrs[1] = bindingTwo.Texture.Handle; + texturePtrs[2] = bindingThree.Texture.Handle; + texturePtrs[3] = bindingFour.Texture.Handle; + + var mipLevels = stackalloc uint[4]; + mipLevels[0] = bindingOne.MipLevel; + mipLevels[1] = bindingTwo.MipLevel; + mipLevels[2] = bindingThree.MipLevel; + mipLevels[3] = bindingFour.MipLevel; Refresh.Refresh_BindComputeTextures( Device.Handle, Handle, - (IntPtr) texturePtrs + (IntPtr) texturePtrs, + (IntPtr) mipLevels ); } /// /// Binds textures to be used in the compute shader. /// - /// A set of textures to bind. + /// A set of texture-level pairs to bind. public unsafe void BindComputeTextures( - in Span textures + in Span bindings ) { #if DEBUG AssertNotSubmitted(); AssertComputePipelineBound(); - AssertComputeTextureCount(textures.Length); + AssertComputeTextureCount(bindings.Length); #endif - var texturePtrs = stackalloc IntPtr[textures.Length]; + var texturePtrs = stackalloc IntPtr[bindings.Length]; + var mipLevels = stackalloc uint[bindings.Length]; - for (var i = 0; i < textures.Length; i += 1) + for (var i = 0; i < bindings.Length; i += 1) { - texturePtrs[i] = textures[i].Handle; + texturePtrs[i] = bindings[i].Texture.Handle; + mipLevels[i] = bindings[i].MipLevel; } Refresh.Refresh_BindComputeTextures( Device.Handle, Handle, - (IntPtr) texturePtrs + (IntPtr) texturePtrs, + (IntPtr) mipLevels ); } @@ -824,8 +856,7 @@ namespace MoonWorks.Graphics public void DispatchCompute( uint groupCountX, uint groupCountY, - uint groupCountZ, - uint computeParamOffset + uint groupCountZ ) { #if DEBUG AssertNotSubmitted(); @@ -842,8 +873,7 @@ namespace MoonWorks.Graphics Handle, groupCountX, groupCountY, - groupCountZ, - computeParamOffset + groupCountZ ); } @@ -1113,7 +1143,7 @@ namespace MoonWorks.Graphics /// The size in bytes of the index buffer elements. /// The offset index for the buffer. public void BindIndexBuffer( - Buffer indexBuffer, + GpuBuffer indexBuffer, IndexElementSize indexElementSize, uint offset = 0 ) @@ -1515,7 +1545,7 @@ namespace MoonWorks.Graphics /// Pushes vertex shader uniforms to the device. /// /// A starting offset value to be used with draw calls. - public unsafe uint PushVertexShaderUniforms( + public unsafe void PushVertexShaderUniforms( void* uniformsPtr, uint size ) { @@ -1527,9 +1557,14 @@ namespace MoonWorks.Graphics { 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 - return Refresh.Refresh_PushVertexShaderUniforms( + Refresh.Refresh_PushVertexShaderUniforms( Device.Handle, Handle, (IntPtr) uniformsPtr, @@ -1541,13 +1576,13 @@ namespace MoonWorks.Graphics /// Pushes vertex shader uniforms to the device. /// /// A starting offset value to be used with draw calls. - public unsafe uint PushVertexShaderUniforms( + public unsafe void PushVertexShaderUniforms( in T uniforms ) where T : unmanaged { fixed (T* uniformsPtr = &uniforms) { - return PushVertexShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); + PushVertexShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); } } @@ -1555,7 +1590,7 @@ namespace MoonWorks.Graphics /// Pushes fragment shader uniforms to the device. /// /// A starting offset to be used with draw calls. - public unsafe uint PushFragmentShaderUniforms( + public unsafe void PushFragmentShaderUniforms( void* uniformsPtr, uint size ) { @@ -1567,9 +1602,14 @@ namespace MoonWorks.Graphics { 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 - return Refresh.Refresh_PushFragmentShaderUniforms( + Refresh.Refresh_PushFragmentShaderUniforms( Device.Handle, Handle, (IntPtr) uniformsPtr, @@ -1581,13 +1621,13 @@ namespace MoonWorks.Graphics /// Pushes fragment shader uniforms to the device. /// /// A starting offset to be used with draw calls. - public unsafe uint PushFragmentShaderUniforms( + public unsafe void PushFragmentShaderUniforms( in T uniforms ) where T : unmanaged { fixed (T* uniformsPtr = &uniforms) { - return PushFragmentShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); + PushFragmentShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); } } @@ -1595,7 +1635,7 @@ namespace MoonWorks.Graphics /// Pushes compute shader uniforms to the device. /// /// A starting offset to be used with dispatch calls. - public unsafe uint PushComputeShaderUniforms( + public unsafe void PushComputeShaderUniforms( void* uniformsPtr, uint size ) { @@ -1607,9 +1647,14 @@ namespace MoonWorks.Graphics { 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 - return Refresh.Refresh_PushComputeShaderUniforms( + Refresh.Refresh_PushComputeShaderUniforms( Device.Handle, Handle, (IntPtr) uniformsPtr, @@ -1621,13 +1666,13 @@ namespace MoonWorks.Graphics /// Pushes compute shader uniforms to the device. /// /// A starting offset to be used with dispatch calls. - public unsafe uint PushComputeShaderUniforms( + public unsafe void PushComputeShaderUniforms( in T uniforms ) where T : unmanaged { fixed (T* uniformsPtr = &uniforms) { - return PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); + PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf()); } } @@ -1638,15 +1683,11 @@ namespace MoonWorks.Graphics /// The starting index offset for the index buffer. /// The number of primitives to draw. /// The number of instances to draw. - /// 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 DrawInstancedPrimitives( uint baseVertex, uint startIndex, uint primitiveCount, - uint instanceCount, - uint vertexParamOffset, - uint fragmentParamOffset + uint instanceCount ) { #if DEBUG @@ -1660,9 +1701,7 @@ namespace MoonWorks.Graphics baseVertex, startIndex, primitiveCount, - instanceCount, - vertexParamOffset, - fragmentParamOffset + instanceCount ); } @@ -1672,14 +1711,10 @@ namespace MoonWorks.Graphics /// The starting index offset for the vertex buffer. /// The starting index offset for the index buffer. /// The number of primitives to draw. - /// 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 DrawIndexedPrimitives( uint baseVertex, uint startIndex, - uint primitiveCount, - uint vertexParamOffset, - uint fragmentParamOffset + uint primitiveCount ) { #if DEBUG @@ -1692,9 +1727,7 @@ namespace MoonWorks.Graphics Handle, baseVertex, startIndex, - primitiveCount, - vertexParamOffset, - fragmentParamOffset + primitiveCount ); } @@ -1703,13 +1736,9 @@ namespace MoonWorks.Graphics /// /// /// - /// - /// public void DrawPrimitives( uint vertexStart, - uint primitiveCount, - uint vertexParamOffset, - uint fragmentParamOffset + uint primitiveCount ) { #if DEBUG @@ -1721,9 +1750,7 @@ namespace MoonWorks.Graphics Device.Handle, Handle, vertexStart, - primitiveCount, - vertexParamOffset, - fragmentParamOffset + primitiveCount ); } @@ -1737,12 +1764,10 @@ namespace MoonWorks.Graphics /// 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( - Buffer buffer, + GpuBuffer buffer, uint offsetInBytes, uint drawCount, - uint stride, - uint vertexParamOffset, - uint fragmentParamOffset + uint stride ) { #if DEBUG @@ -1756,9 +1781,7 @@ namespace MoonWorks.Graphics buffer.Handle, offsetInBytes, drawCount, - stride, - vertexParamOffset, - fragmentParamOffset + stride ); } @@ -1834,352 +1857,350 @@ namespace MoonWorks.Graphics return window.SwapchainTexture; } + // Copy Pass + /// - /// Copies array data into a buffer. + /// 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. /// - /// The buffer to copy to. - /// The array to copy from. - /// Specifies where in the buffer to start copying. - /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred. - public unsafe void SetBufferData( - Buffer buffer, - Span data, - uint bufferOffsetInBytes = 0 - ) where T : unmanaged + public void BeginCopyPass() { - SetBufferData( - buffer, - data, - bufferOffsetInBytes, - 0, - (uint) data.Length +#if DEBUG + AssertNotSubmitted(); + AssertNotInPass("Cannot begin copy pass while in another pass!"); + copyPassActive = true; +#endif + + Refresh.Refresh_BeginCopyPass( + Device.Handle, + Handle ); } /// - /// Copies array data into a buffer. + /// Uploads data from a CpuBuffer to a TextureSlice. + /// This copy occurs on the GPU timeline. + /// + /// Overwriting the contents of the CpuBuffer before the command buffer + /// has finished execution will cause undefined behavior. + /// + /// You MAY assume that the copy has finished for subsequent commands. /// - /// The buffer to copy to. - /// The array to copy from. - /// Specifies where in the buffer to start copying. - /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred. - public unsafe void SetBufferData( - Buffer buffer, - T[] data, - uint bufferOffsetInBytes = 0 - ) where T : unmanaged - { - SetBufferData( - buffer, - new Span(data), - bufferOffsetInBytes, - 0, - (uint) data.Length - ); - } - - /// - /// Copies arbitrary data into a buffer. - /// - /// The buffer to copy into. - /// Pointer to the data to copy into the buffer. - /// Specifies where in the buffer to copy data. - /// The length of data that should be copied. - /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred. - public void SetBufferData( - Buffer buffer, - IntPtr dataPtr, - uint bufferOffsetInBytes, - uint dataLengthInBytes + public void UploadToTexture( + CpuBuffer cpuBuffer, + in TextureSlice textureSlice, + in BufferImageCopy copyParams ) { #if DEBUG AssertNotSubmitted(); - AssertRenderPassInactive("Cannot copy during render pass!"); - AssertNonEmptyCopy(dataLengthInBytes); - AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes); + AssertInCopyPass("Cannot upload to texture outside of copy pass!"); #endif - Refresh.Refresh_SetBufferData( + Refresh.Refresh_UploadToTexture( Device.Handle, Handle, - buffer.Handle, - bufferOffsetInBytes, - dataPtr, - dataLengthInBytes + cpuBuffer.Handle, + textureSlice.ToRefreshTextureSlice(), + copyParams.ToRefresh() ); } /// - /// Copies array data into a buffer. + /// Uploads the contents of an entire buffer to a texture with no mips. /// - /// The buffer to copy to. - /// The span to copy from. - /// Specifies where in the buffer to start copying. - /// The index of the first element to copy from the array. - /// How many elements to copy. - /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred. - public unsafe void SetBufferData( - Buffer buffer, - Span data, - uint bufferOffsetInBytes, - uint startElement, - uint numElements - ) where T : unmanaged - { - var elementSize = Marshal.SizeOf(); - 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 - ); - } - } - - /// - /// Copies array data into a buffer. - /// - /// The buffer to copy to. - /// The span to copy from. - /// Specifies where in the buffer to start copying. - /// The index of the first element to copy from the array. - /// How many elements to copy. - /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred. - public unsafe void SetBufferData( - Buffer buffer, - T[] data, - uint bufferOffsetInBytes, - uint startElement, - uint numElements - ) where T : unmanaged - { - SetBufferData(buffer, new Span(data), bufferOffsetInBytes, startElement, numElements); - } - - public unsafe void SetBufferData( - Buffer buffer, - IntPtr dataPtr, - uint bufferOffsetInElements, - uint numElements - ) where T : unmanaged - { - var elementSize = Marshal.SizeOf(); - 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 + public void UploadToTexture( + CpuBuffer cpuBuffer, + Texture texture + ) { + UploadToTexture( + cpuBuffer, + new TextureSlice(texture), + new BufferImageCopy(0, 0, 0) ); } /// - /// Asynchronously copies data into a texture. + /// Uploads data from a CpuBuffer to a GpuBuffer. + /// This copy occurs on the GPU timeline. + /// + /// Overwriting the contents of the CpuBuffer before the command buffer + /// has finished execution will cause undefined behavior. + /// + /// You MAY assume that the copy has finished for subsequent commands. /// - /// A span of data to copy into the texture. - public unsafe void SetTextureData(Texture texture, Span data) where T : unmanaged - { - SetTextureData(new TextureSlice(texture), data); - } - - /// - /// Asynchronously copies data into a texture. - /// - /// An array of data to copy into the texture. - public unsafe void SetTextureData(Texture texture, T[] data) where T : unmanaged - { - SetTextureData(new TextureSlice(texture), new Span(data)); - } - - /// - /// Asynchronously copies data into a texture slice. - /// - /// The texture slice to copy into. - /// A span of data to copy into the texture. - public unsafe void SetTextureData(in TextureSlice textureSlice, Span data) where T : unmanaged - { - var dataLengthInBytes = (uint) (data.Length * Marshal.SizeOf()); - + public void UploadToBuffer( + CpuBuffer cpuBuffer, + GpuBuffer gpuBuffer, + in BufferCopy copyParams + ) { #if DEBUG AssertNotSubmitted(); - AssertRenderPassInactive("Cannot copy during render pass!"); - AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes); + AssertInCopyPass("Cannot upload to texture outside of copy pass!"); #endif - fixed (T* ptr = data) + Refresh.Refresh_UploadToBuffer( + Device.Handle, + Handle, + cpuBuffer.Handle, + gpuBuffer.Handle, + copyParams.ToRefresh() + ); + } + + /// + /// Copies the entire contents of a CpuBuffer to a GpuBuffer. + /// + public void UploadToBuffer( + CpuBuffer cpuBuffer, + GpuBuffer gpuBuffer + ) { +#if DEBUG + if (cpuBuffer.Size > gpuBuffer.Size) { - Refresh.Refresh_SetTextureData( - Device.Handle, - Handle, - textureSlice.ToRefreshTextureSlice(), - (IntPtr) ptr, - dataLengthInBytes - ); + throw new InvalidOperationException("CpuBuffer copying to GpuBuffer is too large!"); } +#endif + UploadToBuffer( + cpuBuffer, + gpuBuffer, + new BufferCopy(0, 0, cpuBuffer.Size) + ); } /// - /// Asynchronously copies data into a texture slice. + /// Downloads data from a Texture to a CpuBuffer. + /// This copy occurs on the GPU timeline. + /// + /// You MAY NOT assume that the data in the CpuBuffer is + /// fully copied until the command buffer has finished execution. /// - /// The texture slice to copy into. - /// An array of data to copy into the texture. - public unsafe void SetTextureData(in TextureSlice textureSlice, T[] data) where T : unmanaged - { - SetTextureData(textureSlice, new Span(data)); - } - - /// - /// Asynchronously copies data into a texture slice. - /// - /// The texture slice to copy into. - /// A pointer to an array of data to copy from. - /// The amount of data to copy from the array. - public void SetTextureData(in TextureSlice textureSlice, IntPtr dataPtr, uint dataLengthInBytes) - { + public void DownloadFromTexture( + in TextureSlice textureSlice, + CpuBuffer cpuBuffer, + in BufferImageCopy copyParams + ) { #if DEBUG AssertNotSubmitted(); - AssertRenderPassInactive("Cannot copy during render pass!"); - AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes); + AssertInCopyPass("Cannot download from texture outside of copy pass!"); #endif - Refresh.Refresh_SetTextureData( + Refresh.Refresh_DownloadFromTexture( Device.Handle, Handle, textureSlice.ToRefreshTextureSlice(), - dataPtr, - dataLengthInBytes + cpuBuffer.Handle, + copyParams.ToRefresh() ); } /// - /// Asynchronously copies data into a texture. + /// Downloads the contents of a Texture with no mips into a CpuBuffer. /// - /// A pointer to an array of data to copy from. - /// The amount of data to copy from the array. - public void SetTextureData(Texture texture, IntPtr dataPtr, uint dataLengthInBytes) - { - SetTextureData(new TextureSlice(texture), dataPtr, dataLengthInBytes); + public void DownloadFromTexture( + Texture texture, + CpuBuffer cpuBuffer + ) { + DownloadFromTexture( + new TextureSlice(texture), + cpuBuffer, + new BufferImageCopy(0, 0, 0) + ); } /// - /// Asynchronously copies YUV data into three textures. Use with compressed video. + /// Downloads data from a GpuBuffer to a CpuBuffer. + /// This copy occurs on the GPU timeline. + /// + /// You MAY NOT assume that the data in the CpuBuffer is + /// fully copied until the command buffer has finished execution. /// - public void SetTextureDataYUV( - Texture yTexture, - Texture uTexture, - Texture vTexture, - IntPtr yDataPtr, - IntPtr uDataPtr, - IntPtr vDataPtr, - uint yDataLengthInBytes, - uint uvDataLengthInBytes, - uint yStride, - uint uvStride) - { + public void DownloadFromBuffer( + GpuBuffer gpuBuffer, + CpuBuffer cpuBuffer, + in BufferCopy copyParams + ) { #if DEBUG AssertNotSubmitted(); - AssertRenderPassInactive("Cannot copy during render pass!"); + AssertInCopyPass("Cannot download from texture outside of copy pass!"); #endif - Refresh.Refresh_SetTextureDataYUV( + Refresh.Refresh_DownloadFromBuffer( Device.Handle, Handle, - yTexture.Handle, - uTexture.Handle, - vTexture.Handle, - yTexture.Width, - yTexture.Height, - uTexture.Width, - uTexture.Height, - yDataPtr, - uDataPtr, - vDataPtr, - yDataLengthInBytes, - uvDataLengthInBytes, - yStride, - uvStride + gpuBuffer.Handle, + cpuBuffer.Handle, + copyParams.ToRefresh() ); } /// - /// Performs an asynchronous texture-to-texture copy on the GPU. + /// 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. /// - /// The texture slice to copy from. - /// The texture slice to copy to. - /// The filter to use if the sizes of the texture slices differ. public void CopyTextureToTexture( - in TextureSlice sourceTextureSlice, - in TextureSlice destinationTextureSlice, - Filter filter - ) - { + in TextureSlice source, + in TextureSlice destination + ) { #if DEBUG AssertNotSubmitted(); - AssertRenderPassInactive("Cannot copy during render pass!"); + AssertInCopyPass("Cannot download from texture outside of copy pass!"); #endif - var sourceRefreshTextureSlice = sourceTextureSlice.ToRefreshTextureSlice(); - var destRefreshTextureSlice = destinationTextureSlice.ToRefreshTextureSlice(); - Refresh.Refresh_CopyTextureToTexture( Device.Handle, Handle, - sourceRefreshTextureSlice, - destRefreshTextureSlice, - (Refresh.Filter) filter + source.ToRefreshTextureSlice(), + destination.ToRefreshTextureSlice() ); } /// - /// Performs an asynchronous texture-to-buffer copy. - /// Note that the buffer is not guaranteed to be filled until you call GraphicsDevice.Wait() + /// 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 + ) { + CopyTextureToTexture( + new TextureSlice(source), + new TextureSlice(destination) + ); + } + + /// + /// Copies the contents of a Texture to a GpuBuffer. + /// This copy occurs on the GPU timeline. + /// + /// You MAY assume that the copy has finished in subsequent commands. /// - /// - /// public void CopyTextureToBuffer( in TextureSlice textureSlice, - Buffer buffer - ) - { + GpuBuffer buffer, + in BufferImageCopy copyParams + ) { #if DEBUG AssertNotSubmitted(); - AssertRenderPassInactive("Cannot copy during render pass!"); - AssertBufferBoundsCheck(buffer.Size, 0, textureSlice.Size); + AssertInCopyPass("Cannot download from texture outside of copy pass!"); #endif - var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); - Refresh.Refresh_CopyTextureToBuffer( Device.Handle, Handle, - refreshTextureSlice, - buffer.Handle + textureSlice.ToRefreshTextureSlice(), + buffer.Handle, + copyParams.ToRefresh() + ); + } + + /// + /// Copies the entire contents of a Texture to a GpuBuffer. + /// + public void CopyTextureToBuffer( + Texture texture, + GpuBuffer buffer + ) { + CopyTextureToBuffer( + new TextureSlice(texture), + buffer, + new BufferImageCopy(0, 0, 0) + ); + } + + /// + /// Copies the contents of a GpuBuffer to a Texture. + /// This copy occurs on the GPU timeline. + /// + /// You MAY assume that the copy has finished in subsequent commands. + /// + public void CopyBufferToTexture( + GpuBuffer buffer, + in TextureSlice textureSlice, + in BufferImageCopy copyParams + ) { +#if DEBUG + AssertNotSubmitted(); + AssertInCopyPass("Cannot download from texture outside of copy pass!"); +#endif + + Refresh.Refresh_CopyBufferToTexture( + Device.Handle, + Handle, + buffer.Handle, + textureSlice.ToRefreshTextureSlice(), + copyParams.ToRefresh() + ); + } + + /// + /// Copies the entire contents of a Texture with no mips to a GpuBuffer. + /// + public void CopyBufferToTexture( + GpuBuffer buffer, + Texture texture + ) { + CopyBufferToTexture( + buffer, + new TextureSlice(texture), + new BufferImageCopy(0, 0, 0) + ); + } + + /// + /// 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 + ) { +#if DEBUG + AssertNotSubmitted(); + AssertInCopyPass("Cannot download from texture outside of copy pass!"); +#endif + + Refresh.Refresh_CopyBufferToBuffer( + Device.Handle, + Handle, + source.Handle, + destination.Handle, + copyParams.ToRefresh() + ); + } + + /// + /// Copies the entire contents of a GpuBuffer to another GpuBuffer. + /// + public void CopyBufferToBuffer( + GpuBuffer source, + GpuBuffer destination + ) { + CopyBufferToBuffer( + source, + destination, + new BufferCopy(0, 0, source.Size) + ); + } + + 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 ); } @@ -2376,6 +2397,30 @@ namespace MoonWorks.Graphics } } + 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 AssertNotSubmitted() { if (Submitted) diff --git a/src/Graphics/Font/Font.cs b/src/Graphics/Font/Font.cs index 0dd6d1c..109badb 100644 --- a/src/Graphics/Font/Font.cs +++ b/src/Graphics/Font/Font.cs @@ -47,8 +47,26 @@ namespace MoonWorks.Graphics.Font out float distanceRange ); - var texture = Texture.FromImageFile(graphicsDevice, commandBuffer, Path.ChangeExtension(fontPath, ".png")); + var imagePath = Path.ChangeExtension(fontPath, ".png"); + ImageUtils.ImageInfoFromFile(imagePath, out var width, out var height, out var sizeInBytes); + var texture = Texture.CreateTexture2D(graphicsDevice, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); + var cpuBuffer = new CpuBuffer(graphicsDevice, sizeInBytes); + ImageUtils.DecodeIntoCpuBuffer( + imagePath, + cpuBuffer, + 0, + SetDataOptions.Overwrite + ); + + commandBuffer.BeginCopyPass(); + commandBuffer.UploadToTexture( + cpuBuffer, + texture + ); + commandBuffer.EndCopyPass(); + + cpuBuffer.Dispose(); NativeMemory.Free(fontFileByteBuffer); NativeMemory.Free(atlasFileByteBuffer); diff --git a/src/Graphics/Font/TextBatch.cs b/src/Graphics/Font/TextBatch.cs index 9cb909c..3dd301b 100644 --- a/src/Graphics/Font/TextBatch.cs +++ b/src/Graphics/Font/TextBatch.cs @@ -13,10 +13,12 @@ namespace MoonWorks.Graphics.Font private GraphicsDevice GraphicsDevice { get; } public IntPtr Handle { get; } - public Buffer VertexBuffer { get; protected set; } = null; - public Buffer IndexBuffer { get; protected set; } = null; + public GpuBuffer VertexBuffer { get; protected set; } = null; + public GpuBuffer IndexBuffer { get; protected set; } = null; public uint PrimitiveCount { get; protected set; } + private CpuBuffer TransferBuffer; + public Font CurrentFont { get; private set; } private byte* StringBytes; @@ -30,8 +32,10 @@ namespace MoonWorks.Graphics.Font StringBytesLength = 128; StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength); - VertexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT); - IndexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT); + VertexBuffer = GpuBuffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT); + IndexBuffer = GpuBuffer.Create(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT); + + TransferBuffer = CpuBuffer.Create(GraphicsDevice, VertexBuffer.Size + IndexBuffer.Size); } // Call this to initialize or reset the batch. @@ -93,22 +97,38 @@ namespace MoonWorks.Graphics.Font out uint indexDataLengthInBytes ); + var vertexSpan = new Span((void*) vertexDataPointer, (int) vertexDataLengthInBytes); + var indexSpan = new Span((void*) indexDataPointer, (int) indexDataLengthInBytes); + + var newTransferBufferNeeded = false; + if (VertexBuffer.Size < vertexDataLengthInBytes) { VertexBuffer.Dispose(); - VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes); + VertexBuffer = new GpuBuffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes); + newTransferBufferNeeded = true; } if (IndexBuffer.Size < indexDataLengthInBytes) { IndexBuffer.Dispose(); - IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes); + IndexBuffer = new GpuBuffer(GraphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes); + newTransferBufferNeeded = true; + } + + if (newTransferBufferNeeded) + { + TransferBuffer.Dispose(); + TransferBuffer = new CpuBuffer(GraphicsDevice, VertexBuffer.Size + IndexBuffer.Size); } if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0) { - commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes); - commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes); + TransferBuffer.SetData(vertexSpan, SetDataOptions.Discard); + TransferBuffer.SetData(indexSpan, (uint) vertexSpan.Length, SetDataOptions.Overwrite); + + commandBuffer.UploadToBuffer(TransferBuffer, VertexBuffer, new BufferCopy(0, 0, (uint) vertexSpan.Length)); + commandBuffer.UploadToBuffer(TransferBuffer, IndexBuffer, new BufferCopy((uint) vertexSpan.Length, 0, (uint) indexSpan.Length)); } PrimitiveCount = vertexCount / 2; @@ -123,12 +143,12 @@ namespace MoonWorks.Graphics.Font )); commandBuffer.BindVertexBuffers(VertexBuffer); commandBuffer.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo); + commandBuffer.PushVertexShaderUniforms(transformMatrix); + commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange); commandBuffer.DrawIndexedPrimitives( 0, 0, - PrimitiveCount, - commandBuffer.PushVertexShaderUniforms(transformMatrix), - commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange) + PrimitiveCount ); } diff --git a/src/Graphics/ImageUtils.cs b/src/Graphics/ImageUtils.cs new file mode 100644 index 0000000..8d108bf --- /dev/null +++ b/src/Graphics/ImageUtils.cs @@ -0,0 +1,233 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public static class ImageUtils + { + /// + /// Gets pointer to pixel data from compressed image byte data. + /// + /// The returned pointer must be freed by calling FreePixelData. + /// + public static unsafe IntPtr GetPixelDataFromBytes( + Span data, + out uint width, + out uint height, + out uint sizeInBytes + ) { + fixed (byte* ptr = data) + { + var pixelData = + Refresh.Refresh_Image_Load( + (nint) ptr, + data.Length, + out var w, + out var h, + out var len + ); + + width = (uint) w; + height = (uint) h; + sizeInBytes = (uint) len; + + return pixelData; + } + } + + /// + /// Gets pointer to pixel data from a compressed image stream. + /// + /// The returned pointer must be freed by calling FreePixelData. + /// + public static unsafe IntPtr GetPixelDataFromStream( + Stream stream, + out uint width, + out uint height, + out uint sizeInBytes + ) { + var length = stream.Length; + var buffer = NativeMemory.Alloc((nuint) length); + var span = new Span(buffer, (int) length); + stream.ReadExactly(span); + + var pixelData = GetPixelDataFromBytes(span, out width, out height, out sizeInBytes); + + NativeMemory.Free(buffer); + + return pixelData; + } + + /// + /// Gets pointer to pixel data from a compressed image file. + /// + /// The returned pointer must be freed by calling FreePixelData. + /// + public static IntPtr GetPixelDataFromFile( + string path, + out uint width, + out uint height, + out uint sizeInBytes + ) { + var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); + return GetPixelDataFromStream(fileStream, out width, out height, out sizeInBytes); + } + + /// + /// Get metadata from compressed image bytes. + /// + public static unsafe bool ImageInfoFromBytes( + Span data, + out uint width, + out uint height, + out uint sizeInBytes + ) { + fixed (byte* ptr = data) + { + var result = + Refresh.Refresh_Image_Info( + (nint) ptr, + data.Length, + out var w, + out var h, + out var len + ); + + width = (uint) w; + height = (uint) h; + sizeInBytes = (uint) len; + + return Conversions.ByteToBool(result); + } + } + + /// + /// Get metadata from a compressed image stream. + /// + public static unsafe bool ImageInfoFromStream( + Stream stream, + out uint width, + out uint height, + out uint sizeInBytes + ) { + var length = stream.Length; + var buffer = NativeMemory.Alloc((nuint) length); + var span = new Span(buffer, (int) length); + stream.ReadExactly(span); + + var result = ImageInfoFromBytes(span, out width, out height, out sizeInBytes); + + NativeMemory.Free(buffer); + + return result; + } + + /// + /// Get metadata from a compressed image file. + /// + public static bool ImageInfoFromFile( + string path, + out uint width, + out uint height, + out uint sizeInBytes + ) { + var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); + return ImageInfoFromStream(fileStream, out width, out height, out sizeInBytes); + } + + /// + /// Frees pixel data obtained from GetPixelData methods. + /// + public static void FreePixelData(IntPtr pixels) + { + Refresh.Refresh_Image_Free(pixels); + } + + /// + /// Decodes image data into a CpuBuffer to prepare for image upload. + /// + public static unsafe uint DecodeIntoCpuBuffer( + Span data, + CpuBuffer cpuBuffer, + uint bufferOffsetInBytes, + SetDataOptions option + ) { + var pixelData = GetPixelDataFromBytes(data, out var w, out var h, out var sizeInBytes); + var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option); + FreePixelData(pixelData); + return length; + } + + /// + /// Decodes an image stream into a CpuBuffer to prepare for image upload. + /// + public static unsafe uint DecodeIntoCpuBuffer( + Stream stream, + CpuBuffer cpuBuffer, + uint bufferOffsetInBytes, + SetDataOptions option + ) { + var pixelData = GetPixelDataFromStream(stream, out var w, out var h, out var sizeInBytes); + var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option); + FreePixelData(pixelData); + return length; + } + + /// + /// Decodes an image file into a CpuBuffer to prepare for image upload. + /// + public static unsafe uint DecodeIntoCpuBuffer( + string path, + CpuBuffer cpuBuffer, + uint bufferOffsetInBytes, + SetDataOptions option + ) { + var pixelData = GetPixelDataFromFile(path, out var w, out var h, out var sizeInBytes); + var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option); + FreePixelData(pixelData); + return length; + } + + /// + /// Saves pixel data contained in a CpuBuffer to a PNG file. + /// + public static unsafe void SavePNG( + string path, + CpuBuffer cpuBuffer, + uint bufferOffsetInBytes, + int width, + int height, + bool bgra + ) { + var sizeInBytes = width * height * 4; + + var pixelsPtr = NativeMemory.Alloc((nuint) sizeInBytes); + var pixelsSpan = new Span(pixelsPtr, sizeInBytes); + + cpuBuffer.GetData(pixelsSpan, bufferOffsetInBytes); + + if (bgra) + { + // if data is bgra, we have to swap the R and B channels + var rgbaPtr = NativeMemory.Alloc((nuint) sizeInBytes); + var rgbaSpan = new Span(rgbaPtr, sizeInBytes); + + for (var i = 0; i < sizeInBytes; i += 4) + { + rgbaSpan[i] = pixelsSpan[i + 2]; + rgbaSpan[i + 1] = pixelsSpan[i + 1]; + rgbaSpan[i + 2] = pixelsSpan[i]; + rgbaSpan[i + 3] = pixelsSpan[i + 3]; + } + + NativeMemory.Free(pixelsPtr); + pixelsPtr = rgbaPtr; + } + + Refresh.Refresh_Image_SavePNG(path, (nint) pixelsPtr, width, height); + NativeMemory.Free(pixelsPtr); + } + } +} diff --git a/src/Graphics/RefreshEnums.cs b/src/Graphics/RefreshEnums.cs index 81cc353..e773f26 100644 --- a/src/Graphics/RefreshEnums.cs +++ b/src/Graphics/RefreshEnums.cs @@ -297,6 +297,12 @@ namespace MoonWorks.Graphics IntOpaqueWhite } + public enum SetDataOptions + { + Discard, + Overwrite + } + public enum Backend { DontCare, diff --git a/src/Graphics/RefreshStructs.cs b/src/Graphics/RefreshStructs.cs index 6b046bd..acd60b7 100644 --- a/src/Graphics/RefreshStructs.cs +++ b/src/Graphics/RefreshStructs.cs @@ -354,4 +354,60 @@ namespace MoonWorks.Graphics FirstInstance = firstInstance; } } + + [StructLayout(LayoutKind.Sequential)] + public struct BufferCopy + { + public uint SrcOffset; + public uint DstOffset; + public uint Size; + + public BufferCopy( + uint srcOffset, + uint dstOffset, + uint size + ) { + SrcOffset = srcOffset; + DstOffset = dstOffset; + Size = size; + } + + public Refresh.BufferCopy ToRefresh() + { + return new Refresh.BufferCopy + { + srcOffset = SrcOffset, + dstOffset = DstOffset, + size = Size + }; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct BufferImageCopy + { + public uint BufferOffset; + public uint BufferStride; // if 0, image assumed to be tightly packed + public uint BufferImageHeight; // if 0, image assumed to be tightly packed + + public BufferImageCopy( + uint bufferOffset, + uint bufferStride, + uint bufferImageHeight + ) { + BufferOffset = bufferOffset; + BufferStride = bufferStride; + BufferImageHeight = bufferImageHeight; + } + + public Refresh.BufferImageCopy ToRefresh() + { + return new Refresh.BufferImageCopy + { + bufferOffset = BufferOffset, + bufferStride = BufferStride, + bufferImageHeight = BufferImageHeight + }; + } + } } diff --git a/src/Graphics/ResourceInitializer.cs b/src/Graphics/ResourceInitializer.cs new file mode 100644 index 0000000..31298a5 --- /dev/null +++ b/src/Graphics/ResourceInitializer.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace MoonWorks.Graphics +{ + /// + /// A convenience structure for simultaneously creating resources and uploading their data. + /// + /// Note that Upload must be called after the Create methods for the data to actually be uploaded. + /// + public unsafe class ResourceInitializer : GraphicsResource + { + CpuBuffer TransferBuffer; + + byte* data; + uint dataOffset = 0; + uint dataSize = 1024; + + List<(GpuBuffer, uint, uint)> BufferUploads = new List<(GpuBuffer, uint, uint)>(); + List<(Texture, uint, uint)> TextureUploads = new List<(Texture, uint, uint)>(); + + public ResourceInitializer(GraphicsDevice device) : base(device) + { + data = (byte*) NativeMemory.Alloc(dataSize); + } + + /// + /// Creates a GpuBuffer with data to be uploaded. + /// + public GpuBuffer CreateBuffer(Span data, BufferUsageFlags usageFlags) where T : unmanaged + { + var lengthInBytes = (uint) (Marshal.SizeOf() * data.Length); + var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes); + + BufferUploads.Add((gpuBuffer, dataOffset, lengthInBytes)); + + ResizeDataIfNeeded(lengthInBytes); + + fixed (void* spanPtr = data) + { + CopyData(spanPtr, lengthInBytes); + } + + return gpuBuffer; + } + + /// + /// Creates a 2D Texture from compressed image data to be uploaded. + /// + public Texture CreateTexture2D(Span data) + { + var pixelData = ImageUtils.GetPixelDataFromBytes(data, out var width, out var height, out var lengthInBytes); + var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); + TextureUploads.Add((texture, dataOffset, lengthInBytes)); + + ResizeDataIfNeeded(lengthInBytes); + CopyData((void*) pixelData, lengthInBytes); + ImageUtils.FreePixelData(pixelData); + + return texture; + } + + /// + /// Creates a 2D Texture from a compressed image stream to be uploaded. + /// + public Texture CreateTexture2D(Stream stream) + { + var length = stream.Length; + var buffer = NativeMemory.Alloc((nuint) length); + var span = new Span(buffer, (int) length); + stream.ReadExactly(span); + + var texture = CreateTexture2D(span); + + NativeMemory.Free(buffer); + + return texture; + } + + /// + /// Creates a 2D Texture from a compressed image file to be uploaded. + /// + public Texture CreateTexture2D(string path) + { + var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); + return CreateTexture2D(fileStream); + } + + /// + /// Uploads all the data corresponding to the created resources. + /// + public void Upload() + { + if (TransferBuffer == null || TransferBuffer.Size < dataSize) + { + TransferBuffer?.Dispose(); + TransferBuffer = new CpuBuffer(Device, dataSize); + } + + TransferBuffer.SetData(data, new BufferCopy(0, 0, dataSize), SetDataOptions.Discard); + + var commandBuffer = Device.AcquireCommandBuffer(); + + commandBuffer.BeginCopyPass(); + + foreach (var (gpuBuffer, offset, size) in BufferUploads) + { + commandBuffer.UploadToBuffer( + TransferBuffer, + gpuBuffer, + new BufferCopy( + offset, + 0, + size + ) + ); + } + + foreach (var (texture, offset, size) in TextureUploads) + { + commandBuffer.UploadToTexture( + TransferBuffer, + texture, + new BufferImageCopy( + offset, + 0, + 0 + ) + ); + } + + commandBuffer.EndCopyPass(); + Device.Submit(commandBuffer); + + BufferUploads.Clear(); + TextureUploads.Clear(); + dataOffset = 0; + } + + private void ResizeDataIfNeeded(uint lengthInBytes) + { + if (dataOffset + lengthInBytes >= dataSize) + { + dataSize = dataOffset + lengthInBytes; + data = (byte*) NativeMemory.Realloc(data, dataSize); + } + } + + private void CopyData(void* ptr, uint lengthInBytes) + { + NativeMemory.Copy(ptr, data + dataOffset, lengthInBytes); + dataOffset += lengthInBytes; + } + + protected override void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + TransferBuffer.Dispose(); + } + + NativeMemory.Free(data); + } + base.Dispose(disposing); + } + } +} diff --git a/src/Graphics/Resources/Buffer.cs b/src/Graphics/Resources/Buffer.cs deleted file mode 100644 index b034940..0000000 --- a/src/Graphics/Resources/Buffer.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using RefreshCS; - -namespace MoonWorks.Graphics -{ - /// - /// Buffers are generic data containers that can be used by the GPU. - /// - public class Buffer : RefreshResource - { - protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer; - - /// - /// Size in bytes. - /// - public uint Size { get; } - - /// - /// Creates a buffer of appropriate size given a type and element count. - /// - /// The type that the buffer will contain. - /// The GraphicsDevice. - /// Specifies how the buffer will be used. - /// How many elements of type T the buffer will contain. - /// - public unsafe static Buffer Create( - GraphicsDevice device, - BufferUsageFlags usageFlags, - uint elementCount - ) where T : unmanaged - { - return new Buffer( - device, - usageFlags, - (uint) Marshal.SizeOf() * elementCount - ); - } - - /// - /// Creates a buffer. - /// - /// An initialized GraphicsDevice. - /// Specifies how the buffer will be used. - /// The length of the array. Cannot be resized. - public Buffer( - GraphicsDevice device, - BufferUsageFlags usageFlags, - uint sizeInBytes - ) : base(device) - { - Handle = Refresh.Refresh_CreateBuffer( - device.Handle, - (Refresh.BufferUsageFlags) usageFlags, - sizeInBytes - ); - Size = sizeInBytes; - } - - /// - /// Reads data out of a buffer and into a span. - /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. - /// - /// The span that data will be copied to. - /// The length of the data to read. - public unsafe void GetData( - Span data, - uint dataLengthInBytes - ) where T : unmanaged - { -#if DEBUG - if (dataLengthInBytes > Size) - { - Logger.LogWarn("Requested too many bytes from buffer!"); - } - - if (dataLengthInBytes > data.Length * Marshal.SizeOf()) - { - Logger.LogWarn("Data length is larger than the provided Span!"); - } -#endif - - fixed (T* ptr = data) - { - Refresh.Refresh_GetBufferData( - Device.Handle, - Handle, - (IntPtr) ptr, - dataLengthInBytes - ); - } - } - - /// - /// Reads data out of a buffer and into an array. - /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. - /// - /// The span that data will be copied to. - /// The length of the data to read. - public unsafe void GetData( - T[] data, - uint dataLengthInBytes - ) where T : unmanaged - { - GetData(new Span(data), dataLengthInBytes); - } - - /// - /// Reads data out of a buffer and into a span. - /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. - /// Fills the span with as much data from the buffer as it can. - /// - /// The span that data will be copied to. - public unsafe void GetData( - Span data - ) where T : unmanaged - { - var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf(), Size); - GetData(data, (uint) lengthInBytes); - } - - /// - /// Reads data out of a buffer and into an array. - /// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. - /// Fills the array with as much data from the buffer as it can. - /// - /// The span that data will be copied to. - public unsafe void GetData( - T[] data - ) where T : unmanaged - { - var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf(), Size); - GetData(new Span(data), (uint) lengthInBytes); - } - - public static implicit operator BufferBinding(Buffer b) - { - return new BufferBinding(b, 0); - } - } -} diff --git a/src/Graphics/Resources/CpuBuffer.cs b/src/Graphics/Resources/CpuBuffer.cs new file mode 100644 index 0000000..3988ff7 --- /dev/null +++ b/src/Graphics/Resources/CpuBuffer.cs @@ -0,0 +1,158 @@ +using System; +using System.Runtime.InteropServices; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public unsafe class CpuBuffer : RefreshResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyCpuBuffer; + + /// + /// Size in bytes. + /// + public uint Size { get; } + + /// + /// Creates a buffer of requested size given a type and element count. + /// + /// The type that the buffer will contain. + /// The GraphicsDevice. + /// How many elements of type T the buffer will contain. + /// + public unsafe static CpuBuffer Create( + GraphicsDevice device, + uint elementCount + ) where T : unmanaged + { + return new CpuBuffer( + device, + (uint) Marshal.SizeOf() * elementCount + ); + } + + /// + /// Creates a CpuBuffer. + /// + /// An initialized GraphicsDevice. + /// The length of the buffer. Cannot be resized. + public CpuBuffer( + GraphicsDevice device, + uint sizeInBytes + ) : base(device) + { + Handle = Refresh.Refresh_CreateCpuBuffer( + device.Handle, + sizeInBytes + ); + Size = sizeInBytes; + } + + /// + /// Immediately copies data from a data pointer to the CpuBuffer. + /// + /// If setDataOption is DISCARD and this CpuBuffer was used in an Upload command, + /// that command will still use the correct data at the cost of increased memory usage. + /// + /// If setDataOption is OVERWRITE and this CpuBuffer was used in an Upload command, + /// this could cause a data race. + /// + public unsafe void SetData( + byte* dataPtr, + in BufferCopy copyParams, + SetDataOptions setDataOption + ) { + Refresh.Refresh_SetData( + Device.Handle, + (nint) dataPtr, + Handle, + copyParams.ToRefresh(), + (Refresh.SetDataOptions) setDataOption + ); + } + + /// + /// Immediately copies data from a Span to the CpuBuffer. + /// Returns the length of the copy in bytes. + /// + /// If setDataOption is DISCARD and this CpuBuffer was used in an Upload command, + /// that command will still use the correct data at the cost of increased memory usage. + /// + /// If setDataOption is OVERWRITE and this CpuBuffer was used in an Upload command, + /// the data will be overwritten immediately, which could cause a data race. + /// + public unsafe uint SetData( + Span data, + uint bufferOffsetInBytes, + SetDataOptions setDataOption + ) where T : unmanaged + { + var elementSize = Marshal.SizeOf(); + var dataLengthInBytes = (uint) (elementSize * data.Length); + + fixed (T* dataPtr = data) + { + SetData( + (byte*) dataPtr, + new BufferCopy(0, bufferOffsetInBytes, dataLengthInBytes), + setDataOption + ); + } + + return dataLengthInBytes; + } + + /// + /// Immediately copies data from a Span to the CpuBuffer. + /// Returns the length of the copy in bytes. + /// + /// If setDataOption is DISCARD and this CpuBuffer was used in an Upload command, + /// that command will still use the correct data at the cost of increased memory usage. + /// + /// If setDataOption is OVERWRITE and this CpuBuffer was used in an Upload command, + /// the data will be overwritten immediately, which could cause a data race. + /// + public unsafe uint SetData( + Span data, + SetDataOptions setDataOption + ) where T : unmanaged + { + return SetData(data, 0, setDataOption); + } + + /// + /// Immediately copies data from the CpuBuffer into a data pointer. + /// + public unsafe void GetData( + byte* dataPtr, + in BufferCopy copyParams + ) { + Refresh.Refresh_GetData( + Device.Handle, + Handle, + (nint) dataPtr, + copyParams.ToRefresh() + ); + } + + /// + /// Immediately copies data from the CpuBuffer into a Span. + /// + public unsafe void GetData( + Span data, + uint bufferOffsetInBytes + ) where T : unmanaged + { + var elementSize = Marshal.SizeOf(); + var dataLengthInBytes = (uint) (elementSize * data.Length); + + fixed (T* dataPtr = data) + { + GetData( + (byte*) dataPtr, + new BufferCopy(bufferOffsetInBytes, 0, dataLengthInBytes) + ); + } + } + } +} diff --git a/src/Graphics/Resources/GpuBuffer.cs b/src/Graphics/Resources/GpuBuffer.cs new file mode 100644 index 0000000..e517412 --- /dev/null +++ b/src/Graphics/Resources/GpuBuffer.cs @@ -0,0 +1,65 @@ +using System; +using System.Runtime.InteropServices; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + /// + /// GpuBuffers are generic data containers that can be used by the GPU. + /// + public class GpuBuffer : RefreshResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyGpuBuffer; + + /// + /// Size in bytes. + /// + public uint Size { get; } + + /// + /// Creates a buffer of appropriate size given a type and element count. + /// + /// The type that the buffer will contain. + /// The GraphicsDevice. + /// Specifies how the buffer will be used. + /// How many elements of type T the buffer will contain. + /// + public unsafe static GpuBuffer Create( + GraphicsDevice device, + BufferUsageFlags usageFlags, + uint elementCount + ) where T : unmanaged + { + return new GpuBuffer( + device, + usageFlags, + (uint) Marshal.SizeOf() * elementCount + ); + } + + /// + /// Creates a buffer. + /// + /// An initialized GraphicsDevice. + /// Specifies how the buffer will be used. + /// The length of the array. Cannot be resized. + public GpuBuffer( + GraphicsDevice device, + BufferUsageFlags usageFlags, + uint sizeInBytes + ) : base(device) + { + Handle = Refresh.Refresh_CreateGpuBuffer( + device.Handle, + (Refresh.BufferUsageFlags) usageFlags, + sizeInBytes + ); + Size = sizeInBytes; + } + + public static implicit operator BufferBinding(GpuBuffer b) + { + return new BufferBinding(b, 0); + } + } +} diff --git a/src/Graphics/Resources/GraphicsPipeline.cs b/src/Graphics/Resources/GraphicsPipeline.cs index 421e9c5..f854bf9 100644 --- a/src/Graphics/Resources/GraphicsPipeline.cs +++ b/src/Graphics/Resources/GraphicsPipeline.cs @@ -61,12 +61,11 @@ namespace MoonWorks.Graphics refreshGraphicsPipelineCreateInfo.blendConstants[2] = blendConstants.B; refreshGraphicsPipelineCreateInfo.blendConstants[3] = blendConstants.A; - refreshGraphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState.ToRefresh(); + refreshGraphicsPipelineCreateInfo.depthStencilState.stencilState = depthStencilState.StencilState.ToRefresh(); refreshGraphicsPipelineCreateInfo.depthStencilState.compareOp = (Refresh.CompareOp) depthStencilState.CompareOp; refreshGraphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable); refreshGraphicsPipelineCreateInfo.depthStencilState.depthTestEnable = Conversions.BoolToByte(depthStencilState.DepthTestEnable); refreshGraphicsPipelineCreateInfo.depthStencilState.depthWriteEnable = Conversions.BoolToByte(depthStencilState.DepthWriteEnable); - refreshGraphicsPipelineCreateInfo.depthStencilState.frontStencilState = depthStencilState.FrontStencilState.ToRefresh(); refreshGraphicsPipelineCreateInfo.depthStencilState.maxDepthBounds = depthStencilState.MaxDepthBounds; refreshGraphicsPipelineCreateInfo.depthStencilState.minDepthBounds = depthStencilState.MinDepthBounds; refreshGraphicsPipelineCreateInfo.depthStencilState.stencilTestEnable = Conversions.BoolToByte(depthStencilState.StencilTestEnable); diff --git a/src/Graphics/Resources/Texture.cs b/src/Graphics/Resources/Texture.cs index ecf9298..6f5714a 100644 --- a/src/Graphics/Resources/Texture.cs +++ b/src/Graphics/Resources/Texture.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using RefreshCS; namespace MoonWorks.Graphics @@ -23,162 +22,6 @@ namespace MoonWorks.Graphics // FIXME: this allocates a delegate instance protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture; - /// - /// Creates a 2D Texture using PNG or QOI data from raw byte data. - /// - public static unsafe Texture FromImageBytes( - GraphicsDevice device, - CommandBuffer commandBuffer, - Span data - ) { - Texture texture; - - fixed (byte *dataPtr = data) - { - var pixels = Refresh.Refresh_Image_Load((nint) dataPtr, data.Length, out var width, out var height, out var len); - - TextureCreateInfo textureCreateInfo = new TextureCreateInfo(); - textureCreateInfo.Width = (uint) width; - textureCreateInfo.Height = (uint) height; - textureCreateInfo.Depth = 1; - textureCreateInfo.Format = TextureFormat.R8G8B8A8; - textureCreateInfo.IsCube = false; - textureCreateInfo.LevelCount = 1; - textureCreateInfo.SampleCount = SampleCount.One; - textureCreateInfo.UsageFlags = TextureUsageFlags.Sampler; - - texture = new Texture(device, textureCreateInfo); - commandBuffer.SetTextureData(texture, pixels, (uint) len); - - Refresh.Refresh_Image_Free(pixels); - } - - return texture; - } - - /// - /// Creates a 2D Texture using PNG or QOI data from a stream. - /// - public static unsafe Texture FromImageStream( - GraphicsDevice device, - CommandBuffer commandBuffer, - Stream stream - ) { - var length = stream.Length; - var buffer = NativeMemory.Alloc((nuint) length); - var span = new Span(buffer, (int) length); - stream.ReadExactly(span); - - var texture = FromImageBytes(device, commandBuffer, span); - - NativeMemory.Free((void*) buffer); - - return texture; - } - - /// - /// Creates a 2D Texture using PNG or QOI data from a file. - /// - public static Texture FromImageFile( - GraphicsDevice device, - CommandBuffer commandBuffer, - string path - ) { - var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); - return FromImageStream(device, commandBuffer, fileStream); - } - - public static unsafe void SetDataFromImageBytes( - CommandBuffer commandBuffer, - TextureSlice textureSlice, - Span data - ) { - fixed (byte* ptr = data) - { - var pixels = Refresh.Refresh_Image_Load( - (nint) ptr, - (int) data.Length, - out var w, - out var h, - out var len - ); - - commandBuffer.SetTextureData(textureSlice, pixels, (uint) len); - - Refresh.Refresh_Image_Free(pixels); - } - } - - /// - /// Sets data for a texture slice using PNG or QOI data from a stream. - /// - public static unsafe void SetDataFromImageStream( - CommandBuffer commandBuffer, - TextureSlice textureSlice, - Stream stream - ) { - var length = stream.Length; - var buffer = NativeMemory.Alloc((nuint) length); - var span = new Span(buffer, (int) length); - stream.ReadExactly(span); - - SetDataFromImageBytes(commandBuffer, textureSlice, span); - - NativeMemory.Free((void*) buffer); - } - - /// - /// Sets data for a texture slice using PNG or QOI data from a file. - /// - public static void SetDataFromImageFile( - CommandBuffer commandBuffer, - TextureSlice textureSlice, - string path - ) { - var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); - SetDataFromImageStream(commandBuffer, textureSlice, fileStream); - } - - public unsafe static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream) - { - using var reader = new BinaryReader(stream); - Texture texture; - int faces; - ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube); - - if (isCube) - { - texture = CreateTextureCube(graphicsDevice, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels); - faces = 6; - } - else - { - texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels); - faces = 1; - } - - for (int i = 0; i < faces; i += 1) - { - for (int j = 0; j < levels; j += 1) - { - var levelWidth = width >> j; - var levelHeight = height >> j; - - var levelSize = CalculateDDSLevelSize(levelWidth, levelHeight, format); - var byteBuffer = NativeMemory.Alloc((nuint) levelSize); - var byteSpan = new Span(byteBuffer, levelSize); - stream.ReadExactly(byteSpan); - - var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, (uint) i, (uint) j); - commandBuffer.SetTextureData(textureSlice, (nint) byteBuffer, (uint) levelSize); - - NativeMemory.Free(byteBuffer); - } - } - - return texture; - } - /// /// Creates a 2D texture. /// @@ -590,65 +433,6 @@ namespace MoonWorks.Graphics } } - /// - /// Asynchronously saves RGBA or BGRA pixel data to a file in PNG format.
- /// Warning: this is expensive and will block to wait for data download from GPU!
- /// You can avoid blocking by calling this method from a thread. - ///
- public unsafe void SavePNG(string path) - { -#if DEBUG - if (Format != TextureFormat.R8G8B8A8 && Format != TextureFormat.B8G8R8A8) - { - throw new ArgumentException("Texture format must be RGBA or BGRA!", "format"); - } -#endif - - var buffer = new Buffer(Device, 0, Width * Height * 4); // this creates garbage... oh well - - // immediately request the data copy - var commandBuffer = Device.AcquireCommandBuffer(); - commandBuffer.CopyTextureToBuffer(this, buffer); - var fence = Device.SubmitAndAcquireFence(commandBuffer); - - var byteCount = buffer.Size; - - var pixelsPtr = NativeMemory.Alloc((nuint) byteCount); - var pixelsSpan = new Span(pixelsPtr, (int) byteCount); - - Device.WaitForFences(fence); // make sure the data transfer is done... - Device.ReleaseFence(fence); // and then release the fence - - buffer.GetData(pixelsSpan); - - if (Format == TextureFormat.B8G8R8A8) - { - var rgbaPtr = NativeMemory.Alloc((nuint) byteCount); - var rgbaSpan = new Span(rgbaPtr, (int) byteCount); - - for (var i = 0; i < byteCount; i += 4) - { - rgbaSpan[i] = pixelsSpan[i + 2]; - rgbaSpan[i + 1] = pixelsSpan[i + 1]; - rgbaSpan[i + 2] = pixelsSpan[i]; - rgbaSpan[i + 3] = pixelsSpan[i + 3]; - } - - Refresh.Refresh_Image_SavePNG(path, (nint) rgbaPtr, (int) Width, (int) Height); - - NativeMemory.Free((void*) rgbaPtr); - } - else - { - fixed (byte* ptr = pixelsSpan) - { - Refresh.Refresh_Image_SavePNG(path, (nint) ptr, (int) Width, (int) Height); - } - } - - NativeMemory.Free(pixelsPtr); - } - public static uint BytesPerPixel(TextureFormat format) { switch (format) diff --git a/src/Graphics/State/DepthStencilState.cs b/src/Graphics/State/DepthStencilState.cs index 000b86d..8f0f96c 100644 --- a/src/Graphics/State/DepthStencilState.cs +++ b/src/Graphics/State/DepthStencilState.cs @@ -11,14 +11,9 @@ public bool DepthTestEnable; /// - /// Describes the stencil operation for back-facing primitives. + /// Describes the stencil operation. /// - public StencilOpState BackStencilState; - - /// - /// Describes the stencil operation for front-facing primitives. - /// - public StencilOpState FrontStencilState; + public StencilOpState StencilState; /// /// The comparison operator used in the depth test. diff --git a/src/Graphics/TextureSlice.cs b/src/Graphics/TextureSlice.cs index f0f8e30..ece6109 100644 --- a/src/Graphics/TextureSlice.cs +++ b/src/Graphics/TextureSlice.cs @@ -8,36 +8,31 @@ namespace MoonWorks.Graphics /// public struct TextureSlice { - public Texture Texture { get; } - public Rect Rectangle { get; } - public uint Depth { get; } - public uint Layer { get; } - public uint Level { get; } + public Texture Texture; + public uint MipLevel; + public uint BaseLayer; + public uint LayerCount; + public uint X; + public uint Y; + public uint Z; + public uint Width; + public uint Height; + public uint Depth; - public uint Size => (uint) (Rectangle.W * Rectangle.H * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)); + public uint Size => (Width * Height * Depth * LayerCount * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)) >> (int) MipLevel; public TextureSlice(Texture texture) { Texture = texture; - Rectangle = new Rect - { - X = 0, - Y = 0, - W = (int) texture.Width, - H = (int) texture.Height - }; - Depth = 0; - Layer = 0; - Level = 0; - } - - public TextureSlice(Texture texture, Rect rectangle, uint depth = 0, uint layer = 0, uint level = 0) - { - Texture = texture; - Rectangle = rectangle; - Depth = depth; - Layer = layer; - Level = level; + MipLevel = 0; + BaseLayer = 0; + LayerCount = (uint) (texture.IsCube ? 6 : 1); + X = 0; + Y = 0; + Z = 0; + Width = texture.Width; + Height = texture.Height; + Depth = texture.Depth; } public Refresh.TextureSlice ToRefreshTextureSlice() @@ -45,10 +40,15 @@ namespace MoonWorks.Graphics Refresh.TextureSlice textureSlice = new Refresh.TextureSlice { texture = Texture.Handle, - rectangle = Rectangle.ToRefresh(), - depth = Depth, - layer = Layer, - level = Level + mipLevel = MipLevel, + baseLayer = BaseLayer, + layerCount = LayerCount, + x = X, + y = Y, + z = Z, + w = Width, + h = Height, + d = Depth }; return textureSlice; diff --git a/src/Video/VideoPlayer.cs b/src/Video/VideoPlayer.cs index f2d079a..f2c83bb 100644 --- a/src/Video/VideoPlayer.cs +++ b/src/Video/VideoPlayer.cs @@ -28,6 +28,8 @@ namespace MoonWorks.Video private Texture vTexture = null; private Sampler LinearSampler; + private CpuBuffer TransferBuffer; + private int currentFrame; private Stopwatch timer; @@ -53,6 +55,8 @@ namespace MoonWorks.Video { Stop(); + var needNewTransferBuffer = TransferBuffer == null; + if (RenderTexture == null) { RenderTexture = CreateRenderTexture(GraphicsDevice, video.Width, video.Height); @@ -83,18 +87,31 @@ namespace MoonWorks.Video { yTexture.Dispose(); yTexture = CreateSubTexture(GraphicsDevice, video.Width, video.Height); + needNewTransferBuffer = true; } if (video.UVWidth != uTexture.Width || video.UVHeight != uTexture.Height) { uTexture.Dispose(); uTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight); + needNewTransferBuffer = true; } if (video.UVWidth != vTexture.Width || video.UVHeight != vTexture.Height) { vTexture.Dispose(); vTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight); + needNewTransferBuffer = true; + } + + if (needNewTransferBuffer) + { + if (TransferBuffer != null) + { + TransferBuffer.Dispose(); + } + + TransferBuffer = new CpuBuffer(Device, yTexture.Size + uTexture.Size + vTexture.Size); } Video = video; @@ -235,17 +252,44 @@ namespace MoonWorks.Video { var commandBuffer = GraphicsDevice.AcquireCommandBuffer(); - commandBuffer.SetTextureDataYUV( + var ySpan = new Span((void*) CurrentStream.yDataHandle, (int) CurrentStream.yDataLength); + var uSpan = new Span((void*) CurrentStream.uDataHandle, (int) CurrentStream.uvDataLength); + var vSpan = new Span((void*) CurrentStream.vDataHandle, (int) CurrentStream.uvDataLength); + + TransferBuffer.SetData(ySpan, SetDataOptions.Discard); + TransferBuffer.SetData(uSpan, (uint) ySpan.Length, SetDataOptions.Overwrite); + TransferBuffer.SetData(vSpan, (uint) (ySpan.Length + uSpan.Length), SetDataOptions.Overwrite); + + commandBuffer.UploadToTexture( + TransferBuffer, yTexture, + new BufferImageCopy + { + BufferOffset = 0, + BufferStride = CurrentStream.yStride, + BufferImageHeight = yTexture.Height + } + ); + + commandBuffer.UploadToTexture( + TransferBuffer, uTexture, + new BufferImageCopy{ + BufferOffset = (uint) ySpan.Length, + BufferStride = CurrentStream.uvStride, + BufferImageHeight = uTexture.Height + } + ); + + commandBuffer.UploadToTexture( + TransferBuffer, vTexture, - CurrentStream.yDataHandle, - CurrentStream.uDataHandle, - CurrentStream.vDataHandle, - CurrentStream.yDataLength, - CurrentStream.uvDataLength, - CurrentStream.yStride, - CurrentStream.uvStride + new BufferImageCopy + { + BufferOffset = (uint) (ySpan.Length + uSpan.Length), + BufferStride = CurrentStream.uvStride, + BufferImageHeight = vTexture.Height + } ); commandBuffer.BeginRenderPass( @@ -259,7 +303,7 @@ namespace MoonWorks.Video new TextureSamplerBinding(vTexture, LinearSampler) ); - commandBuffer.DrawPrimitives(0, 1, 0, 0); + commandBuffer.DrawPrimitives(0, 1); commandBuffer.EndRenderPass();