compute sprite batch

compute_sprite_batch
cosmonaut 2024-06-11 17:20:47 -07:00
parent f17e9851f3
commit 041b52ed44
12 changed files with 406 additions and 47 deletions

Binary file not shown.

View File

@ -1,7 +1,7 @@
#version 450
layout (local_size_x = 8) in;
layout (set = 1, binding = 0) buffer outBuffer
layout (set = 1, binding = 0) writeonly buffer outBuffer
{
uint squares[];
};

View File

@ -0,0 +1,79 @@
#version 450
struct SpriteComputeData
{
vec3 position;
float rotation;
vec2 scale;
vec4 color;
};
struct SpriteVertex
{
vec4 position;
vec2 texcoord;
vec4 color;
};
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout (std430, set = 0, binding = 0) readonly buffer inBuffer
{
SpriteComputeData computeData[];
};
layout (std430, set = 1, binding = 0) writeonly buffer outBuffer
{
SpriteVertex vertexData[];
};
void main()
{
uint n = gl_GlobalInvocationID.x;
SpriteComputeData currentSpriteData = computeData[n];
mat4 Scale = mat4(
currentSpriteData.scale.x, 0, 0, 0,
0, currentSpriteData.scale.y, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
float c = cos(currentSpriteData.rotation);
float s = sin(currentSpriteData.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, 0,
0, 1, 0, 0,
0, 0, 1, 0,
currentSpriteData.position.x, currentSpriteData.position.y, currentSpriteData.position.z, 1
);
mat4 Model = Translation * Rotation * Scale;
vec4 topLeft = vec4(0, 0, 0, 1);
vec4 topRight = vec4(1, 0, 0, 1);
vec4 bottomLeft = vec4(0, 1, 0, 1);
vec4 bottomRight = vec4(1, 1, 0, 1);
vertexData[n*4] .position = Model * topLeft;
vertexData[n*4+1].position = Model * topRight;
vertexData[n*4+2].position = Model * bottomLeft;
vertexData[n*4+3].position = Model * bottomRight;
vertexData[n*4] .texcoord = vec2(0, 0);
vertexData[n*4+1].texcoord = vec2(1, 0);
vertexData[n*4+2].texcoord = vec2(0, 1);
vertexData[n*4+3].texcoord = vec2(1, 1);
vertexData[n*4] .color = currentSpriteData.color;
vertexData[n*4+1].color = currentSpriteData.color;
vertexData[n*4+2].color = currentSpriteData.color;
vertexData[n*4+3].color = currentSpriteData.color;
}

View File

@ -0,0 +1,13 @@
#version 450
layout (location = 0) in vec2 TexCoord;
layout (location = 1) in vec4 Color;
layout (location = 0) out vec4 FragColor;
layout(set = 2, binding = 0) uniform sampler2D Sampler;
void main()
{
FragColor = Color * texture(Sampler, TexCoord);
}

View File

@ -0,0 +1,20 @@
#version 450
layout (location = 0) in vec4 Position;
layout (location = 1) in vec2 TexCoord;
layout (location = 2) in vec4 Color;
layout (location = 0) out vec2 outTexCoord;
layout (location = 1) out vec4 outColor;
layout (set = 1, binding = 0) uniform UniformBlock
{
mat4x4 MatrixTransform;
};
void main()
{
outTexCoord = TexCoord;
outColor = Color;
gl_Position = MatrixTransform * Position;
}

View File

@ -14,10 +14,12 @@ public struct PositionVertex : IVertexType
Position = position;
}
public static VertexElementFormat[] Formats { get; } = new VertexElementFormat[1]
{
public static VertexElementFormat[] Formats { get; } =
[
VertexElementFormat.Vector3
};
];
public static uint[] Offsets { get; } = [ 0 ];
public override string ToString()
{
@ -37,11 +39,17 @@ public struct PositionColorVertex : IVertexType
Color = color;
}
public static VertexElementFormat[] Formats { get; } = new VertexElementFormat[2]
{
public static VertexElementFormat[] Formats { get; } =
[
VertexElementFormat.Vector3,
VertexElementFormat.Color
};
];
public static uint[] Offsets { get; } =
[
0,
12
];
public override string ToString()
{
@ -67,8 +75,41 @@ public struct PositionTextureVertex : IVertexType
VertexElementFormat.Vector2
};
public static uint[] Offsets { get; } =
[
0,
12
];
public override string ToString()
{
return Position + " | " + TexCoord;
}
}
[StructLayout(LayoutKind.Explicit, Size = 48)]
struct PositionTextureColorVertex : IVertexType
{
[FieldOffset(0)]
public Vector4 Position;
[FieldOffset(16)]
public Vector2 TexCoord;
[FieldOffset(32)]
public Vector4 Color;
public static VertexElementFormat[] Formats { get; } =
[
VertexElementFormat.Vector4,
VertexElementFormat.Vector2,
VertexElementFormat.Vector4
];
public static uint[] Offsets { get; } =
[
0,
16,
32
];
}

View File

@ -0,0 +1,244 @@
using System;
using System.Runtime.InteropServices;
using MoonWorks;
using MoonWorks.Graphics;
using MoonWorks.Input;
using MoonWorks.Math.Float;
using Buffer = MoonWorks.Graphics.Buffer;
namespace MoonWorksGraphicsTests;
class ComputeSpriteBatchExample : Example
{
ComputePipeline ComputePipeline;
GraphicsPipeline RenderPipeline;
Sampler Sampler;
Texture SpriteTexture;
TransferBuffer SpriteComputeTransferBuffer;
Buffer SpriteComputeBuffer;
Buffer SpriteVertexBuffer;
Buffer SpriteIndexBuffer;
const int MAX_SPRITE_COUNT = 8192;
Random Random = new Random();
[StructLayout(LayoutKind.Explicit, Size = 48)]
struct ComputeSpriteData
{
[FieldOffset(0)]
public Vector3 Position;
[FieldOffset(12)]
public float Rotation;
[FieldOffset(16)]
public Vector2 Size;
[FieldOffset(32)]
public Vector4 Color;
}
public override unsafe void Init(Window window, GraphicsDevice graphicsDevice, Inputs inputs)
{
Window = window;
GraphicsDevice = graphicsDevice;
Window.SetTitle("ComputeSpriteBatch");
Shader vertShader = new Shader(
GraphicsDevice,
TestUtils.GetShaderPath("TexturedQuadColorWithMatrix.vert"),
"main",
new ShaderCreateInfo
{
ShaderStage = ShaderStage.Vertex,
ShaderFormat = ShaderFormat.SPIRV,
UniformBufferCount = 1
}
);
Shader fragShader = new Shader(
GraphicsDevice,
TestUtils.GetShaderPath("TexturedQuadColor.frag"),
"main",
new ShaderCreateInfo
{
ShaderStage = ShaderStage.Fragment,
ShaderFormat = ShaderFormat.SPIRV,
SamplerCount = 1
}
);
GraphicsPipelineCreateInfo renderPipelineCreateInfo = TestUtils.GetStandardGraphicsPipelineCreateInfo(
Window.SwapchainFormat,
vertShader,
fragShader
);
renderPipelineCreateInfo.VertexInputState = VertexInputState.CreateSingleBinding<PositionTextureColorVertex>();
RenderPipeline = new GraphicsPipeline(GraphicsDevice, renderPipelineCreateInfo);
ComputePipeline = new ComputePipeline(
GraphicsDevice,
TestUtils.GetShaderPath("SpriteBatch.comp"),
"main",
new ComputePipelineCreateInfo
{
ShaderFormat = ShaderFormat.SPIRV,
ReadOnlyStorageBufferCount = 1,
ReadWriteStorageBufferCount = 1,
ThreadCountX = 64,
ThreadCountY = 1,
ThreadCountZ = 1
}
);
Sampler = new Sampler(GraphicsDevice, SamplerCreateInfo.PointClamp);
// Create and populate the sprite texture
var resourceUploader = new ResourceUploader(GraphicsDevice);
SpriteTexture = resourceUploader.CreateTexture2DFromCompressed(TestUtils.GetTexturePath("ravioli.png"));
resourceUploader.Upload();
resourceUploader.Dispose();
SpriteComputeTransferBuffer = TransferBuffer.Create<ComputeSpriteData>(
GraphicsDevice,
TransferUsage.Buffer,
TransferBufferMapFlags.Write,
MAX_SPRITE_COUNT
);
SpriteComputeBuffer = Buffer.Create<ComputeSpriteData>(
GraphicsDevice,
BufferUsageFlags.ComputeStorageRead,
MAX_SPRITE_COUNT
);
SpriteVertexBuffer = Buffer.Create<PositionTextureColorVertex>(
GraphicsDevice,
BufferUsageFlags.ComputeStorageWrite | BufferUsageFlags.Vertex,
MAX_SPRITE_COUNT * 4
);
SpriteIndexBuffer = Buffer.Create<uint>(
GraphicsDevice,
BufferUsageFlags.Index,
MAX_SPRITE_COUNT * 6
);
TransferBuffer spriteIndexTransferBuffer = TransferBuffer.Create<uint>(
GraphicsDevice,
TransferUsage.Buffer,
TransferBufferMapFlags.Write,
MAX_SPRITE_COUNT * 6
);
spriteIndexTransferBuffer.Map(false, out byte* mapPointer);
uint *indexPointer = (uint*) mapPointer;
for (uint i = 0, j = 0; i < MAX_SPRITE_COUNT * 6; i += 6, j += 4)
{
indexPointer[i] = j;
indexPointer[i + 1] = j + 1;
indexPointer[i + 2] = j + 2;
indexPointer[i + 3] = j + 3;
indexPointer[i + 4] = j + 2;
indexPointer[i + 5] = j + 1;
}
spriteIndexTransferBuffer.Unmap();
var cmdbuf = GraphicsDevice.AcquireCommandBuffer();
var copyPass = cmdbuf.BeginCopyPass();
copyPass.UploadToBuffer(spriteIndexTransferBuffer, SpriteIndexBuffer, false);
cmdbuf.EndCopyPass(copyPass);
GraphicsDevice.Submit(cmdbuf);
}
public override void Update(TimeSpan delta)
{
}
public override unsafe void Draw(double alpha)
{
Matrix4x4 cameraMatrix =
Matrix4x4.CreateOrthographicOffCenter(
0,
640,
480,
0,
0,
-1f
);
CommandBuffer cmdbuf = GraphicsDevice.AcquireCommandBuffer();
Texture swapchainTexture = cmdbuf.AcquireSwapchainTexture(Window);
if (swapchainTexture != null)
{
// Build sprite compute transfer
SpriteComputeTransferBuffer.Map(true, out byte* mapPointer);
ComputeSpriteData *dataPointer = (ComputeSpriteData*) mapPointer;
for (var i = 0; i < MAX_SPRITE_COUNT; i += 1)
{
dataPointer[i] = new ComputeSpriteData
{
Position = new Vector3(Random.Next(640), Random.Next(480), 0),
Rotation = (float) (Random.NextDouble() * System.Math.PI * 2),
Size = new Vector2(32, 32),
Color = new Vector4(1f, 1f, 1f, 1f)
};
}
SpriteComputeTransferBuffer.Unmap();
// Upload compute data to buffer
var copyPass = cmdbuf.BeginCopyPass();
copyPass.UploadToBuffer(SpriteComputeTransferBuffer, SpriteComputeBuffer, true);
cmdbuf.EndCopyPass(copyPass);
// Set up compute pass to build sprite vertex buffer
var computePass = cmdbuf.BeginComputePass(new StorageBufferReadWriteBinding
{
Buffer = SpriteVertexBuffer,
Cycle = true
});
computePass.BindComputePipeline(ComputePipeline);
computePass.BindStorageBuffer(SpriteComputeBuffer);
computePass.Dispatch(MAX_SPRITE_COUNT / 64, 1, 1);
cmdbuf.EndComputePass(computePass);
// Render sprites using vertex buffer
var renderPass = cmdbuf.BeginRenderPass(
new ColorAttachmentInfo(swapchainTexture, false, Color.Black)
);
renderPass.BindGraphicsPipeline(RenderPipeline);
renderPass.BindVertexBuffer(SpriteVertexBuffer);
renderPass.BindIndexBuffer(SpriteIndexBuffer, IndexElementSize.ThirtyTwo);
renderPass.BindFragmentSampler(new TextureSamplerBinding(SpriteTexture, Sampler));
renderPass.PushVertexUniformData(cameraMatrix);
renderPass.DrawIndexedPrimitives(0, 0, MAX_SPRITE_COUNT * 2);
cmdbuf.EndRenderPass(renderPass);
}
GraphicsDevice.Submit(cmdbuf);
}
public override void Destroy()
{
ComputePipeline.Dispose();
RenderPipeline.Dispose();
Sampler.Dispose();
SpriteTexture.Dispose();
SpriteComputeTransferBuffer.Dispose();
SpriteComputeBuffer.Dispose();
SpriteVertexBuffer.Dispose();
SpriteIndexBuffer.Dispose();
}
}

View File

@ -17,45 +17,6 @@
<ProjectReference Include="..\MoonWorks\MoonWorks.csproj" />
</ItemGroup>
<!-- TODO: remove this once examples are fully converted -->
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);Examples\**\*</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<Compile Include="Examples\Example.cs" />
<Compile Include="Examples\BasicComputeExample.cs" />
<Compile Include="Examples\BasicStencilExample.cs" />
<Compile Include="Examples\BasicTriangleExample.cs" />
<Compile Include="Examples\ClearScreenExample.cs" />
<Compile Include="Examples\ClearScreen_MultiWindowExample.cs" />
<Compile Include="Examples\CompressedTexturesExample.cs" />
<Compile Include="Examples\ComputeUniformsExample.cs" />
<Compile Include="Examples\CopyTextureExample.cs" />
<Compile Include="Examples\CubeExample.cs" />
<Compile Include="Examples\CullFaceExample.cs" />
<Compile Include="Examples\DepthMSAAExample.cs" />
<Compile Include="Examples\DrawIndirectExample.cs" />
<Compile Include="Examples\GetBufferDataExample.cs" />
<Compile Include="Examples\InstancingAndOffsetsExample.cs" />
<Compile Include="Examples\MSAACubeExample.cs" />
<Compile Include="Examples\MSAAExample.cs" />
<Compile Include="Examples\RenderTexture2DArrayExample.cs" />
<Compile Include="Examples\RenderTexture2DExample.cs" />
<Compile Include="Examples\RenderTextureCubeExample.cs" />
<Compile Include="Examples\RenderTextureMipmapsExample.cs" />
<Compile Include="Examples\StoreLoadExample.cs" />
<Compile Include="Examples\Texture3DCopyExample.cs" />
<Compile Include="Examples\Texture3DExample.cs" />
<Compile Include="Examples\TexturedAnimatedQuadExample.cs" />
<Compile Include="Examples\TexturedQuadExample.cs" />
<Compile Include="Examples\TextureMipmapsExample.cs" />
<Compile Include="Examples\TriangleVertexBufferExample.cs" />
<Compile Include="Examples\VertexSamplerExample.cs" />
<Compile Include="Examples\VideoPlayerExample.cs" />
<Compile Include="Examples\WindowResizingExample.cs" />
</ItemGroup>
<Import Project=".\CopyMoonlibs.targets" />
</Project>

View File

@ -37,7 +37,8 @@ class Program : Game
new TriangleVertexBufferExample(),
new VertexSamplerExample(),
new VideoPlayerExample(),
new WindowResizingExample()
new WindowResizingExample(),
new ComputeSpriteBatchExample()
];
int ExampleIndex = 0;