The problem with SDL is that SDL is not thread safe and SDL render functions (such as SDL_CreateTexture, SDL_RenderCopy and everything else that relies on the SDL window) can’t / shouldn’t be executed from a thread that is not the same thread that created the SDL window. I’m doing just that in my framework I’m using when making games. Because of that, my framework is really buggy and crashes sometimes during startup and it’s because I’m using SDL_CreateTextureFromSurface and such which, like I said, shouldn’t be executed in a thread that is not the same as the thread that created the window. So yeah… I need to change some stuff in my framework.
In the code example below, I’m creating a tileset texture containing all the tile types and creating the actual tile map. In each tile (tile struct) there’s a clip quad that handles which part of the tileset texture to render.
I’m also creating a target texture which I render all the tiles to and then I render the actual target texture.
I don’t know if it’s the best way to render a tilemap but this is how I usually do it.
Let me know if you’re wondering something.
Code:
#include "SDL.h"
#include "SDL_image.h"
#include <iostream>
#include <vector>
SDL_Window* m_pWindow = NULL;
SDL_Renderer* m_pRenderer = NULL;
// The texture containing a tileset for all the tile types
SDL_Texture* m_pTilesetTexture = NULL;
// The render target texture
SDL_Texture* m_pTargetTexture = NULL;
SDL_Event m_Event;
bool m_Running = true;
// A tile
struct STile
{
SDL_Rect TileQuadPosition;
SDL_Rect TileClipQuad;
int TileType;
};
typedef std::vector<STile*> TileList;
// The actual tile list
TileList m_TileList;
int main(int argv, char* args[])
{
// Initialize SDL
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
// If SDL for some reason fails to initialize, print out an error message in the console
printf("Error: failed to initialize SDL\n");
}
// Initialize SDL Image
if(IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) == 0)
{
// If SDL Image for some reason fails to initialize, print out an error message in the console
printf("Error: Failed to initialize SDL Image\n");
}
else
{
// Create the SDL window
m_pWindow = SDL_CreateWindow("WindowTitle", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
// If the SDL window has been successfully created
if(m_pWindow)
{
// Create the SDL renderer (render to texture mode enabled)
m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
// If the SDL renderer has been successfully created
if(m_pRenderer)
{
// Set the render draw color
SDL_SetRenderDrawColor(m_pRenderer, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
// Create the tileset texture
m_pTilesetTexture = IMG_LoadTexture(m_pRenderer, "Tileset.png");
// Create an empty texture with render target mode
m_pTargetTexture = SDL_CreateTexture(m_pRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 800, 600);
// Make sure that the tile list is cleared / empty at program start
m_TileList.clear();
// Pretend that this function loads a map file, sets the tile type, positions the tile quad and sets the clip quad (see the tile struct above)
CreateTileMap();
}
}
}
while(m_Running)
{
while(SDL_PollEvent(&m_Event))
{
switch(m_Event.type)
{
case SDL_QUIT:
{
m_Running = false;
break;
}
}
}
// Only need to render the tile list if a tileset texture has been successfully created
if(m_pTilesetTexture)
{
// Clear the current render target
SDL_RenderClear(m_pRenderer);
// Set the target texture to be the current render target
if(m_pTargetTexture)
SDL_SetRenderTarget(m_pRenderer, m_pTargetTexture);
TileList::iterator it = m_TileList.begin();
for(; it != m_TileList.end(); ++it)
{
if((*it))
{
// Render each tile
SDL_RenderCopy(m_pRenderer, m_pTilesetTexture, &(*it)->TileClipQuad, &(*it)->TileQuadPosition);
}
}
// Reset the render target
SDL_SetRenderTarget(m_pRenderer, NULL);
// Render the target texture
if(m_pTargetTexture)
SDL_RenderCopy(m_pRenderer, m_pTargetTexture, NULL, NULL);
// Update the screen
SDL_RenderPresent(m_pRenderer);
}
}
// Destroy the target texture
if(m_pTargetTexture)
SDL_DestroyTexture(m_pTargetTexture);
// Destroy the tileset texture
if(m_pTilesetTexture)
SDL_DestroyTexture(m_pTilesetTexture);
TileList::iterator it = m_TileList.begin();
// Destroy the tile list
for(; it != m_TileList.end(); ++it)
{
if((*it))
delete (*it);
}
m_TileList.clear();
// Destroy the SDL renderer
if(m_pRenderer)
SDL_DestroyRenderer(m_pRenderer);
// Destroy the SDL window
if(m_pWindow)
SDL_DestroyWindow(m_pWindow);
// Quit SDL
SDL_Quit();
return 0;
}