Texture to texture rendering (offscreen)

Hello. I have an idea about developing a game and I hope SDL2 is good tool to help me in game development.
I’m new in SDL and I have some newbie questions about working with SDL.

First of all I want to understand working with renderer and textures.
My idea is doing some offscreen rendering from texture to texture. And then render some of this graphics data to window on the screen. I mean 2 separate rendering processes: first offsceen and second to screen.
[list=1:66121f3918][*:66121f3918] I can’t understand why I can create renderer only assigned to window, if I want to use this renderer only to make offscreen rendering from texture to texture? How correctly create renderer to rendering from texture to texture?

[*:66121f3918] Can I create 2 separate renderers: first to offscreen rendering (rendercopy from texture to texture) and second to render graphics to window?

[*:66121f3918] Can I use 2 renderers in 2 threads: first renderer is working in separate background thread and second renderer is working in main thread? I mean 2 threads don’t share one renderer, but share a texture, and only one thread always write to this texture and other thread only read from this texture.

[*:66121f3918] Why I can’t load image from file to texture? I must load it to surface and only from surface to texture. This is not a problem, but this is strange for me: why SDL2 developers don’t implement a function to load image from file to texture without surface in the middle?[/list:o:66121f3918]May be I ask more questions later. Thanks for answers.

1 and 2:

When you’re creating a SDL window and at the same time creating a SDL Renderer, you’re assigning that renderer to the SDL window. You can of course create multiple windows and multiple renderers if you want / need to.

In your case, you only need to create one window and one renderer. If you want to render one texture onto another texture it is done like this:

  1. Create one texture, let’s call it Texture1.
  2. Create an empty texture, let’s call it TargetTexture.
  3. Change the current render target to TargetTexture.
  4. Render Texture1 as usual (with SDL_RenderCopy or SDL_RenderCopyEx). Texture1 has now been rendered onto TargetTexture and won’t be rendered in the window until TargetTexture is rendered.
  5. Reset the render target.
  6. Render TargetTexture as usual (with SDL_RenderCopy or SDL_RenderCopyEx).

3. As far as I understand, you can create one renderer in the main thread and one renderer in another thread, BUT, the only thread that SDL render functions can / should be used in, is in the same thread as the window has been created in (which is usually in the main thread). This mean that the second renderer (which has been created in another thread), will be useless, unless you’ve created a secondary window in that thread of course.

4. There’s no function in SDL2 that takes an image and creates a SDL_Texture from it, but the SDL_Image external library has a function named IMG_LoadTexture though, that takes an image and creates a SDL_Texture from it.

I hope this answered your question. Don’t hesitate to ask more if you have more questions.

Naith, many thanks for your answers.

I try to explain why I want to use threads to render. In my mind my future game work like this: I create a texture and load an image to this texture (call it “tileset”). Next I create new texture to use it as game map (call it “map”). To draw “map” I copy tiles images from “tileset” to “map”. But “map” is not what user see in window - every time user see only part of map. This all is not interesting :slight_smile:
Interesting next: when user see some interaction in window (including animations), this time other (background) renderer make some changes on “map” in parallel. Two rendering processes in parallel (not always parallel, but frequently). May be this is impossible?

For example: User see animation in window with frequency about 60 fps (this is not final number, this is only for example). This mean every 1/60 of second image in window must be re-rendered (not very easy work). This animation almost every time. Same time some event in game engine - need to make changes on map (copy some new tiles from “tileset” to “map”). User should not feel this background rendering as lag in animation. This is what I want.

I think now: may be not to try share texture between 2 threads with 2 renderers (may be this is impossible). May be create second thread “isolated” - with its own source and target textures. This thread create own copy of “map” and make changes on this copy. And next “map” texture used in main thread is replaced by new version of “map” from second thread.

I think I understand what you want to do and what you want to avoid. I actually don’t think you’ll need another thread and do all that “behind the scenes”-changes on the textures. If you design your game- and code correctly, you won’t see any flickering, lag or other weird artifacts when everything is rendered in the window. I’ve made several tilemap-tests, with tilemap’s that’s being rendered in 1 thread, and I’ve never gotten any lag whatsoever whenever I used an animated tile (for example). One thing that’ve learned is that you shouldn’t use multithreading if it’s not necessary and in your case I don’t think it’s necessary. Sorry if this isn’t as good answer as you would want.

This is good answer. I think about make rendering without threads too. But I like idea with threads, may be I’m wrong.
I have some experience developing with threads and I like it :slight_smile: Threads can help doing many interesting things. And to better use power of modern multicore CPUs (even my smartphone is multicore :)) If I find a way how to make all rendering in main thread, then I use multithreading for other things. Synchronization is not a problem.

May be you can show me a way how better make rendering in one thread in my case? May be some hints?

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;
}

Thank you for code, but I want to ask: how many tiles you copy per one re-draw cycle (or per frame), what size of tiles and how many time you use to copy all this tiles? If this is really small time, then I think I doesn’t need rendering in separate thread. But what if I want to copy about 300 tiles (32x32 pixels), about 4-5 times per second? This is about 1500 rendercopy operations per second. Is this number not too big?

The number of tiles being rendered is all up to you really but I usually use 32x32 tiles and fill the whole window with them and if I use a window size of 800x600 it’s about 450-500 tiles. Just remember that I don’t need to render the whole tile map every frame. I only need to render the tiles that are visible in the window, even if the tilemap contains 2000-3000 tiles.

Let’s say I have a window of 800x600 size. This means that, each frame, the game will render 450-500 tiles to the target texture and then render the target texture. This is made every frame. When I want to check how optimized the code is, and want’s to check how the FPS is during rendering, I always turn of the “check if the tile is visible in the window”-function and checks how much the FPS drops. This means that I render a lot of tiles I don’t need to render but I haven’t had any problems with the FPS. So yeah… I don’t think you need to worry much but that of course depends of what your computer can handle, how many tiles you’re rendering and such. I don’t think 1500 operations is too much. But like I said, you don’t need to render the tiles that are not visible in the window.

Do you render all 500 tiles again and again each frame? IMO this is not best solution for performance. Am I wrong? And don’t forget about layers and mobs. If you have more than 1 layer in map, then multiply 500 tiles by layers count. Plus mobs. Mobs rendered each frame. I don’t know better solution for mobs.

No, I render each tile 1 time, each frame. Why would I render each tile more than 1 time, each frame?

No, I render each tile 1 time, each frame. Why would I render each tile more than 1 time, each frame?
I mean if your visible area include 500 tiles, then you render all this 500 tiles each frame? Next frame you render again all this 500 tiles. Tiles not changed yet, but you re-render all tiles again. If I correctly understand, this not best solution for performance IMO. Or I’m not correct understand?

I mean if your visible area include 500 tiles, then you render all this 500 tiles each frame? Next frame you render again all this 500 tiles. Tiles not changed yet, but you re-render all tiles again.
Correct. All the tiles that are inside the camera (i.e visible in the window) is being rendered one frame and the next frame the same is done. If I don’t render the tiles each frame, for example render first frame, don’t render second frame and then render again the third frame, there will be flickering. So even if it’s not best solution for performance or not, they need to be rendered each frame.