Editor version 2022.3.23f, Entities package version 1.2.0 (but also tried with 1.0.16)
Setup: Spawner system instantiates 50000 of the prefab (either 2D square or 3D cube ) assigned to the spawner’s ItemPrefab property. The 2D square created using 2D Object > Sprites > Square, with all default settings. The 3D cube created using 3D Object > Cube, with all default settings. Both have authoring mono behavior. They do not have any other mono behaviors.
github for the project: GitHub - azarg/2D-vs-3D-ECS-performance
With this setup my computer can render 50k 2D sprites at about 20fps.
However, 50k 3D cubes render at 200fps!
Question: why is rendering 2D sprites has worse performance than rendering 3D cubes using ECS?


Entities Graphics doesn’t have native 2D rendering support. It is using GameObjects for sprites.
2 Likes
Thank you! That probably explains why I see a “copy” job in the profiler when rendering 2D sprites. It probably copies entity information into GameObjects, which actually makes rendering 2D sprites with entities slightly worse performant than rendering them with GameObjects. This is obviously just the rendering. Once complex behavior (movement, distance check, collusion, etc) is added to entities then they’ll benefit from those calculations being burst compiled and / or run in parallel jobs, vs having those calculations done in MonoBehaviors.
But, does this all mean the following two approaches will have about the same performance if I need to render many 2D sprites each needing to perform a complex calculation in every frame??
-
Create sprites as entities. Have a ISystem that starts a parallel job (assuming calculations can be parallelized) to do all of the complex calculations for each entity.
-
Create sprites as GameObjects. Keep references to them in a single array. Have a single manager object that has access to the array. In the Update() of the manager object, start these sequential parallel jobs a) copy necessary information (such as position) from the GameObjects array to native arrays b) perform complex calculations and save results in a native array c) copy results from the output native array back to GameObjects array
The performance advantage of (1) is that you don’t have to perform the data copy from managed to unmanaged, because the ECS data already lives there. The advantage of (2) is that you don’t have some of the overhead of ECS, which can be excessive when you don’t need multiple archetypes.
I’d also suggest not relying on default baking for sprites and instead use your own entity-to-GameObject binding system that is pooling-aware for your sprite GameObjects.
Thank you! I’m testing the 2nd options. Initially performance was not as good as entities solution, but then I discovered IJobParallelForTransform. Now I can run all simulation in burst compiled parallel jobs, and then run an IJobParallelForTransform job to update the game object transforms. Performance is at par with entities approach.
@DreamingImLatios you mentioned not relying on default baking for sprites and intead using own entity-to-GameObject binding system that is pooling-aware. Do you have an example of this by any chance?
No. I rarely use GameObjects these days. And if I were to do something 2D, I’d actually probably build a custom solution that makes sprites work with native Entities Graphics (or rather my rewrite of it).
that’s cool. thank you anyways. In case if anyone gets here, I am looking for a faster sprite renderer to replace the default SpriteRenderer. Ideally one that uses latest DOTS.
I’ve found these two, but they’re not updated to latest DOTS packages: - GitHub - fabriziospadaro/SpriteSheetRenderer: Spritesheet renderer is a powerful Unity ECS API to render massive numbers of sprites using the new dots stack, taking full advantage of Jobs, DynamicBuffers and ComputeBuffer
EDIT: actually NSprites is almost up to date. pretty awesome btw.