# SPRITEBATCHING ` all code here is psuedocode, and this idea / proposal is in no way final` --- Rendering large amounts of sprites individually can severely tank performance.<br> Each sprite would typically need its own draw call, which can introduce a lot of overhead (switching textures, submitting geometry). <br> This makes draw calls relatively expensive when not used sparingly. The objective of spritebatching is to reduce draw calls by grouping multiple sprites into one larger draw call.<br> This is achieved by combining all necessary geometry of multiple sprites into one compound mesh, which is then submitted to the GPU. --- ## basics 1.0 : batch data --- The core of the spritebatching system for Anemoia revolves around `BatchData` and `BatchContainer`. Batch data contains all necessary data for a batch. (shocker) ```csharp struct BatchData() { public Entity[] Entities; public Mesh BatchMesh; } ``` `Entities` is a buffer that stores all the entities / sprites that belong to this batch (consequentially their components as well).<br> `Mesh` is the "compound mesH" that was mentioned before, it contains the vertices, indices, and texture coordinates of all the sprites in a batch.<br> This is what is actually sent to the GPU to be rendered. ### notes #### a more complete impl could include: - Material: A reference to a material used for rendering the batch. Typically contains shader uniforms and textures. Could potentially batch by material. - Layer: A layer Id or mask, used for controlling rendering depth and order. ### why not a component? - Components are associated with individual entities, whereas `BatchData` represents a *group* of them. --- ## basics 1.1 : batch containers --- `BatchContainer`s act as central repositories for all active batches. Its... a container. ```csharp struct BatchContainer() : IComponent { public Dictionary<uint, BatchData> Batches; } ``` `Batches` is a dictionary that stores all active `BatchData`s. It is keyed by a `uint` that uniquely identifies a batch.<br> The key could be derived from sprites material ID, layer ID, or a combination of both. `BatchContainer` is a "Global Component" meaning it does not belong to any entity in particular. This makes it easy to access and modify from any other system. --- ## systems 1.0 : batch orchestration --- This is responsible for initializing `BatchContainer`as a global component. ```csharp GlobalWorld.Add(new BatchContainer()); ``` No explanation needed, this is very straightforward. --- ## systems 1.1 : batch population --- This is the core of the batching system. It will iterate through all entities that contain both a Sprite and Transform component, and adds them to the appropriate batch. ### process: - Iterate through eligible entities. - Get material and layer of entities. - Generate a key based on material and layer identifiers. - Get batches, or create them if they dont exist. - Add entities to the buffer of an appropriate batch. ```csharp /* if entity does not have needed components, cancel */ var layer = entity.Get<Sprite>().Layer; var material = entity.Get<Sprite>().Material; var key = GetBatchKey(material.Id, layer.Id); /* look for batches, if an appropriate batch doesnt exist, create one and add valid entities to id */ ``` --- ## systems 1.2 : batch rendering --- This system is responsible for taking the populated batches, and rendering them to the screen. ### process: - Iterate through batches. - For each batch, update its compound mesh. - Set layer and materials. - Render the batch. ```csharp var batchesToKill = new List<uint>(); var batches = container.Batches; /* iterate through batches create compound mesh if it doesnt exist look for batches that have no entities, add them to the kill pool */ /* iterate through entities in batches get its components, sprite, transform etc create vertices and indices accordingly push them to the compound mesh send batches to geometry pass to be rendered dispose all batches in kill pool */ ```