Rendering into texture and showing it without it becoming invalid

Using SDL3. What I want to do is to have a texture that I can render onto (using RenderLine etc.), and periodically show it in a window, but without the texture becoming invalid. So I want to treat the texture as a permanent back buffer that I can reuse, show, update and show again. But I haven’t been able to figure out how to do that. I have tried a number of things, but I keep running into problems (like nothing showing, things only partially showing, etc.). Can anyone point me in the right direction, assuming this is possible at all?

(Note that using a STREAMING i/o a TARGET texture, this works fine, but I don’t want to reinvent the wheel by creating my own RenderLine etc.)

Is there a reason why you use SDL_TEXTUREACCESS_STREAMING instead of SDL_TEXTUREACCESS_TARGET?

Sorry for the confusion, but I do use TARGET. I was just mentioning that using STREAMING it works fine, but that prevents me from using RenderLine etc.

Ah, but in that case I think there is no reason why it shouldn’t work. Care to post a small example to demonstrate the problem?

1 Like

Well, like the documentation says, “The backbuffer should be considered invalidated after each present; do not assume that previous contents will exist between frames.” What I’ve seen happening is that depending on the delay time I take when debugging between the RenderLine and RenderPresent, it may or may not “work” (i.e. may or may not show what I intended).

But SDL_RenderPresent is not used when rendering to textures (see note at the end).

2 Likes

You’re right, but after rendering to the Texture I wanted to show it on screen, so I changed the rendering target to the default (using NULL) and then did a RenderTexture. But that also didn’t quite work. Hence my question: what functions should I work to make it work.

You can easily use a texture as a back buffer, where you can paint something once, and then use it repeatedly to render the contents of the window (or the contents of other texture buffers).

The content of textures created manually, as targets, will not change spontaneously. The invalidation quoted from the documentation applies only to the swapchain texture (the internal texture representing the window content). What these words want to convey is to inform that you should render each frame from scratch and repaint the entire area of the window, instead of repainting only the sub-areas in which something is to change.

It should be noted here that the SDL_RenderPresent function should be called only once in each game frame, and always after the rendering process is complete, because (in simple terms) it is used to repaint the window on the screen. Calling it multiple times within a single game frame makes no sense.

If you want to render something on the target texture, you change the target to this texture using SDL_SetRenderTarget, then paint on it what you need (e.g. lines, rectangles, other textures, etc.) and that’s it — you have what you need. Then you can change the target to another texture-buffer, paint something on it, change to another texture, paint something, and so on and so on. Finally, when everything is rendered, set the target to null and call SDL_RenderPresent to show the new game frame on the screen.

1 Like

It should. It’s what I do (in SDL2) and it works flawlessly on all platforms.

I assume you have code similar to this (again SDL2, but I don’t think SDL3 is very different):

	SDL_SetRenderTarget(renderer, NULL);
	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
	SDL_RenderClear(renderer);
	SDL_RenderCopy(renderer, texture, &SrcRect, &DestRect);
	SDL_RenderPresent(renderer);
	SDL_SetRenderTarget(renderer, texture);

@jalnl : At this point I think it would be helpfull if you posted some code showing what you are trying to do.

1 Like

try this
`

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

// https://lazyfoo.net/tutorials/SDL/43_render_to_texture/index.php

int main(int argc, char *argv[])
{
	SDL_Window *window = NULL;
	SDL_Renderer *renderer = NULL;
	SDL_Texture *rendering_texture = NULL;
	SDL_Surface *surface = NULL;
	SDL_Texture *bitmap = NULL;
	SDL_Event event;
	
	if (!SDL_Init(SDL_INIT_VIDEO)) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
		return 3;
	}
	
	if (!SDL_CreateWindowAndRenderer("Hello SDL", SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s", SDL_GetError());
		return 3;
	}
	
	surface = SDL_LoadBMP("sample.bmp");
	if (!surface) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create surface from image: %s", SDL_GetError());
		return 3;
	}
	
	bitmap = SDL_CreateTextureFromSurface(renderer, surface);
	if (!bitmap) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture from surface: %s", SDL_GetError());
		return 3;
	}
	
	SDL_DestroySurface(surface);
		
	rendering_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT);
	if (!rendering_texture) {
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture with pixelformat: %s", SDL_GetError());
		return 3;
	}
	
	while (1) {
		SDL_PollEvent(&event);
		if (event.type == SDL_EVENT_QUIT) {
			break;
		}
		
		SDL_SetRenderTarget(renderer, rendering_texture);
		
		// start drawing stuff here 
			
		SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
		SDL_RenderClear(renderer);
		
		//Render green filled quad
		SDL_FRect fillRect = {SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2};
		SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);		
		SDL_RenderFillRect(renderer, &fillRect);
		
		//Render blue outlined quad
		SDL_FRect outlineRect = {SCREEN_WIDTH / 6, SCREEN_HEIGHT / 6, SCREEN_WIDTH * 2 / 3, SCREEN_HEIGHT * 2 / 3};
		SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);		
		SDL_RenderRect(renderer, &outlineRect);
		
		//Draw white horizontal line
		SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);		
		SDL_RenderLine(renderer, 0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
		
		SDL_FRect bmpDestination = {0, 0, 40, 40};  
		SDL_RenderTexture(renderer, bitmap, NULL, &bmpDestination);
		
		// stop drawing stuff
		
		SDL_SetRenderTarget(renderer, NULL);
		
		SDL_FRect renderQuad = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
		SDL_RenderTexture(renderer, rendering_texture, &renderQuad, NULL);
		
		SDL_RenderPresent(renderer);
	}
	
	SDL_DestroyTexture(bitmap);
	SDL_DestroyTexture(rendering_texture);
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	
	SDL_Quit();
	
	return 0;
}

`