diff --git a/lib/RefreshCS b/lib/RefreshCS index 3379b95..2e3871a 160000 --- a/lib/RefreshCS +++ b/lib/RefreshCS @@ -1 +1 @@ -Subproject commit 3379b95e449904b30d66d8bc17e4da83fc60c1be +Subproject commit 2e3871a03d99e413f84748e40bdb6c03df2ecd1c diff --git a/src/Buffer.cs b/src/Buffer.cs index 7248001..e48ec19 100644 --- a/src/Buffer.cs +++ b/src/Buffer.cs @@ -7,8 +7,11 @@ namespace Campari { protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer; - /* FIXME: could we have a special flags struct? */ - public Buffer(RefreshDevice device, uint usageFlags, uint sizeInBytes) : base(device) + public Buffer( + RefreshDevice device, + Refresh.BufferUsageFlags usageFlags, + uint sizeInBytes + ) : base(device) { Handle = Refresh.Refresh_CreateBuffer( device.Handle, diff --git a/src/GraphicsPipeline.cs b/src/GraphicsPipeline.cs index 169ce67..e0ccee4 100644 --- a/src/GraphicsPipeline.cs +++ b/src/GraphicsPipeline.cs @@ -23,17 +23,23 @@ namespace Campari RenderPass renderPass ) : base(device) { - var blendStateHandle = GCHandle.Alloc(colorBlendState.ColorTargetBlendStates, GCHandleType.Pinned); 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 = blendStateHandle.AddrOfPinnedObject(); + 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; @@ -89,7 +95,6 @@ namespace Campari Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, ref graphicsPipelineCreateInfo); - blendStateHandle.Free(); vertexAttributesHandle.Free(); vertexBindingsHandle.Free(); viewportHandle.Free(); diff --git a/src/Sampler.cs b/src/Sampler.cs index 6a448ed..12f6e53 100644 --- a/src/Sampler.cs +++ b/src/Sampler.cs @@ -9,10 +9,15 @@ namespace Campari public Sampler( RefreshDevice device, - ref Refresh.SamplerStateCreateInfo samplerStateCreateInfo + ref SamplerState samplerState ) : base(device) { - Handle = Refresh.Refresh_CreateSampler(device.Handle, ref samplerStateCreateInfo); + var refreshSamplerStateCreateInfo = samplerState.ToRefreshSamplerStateCreateInfo(); + + Handle = Refresh.Refresh_CreateSampler( + device.Handle, + ref refreshSamplerStateCreateInfo + ); } } } diff --git a/src/State/ColorBlendState.cs b/src/State/ColorBlendState.cs index ee460b8..90750a9 100644 --- a/src/State/ColorBlendState.cs +++ b/src/State/ColorBlendState.cs @@ -7,6 +7,6 @@ namespace Campari public bool LogicOpEnable; public Refresh.LogicOp LogicOp; public BlendConstants BlendConstants; - public Refresh.ColorTargetBlendState[] ColorTargetBlendStates; + public ColorTargetBlendState[] ColorTargetBlendStates; } } diff --git a/src/State/ColorTargetBlendState.cs b/src/State/ColorTargetBlendState.cs new file mode 100644 index 0000000..22b193b --- /dev/null +++ b/src/State/ColorTargetBlendState.cs @@ -0,0 +1,85 @@ +using RefreshCS; + +namespace Campari +{ + 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/State/DepthStencilState.cs b/src/State/DepthStencilState.cs index ac83b75..ce8b856 100644 --- a/src/State/DepthStencilState.cs +++ b/src/State/DepthStencilState.cs @@ -13,5 +13,31 @@ namespace Campari 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/State/MultisampleState.cs b/src/State/MultisampleState.cs index 9fafeb4..491f6b5 100644 --- a/src/State/MultisampleState.cs +++ b/src/State/MultisampleState.cs @@ -6,5 +6,11 @@ namespace Campari { 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/State/RasterizerState.cs b/src/State/RasterizerState.cs index 9bca5bf..2b6d125 100644 --- a/src/State/RasterizerState.cs +++ b/src/State/RasterizerState.cs @@ -13,5 +13,41 @@ namespace Campari 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/State/SamplerState.cs b/src/State/SamplerState.cs new file mode 100644 index 0000000..69e4449 --- /dev/null +++ b/src/State/SamplerState.cs @@ -0,0 +1,135 @@ +using RefreshCS; + +namespace Campari +{ + 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/State/TextureCreateInfo.cs b/src/State/TextureCreateInfo.cs new file mode 100644 index 0000000..9a32d9f --- /dev/null +++ b/src/State/TextureCreateInfo.cs @@ -0,0 +1,31 @@ +using RefreshCS; + +namespace Campari +{ + 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/Texture.cs b/src/Texture.cs index 140641f..62d36cd 100644 --- a/src/Texture.cs +++ b/src/Texture.cs @@ -21,34 +21,111 @@ namespace Campari out var channels ); - Refresh.TextureCreateInfo textureCreateInfo; - textureCreateInfo.width = (uint) width; - textureCreateInfo.height = (uint) height; - textureCreateInfo.depth = 1; - textureCreateInfo.format = Refresh.ColorFormat.R8G8B8A8; - textureCreateInfo.isCube = 0; - textureCreateInfo.levelCount = 1; - textureCreateInfo.sampleCount = Refresh.SampleCount.One; - textureCreateInfo.usageFlags = (uint) Refresh.TextureUsageFlagBits.SamplerBit; + Campari.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)); + texture.SetData(pixels, (uint)(width * height * 4)); Refresh.Refresh_Image_Free(pixels); return texture; } - public Texture(RefreshDevice device, ref Refresh.TextureCreateInfo textureCreateInfo) : base(device) + public static Texture CreateTexture2D( + RefreshDevice device, + uint width, + uint height, + Refresh.ColorFormat format, + Refresh.TextureUsageFlags usageFlags, + Refresh.SampleCount sampleCount = Refresh.SampleCount.One, + uint levelCount = 1 + ) { + var textureCreateInfo = new Campari.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( + RefreshDevice 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 Campari.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( + RefreshDevice device, + uint size, + Refresh.ColorFormat format, + Refresh.TextureUsageFlags usageFlags, + Refresh.SampleCount sampleCount = Refresh.SampleCount.One, + uint levelCount = 1 + ) + { + var textureCreateInfo = new Campari.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(RefreshDevice device, ref Campari.TextureCreateInfo textureCreateInfo) : base(device) + { + var refreshTextureCreateInfo = textureCreateInfo.ToRefreshTextureCreateInfo(); + Handle = Refresh.Refresh_CreateTexture( device.Handle, - ref textureCreateInfo + ref refreshTextureCreateInfo ); - Format = textureCreateInfo.format; - Width = textureCreateInfo.width; - Height = textureCreateInfo.height; + Format = textureCreateInfo.Format; + Width = textureCreateInfo.Width; + Height = textureCreateInfo.Height; } public void SetData(IntPtr data, uint dataLengthInBytes) @@ -57,8 +134,8 @@ namespace Campari textureSlice.texture = Handle; textureSlice.rectangle.x = 0; textureSlice.rectangle.y = 0; - textureSlice.rectangle.w = (int) Width; - textureSlice.rectangle.h = (int) Height; + textureSlice.rectangle.w = (int)Width; + textureSlice.rectangle.h = (int)Height; textureSlice.level = 0; textureSlice.layer = 0; textureSlice.depth = 0; @@ -70,5 +147,17 @@ namespace Campari dataLengthInBytes ); } + + public void SetData(ref TextureSlice textureSlice, IntPtr data, uint dataLengthInBytes) + { + var refreshTextureSlice = textureSlice.ToRefreshTextureSlice(); + + Refresh.Refresh_SetTextureData( + Device.Handle, + ref refreshTextureSlice, + data, + dataLengthInBytes + ); + } } }