From 2cabaa6186b62416f84c519c792fba3958a5df49 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 19 Jan 2021 19:33:27 -0800 Subject: [PATCH] replace Campari with MoonWorks.Graphics --- .gitmodules | 6 +- MoonWorks.csproj | 3 +- MoonWorks.sln | 18 +- lib/Campari | 1 - lib/RefreshCS | 1 + src/Game.cs | 2 +- src/Graphics/BlendConstants.cs | 10 + src/Graphics/Buffer.cs | 73 +++ src/Graphics/BufferBinding.cs | 14 + src/Graphics/Bytecode.cs | 33 ++ src/Graphics/ColorTarget.cs | 50 +++ src/Graphics/CommandBuffer.cs | 414 ++++++++++++++++++ src/Graphics/ComputePipeline.cs | 39 ++ src/Graphics/Conversions.cs | 10 + src/Graphics/DepthStencilTarget.cs | 26 ++ src/Graphics/Framebuffer.cs | 51 +++ src/Graphics/GraphicsDevice.cs | 109 +++++ src/Graphics/GraphicsPipeline.cs | 104 +++++ src/Graphics/GraphicsResource.cs | 42 ++ src/Graphics/RenderPass.cs | 45 ++ src/Graphics/Sampler.cs | 23 + src/Graphics/ShaderModule.cs | 23 + src/Graphics/State/ColorBlendState.cs | 12 + src/Graphics/State/ColorTargetBlendState.cs | 85 ++++ src/Graphics/State/DepthStencilState.cs | 43 ++ .../State/GraphicsPipelineLayoutCreateInfo.cs | 8 + src/Graphics/State/MultisampleState.cs | 16 + src/Graphics/State/RasterizerState.cs | 53 +++ src/Graphics/State/SamplerState.cs | 135 ++++++ src/Graphics/State/ShaderStageState.cs | 9 + src/Graphics/State/TextureCreateInfo.cs | 31 ++ src/Graphics/State/VertexInputState.cs | 10 + src/Graphics/State/ViewportState.cs | 10 + src/Graphics/Texture.cs | 171 ++++++++ src/Graphics/TextureSamplerBinding.cs | 14 + src/Graphics/TextureSlice.cs | 51 +++ src/Window.cs | 2 +- 37 files changed, 1727 insertions(+), 20 deletions(-) delete mode 160000 lib/Campari create mode 160000 lib/RefreshCS create mode 100644 src/Graphics/BlendConstants.cs create mode 100644 src/Graphics/Buffer.cs create mode 100644 src/Graphics/BufferBinding.cs create mode 100644 src/Graphics/Bytecode.cs create mode 100644 src/Graphics/ColorTarget.cs create mode 100644 src/Graphics/CommandBuffer.cs create mode 100644 src/Graphics/ComputePipeline.cs create mode 100644 src/Graphics/Conversions.cs create mode 100644 src/Graphics/DepthStencilTarget.cs create mode 100644 src/Graphics/Framebuffer.cs create mode 100644 src/Graphics/GraphicsDevice.cs create mode 100644 src/Graphics/GraphicsPipeline.cs create mode 100644 src/Graphics/GraphicsResource.cs create mode 100644 src/Graphics/RenderPass.cs create mode 100644 src/Graphics/Sampler.cs create mode 100644 src/Graphics/ShaderModule.cs create mode 100644 src/Graphics/State/ColorBlendState.cs create mode 100644 src/Graphics/State/ColorTargetBlendState.cs create mode 100644 src/Graphics/State/DepthStencilState.cs create mode 100644 src/Graphics/State/GraphicsPipelineLayoutCreateInfo.cs create mode 100644 src/Graphics/State/MultisampleState.cs create mode 100644 src/Graphics/State/RasterizerState.cs create mode 100644 src/Graphics/State/SamplerState.cs create mode 100644 src/Graphics/State/ShaderStageState.cs create mode 100644 src/Graphics/State/TextureCreateInfo.cs create mode 100644 src/Graphics/State/VertexInputState.cs create mode 100644 src/Graphics/State/ViewportState.cs create mode 100644 src/Graphics/Texture.cs create mode 100644 src/Graphics/TextureSamplerBinding.cs create mode 100644 src/Graphics/TextureSlice.cs diff --git a/.gitmodules b/.gitmodules index fd23d35..b841a37 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "lib/SDL2-CS"] path = lib/SDL2-CS url = https://github.com/flibitijibibo/SDL2-CS.git -[submodule "lib/Campari"] - path = lib/Campari - url = https://gitea.moonside.games/MoonsideGames/Campari.git [submodule "lib/FAudio"] path = lib/FAudio url = https://github.com/FNA-XNA/FAudio.git +[submodule "lib/RefreshCS"] + path = lib/RefreshCS + url = https://gitea.moonside.games/MoonsideGames/RefreshCS.git diff --git a/MoonWorks.csproj b/MoonWorks.csproj index 1eed3d5..ea84153 100644 --- a/MoonWorks.csproj +++ b/MoonWorks.csproj @@ -12,8 +12,7 @@ - - + diff --git a/MoonWorks.sln b/MoonWorks.sln index 2e2bd35..1c66aa8 100644 --- a/MoonWorks.sln +++ b/MoonWorks.sln @@ -10,12 +10,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoonWorks", "MoonWorks.cspr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS.Core", "lib\SDL2-CS\SDL2-CS.Core.csproj", "{0929F2D8-8FE4-4452-AD1E-50760A1A19A5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Campari", "lib\Campari\Campari.csproj", "{D09577DE-99F5-4C30-9165-4012C37CE0CE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RefreshCS", "lib\Campari\lib\RefreshCS\RefreshCS.csproj", "{66116A40-B360-4BA3-966A-A54F3E562EC1}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FAudio-CS.Core", "lib\FAudio\csharp\FAudio-CS.Core.csproj", "{608AA31D-F163-4096-B4EF-B9C7D21D52BB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RefreshCS", "lib\RefreshCS\RefreshCS.csproj", "{AD7C94E4-0AFA-44CA-889C-110142369893}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -30,18 +28,14 @@ Global {0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Debug|x64.Build.0 = Debug|x64 {0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Release|x64.ActiveCfg = Release|x64 {0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Release|x64.Build.0 = Release|x64 - {D09577DE-99F5-4C30-9165-4012C37CE0CE}.Debug|x64.ActiveCfg = Debug|x64 - {D09577DE-99F5-4C30-9165-4012C37CE0CE}.Debug|x64.Build.0 = Debug|x64 - {D09577DE-99F5-4C30-9165-4012C37CE0CE}.Release|x64.ActiveCfg = Release|x64 - {D09577DE-99F5-4C30-9165-4012C37CE0CE}.Release|x64.Build.0 = Release|x64 - {66116A40-B360-4BA3-966A-A54F3E562EC1}.Debug|x64.ActiveCfg = Debug|x64 - {66116A40-B360-4BA3-966A-A54F3E562EC1}.Debug|x64.Build.0 = Debug|x64 - {66116A40-B360-4BA3-966A-A54F3E562EC1}.Release|x64.ActiveCfg = Release|x64 - {66116A40-B360-4BA3-966A-A54F3E562EC1}.Release|x64.Build.0 = Release|x64 {608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Debug|x64.ActiveCfg = Debug|x64 {608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Debug|x64.Build.0 = Debug|x64 {608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Release|x64.ActiveCfg = Release|x64 {608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Release|x64.Build.0 = Release|x64 + {AD7C94E4-0AFA-44CA-889C-110142369893}.Debug|x64.ActiveCfg = Debug|x64 + {AD7C94E4-0AFA-44CA-889C-110142369893}.Debug|x64.Build.0 = Debug|x64 + {AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.ActiveCfg = Release|x64 + {AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/lib/Campari b/lib/Campari deleted file mode 160000 index f01b9a3..0000000 --- a/lib/Campari +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f01b9a3a9acff8672e0d7da253ee2402fcdaa281 diff --git a/lib/RefreshCS b/lib/RefreshCS new file mode 160000 index 0000000..ba183e8 --- /dev/null +++ b/lib/RefreshCS @@ -0,0 +1 @@ +Subproject commit ba183e8c0f9d21e4397d76941078b4969d4f6686 diff --git a/src/Game.cs b/src/Game.cs index e8b1eb8..2103f45 100644 --- a/src/Game.cs +++ b/src/Game.cs @@ -1,5 +1,5 @@ using SDL2; -using Campari; +using MoonWorks.Graphics; using System.Collections.Generic; using MoonWorks.Audio; diff --git a/src/Graphics/BlendConstants.cs b/src/Graphics/BlendConstants.cs new file mode 100644 index 0000000..71a3582 --- /dev/null +++ b/src/Graphics/BlendConstants.cs @@ -0,0 +1,10 @@ +namespace MoonWorks.Graphics +{ + public struct BlendConstants + { + public float R; + public float G; + public float B; + public float A; + } +} diff --git a/src/Graphics/Buffer.cs b/src/Graphics/Buffer.cs new file mode 100644 index 0000000..a53a850 --- /dev/null +++ b/src/Graphics/Buffer.cs @@ -0,0 +1,73 @@ +using System; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class Buffer : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer; + + public Buffer( + GraphicsDevice device, + Refresh.BufferUsageFlags usageFlags, + uint sizeInBytes + ) : base(device) + { + Handle = Refresh.Refresh_CreateBuffer( + device.Handle, + usageFlags, + sizeInBytes + ); + } + + public unsafe void SetData( + uint offsetInBytes, + T[] data, + uint dataLengthInBytes + ) where T : unmanaged + { + fixed (T* ptr = &data[0]) + { + Refresh.Refresh_SetBufferData( + Device.Handle, + Handle, + offsetInBytes, + (IntPtr) ptr, + dataLengthInBytes + ); + } + } + + public unsafe void SetData( + uint offsetInBytes, + T* data, + uint dataLengthInBytes + ) where T : unmanaged + { + Refresh.Refresh_SetBufferData( + Device.Handle, + Handle, + offsetInBytes, + (IntPtr) data, + dataLengthInBytes + ); + } + + // NOTE: You want to wait on the device before calling this + public unsafe void GetData( + T[] data, + uint dataLengthInBytes + ) where T : unmanaged + { + fixed (T* ptr = &data[0]) + { + Refresh.Refresh_GetBufferData( + Device.Handle, + Handle, + (IntPtr)ptr, + dataLengthInBytes + ); + } + } + } +} diff --git a/src/Graphics/BufferBinding.cs b/src/Graphics/BufferBinding.cs new file mode 100644 index 0000000..adc4ed8 --- /dev/null +++ b/src/Graphics/BufferBinding.cs @@ -0,0 +1,14 @@ +namespace MoonWorks.Graphics +{ + public struct BufferBinding + { + public Buffer Buffer; + public ulong Offset; + + public BufferBinding(Buffer buffer, ulong offset) + { + Buffer = buffer; + Offset = offset; + } + } +} diff --git a/src/Graphics/Bytecode.cs b/src/Graphics/Bytecode.cs new file mode 100644 index 0000000..f9a2982 --- /dev/null +++ b/src/Graphics/Bytecode.cs @@ -0,0 +1,33 @@ +using System.IO; + +namespace MoonWorks.Graphics +{ + public static class Bytecode + { + public static uint[] ReadBytecode(FileInfo fileInfo) + { + byte[] data; + int size; + using (FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read)) + { + size = (int)stream.Length; + data = new byte[size]; + stream.Read(data, 0, size); + } + + uint[] uintData = new uint[size / 4]; + using (var memoryStream = new MemoryStream(data)) + { + using (var reader = new BinaryReader(memoryStream)) + { + for (int i = 0; i < size / 4; i++) + { + uintData[i] = reader.ReadUInt32(); + } + } + } + + return uintData; + } + } +} diff --git a/src/Graphics/ColorTarget.cs b/src/Graphics/ColorTarget.cs new file mode 100644 index 0000000..25fd22a --- /dev/null +++ b/src/Graphics/ColorTarget.cs @@ -0,0 +1,50 @@ +using System; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class ColorTarget : GraphicsResource + { + public uint Width { get; } + public uint Height { get; } + + public Texture Texture { get; } + public Refresh.ColorFormat Format => Texture.Format; + + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyColorTarget; + + public static ColorTarget CreateBackedColorTarget2D( + GraphicsDevice device, + uint width, + uint height, + Refresh.ColorFormat format, + bool canBeSampled, + Refresh.SampleCount sampleCount = Refresh.SampleCount.One, + uint levelCount = 1 + ) + { + var flags = Refresh.TextureUsageFlags.ColorTargetBit; + if (canBeSampled) { flags |= Refresh.TextureUsageFlags.SamplerBit; } + + var texture = Texture.CreateTexture2D( + device, + width, + height, + format, + flags, + sampleCount, + levelCount + ); + + var textureSlice = new TextureSlice(texture); + + return new ColorTarget(device, sampleCount, ref textureSlice); + } + + public ColorTarget(GraphicsDevice device, Refresh.SampleCount sampleCount, ref TextureSlice textureSlice) : base(device) + { + var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); + Handle = Refresh.Refresh_CreateColorTarget(device.Handle, sampleCount, ref refreshTextureSlice); + } + } +} diff --git a/src/Graphics/CommandBuffer.cs b/src/Graphics/CommandBuffer.cs new file mode 100644 index 0000000..7448380 --- /dev/null +++ b/src/Graphics/CommandBuffer.cs @@ -0,0 +1,414 @@ +using System; +using System.Runtime.InteropServices; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class CommandBuffer + { + public GraphicsDevice Device { get; } + public IntPtr Handle { get; internal set; } + + // called from RefreshDevice + internal CommandBuffer(GraphicsDevice device) + { + Device = device; + Handle = IntPtr.Zero; + } + + public unsafe void BeginRenderPass( + RenderPass renderPass, + Framebuffer framebuffer, + ref Refresh.Rect renderArea, + ref Refresh.DepthStencilValue depthStencilClearValue, + params Refresh.Color[] clearColors + ) { + fixed (Refresh.Color* clearColorPtr = &clearColors[0]) + { + Refresh.Refresh_BeginRenderPass( + Device.Handle, + Handle, + renderPass.Handle, + framebuffer.Handle, + ref renderArea, + (IntPtr) clearColorPtr, + (uint)clearColors.Length, + ref depthStencilClearValue + ); + } + } + + public unsafe void BeginRenderPass( + RenderPass renderPass, + Framebuffer framebuffer, + ref Refresh.Rect renderArea, + params Refresh.Color[] clearColors + ) { + fixed (Refresh.Color* clearColorPtr = &clearColors[0]) + { + Refresh.Refresh_BeginRenderPass( + Device.Handle, + Handle, + renderPass.Handle, + framebuffer.Handle, + ref renderArea, + (IntPtr) clearColorPtr, + (uint) clearColors.Length, + IntPtr.Zero + ); + } + } + + public void BindComputePipeline( + ComputePipeline computePipeline + ) { + Refresh.Refresh_BindComputePipeline( + Device.Handle, + Handle, + computePipeline.Handle + ); + } + + public unsafe uint PushComputeShaderParams( + params T[] uniforms + ) where T : unmanaged + { + fixed (T* ptr = &uniforms[0]) + { + return Refresh.Refresh_PushComputeShaderParams( + Device.Handle, + Handle, + (IntPtr) ptr, + (uint) uniforms.Length + ); + } + } + + public unsafe void BindComputeBuffers( + params Buffer[] buffers + ) { + var bufferPtrs = stackalloc IntPtr[buffers.Length]; + + for (var i = 0; i < buffers.Length; i += 1) + { + bufferPtrs[i] = buffers[i].Handle; + } + + Refresh.Refresh_BindComputeBuffers( + Device.Handle, + Handle, + (IntPtr) bufferPtrs + ); + } + + public unsafe void BindComputeTextures( + params Texture[] textures + ) { + var texturePtrs = stackalloc IntPtr[textures.Length]; + + for (var i = 0; i < textures.Length; i += 1) + { + texturePtrs[i] = textures[i].Handle; + } + + Refresh.Refresh_BindComputeTextures( + Device.Handle, + Handle, + (IntPtr) texturePtrs + ); + } + + public void BindGraphicsPipeline( + GraphicsPipeline graphicsPipeline + ) { + Refresh.Refresh_BindGraphicsPipeline( + Device.Handle, + Handle, + graphicsPipeline.Handle + ); + } + + public unsafe uint PushVertexShaderParams( + params T[] uniforms + ) where T : unmanaged + { + fixed (T* ptr = &uniforms[0]) + { + return Refresh.Refresh_PushVertexShaderParams( + Device.Handle, + Handle, + (IntPtr) ptr, + (uint) uniforms.Length + ); + } + } + + public unsafe uint PushFragmentShaderParams( + params T[] uniforms + ) where T : unmanaged + { + fixed (T* ptr = &uniforms[0]) + { + return Refresh.Refresh_PushFragmentShaderParams( + Device.Handle, + Handle, + (IntPtr) ptr, + (uint) uniforms.Length + ); + } + } + + public unsafe void BindVertexBuffers( + uint firstBinding, + params BufferBinding[] bufferBindings + ) { + var bufferPtrs = stackalloc IntPtr[bufferBindings.Length]; + var offsets = stackalloc ulong[bufferBindings.Length]; + + for (var i = 0; i < bufferBindings.Length; i += 1) + { + bufferPtrs[i] = bufferBindings[i].Buffer.Handle; + offsets[i] = bufferBindings[i].Offset; + } + + Refresh.Refresh_BindVertexBuffers( + Device.Handle, + Handle, + firstBinding, + (uint) bufferBindings.Length, + (IntPtr) bufferPtrs, + (IntPtr) offsets + ); + } + + public void BindIndexBuffer( + Buffer indexBuffer, + uint offset, + Refresh.IndexElementSize indexElementSize + ) { + Refresh.Refresh_BindIndexBuffer( + Device.Handle, + Handle, + indexBuffer.Handle, + offset, + indexElementSize + ); + } + + public unsafe void BindVertexSamplers( + Texture[] textures, + Sampler[] samplers + ) { + var texturePtrs = stackalloc IntPtr[textures.Length]; + var samplerPtrs = stackalloc IntPtr[samplers.Length]; + + for (var i = 0; i < textures.Length; i += 1) + { + texturePtrs[i] = textures[i].Handle; + } + + for (var i = 0; i < samplers.Length; i += 1) + { + samplerPtrs[i] = samplers[i].Handle; + } + + Refresh.Refresh_BindVertexSamplers( + Device.Handle, + Handle, + (IntPtr) texturePtrs, + (IntPtr) samplerPtrs + ); + } + + public unsafe void BindFragmentSamplers( + params TextureSamplerBinding[] textureSamplerBindings + ) { + var texturePtrs = stackalloc IntPtr[textureSamplerBindings.Length]; + var samplerPtrs = stackalloc IntPtr[textureSamplerBindings.Length]; + + for (var i = 0; i < textureSamplerBindings.Length; i += 1) + { + texturePtrs[i] = textureSamplerBindings[i].Texture.Handle; + samplerPtrs[i] = textureSamplerBindings[i].Sampler.Handle; + } + + Refresh.Refresh_BindFragmentSamplers( + Device.Handle, + Handle, + (IntPtr) texturePtrs, + (IntPtr) samplerPtrs + ); + } + + public void Clear( + ref Refresh.Rect clearRect, + Refresh.ClearOptionsFlags clearOptions, + ref Refresh.Color[] colors, + float depth, + int stencil + ) { + Refresh.Refresh_Clear( + Device.Handle, + Handle, + ref clearRect, + clearOptions, + ref colors, + (uint) colors.Length, + depth, + stencil + ); + } + + public void DrawInstancedPrimitives( + uint baseVertex, + uint startIndex, + uint primitiveCount, + uint instanceCount, + uint vertexParamOffset, + uint fragmentParamOffset + ) { + Refresh.Refresh_DrawInstancedPrimitives( + Device.Handle, + Handle, + baseVertex, + startIndex, + primitiveCount, + instanceCount, + vertexParamOffset, + fragmentParamOffset + ); + } + + public void DrawIndexedPrimitives( + uint baseVertex, + uint startIndex, + uint primitiveCount, + uint vertexParamOffset, + uint fragmentParamOffset + ) { + Refresh.Refresh_DrawIndexedPrimitives( + Device.Handle, + Handle, + baseVertex, + startIndex, + primitiveCount, + vertexParamOffset, + fragmentParamOffset + ); + } + + public void DrawPrimitives( + uint vertexStart, + uint primitiveCount, + uint vertexParamOffset, + uint fragmentParamOffset + ) { + Refresh.Refresh_DrawPrimitives( + Device.Handle, + Handle, + vertexStart, + primitiveCount, + vertexParamOffset, + fragmentParamOffset + ); + } + + public void EndRenderPass() + { + Refresh.Refresh_EndRenderPass( + Device.Handle, + Handle + ); + } + + public void QueuePresent( + ref TextureSlice textureSlice, + ref Refresh.Rect destinationRectangle, + Refresh.Filter filter + ) { + var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); + + Refresh.Refresh_QueuePresent( + Device.Handle, + Handle, + ref refreshTextureSlice, + ref destinationRectangle, + filter + ); + } + + public void QueuePresent( + ref TextureSlice textureSlice, + Refresh.Filter filter + ) { + var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); + + Refresh.Refresh_QueuePresent( + Device.Handle, + Handle, + ref refreshTextureSlice, + IntPtr.Zero, + filter + ); + } + + public void QueuePresent( + Texture texture, + Refresh.Filter filter + ) { + var refreshTextureSlice = new Refresh.TextureSlice + { + texture = texture.Handle, + rectangle = new Refresh.Rect + { + x = 0, + y = 0, + w = (int) texture.Width, + h = (int) texture.Height + }, + layer = 0, + level = 0, + depth = 0 + }; + + Refresh.Refresh_QueuePresent( + Device.Handle, + Handle, + ref refreshTextureSlice, + IntPtr.Zero, + filter + ); + } + + public void CopyTextureToTexture( + ref TextureSlice sourceTextureSlice, + ref TextureSlice destinationTextureSlice, + Refresh.Filter filter + ) { + var sourceRefreshTextureSlice = sourceTextureSlice.ToRefreshTextureSlice(); + var destRefreshTextureSlice = destinationTextureSlice.ToRefreshTextureSlice(); + + Refresh.Refresh_CopyTextureToTexture( + Device.Handle, + Handle, + ref sourceRefreshTextureSlice, + ref destRefreshTextureSlice, + filter + ); + } + + public void CopyTextureToBuffer( + ref TextureSlice textureSlice, + Buffer buffer + ) { + var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); + + Refresh.Refresh_CopyTextureToBuffer( + Device.Handle, + Handle, + ref refreshTextureSlice, + buffer.Handle + ); + } + } +} diff --git a/src/Graphics/ComputePipeline.cs b/src/Graphics/ComputePipeline.cs new file mode 100644 index 0000000..9554e9f --- /dev/null +++ b/src/Graphics/ComputePipeline.cs @@ -0,0 +1,39 @@ +using RefreshCS; +using System; + +namespace MoonWorks.Graphics +{ + public class ComputePipeline : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyComputePipeline; + + public unsafe ComputePipeline( + GraphicsDevice device, + ShaderStageState computeShaderState, + uint bufferBindingCount, + uint imageBindingCount + ) : base(device) { + var computePipelineLayoutCreateInfo = new Refresh.ComputePipelineLayoutCreateInfo + { + bufferBindingCount = bufferBindingCount, + imageBindingCount = imageBindingCount + }; + + var computePipelineCreateInfo = new Refresh.ComputePipelineCreateInfo + { + pipelineLayoutCreateInfo = computePipelineLayoutCreateInfo, + computeShaderState = new Refresh.ShaderStageState + { + entryPointName = computeShaderState.EntryPointName, + shaderModule = computeShaderState.ShaderModule.Handle, + uniformBufferSize = computeShaderState.UniformBufferSize + } + }; + + Handle = Refresh.Refresh_CreateComputePipeline( + device.Handle, + ref computePipelineCreateInfo + ); + } + } +} diff --git a/src/Graphics/Conversions.cs b/src/Graphics/Conversions.cs new file mode 100644 index 0000000..c7d04b7 --- /dev/null +++ b/src/Graphics/Conversions.cs @@ -0,0 +1,10 @@ +namespace MoonWorks.Graphics +{ + public static class Conversions + { + public static byte BoolToByte(bool b) + { + return (byte)(b ? 1 : 0); + } + } +} diff --git a/src/Graphics/DepthStencilTarget.cs b/src/Graphics/DepthStencilTarget.cs new file mode 100644 index 0000000..e123680 --- /dev/null +++ b/src/Graphics/DepthStencilTarget.cs @@ -0,0 +1,26 @@ +using System; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class DepthStencilTarget : GraphicsResource + { + public uint Width { get; } + public uint Height { get; } + public Refresh.DepthFormat Format { get; } + + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyDepthStencilTarget; + + public DepthStencilTarget( + GraphicsDevice device, + uint width, + uint height, + Refresh.DepthFormat depthFormat + ) : base(device) + { + Handle = Refresh.Refresh_CreateDepthStencilTarget(device.Handle, width, height, depthFormat); + Width = width; + Height = height; + } + } +} diff --git a/src/Graphics/Framebuffer.cs b/src/Graphics/Framebuffer.cs new file mode 100644 index 0000000..3f6f8ad --- /dev/null +++ b/src/Graphics/Framebuffer.cs @@ -0,0 +1,51 @@ +using System; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class Framebuffer : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyFramebuffer; + + public unsafe Framebuffer( + GraphicsDevice device, + uint width, + uint height, + RenderPass renderPass, + DepthStencilTarget depthStencilTarget, /* can be NULL */ + params ColorTarget[] colorTargets + ) : base(device) + { + IntPtr[] colorTargetHandles = new IntPtr[colorTargets.Length]; + for (var i = 0; i < colorTargets.Length; i += 1) + { + colorTargetHandles[i] = colorTargets[i].Handle; + } + + IntPtr depthStencilTargetHandle; + if (depthStencilTarget == null) + { + depthStencilTargetHandle = IntPtr.Zero; + } + else + { + depthStencilTargetHandle = depthStencilTarget.Handle; + } + + fixed (IntPtr* colorTargetHandlesPtr = colorTargetHandles) + { + Refresh.FramebufferCreateInfo framebufferCreateInfo = new Refresh.FramebufferCreateInfo + { + width = width, + height = height, + colorTargetCount = (uint) colorTargets.Length, + pColorTargets = (IntPtr) colorTargetHandlesPtr, + depthStencilTarget = depthStencilTargetHandle, + renderPass = renderPass.Handle + }; + + Handle = Refresh.Refresh_CreateFramebuffer(device.Handle, ref framebufferCreateInfo); + } + } + } +} diff --git a/src/Graphics/GraphicsDevice.cs b/src/Graphics/GraphicsDevice.cs new file mode 100644 index 0000000..fb39253 --- /dev/null +++ b/src/Graphics/GraphicsDevice.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class GraphicsDevice : IDisposable + { + public IntPtr Handle { get; } + + public bool IsDisposed { get; private set; } + + private readonly Queue commandBufferPool; + + public GraphicsDevice( + IntPtr deviceWindowHandle, + Refresh.PresentMode presentMode, + bool debugMode, + int initialCommandBufferPoolSize = 4 + ) { + var presentationParameters = new Refresh.PresentationParameters + { + deviceWindowHandle = deviceWindowHandle, + presentMode = presentMode + }; + + Handle = Refresh.Refresh_CreateDevice( + ref presentationParameters, + Conversions.BoolToByte(debugMode) + ); + + commandBufferPool = new Queue(initialCommandBufferPoolSize); + for (var i = 0; i < initialCommandBufferPoolSize; i += 1) + { + commandBufferPool.Enqueue(new CommandBuffer(this)); + } + } + + public CommandBuffer AcquireCommandBuffer() + { + var commandBufferHandle = Refresh.Refresh_AcquireCommandBuffer(Handle, 0); + if (commandBufferPool.Count == 0) + { + commandBufferPool.Enqueue(new CommandBuffer(this)); + } + + var commandBuffer = commandBufferPool.Dequeue(); + commandBuffer.Handle = commandBufferHandle; + + return commandBuffer; + } + + public unsafe void Submit(params CommandBuffer[] commandBuffers) + { + var commandBufferPtrs = stackalloc IntPtr[commandBuffers.Length]; + + for (var i = 0; i < commandBuffers.Length; i += 1) + { + commandBufferPtrs[i] = commandBuffers[i].Handle; + } + + Refresh.Refresh_Submit( + Handle, + (uint) commandBuffers.Length, + (IntPtr) commandBufferPtrs + ); + + // return to pool + for (var i = 0; i < commandBuffers.Length; i += 1) + { + commandBuffers[i].Handle = IntPtr.Zero; + commandBufferPool.Enqueue(commandBuffers[i]); + } + } + + public void Wait() + { + Refresh.Refresh_Wait(Handle); + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + } + + Refresh.Refresh_DestroyDevice(Handle); + IsDisposed = true; + } + } + + // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~GraphicsDevice() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Graphics/GraphicsPipeline.cs b/src/Graphics/GraphicsPipeline.cs new file mode 100644 index 0000000..d83c0b3 --- /dev/null +++ b/src/Graphics/GraphicsPipeline.cs @@ -0,0 +1,104 @@ +using System; +using System.Runtime.InteropServices; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class GraphicsPipeline : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyGraphicsPipeline; + + public unsafe GraphicsPipeline( + GraphicsDevice device, + ColorBlendState colorBlendState, + DepthStencilState depthStencilState, + ShaderStageState vertexShaderState, + ShaderStageState fragmentShaderState, + MultisampleState multisampleState, + GraphicsPipelineLayoutCreateInfo pipelineLayoutCreateInfo, + RasterizerState rasterizerState, + Refresh.PrimitiveType primitiveType, + VertexInputState vertexInputState, + ViewportState viewportState, + RenderPass renderPass + ) : base(device) + { + var vertexAttributesHandle = GCHandle.Alloc(vertexInputState.VertexAttributes, GCHandleType.Pinned); + var vertexBindingsHandle = GCHandle.Alloc(vertexInputState.VertexBindings, GCHandleType.Pinned); + var viewportHandle = GCHandle.Alloc(viewportState.Viewports, GCHandleType.Pinned); + var scissorHandle = GCHandle.Alloc(viewportState.Scissors, GCHandleType.Pinned); + + var colorTargetBlendStates = stackalloc Refresh.ColorTargetBlendState[colorBlendState.ColorTargetBlendStates.Length]; + + for (var i = 0; i < colorBlendState.ColorTargetBlendStates.Length; i += 1) + { + colorTargetBlendStates[i] = colorBlendState.ColorTargetBlendStates[i].ToRefreshColorTargetBlendState(); + } + + Refresh.GraphicsPipelineCreateInfo graphicsPipelineCreateInfo; + + graphicsPipelineCreateInfo.colorBlendState.logicOpEnable = Conversions.BoolToByte(colorBlendState.LogicOpEnable); + graphicsPipelineCreateInfo.colorBlendState.logicOp = colorBlendState.LogicOp; + graphicsPipelineCreateInfo.colorBlendState.blendStates = (IntPtr) colorTargetBlendStates; + graphicsPipelineCreateInfo.colorBlendState.blendStateCount = (uint) colorBlendState.ColorTargetBlendStates.Length; + graphicsPipelineCreateInfo.colorBlendState.blendConstants[0] = colorBlendState.BlendConstants.R; + graphicsPipelineCreateInfo.colorBlendState.blendConstants[1] = colorBlendState.BlendConstants.G; + graphicsPipelineCreateInfo.colorBlendState.blendConstants[2] = colorBlendState.BlendConstants.B; + graphicsPipelineCreateInfo.colorBlendState.blendConstants[3] = colorBlendState.BlendConstants.A; + + graphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState; + graphicsPipelineCreateInfo.depthStencilState.compareOp = depthStencilState.CompareOp; + graphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable); + graphicsPipelineCreateInfo.depthStencilState.depthTestEnable = Conversions.BoolToByte(depthStencilState.DepthTestEnable); + graphicsPipelineCreateInfo.depthStencilState.depthWriteEnable = Conversions.BoolToByte(depthStencilState.DepthWriteEnable); + graphicsPipelineCreateInfo.depthStencilState.frontStencilState = depthStencilState.FrontStencilState; + graphicsPipelineCreateInfo.depthStencilState.maxDepthBounds = depthStencilState.MaxDepthBounds; + graphicsPipelineCreateInfo.depthStencilState.minDepthBounds = depthStencilState.MinDepthBounds; + graphicsPipelineCreateInfo.depthStencilState.stencilTestEnable = Conversions.BoolToByte(depthStencilState.StencilTestEnable); + + graphicsPipelineCreateInfo.vertexShaderState.entryPointName = vertexShaderState.EntryPointName; + graphicsPipelineCreateInfo.vertexShaderState.shaderModule = vertexShaderState.ShaderModule.Handle; + graphicsPipelineCreateInfo.vertexShaderState.uniformBufferSize = vertexShaderState.UniformBufferSize; + + graphicsPipelineCreateInfo.fragmentShaderState.entryPointName = fragmentShaderState.EntryPointName; + graphicsPipelineCreateInfo.fragmentShaderState.shaderModule = fragmentShaderState.ShaderModule.Handle; + graphicsPipelineCreateInfo.fragmentShaderState.uniformBufferSize = fragmentShaderState.UniformBufferSize; + + graphicsPipelineCreateInfo.multisampleState.multisampleCount = multisampleState.MultisampleCount; + graphicsPipelineCreateInfo.multisampleState.sampleMask = multisampleState.SampleMask; + + graphicsPipelineCreateInfo.pipelineLayoutCreateInfo.vertexSamplerBindingCount = pipelineLayoutCreateInfo.VertexSamplerBindingCount; + graphicsPipelineCreateInfo.pipelineLayoutCreateInfo.fragmentSamplerBindingCount = pipelineLayoutCreateInfo.FragmentSamplerBindingCount; + + graphicsPipelineCreateInfo.rasterizerState.cullMode = rasterizerState.CullMode; + graphicsPipelineCreateInfo.rasterizerState.depthBiasClamp = rasterizerState.DepthBiasClamp; + graphicsPipelineCreateInfo.rasterizerState.depthBiasConstantFactor = rasterizerState.DepthBiasConstantFactor; + graphicsPipelineCreateInfo.rasterizerState.depthBiasEnable = Conversions.BoolToByte(rasterizerState.DepthBiasEnable); + graphicsPipelineCreateInfo.rasterizerState.depthBiasSlopeFactor = rasterizerState.DepthBiasSlopeFactor; + graphicsPipelineCreateInfo.rasterizerState.depthClampEnable = Conversions.BoolToByte(rasterizerState.DepthClampEnable); + graphicsPipelineCreateInfo.rasterizerState.fillMode = rasterizerState.FillMode; + graphicsPipelineCreateInfo.rasterizerState.frontFace = rasterizerState.FrontFace; + graphicsPipelineCreateInfo.rasterizerState.lineWidth = rasterizerState.LineWidth; + + graphicsPipelineCreateInfo.vertexInputState.vertexAttributes = vertexAttributesHandle.AddrOfPinnedObject(); + graphicsPipelineCreateInfo.vertexInputState.vertexAttributeCount = (uint) vertexInputState.VertexAttributes.Length; + graphicsPipelineCreateInfo.vertexInputState.vertexBindings = vertexBindingsHandle.AddrOfPinnedObject(); + graphicsPipelineCreateInfo.vertexInputState.vertexBindingCount = (uint) vertexInputState.VertexBindings.Length; + + graphicsPipelineCreateInfo.viewportState.viewports = viewportHandle.AddrOfPinnedObject(); + graphicsPipelineCreateInfo.viewportState.viewportCount = (uint) viewportState.Viewports.Length; + graphicsPipelineCreateInfo.viewportState.scissors = scissorHandle.AddrOfPinnedObject(); + graphicsPipelineCreateInfo.viewportState.scissorCount = (uint) viewportState.Scissors.Length; + + graphicsPipelineCreateInfo.primitiveType = primitiveType; + graphicsPipelineCreateInfo.renderPass = renderPass.Handle; + + Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, ref graphicsPipelineCreateInfo); + + vertexAttributesHandle.Free(); + vertexBindingsHandle.Free(); + viewportHandle.Free(); + scissorHandle.Free(); + } + } +} diff --git a/src/Graphics/GraphicsResource.cs b/src/Graphics/GraphicsResource.cs new file mode 100644 index 0000000..41ed183 --- /dev/null +++ b/src/Graphics/GraphicsResource.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MoonWorks.Graphics +{ + public abstract class GraphicsResource : IDisposable + { + public GraphicsDevice Device { get; } + public IntPtr Handle { get; protected set; } + + public bool IsDisposed { get; private set; } + protected abstract Action QueueDestroyFunction { get; } + + public GraphicsResource(GraphicsDevice device) + { + Device = device; + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + QueueDestroyFunction(Device.Handle, Handle); + IsDisposed = true; + } + } + + ~GraphicsResource() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Graphics/RenderPass.cs b/src/Graphics/RenderPass.cs new file mode 100644 index 0000000..c8c123b --- /dev/null +++ b/src/Graphics/RenderPass.cs @@ -0,0 +1,45 @@ +using System; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class RenderPass : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyRenderPass; + + public unsafe RenderPass( + GraphicsDevice device, + params Refresh.ColorTargetDescription[] colorTargetDescriptions + ) : base(device) + { + fixed (Refresh.ColorTargetDescription* ptr = colorTargetDescriptions) + { + Refresh.RenderPassCreateInfo renderPassCreateInfo; + renderPassCreateInfo.colorTargetCount = (uint) colorTargetDescriptions.Length; + renderPassCreateInfo.colorTargetDescriptions = (IntPtr) ptr; + renderPassCreateInfo.depthStencilTargetDescription = IntPtr.Zero; + + Handle = Refresh.Refresh_CreateRenderPass(device.Handle, ref renderPassCreateInfo); + } + } + + public unsafe RenderPass( + GraphicsDevice device, + Refresh.DepthStencilTargetDescription depthStencilTargetDescription, + params Refresh.ColorTargetDescription[] colorTargetDescriptions + ) : base(device) + { + Refresh.DepthStencilTargetDescription* depthStencilPtr = &depthStencilTargetDescription; + + fixed (Refresh.ColorTargetDescription* colorPtr = colorTargetDescriptions) + { + Refresh.RenderPassCreateInfo renderPassCreateInfo; + renderPassCreateInfo.colorTargetCount = (uint)colorTargetDescriptions.Length; + renderPassCreateInfo.colorTargetDescriptions = (IntPtr)colorPtr; + renderPassCreateInfo.depthStencilTargetDescription = (IntPtr) depthStencilPtr; + + Handle = Refresh.Refresh_CreateRenderPass(device.Handle, ref renderPassCreateInfo); + } + } + } +} diff --git a/src/Graphics/Sampler.cs b/src/Graphics/Sampler.cs new file mode 100644 index 0000000..84277c4 --- /dev/null +++ b/src/Graphics/Sampler.cs @@ -0,0 +1,23 @@ +using System; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class Sampler : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroySampler; + + public Sampler( + GraphicsDevice device, + ref SamplerState samplerState + ) : base(device) + { + var refreshSamplerStateCreateInfo = samplerState.ToRefreshSamplerStateCreateInfo(); + + Handle = Refresh.Refresh_CreateSampler( + device.Handle, + ref refreshSamplerStateCreateInfo + ); + } + } +} diff --git a/src/Graphics/ShaderModule.cs b/src/Graphics/ShaderModule.cs new file mode 100644 index 0000000..2892bd0 --- /dev/null +++ b/src/Graphics/ShaderModule.cs @@ -0,0 +1,23 @@ +using RefreshCS; +using System; +using System.IO; + +namespace MoonWorks.Graphics +{ + public class ShaderModule : GraphicsResource + { + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyShaderModule; + + public unsafe ShaderModule(GraphicsDevice device, FileInfo fileInfo) : base(device) + { + fixed (uint* ptr = Bytecode.ReadBytecode(fileInfo)) + { + Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo; + shaderModuleCreateInfo.codeSize = (UIntPtr) fileInfo.Length; + shaderModuleCreateInfo.byteCode = (IntPtr) ptr; + + Handle = Refresh.Refresh_CreateShaderModule(device.Handle, ref shaderModuleCreateInfo); + } + } + } +} diff --git a/src/Graphics/State/ColorBlendState.cs b/src/Graphics/State/ColorBlendState.cs new file mode 100644 index 0000000..c0b54b1 --- /dev/null +++ b/src/Graphics/State/ColorBlendState.cs @@ -0,0 +1,12 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public unsafe struct ColorBlendState + { + public bool LogicOpEnable; + public Refresh.LogicOp LogicOp; + public BlendConstants BlendConstants; + public ColorTargetBlendState[] ColorTargetBlendStates; + } +} diff --git a/src/Graphics/State/ColorTargetBlendState.cs b/src/Graphics/State/ColorTargetBlendState.cs new file mode 100644 index 0000000..6506c1c --- /dev/null +++ b/src/Graphics/State/ColorTargetBlendState.cs @@ -0,0 +1,85 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct ColorTargetBlendState + { + public bool BlendEnable; + public Refresh.BlendOp AlphaBlendOp; + public Refresh.BlendOp ColorBlendOp; + public Refresh.ColorComponentFlags ColorWriteMask; + public Refresh.BlendFactor DestinationAlphaBlendFactor; + public Refresh.BlendFactor DestinationColorBlendFactor; + public Refresh.BlendFactor SourceAlphaBlendFactor; + public Refresh.BlendFactor SourceColorBlendFactor; + + public static readonly ColorTargetBlendState Additive = new ColorTargetBlendState + { + BlendEnable = true, + AlphaBlendOp = Refresh.BlendOp.Add, + ColorBlendOp = Refresh.BlendOp.Add, + ColorWriteMask = Refresh.ColorComponentFlags.RGBA, + SourceColorBlendFactor = Refresh.BlendFactor.SourceAlpha, + SourceAlphaBlendFactor = Refresh.BlendFactor.SourceAlpha, + DestinationColorBlendFactor = Refresh.BlendFactor.One, + DestinationAlphaBlendFactor = Refresh.BlendFactor.One + }; + + public static readonly ColorTargetBlendState AlphaBlend = new ColorTargetBlendState + { + BlendEnable = true, + AlphaBlendOp = Refresh.BlendOp.Add, + ColorBlendOp = Refresh.BlendOp.Add, + ColorWriteMask = Refresh.ColorComponentFlags.RGBA, + SourceColorBlendFactor = Refresh.BlendFactor.One, + SourceAlphaBlendFactor = Refresh.BlendFactor.One, + DestinationColorBlendFactor = Refresh.BlendFactor.OneMinusSourceAlpha, + DestinationAlphaBlendFactor = Refresh.BlendFactor.OneMinusSourceAlpha + }; + + public static readonly ColorTargetBlendState NonPremultiplied = new ColorTargetBlendState + { + BlendEnable = true, + AlphaBlendOp = Refresh.BlendOp.Add, + ColorBlendOp = Refresh.BlendOp.Add, + ColorWriteMask = Refresh.ColorComponentFlags.RGBA, + SourceColorBlendFactor = Refresh.BlendFactor.SourceAlpha, + SourceAlphaBlendFactor = Refresh.BlendFactor.SourceAlpha, + DestinationColorBlendFactor = Refresh.BlendFactor.OneMinusSourceAlpha, + DestinationAlphaBlendFactor = Refresh.BlendFactor.OneMinusSourceAlpha + }; + + public static readonly ColorTargetBlendState Opaque = new ColorTargetBlendState + { + BlendEnable = true, + AlphaBlendOp = Refresh.BlendOp.Add, + ColorBlendOp = Refresh.BlendOp.Add, + ColorWriteMask = Refresh.ColorComponentFlags.RGBA, + SourceColorBlendFactor = Refresh.BlendFactor.One, + SourceAlphaBlendFactor = Refresh.BlendFactor.One, + DestinationColorBlendFactor = Refresh.BlendFactor.Zero, + DestinationAlphaBlendFactor = Refresh.BlendFactor.Zero + }; + + public static readonly ColorTargetBlendState None = new ColorTargetBlendState + { + BlendEnable = false, + ColorWriteMask = Refresh.ColorComponentFlags.RGBA + }; + + public Refresh.ColorTargetBlendState ToRefreshColorTargetBlendState() + { + return new Refresh.ColorTargetBlendState + { + blendEnable = Conversions.BoolToByte(BlendEnable), + alphaBlendOp = AlphaBlendOp, + colorBlendOp = ColorBlendOp, + colorWriteMask = ColorWriteMask, + destinationAlphaBlendFactor = DestinationAlphaBlendFactor, + destinationColorBlendFactor = DestinationColorBlendFactor, + sourceAlphaBlendFactor = SourceAlphaBlendFactor, + sourceColorBlendFactor = SourceColorBlendFactor + }; + } + } +} diff --git a/src/Graphics/State/DepthStencilState.cs b/src/Graphics/State/DepthStencilState.cs new file mode 100644 index 0000000..a1e2790 --- /dev/null +++ b/src/Graphics/State/DepthStencilState.cs @@ -0,0 +1,43 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct DepthStencilState + { + public bool DepthTestEnable; + public Refresh.StencilOpState BackStencilState; + public Refresh.StencilOpState FrontStencilState; + public Refresh.CompareOp CompareOp; + public bool DepthBoundsTestEnable; + public bool DepthWriteEnable; + public float MinDepthBounds; + public float MaxDepthBounds; + public bool StencilTestEnable; + + public static readonly DepthStencilState DepthReadWrite = new DepthStencilState + { + DepthTestEnable = true, + DepthWriteEnable = true, + DepthBoundsTestEnable = false, + StencilTestEnable = false, + CompareOp = Refresh.CompareOp.LessOrEqual + }; + + public static readonly DepthStencilState DepthRead = new DepthStencilState + { + DepthTestEnable = true, + DepthWriteEnable = false, + DepthBoundsTestEnable = false, + StencilTestEnable = false, + CompareOp = Refresh.CompareOp.LessOrEqual + }; + + public static readonly DepthStencilState Disable = new DepthStencilState + { + DepthTestEnable = false, + DepthWriteEnable = false, + DepthBoundsTestEnable = false, + StencilTestEnable = false + }; + } +} diff --git a/src/Graphics/State/GraphicsPipelineLayoutCreateInfo.cs b/src/Graphics/State/GraphicsPipelineLayoutCreateInfo.cs new file mode 100644 index 0000000..4ff9364 --- /dev/null +++ b/src/Graphics/State/GraphicsPipelineLayoutCreateInfo.cs @@ -0,0 +1,8 @@ +namespace MoonWorks.Graphics +{ + public struct GraphicsPipelineLayoutCreateInfo + { + public uint VertexSamplerBindingCount; + public uint FragmentSamplerBindingCount; + } +} diff --git a/src/Graphics/State/MultisampleState.cs b/src/Graphics/State/MultisampleState.cs new file mode 100644 index 0000000..10f29f2 --- /dev/null +++ b/src/Graphics/State/MultisampleState.cs @@ -0,0 +1,16 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct MultisampleState + { + public Refresh.SampleCount MultisampleCount; + public uint SampleMask; + + public static readonly MultisampleState None = new MultisampleState + { + MultisampleCount = Refresh.SampleCount.One, + SampleMask = uint.MaxValue + }; + } +} diff --git a/src/Graphics/State/RasterizerState.cs b/src/Graphics/State/RasterizerState.cs new file mode 100644 index 0000000..ca78d36 --- /dev/null +++ b/src/Graphics/State/RasterizerState.cs @@ -0,0 +1,53 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct RasterizerState + { + public Refresh.CullMode CullMode; + public float DepthBiasClamp; + public float DepthBiasConstantFactor; + public bool DepthBiasEnable; + public float DepthBiasSlopeFactor; + public bool DepthClampEnable; + public Refresh.FillMode FillMode; + public Refresh.FrontFace FrontFace; + public float LineWidth; + + public static readonly RasterizerState CullClockwise = new RasterizerState + { + CullMode = Refresh.CullMode.Front, + FrontFace = Refresh.FrontFace.Clockwise, + FillMode = Refresh.FillMode.Fill, + DepthBiasEnable = false, + LineWidth = 1f + }; + + public static readonly RasterizerState CullCounterClockwise = new RasterizerState + { + CullMode = Refresh.CullMode.Back, + FrontFace = Refresh.FrontFace.Clockwise, + FillMode = Refresh.FillMode.Fill, + DepthBiasEnable = false, + LineWidth = 1f + }; + + public static readonly RasterizerState CullNone = new RasterizerState + { + CullMode = Refresh.CullMode.None, + FrontFace = Refresh.FrontFace.Clockwise, + FillMode = Refresh.FillMode.Fill, + DepthBiasEnable = false, + LineWidth = 1f + }; + + public static readonly RasterizerState Wireframe = new RasterizerState + { + CullMode = Refresh.CullMode.None, + FrontFace = Refresh.FrontFace.Clockwise, + FillMode = Refresh.FillMode.Fill, + DepthBiasEnable = false, + LineWidth = 1f + }; + } +} diff --git a/src/Graphics/State/SamplerState.cs b/src/Graphics/State/SamplerState.cs new file mode 100644 index 0000000..b6c13e4 --- /dev/null +++ b/src/Graphics/State/SamplerState.cs @@ -0,0 +1,135 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct SamplerState + { + public Refresh.Filter MinFilter; + public Refresh.Filter MagFilter; + public Refresh.SamplerMipmapMode MipmapMode; + public Refresh.SamplerAddressMode AddressModeU; + public Refresh.SamplerAddressMode AddressModeV; + public Refresh.SamplerAddressMode AddressModeW; + public float MipLodBias; + public bool AnisotropyEnable; + public float MaxAnisotropy; + public bool CompareEnable; + public Refresh.CompareOp CompareOp; + public float MinLod; + public float MaxLod; + public Refresh.BorderColor BorderColor; + + public static readonly SamplerState AnisotropicClamp = new SamplerState + { + MinFilter = Refresh.Filter.Linear, + MagFilter = Refresh.Filter.Linear, + MipmapMode = Refresh.SamplerMipmapMode.Linear, + AddressModeU = Refresh.SamplerAddressMode.ClampToEdge, + AddressModeV = Refresh.SamplerAddressMode.ClampToEdge, + AddressModeW = Refresh.SamplerAddressMode.ClampToEdge, + CompareEnable = false, + AnisotropyEnable = true, + MaxAnisotropy = 4, + MipLodBias = 0f, + MinLod = 0, + MaxLod = 1000 /* VK_LOD_CLAMP_NONE */ + }; + + public static readonly SamplerState AnisotropicWrap = new SamplerState + { + MinFilter = Refresh.Filter.Linear, + MagFilter = Refresh.Filter.Linear, + MipmapMode = Refresh.SamplerMipmapMode.Linear, + AddressModeU = Refresh.SamplerAddressMode.Repeat, + AddressModeV = Refresh.SamplerAddressMode.Repeat, + AddressModeW = Refresh.SamplerAddressMode.Repeat, + CompareEnable = false, + AnisotropyEnable = true, + MaxAnisotropy = 4, + MipLodBias = 0f, + MinLod = 0, + MaxLod = 1000 /* VK_LOD_CLAMP_NONE */ + }; + + public static readonly SamplerState LinearClamp = new SamplerState + { + MinFilter = Refresh.Filter.Linear, + MagFilter = Refresh.Filter.Linear, + MipmapMode = Refresh.SamplerMipmapMode.Linear, + AddressModeU = Refresh.SamplerAddressMode.ClampToEdge, + AddressModeV = Refresh.SamplerAddressMode.ClampToEdge, + AddressModeW = Refresh.SamplerAddressMode.ClampToEdge, + CompareEnable = false, + AnisotropyEnable = false, + MipLodBias = 0f, + MinLod = 0, + MaxLod = 1000 + }; + + public static readonly SamplerState LinearWrap = new SamplerState + { + MinFilter = Refresh.Filter.Linear, + MagFilter = Refresh.Filter.Linear, + MipmapMode = Refresh.SamplerMipmapMode.Linear, + AddressModeU = Refresh.SamplerAddressMode.Repeat, + AddressModeV = Refresh.SamplerAddressMode.Repeat, + AddressModeW = Refresh.SamplerAddressMode.Repeat, + CompareEnable = false, + AnisotropyEnable = false, + MipLodBias = 0f, + MinLod = 0, + MaxLod = 1000 + }; + + public static readonly SamplerState PointClamp = new SamplerState + { + MinFilter = Refresh.Filter.Nearest, + MagFilter = Refresh.Filter.Nearest, + MipmapMode = Refresh.SamplerMipmapMode.Nearest, + AddressModeU = Refresh.SamplerAddressMode.ClampToEdge, + AddressModeV = Refresh.SamplerAddressMode.ClampToEdge, + AddressModeW = Refresh.SamplerAddressMode.ClampToEdge, + CompareEnable = false, + AnisotropyEnable = false, + MipLodBias = 0f, + MinLod = 0, + MaxLod = 1000 + }; + + public static readonly SamplerState PointWrap = new SamplerState + { + MinFilter = Refresh.Filter.Nearest, + MagFilter = Refresh.Filter.Nearest, + MipmapMode = Refresh.SamplerMipmapMode.Nearest, + AddressModeU = Refresh.SamplerAddressMode.Repeat, + AddressModeV = Refresh.SamplerAddressMode.Repeat, + AddressModeW = Refresh.SamplerAddressMode.Repeat, + CompareEnable = false, + AnisotropyEnable = false, + MipLodBias = 0f, + MinLod = 0, + MaxLod = 1000 + }; + + public Refresh.SamplerStateCreateInfo ToRefreshSamplerStateCreateInfo() + { + return new Refresh.SamplerStateCreateInfo + { + minFilter = MinFilter, + magFilter = MagFilter, + mipmapMode = MipmapMode, + addressModeU = AddressModeU, + addressModeV = AddressModeV, + addressModeW = AddressModeW, + mipLodBias = MipLodBias, + anisotropyEnable = Conversions.BoolToByte(AnisotropyEnable), + maxAnisotropy = MaxAnisotropy, + compareEnable = Conversions.BoolToByte(CompareEnable), + compareOp = CompareOp, + minLod = MinLod, + maxLod = MaxLod, + borderColor = BorderColor + }; + } + } +} diff --git a/src/Graphics/State/ShaderStageState.cs b/src/Graphics/State/ShaderStageState.cs new file mode 100644 index 0000000..133b4a6 --- /dev/null +++ b/src/Graphics/State/ShaderStageState.cs @@ -0,0 +1,9 @@ +namespace MoonWorks.Graphics +{ + public struct ShaderStageState + { + public ShaderModule ShaderModule; + public string EntryPointName; + public uint UniformBufferSize; + } +} diff --git a/src/Graphics/State/TextureCreateInfo.cs b/src/Graphics/State/TextureCreateInfo.cs new file mode 100644 index 0000000..4e8512a --- /dev/null +++ b/src/Graphics/State/TextureCreateInfo.cs @@ -0,0 +1,31 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct TextureCreateInfo + { + public uint Width; + public uint Height; + public uint Depth; + public bool IsCube; + public Refresh.SampleCount SampleCount; + public uint LevelCount; + public Refresh.ColorFormat Format; + public Refresh.TextureUsageFlags UsageFlags; + + public Refresh.TextureCreateInfo ToRefreshTextureCreateInfo() + { + return new Refresh.TextureCreateInfo + { + width = Width, + height = Height, + depth = Depth, + isCube = Conversions.BoolToByte(IsCube), + sampleCount = SampleCount, + levelCount = LevelCount, + format = Format, + usageFlags = UsageFlags + }; + } + } +} diff --git a/src/Graphics/State/VertexInputState.cs b/src/Graphics/State/VertexInputState.cs new file mode 100644 index 0000000..070a3bc --- /dev/null +++ b/src/Graphics/State/VertexInputState.cs @@ -0,0 +1,10 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct VertexInputState + { + public Refresh.VertexBinding[] VertexBindings; + public Refresh.VertexAttribute[] VertexAttributes; + } +} diff --git a/src/Graphics/State/ViewportState.cs b/src/Graphics/State/ViewportState.cs new file mode 100644 index 0000000..8ba36fb --- /dev/null +++ b/src/Graphics/State/ViewportState.cs @@ -0,0 +1,10 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct ViewportState + { + public Refresh.Viewport[] Viewports; + public Refresh.Rect[] Scissors; + } +} diff --git a/src/Graphics/Texture.cs b/src/Graphics/Texture.cs new file mode 100644 index 0000000..573a84c --- /dev/null +++ b/src/Graphics/Texture.cs @@ -0,0 +1,171 @@ +using System; +using System.IO; +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public class Texture : GraphicsResource + { + public uint Width { get; } + public uint Height { get; } + public Refresh.ColorFormat Format { get; } + + protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture; + + public static Texture LoadPNG(GraphicsDevice device, FileInfo fileInfo) + { + var pixels = Refresh.Refresh_Image_Load( + fileInfo.FullName, + out var width, + out var height, + out var channels + ); + + MoonWorks.Graphics.TextureCreateInfo textureCreateInfo; + textureCreateInfo.Width = (uint)width; + textureCreateInfo.Height = (uint)height; + textureCreateInfo.Depth = 1; + textureCreateInfo.Format = Refresh.ColorFormat.R8G8B8A8; + textureCreateInfo.IsCube = false; + textureCreateInfo.LevelCount = 1; + textureCreateInfo.SampleCount = Refresh.SampleCount.One; + textureCreateInfo.UsageFlags = Refresh.TextureUsageFlags.SamplerBit; + + var texture = new Texture(device, ref textureCreateInfo); + + texture.SetData(pixels, (uint)(width * height * 4)); + + Refresh.Refresh_Image_Free(pixels); + return texture; + } + + public unsafe static void SavePNG(string path, int width, int height, byte[] pixels) + { + fixed (byte* ptr = &pixels[0]) + { + Refresh.Refresh_Image_SavePNG(path, width, height, (IntPtr) ptr); + } + } + + public static Texture CreateTexture2D( + GraphicsDevice device, + uint width, + uint height, + Refresh.ColorFormat format, + Refresh.TextureUsageFlags usageFlags, + Refresh.SampleCount sampleCount = Refresh.SampleCount.One, + uint levelCount = 1 + ) + { + var textureCreateInfo = new MoonWorks.Graphics.TextureCreateInfo + { + Width = width, + Height = height, + Depth = 1, + IsCube = false, + SampleCount = sampleCount, + LevelCount = levelCount, + Format = format, + UsageFlags = usageFlags + }; + + return new Texture(device, ref textureCreateInfo); + } + + public static Texture CreateTexture3D( + GraphicsDevice device, + uint width, + uint height, + uint depth, + Refresh.ColorFormat format, + Refresh.TextureUsageFlags usageFlags, + Refresh.SampleCount sampleCount = Refresh.SampleCount.One, + uint levelCount = 1 + ) + { + var textureCreateInfo = new MoonWorks.Graphics.TextureCreateInfo + { + Width = width, + Height = height, + Depth = depth, + IsCube = false, + SampleCount = sampleCount, + LevelCount = levelCount, + Format = format, + UsageFlags = usageFlags + }; + + return new Texture(device, ref textureCreateInfo); + } + + public static Texture CreateTextureCube( + GraphicsDevice device, + uint size, + Refresh.ColorFormat format, + Refresh.TextureUsageFlags usageFlags, + Refresh.SampleCount sampleCount = Refresh.SampleCount.One, + uint levelCount = 1 + ) + { + var textureCreateInfo = new MoonWorks.Graphics.TextureCreateInfo + { + Width = size, + Height = size, + Depth = 1, + IsCube = true, + SampleCount = sampleCount, + LevelCount = levelCount, + Format = format, + UsageFlags = usageFlags + }; + + return new Texture(device, ref textureCreateInfo); + } + + public Texture(GraphicsDevice device, ref MoonWorks.Graphics.TextureCreateInfo textureCreateInfo) : base(device) + { + var refreshTextureCreateInfo = textureCreateInfo.ToRefreshTextureCreateInfo(); + + Handle = Refresh.Refresh_CreateTexture( + device.Handle, + ref refreshTextureCreateInfo + ); + + Format = textureCreateInfo.Format; + Width = textureCreateInfo.Width; + Height = textureCreateInfo.Height; + } + + public void SetData(IntPtr data, uint dataLengthInBytes) + { + Refresh.TextureSlice textureSlice; + textureSlice.texture = Handle; + textureSlice.rectangle.x = 0; + textureSlice.rectangle.y = 0; + textureSlice.rectangle.w = (int)Width; + textureSlice.rectangle.h = (int)Height; + textureSlice.level = 0; + textureSlice.layer = 0; + textureSlice.depth = 0; + + Refresh.Refresh_SetTextureData( + Device.Handle, + ref textureSlice, + data, + dataLengthInBytes + ); + } + + public void SetData(ref TextureSlice textureSlice, IntPtr data, uint dataLengthInBytes) + { + var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); + + Refresh.Refresh_SetTextureData( + Device.Handle, + ref refreshTextureSlice, + data, + dataLengthInBytes + ); + } + } +} diff --git a/src/Graphics/TextureSamplerBinding.cs b/src/Graphics/TextureSamplerBinding.cs new file mode 100644 index 0000000..cad832f --- /dev/null +++ b/src/Graphics/TextureSamplerBinding.cs @@ -0,0 +1,14 @@ +namespace MoonWorks.Graphics +{ + public struct TextureSamplerBinding + { + public Texture Texture; + public Sampler Sampler; + + public TextureSamplerBinding(Texture texture, Sampler sampler) + { + Texture = texture; + Sampler = sampler; + } + } +} diff --git a/src/Graphics/TextureSlice.cs b/src/Graphics/TextureSlice.cs new file mode 100644 index 0000000..9b0fb7b --- /dev/null +++ b/src/Graphics/TextureSlice.cs @@ -0,0 +1,51 @@ +using RefreshCS; + +namespace MoonWorks.Graphics +{ + public struct TextureSlice + { + public Texture Texture { get; } + public Refresh.Rect Rectangle { get; } + public uint Depth { get; } + public uint Layer { get; } + public uint Level { get; } + + public TextureSlice(Texture texture) + { + Texture = texture; + Rectangle = new Refresh.Rect + { + x = 0, + y = 0, + w = (int) texture.Width, + h = (int) texture.Height + }; + Depth = 0; + Layer = 0; + Level = 0; + } + + public TextureSlice(Texture texture, Refresh.Rect rectangle, uint depth = 0, uint layer = 0, uint level = 0) + { + Texture = texture; + Rectangle = rectangle; + Depth = depth; + Layer = layer; + Level = level; + } + + public RefreshCS.Refresh.TextureSlice ToRefreshTextureSlice() + { + RefreshCS.Refresh.TextureSlice textureSlice = new RefreshCS.Refresh.TextureSlice + { + texture = Texture.Handle, + rectangle = Rectangle, + depth = Depth, + layer = Layer, + level = Level + }; + + return textureSlice; + } + } +} diff --git a/src/Window.cs b/src/Window.cs index 7e72f5a..cc23be1 100644 --- a/src/Window.cs +++ b/src/Window.cs @@ -24,7 +24,7 @@ namespace MoonWorks ScreenMode = windowCreateInfo.ScreenMode; Handle = SDL.SDL_CreateWindow( - "CampariTest", + "MoonWorks.GraphicsTest", SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, (int)windowCreateInfo.WindowWidth,