diff --git a/MoonWorks.Test.Common/Content/Shaders/Compiled/InstancedSpriteBatch.vert.refresh b/MoonWorks.Test.Common/Content/Shaders/Compiled/InstancedSpriteBatch.vert.refresh new file mode 100644 index 0000000..6e5a784 Binary files /dev/null and b/MoonWorks.Test.Common/Content/Shaders/Compiled/InstancedSpriteBatch.vert.refresh differ diff --git a/MoonWorks.Test.Common/Content/Shaders/Source/InstancedSpriteBatch.vert b/MoonWorks.Test.Common/Content/Shaders/Source/InstancedSpriteBatch.vert new file mode 100644 index 0000000..43031a4 --- /dev/null +++ b/MoonWorks.Test.Common/Content/Shaders/Source/InstancedSpriteBatch.vert @@ -0,0 +1,45 @@ +#version 450 + +layout (location = 0) in vec3 Position; +layout (location = 1) in vec3 Translation; +layout (location = 2) in float Rotation; +layout (location = 3) in vec2 Scale; +layout (location = 4) in vec4 Color; +layout (location = 5) in vec2[4] UV; + +layout (location = 0) out vec2 outTexCoord; +layout (location = 1) out vec4 outVertexColor; + +layout (binding = 0, set = 2) uniform UniformBlock +{ + mat4x4 View; + mat4x4 Projection; +}; + +void main() +{ + mat4 Scale = mat4( + Scale.x, 0, 0, 0, + 0, Scale.y, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + float c = cos(Rotation); + float s = sin(Rotation); + mat4 Rotation = mat4( + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + mat4 Translation = mat4( + 1, 0, 0, Position.x, + 0, 1, 0, Position.y, + 0, 0, 1, Position.z, + 0, 0, 0, 1 + ); + mat4 Model = Scale * Rotation * Translation; + gl_Position = Model * View * Projection * vec4(Position, 1); + outTexCoord = UV[gl_VertexIndex % 4]; + outVertexColor = Color; +} diff --git a/MoonWorksGraphicsTests.sln b/MoonWorksGraphicsTests.sln index 2773e38..d34d756 100644 --- a/MoonWorksGraphicsTests.sln +++ b/MoonWorksGraphicsTests.sln @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowResizing", "WindowRes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StoreLoad", "StoreLoad\StoreLoad.csproj", "{CD31F1B5-C76A-428A-A812-8DFD6CAB20A9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpriteBatch", "SpriteBatch\SpriteBatch.csproj", "{40E25B99-1196-4695-99A6-C0A8EF385539}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -183,6 +185,10 @@ Global {CD31F1B5-C76A-428A-A812-8DFD6CAB20A9}.Debug|x64.Build.0 = Debug|x64 {CD31F1B5-C76A-428A-A812-8DFD6CAB20A9}.Release|x64.ActiveCfg = Release|x64 {CD31F1B5-C76A-428A-A812-8DFD6CAB20A9}.Release|x64.Build.0 = Release|x64 + {40E25B99-1196-4695-99A6-C0A8EF385539}.Debug|x64.ActiveCfg = Debug|Any CPU + {40E25B99-1196-4695-99A6-C0A8EF385539}.Debug|x64.Build.0 = Debug|Any CPU + {40E25B99-1196-4695-99A6-C0A8EF385539}.Release|x64.ActiveCfg = Release|Any CPU + {40E25B99-1196-4695-99A6-C0A8EF385539}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SpriteBatch/SpriteBatch.csproj b/SpriteBatch/SpriteBatch.csproj new file mode 100644 index 0000000..af5328d --- /dev/null +++ b/SpriteBatch/SpriteBatch.csproj @@ -0,0 +1,17 @@ + + + + + + + + + Exe + net8.0 + enable + true + + + + + diff --git a/SpriteBatch/SpriteBatchGame.cs b/SpriteBatch/SpriteBatchGame.cs new file mode 100644 index 0000000..e445f25 --- /dev/null +++ b/SpriteBatch/SpriteBatchGame.cs @@ -0,0 +1,152 @@ +using System; +using MoonWorks.Graphics; +using MoonWorks.Math.Float; + +namespace MoonWorks.Test +{ + class SpriteBatchGame : Game + { + GraphicsPipeline spriteBatchPipeline; + Graphics.Buffer quadVertexBuffer; + Graphics.Buffer quadIndexBuffer; + SpriteBatch SpriteBatch; + + public unsafe SpriteBatchGame() : base(TestUtils.GetStandardWindowCreateInfo(), TestUtils.GetStandardFrameLimiterSettings(), 60, true) + { + ShaderModule vertShaderModule = new ShaderModule(GraphicsDevice, TestUtils.GetShaderPath("InstancedSpriteBatch.vert")); + ShaderModule fragShaderModule = new ShaderModule(GraphicsDevice, TestUtils.GetShaderPath("InstancedSpriteBatch.frag")); + + var vertexBufferDescription = VertexBindingAndAttributes.Create(0); + var instanceBufferDescription = VertexBindingAndAttributes.Create(1, VertexInputRate.Instance); + + GraphicsPipelineCreateInfo pipelineCreateInfo = TestUtils.GetStandardGraphicsPipelineCreateInfo( + MainWindow.SwapchainFormat, + vertShaderModule, + fragShaderModule + ); + + pipelineCreateInfo.VertexInputState = new VertexInputState([ + vertexBufferDescription, + instanceBufferDescription + ]); + + spriteBatchPipeline = new GraphicsPipeline(GraphicsDevice, pipelineCreateInfo); + + quadVertexBuffer = Graphics.Buffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, 4); + quadIndexBuffer = Graphics.Buffer.Create(GraphicsDevice, BufferUsageFlags.Index, 6); + + var vertices = stackalloc PositionVertex[4]; + vertices[0].Position = new Math.Float.Vector3(0, 0, 0); + vertices[1].Position = new Math.Float.Vector3(1, 0, 0); + vertices[2].Position = new Math.Float.Vector3(0, 1, 0); + vertices[3].Position = new Math.Float.Vector3(1, 1, 0); + + var indices = stackalloc ushort[6] + { + 0, 1, 2, + 2, 1, 3 + }; + + var cmdbuf = GraphicsDevice.AcquireCommandBuffer(); + cmdbuf.SetBufferData(quadVertexBuffer, new Span(vertices, 4)); + cmdbuf.SetBufferData(quadIndexBuffer, new Span(indices, 6)); + GraphicsDevice.Submit(cmdbuf); + + SpriteBatch = new SpriteBatch(GraphicsDevice); + } + + protected override void Update(TimeSpan delta) + { + + } + + protected override void Draw(double alpha) + { + CommandBuffer cmdbuf = GraphicsDevice.AcquireCommandBuffer(); + Texture? swapchain = cmdbuf.AcquireSwapchainTexture(MainWindow); + if (swapchain != null) + { + for (var i = 0; i < 1024; i += 1) + { + SpriteBatch.Add() + } + + SpriteBatch.Upload(cmdbuf); + + cmdbuf.BeginRenderPass(new ColorAttachmentInfo(swapchain, Color.Black)); + cmdbuf.BindGraphicsPipeline(spriteBatchPipeline); + cmdbuf.BindVertexBuffers( + new BufferBinding(quadVertexBuffer, 0), + new BufferBinding(SpriteBatch.) + ) + cmdbuf.EndRenderPass(); + } + GraphicsDevice.Submit(cmdbuf); + } + + public static void Main(string[] args) + { + SpriteBatchGame game = new SpriteBatchGame(); + game.Run(); + } + } + + public struct SpriteInstanceData : IVertexType + { + public Vector3 Translation; + public float Rotation; + public Vector2 Scale; + public Color Color; + public Vector2 UV0; + public Vector2 UV1; + public Vector2 UV2; + public Vector2 UV3; + + public static VertexElementFormat[] Formats => + [ + VertexElementFormat.Vector3, + VertexElementFormat.Float, + VertexElementFormat.Vector2, + VertexElementFormat.Color, + VertexElementFormat.Vector2, + VertexElementFormat.Vector2, + VertexElementFormat.Vector2, + VertexElementFormat.Vector2 + ]; + } + + class SpriteBatch + { + GraphicsDevice GraphicsDevice; + public Graphics.Buffer BatchBuffer; + SpriteInstanceData[] InstanceDatas; + int Index; + + public SpriteBatch(GraphicsDevice graphicsDevice) + { + GraphicsDevice = graphicsDevice; + BatchBuffer = Graphics.Buffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, 1024); + InstanceDatas = new SpriteInstanceData[1024]; + Index = 0; + } + + public void Add(Vector3 position, float rotation, Vector2 size, Color color) + { + InstanceDatas[Index].Translation = position; + InstanceDatas[Index].Rotation = rotation; + InstanceDatas[Index].Scale = size; + InstanceDatas[Index].Color = color; + InstanceDatas[Index].UV0 = new Vector2(0, 0); + InstanceDatas[Index].UV1 = new Vector2(0, 1); + InstanceDatas[Index].UV2 = new Vector2(1, 0); + InstanceDatas[Index].UV3 = new Vector2(1, 1); + Index += 1; + } + + public void Upload(CommandBuffer commandBuffer) + { + commandBuffer.SetBufferData(BatchBuffer, InstanceDatas, 0, 0, (uint) Index); + Index = 0; + } + } +} diff --git a/StoreLoad/StoreLoad.csproj b/StoreLoad/StoreLoad.csproj index fb73c0c..5c541ff 100644 --- a/StoreLoad/StoreLoad.csproj +++ b/StoreLoad/StoreLoad.csproj @@ -9,7 +9,6 @@ Exe net8.0 enable - x64