191 lines
6.1 KiB
C#
191 lines
6.1 KiB
C#
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using MoonWorks.Graphics;
|
|
using MoonWorks.Math;
|
|
|
|
namespace MoonWorksComputeSpriteBatch
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct SpriteBatchUniforms
|
|
{
|
|
public uint VertexCount { get; set; }
|
|
}
|
|
|
|
public class SpriteBatch
|
|
{
|
|
public const int MAX_SPRITES = 65536;
|
|
private const int MAX_VERTICES = MAX_SPRITES * 4;
|
|
private const int MAX_INDICES = MAX_SPRITES * 6;
|
|
private const int MAX_MATRICES = MAX_SPRITES;
|
|
|
|
private static ComputePipeline ComputePipeline = null;
|
|
|
|
private Buffer VertexBuffer { get; }
|
|
private Buffer IndexBuffer { get; }
|
|
private Buffer TransformBuffer { get; }
|
|
|
|
private readonly VertexPositionTexcoord[] Vertices;
|
|
private static readonly short[] Indices = GenerateIndexArray();
|
|
private readonly Matrix4x4[] Transforms;
|
|
|
|
private Texture CurrentTexture { get; set; }
|
|
private Sampler CurrentSampler { get; set; }
|
|
private uint VertexCount { get; set; }
|
|
private uint TransformCount { get; set; }
|
|
|
|
public SpriteBatch(GraphicsDevice graphicsDevice)
|
|
{
|
|
if (ComputePipeline == null)
|
|
{
|
|
var computeShaderModule = new ShaderModule(graphicsDevice, Path.Combine(System.Environment.CurrentDirectory, "Content", "spritebatch.comp.spv"));
|
|
|
|
var computeShaderInfo = new ComputeShaderInfo
|
|
{
|
|
ShaderModule = computeShaderModule,
|
|
EntryPointName = "main",
|
|
UniformBufferSize = (uint)Marshal.SizeOf<SpriteBatchUniforms>(),
|
|
BufferBindingCount = 2,
|
|
ImageBindingCount = 0
|
|
};
|
|
|
|
ComputePipeline = new ComputePipeline(graphicsDevice, computeShaderInfo);
|
|
}
|
|
|
|
Vertices = new VertexPositionTexcoord[MAX_VERTICES];
|
|
VertexBuffer = new Buffer(
|
|
graphicsDevice,
|
|
BufferUsageFlags.Vertex | BufferUsageFlags.Compute,
|
|
(uint)(MAX_VERTICES * Marshal.SizeOf<VertexPositionTexcoord>())
|
|
);
|
|
|
|
IndexBuffer = new Buffer(
|
|
graphicsDevice,
|
|
BufferUsageFlags.Index | BufferUsageFlags.Compute,
|
|
MAX_INDICES * sizeof(short)
|
|
);
|
|
|
|
Transforms = new Matrix4x4[MAX_MATRICES];
|
|
TransformBuffer = new Buffer(
|
|
graphicsDevice,
|
|
BufferUsageFlags.Compute,
|
|
(uint)(MAX_MATRICES * Marshal.SizeOf<Matrix4x4>())
|
|
);
|
|
|
|
var commandBuffer = graphicsDevice.AcquireCommandBuffer();
|
|
commandBuffer.SetBufferData(IndexBuffer, Indices);
|
|
graphicsDevice.Submit(commandBuffer);
|
|
}
|
|
|
|
public void Start(Texture texture, Sampler sampler)
|
|
{
|
|
TransformCount = 0;
|
|
VertexCount = 0;
|
|
CurrentTexture = texture;
|
|
CurrentSampler = sampler;
|
|
}
|
|
|
|
public void Add(Sprite sprite, Matrix4x4 transform, Color color)
|
|
{
|
|
Vertices[VertexCount].position.X = 0;
|
|
Vertices[VertexCount].position.Y = 0;
|
|
Vertices[VertexCount].texcoord.X = sprite.Texcoord.X;
|
|
Vertices[VertexCount].texcoord.Y = sprite.Texcoord.Y;
|
|
Vertices[VertexCount].color.X = color.R / 255f;
|
|
Vertices[VertexCount].color.Y = color.G / 255f;
|
|
Vertices[VertexCount].color.Z = color.B / 255f;
|
|
Vertices[VertexCount].color.W = color.A / 255f;
|
|
|
|
Vertices[VertexCount + 1].position.X = sprite.Width;
|
|
Vertices[VertexCount + 1].position.Y = 0;
|
|
Vertices[VertexCount + 1].texcoord.X = sprite.Texcoord.X + sprite.Texcoord.W;
|
|
Vertices[VertexCount + 1].texcoord.Y = sprite.Texcoord.Y;
|
|
Vertices[VertexCount + 1].color.X = color.R / 255f;
|
|
Vertices[VertexCount + 1].color.Y = color.G / 255f;
|
|
Vertices[VertexCount + 1].color.Z = color.B / 255f;
|
|
Vertices[VertexCount + 1].color.W = color.A / 255f;
|
|
|
|
Vertices[VertexCount + 2].position.X = 0;
|
|
Vertices[VertexCount + 2].position.Y = sprite.Height;
|
|
Vertices[VertexCount + 2].texcoord.X = sprite.Texcoord.X;
|
|
Vertices[VertexCount + 2].texcoord.Y = sprite.Texcoord.Y + sprite.Texcoord.H;
|
|
Vertices[VertexCount + 2].color.X = color.R / 255f;
|
|
Vertices[VertexCount + 2].color.Y = color.G / 255f;
|
|
Vertices[VertexCount + 2].color.Z = color.B / 255f;
|
|
Vertices[VertexCount + 2].color.W = color.A / 255f;
|
|
|
|
Vertices[VertexCount + 3].position.X = sprite.Width;
|
|
Vertices[VertexCount + 3].position.Y = sprite.Height;
|
|
Vertices[VertexCount + 3].texcoord.X = sprite.Texcoord.X + sprite.Texcoord.W;
|
|
Vertices[VertexCount + 3].texcoord.Y = sprite.Texcoord.Y + sprite.Texcoord.H;
|
|
Vertices[VertexCount + 3].color.X = color.R / 255f;
|
|
Vertices[VertexCount + 3].color.Y = color.G / 255f;
|
|
Vertices[VertexCount + 3].color.Z = color.B / 255f;
|
|
Vertices[VertexCount + 3].color.W = color.A / 255f;
|
|
|
|
VertexCount += 4;
|
|
|
|
Transforms[TransformCount] = transform;
|
|
TransformCount += 1;
|
|
}
|
|
|
|
public void Flush(
|
|
CommandBuffer commandBuffer,
|
|
ColorAttachmentInfo colorAttachmentInfo,
|
|
GraphicsPipeline graphicsPipeline,
|
|
CameraUniforms cameraUniforms
|
|
)
|
|
{
|
|
if (VertexCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
commandBuffer.SetBufferData(VertexBuffer, Vertices, 0, 0, VertexCount);
|
|
commandBuffer.SetBufferData(TransformBuffer, Transforms, 0, 0, TransformCount);
|
|
|
|
commandBuffer.BindComputePipeline(ComputePipeline);
|
|
commandBuffer.BindComputeBuffers(VertexBuffer, TransformBuffer);
|
|
var offset = commandBuffer.PushComputeShaderUniforms(new SpriteBatchUniforms
|
|
{
|
|
VertexCount = VertexCount
|
|
});
|
|
commandBuffer.DispatchCompute(System.Math.Max(1, VertexCount / 256), 1, 1, offset);
|
|
|
|
commandBuffer.BeginRenderPass(colorAttachmentInfo);
|
|
commandBuffer.BindGraphicsPipeline(graphicsPipeline);
|
|
commandBuffer.BindVertexBuffers(VertexBuffer);
|
|
commandBuffer.BindIndexBuffer(IndexBuffer, IndexElementSize.Sixteen);
|
|
commandBuffer.BindFragmentSamplers(new TextureSamplerBinding { Texture = CurrentTexture, Sampler = CurrentSampler });
|
|
offset = commandBuffer.PushVertexShaderUniforms(cameraUniforms);
|
|
|
|
commandBuffer.DrawIndexedPrimitives(
|
|
0,
|
|
0,
|
|
VertexCount / 2,
|
|
offset,
|
|
0
|
|
);
|
|
|
|
commandBuffer.EndRenderPass();
|
|
|
|
VertexCount = 0;
|
|
TransformCount = 0;
|
|
}
|
|
|
|
private static short[] GenerateIndexArray()
|
|
{
|
|
var result = new short[MAX_INDICES];
|
|
for (int i = 0, j = 0; i < MAX_INDICES; i += 6, j += 4)
|
|
{
|
|
result[i] = (short)j;
|
|
result[i + 1] = (short)(j + 1);
|
|
result[i + 2] = (short)(j + 2);
|
|
result[i + 3] = (short)(j + 2);
|
|
result[i + 4] = (short)(j + 1);
|
|
result[i + 5] = (short)(j + 3);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|