If you want to know how I managed to draw 500k sf::Sprite objects at over ~60FPS compared to upstream’s ~3FPS, keep on reading!
Let’s talk about a long-standing problem with upstream SFML: it has always issued one draw call per drawable object (sometimes even two1).
This approach doesn’t scale well, forcing users to resort to lower-level primitives for performance-critical features like particle systems or games with a large number of active entities.
It’s no surprise that batching in SFML has been on the community wish list for ages2. There have been a few solid attempts to implement it, but they never made it into the main codebase. Two notable pull requests (PR #1802 and PR #2166) showcase these efforts. In my opinion, the first one was going in the right direction, while the second one is a bit too complicated.
The main stumbling block in previous attempts was dealing with drawables sporting different textures. Those proposed batching systems tried to solve the issue by sorting the drawables by texture, which would mess up the original drawing order and complicate their internals.