using System; using System.IO; using System.Runtime.InteropServices; using MoonWorks; using MoonWorks.Graphics; using MoonWorks.Math; using MoonWorks.Window; namespace MoonWorksComputeSpriteBatch { public class TestGame : Game { private RenderPass mainRenderPass; private RenderTarget mainColorTarget; private Framebuffer mainFramebuffer; private Rect mainRenderArea; private GraphicsPipeline spritePipeline; private SpriteBatch spriteBatch; private Texture whitePixel; private Sampler sampler; private const int SPRITECOUNT = 8092; private Vector3[] positions = new Vector3[SPRITECOUNT]; private uint windowWidth; private uint windowHeight; private Random random = new Random(); public TestGame(WindowCreateInfo windowCreateInfo, PresentMode presentMode, int targetTimestep = 60, bool debugMode = false) : base(windowCreateInfo, presentMode, targetTimestep, debugMode) { windowWidth = windowCreateInfo.WindowWidth; windowHeight = windowCreateInfo.WindowHeight; var vertexShaderModule = new ShaderModule(GraphicsDevice, Path.Combine(Environment.CurrentDirectory, "Content", "sprite.vert.spv")); var fragmentShaderModule = new ShaderModule(GraphicsDevice, Path.Combine(Environment.CurrentDirectory, "Content", "sprite.frag.spv")); ColorTargetDescription colorTargetDescription = new ColorTargetDescription { Format = TextureFormat.R8G8B8A8, MultisampleCount = SampleCount.One, LoadOp = LoadOp.Clear, StoreOp = StoreOp.Store }; mainRenderPass = new RenderPass(GraphicsDevice, colorTargetDescription); mainColorTarget = RenderTarget.CreateBackedRenderTarget( GraphicsDevice, windowWidth, windowHeight, TextureFormat.R8G8B8A8, false ); mainFramebuffer = new Framebuffer( GraphicsDevice, windowWidth, windowHeight, mainRenderPass, null, mainColorTarget ); mainRenderArea = new Rect { X = 0, Y = 0, W = (int) windowWidth, H = (int) windowHeight }; /* Pipeline */ ColorTargetBlendState[] colorTargetBlendStates = new ColorTargetBlendState[1] { ColorTargetBlendState.None }; ColorBlendState colorBlendState = new ColorBlendState { LogicOpEnable = false, LogicOp = LogicOp.NoOp, BlendConstants = new BlendConstants(), ColorTargetBlendStates = colorTargetBlendStates }; DepthStencilState depthStencilState = DepthStencilState.Disable; ShaderStageState vertexShaderState = new ShaderStageState { ShaderModule = vertexShaderModule, EntryPointName = "main", UniformBufferSize = (uint) Marshal.SizeOf() }; ShaderStageState fragmentShaderState = new ShaderStageState { ShaderModule = fragmentShaderModule, EntryPointName = "main", UniformBufferSize = 0 }; MultisampleState multisampleState = MultisampleState.None; GraphicsPipelineLayoutInfo pipelineLayoutInfo = new GraphicsPipelineLayoutInfo { VertexSamplerBindingCount = 0, FragmentSamplerBindingCount = 1 }; RasterizerState rasterizerState = RasterizerState.CCW_CullNone; var vertexBindings = new VertexBinding[1] { new VertexBinding { Binding = 0, InputRate = VertexInputRate.Vertex, Stride = (uint) Marshal.SizeOf() } }; var vertexAttributes = new VertexAttribute[2] { new VertexAttribute { Binding = 0, Location = 0, Format = VertexElementFormat.Vector3, Offset = 0 }, new VertexAttribute { Binding = 0, Location = 1, Format = VertexElementFormat.Vector2, Offset = (uint) Marshal.OffsetOf("texcoord") } }; VertexInputState vertexInputState = new VertexInputState { VertexBindings = vertexBindings, VertexAttributes = vertexAttributes }; var viewports = new Viewport[1] { new Viewport { X = 0, Y = 0, W = windowWidth, H = windowHeight, MinDepth = 0, MaxDepth = 1 } }; var scissors = new Rect[1] { new Rect { X = 0, Y = 0, W = (int) windowWidth, H = (int) windowHeight } }; ViewportState viewportState = new ViewportState { Viewports = viewports, Scissors = scissors }; var graphicsPipelineCreateInfo = new GraphicsPipelineCreateInfo { ColorBlendState = colorBlendState, DepthStencilState = depthStencilState, VertexShaderState = vertexShaderState, FragmentShaderState = fragmentShaderState, MultisampleState = multisampleState, PipelineLayoutInfo = pipelineLayoutInfo, RasterizerState = rasterizerState, PrimitiveType = PrimitiveType.TriangleList, VertexInputState = vertexInputState, ViewportState = viewportState, RenderPass = mainRenderPass }; spritePipeline = new GraphicsPipeline(GraphicsDevice, graphicsPipelineCreateInfo); spriteBatch = new SpriteBatch(GraphicsDevice); whitePixel = Texture.CreateTexture2D(GraphicsDevice, 1, 1, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); var commandBuffer = GraphicsDevice.AcquireCommandBuffer(); commandBuffer.SetTextureData(whitePixel, new Color[] { Color.White }); GraphicsDevice.Submit(commandBuffer); sampler = new Sampler(GraphicsDevice, SamplerCreateInfo.PointWrap); } protected override void Update(TimeSpan dt) { for (var i = 0; i < SPRITECOUNT; i += 1) { positions[i].X = (float) (random.NextDouble() * windowWidth) - 64; positions[i].Y = (float) (random.NextDouble() * windowHeight) - 64; } } protected override void Draw(TimeSpan dt, double alpha) { var commandBuffer = GraphicsDevice.AcquireCommandBuffer(); var viewProjection = Matrix4x4.CreateLookAt(new Vector3(windowWidth / 2, windowHeight / 2, 1), new Vector3(windowWidth / 2, windowHeight / 2, 0), Vector3.Up) * Matrix4x4.CreateOrthographic(windowWidth, windowHeight, 0.1f, 1000); spriteBatch.Start(whitePixel, sampler); for (var i = 0; i < SPRITECOUNT; i += 1) { var transform = Matrix4x4.CreateTranslation(positions[i]); spriteBatch.Add(new Sprite(new Rect { X = 0, Y = 0, W = 0, H = 0 }, 128, 128), transform); } spriteBatch.Flush(commandBuffer, mainRenderPass, mainFramebuffer, mainRenderArea, spritePipeline, new CameraUniforms { viewProjectionMatrix = viewProjection }); commandBuffer.QueuePresent(mainColorTarget.TextureSlice, Filter.Nearest); GraphicsDevice.Submit(commandBuffer); } } }