Feature proposal: Tilemaps in SDL_Renderer

I would like to add support for tile rendering to SDL_Renderer. Is this a feature other people would use? I thought I’d ask here in case this is totally out of scope for SDL2. I have implemented this a couple of times on top of SDL and OpenGL, but it might make more sense to have this as part of SDL_Renderer.

My idea is a simple function to render a single tiled layer. (Maybe the map data should live in graphics memory too, like an SDL_Texture, but that would make the API more complex)

Tile maps can already be implemented on top of the current SDL_Renderer, but it could be much faster to implement them with shaders, so the tile rendering would have to be part of SDL2 itself. (Or maybe not, and batching is fast enough? Benchmarks will tell.) I can write software, GL, and GLES implementations, but I would probably need some help with the Metal backend.

I would propose an API that looks something like this:

struct SDL_TileSheet {
   SDL_Texture*    texture,
   int tile_width;
   int tile_height;
}

struct SDL_TileMap {
   Uint16*    data,
   int columns;
   int rows;
}

int SDL_RenderTiles(SDL_Renderer*   renderer,
                   SDL_TileSheet*    tilesheet,
                   SDL_TileMap* tilemap,
                   const SDL_Rect* srcrect,
                   const SDL_Rect* dstrect)

of course if the tile data lives on the GPU too, it would have to be similar to SDL_Texture and have a bunch of helper functions.

1 Like

I actually implemented this myself, though my fork is quite stale. I don’t have a custom structure, you just provide a list of Src and Dst Rects, and it just does the UV mapping in the queue all at once instead of having to make multiple SDL_alloc calls for each rendercopy. Mine is currently only implemented for OpenGL and D3D, and may have some bugs? It works for my particular use case well

Fork/Commit/Relevant parts:
OpenGL:

D3D:

excuse my messy coding habits

This is a really good idea, but it is slightly redundant now that SDL2 has batching in Renderer, although your explicit approach is probably still faster than autobatching. Did you try to get this merged into mainline SDL2 at some point, or was it always meant to be just your personal fork?

Your code is basically what I would want a fallback implementation of this feature to look like.

I hope that tile map rendering in a shader will be substantially faster than your approach, but maybe it’s not, in which case the overhead of additional draw-calls (one per tile layer in the worst case) might not make this worth it. A sufficiently smart tile system could always decide to use the batching system if it’s only for a handful of tiles, but I probably wouldn’t go that route.

If nobody else wants/needs this, it would make more sense to implement it on top of SDL2, but there is no good way to extend the SDL_Renderer system apart from forking SDL (like you did)

The advantage of my implementation over the current batch process is that it allocates the vertices in one call rather than once for every tile (though im not sure what the allocation algorithm is for SDL’s custom malloc). This is probably a micro optimization, but it follows basically the same pattern as SDL_FillRectsF, but with UV mapping.

I didn’t push to SDL because at the time, SDL wasn’t hosted on github, and my fork is based on a mirror repo. Im not familiar with the Open Source community around SDL really, and since my game is targeting PC mostly, I haven’t bothered to do the Metal bindings (and I dont have a MacOS device to test on anyways). It would require some better test coverage as well and probably support on d3d11 (current d3d bindings are 9), as well as mobile testing. The feature is really implemented for my specific use case (pc, on my particular engine), but I’ve made it publicly available in case anyone wants to copy it.

I’m not familiar enough with the entirety of SDL bindings to know enough about how shaders are implemented, but it was my understanding that SDL’s openGL bindings are based on the fixed-function (compatibility) api and not the 3+ shader API, but once again I dont know a ton about this stuff