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,