We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 4542

Summary: Image flipped vertically when rendering on texture
Product: SDL Reporter: Alain <alain.bonnefoy>
Component: renderAssignee: Sam Lantinga <slouken>
Status: RESOLVED FIXED QA Contact: Sam Lantinga <slouken>
Severity: major    
Priority: P2 CC: alain.bonnefoy, ant, sylvain.becker
Version: 2.0.9   
Hardware: All   
OS: Linux   

Description Alain 2019-03-08 08:22:36 UTC
Hello,

I'm using the SDL 2.0.9 with patch 12581 (for eGalax touchscreen support), on Linux with openglES2, with or without X server.

I just noticed that when I render an image directly on screen everything is working fine but when I render this image on a texture and then this texture on screen, the result is flipped vertically.

The test case is this one:

#include <stdio.h>
#include <stdlib.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_image.h>

SDL_Texture *WID_loadTexture( SDL_Renderer *renderer, char *name)
{
	SDL_Texture *texture = NULL;

	if(renderer == NULL)
	{
		printf("%s\n", SDL_GetError());
		SDL_ClearError();
		return NULL;
	}

	SDL_Surface* surface = IMG_Load( name );
	if (surface == NULL)
	{
		printf("%s\n", SDL_GetError());
	}
	else
	{
		texture = SDL_CreateTextureFromSurface( renderer, surface );
		if (texture == NULL) {
			printf("%s\n", SDL_GetError());
			SDL_ClearError();
			return NULL;
		}
		SDL_FreeSurface( surface );
	}

	return texture;
}


int main(void) {
	SDL_Window *WID_main_window;
	SDL_Renderer *WID_main_renderer;
    SDL_Event sdlevt;


    if ( SDL_Init( SDL_INIT_EVERYTHING) < 0 ) {
        exit(-1);
    }

	int video_driver_index = 1;
	int renderer_index = 1;

    SDL_RendererInfo info;

    setenv("SDL_VIDEODRIVER",SDL_GetVideoDriver(video_driver_index),1);

	SDL_GetRenderDriverInfo(renderer_index,&info);

	if(SDL_SetHint(SDL_HINT_RENDER_DRIVER, info.name) != SDL_TRUE)
	{
		printf("SDL_SetHint %s\n", SDL_GetError());
		exit(-1);
	}

	if ((WID_main_window = SDL_CreateWindow("icoone", 0, 0, 1024, 768, SDL_WINDOW_SHOWN)) == NULL) {
		exit(-1);
	}

	if ((WID_main_renderer = SDL_CreateRenderer(WID_main_window, renderer_index, info.flags)) == NULL) {
		exit(-1);
	}

    if (SDL_SetRenderTarget(WID_main_renderer, NULL) != 0) {
		printf("SDL_SetRenderTarget %s\n", SDL_GetError());
		exit(-1);
	}
	SDL_SetRenderDrawColor(WID_main_renderer, 0x80, 0x80, 0x80, SDL_ALPHA_OPAQUE);

	SDL_Texture *drawingTexture;

	if ((drawingTexture = SDL_CreateTexture(WID_main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1024, 768)) == NULL) {
		printf("SDL_CreateTexture %s\n", SDL_GetError());
		exit(-1);
	}

	SDL_Texture			*backgroundImage;
	if ((backgroundImage = WID_loadTexture(WID_main_renderer, "visage.jpg")) == NULL) {
		exit(-1);
	}

	SDL_RenderClear(WID_main_renderer);

	if (SDL_SetRenderTarget(WID_main_renderer, NULL) != 0) {
		fprintf(stderr, "SDL_SetRenderTarget error %s\n", SDL_GetError());
		exit(-1);
	}

	if (SDL_RenderCopy( WID_main_renderer, backgroundImage, NULL, NULL) != 0) {
		fprintf(stderr, "SDL_RenderCopy error %s\n", SDL_GetError());
		exit(-1);
	}

	SDL_RenderPresent(WID_main_renderer);

	SDL_Delay(2000);

	SDL_RenderClear(WID_main_renderer);

	if (SDL_SetRenderTarget(WID_main_renderer, drawingTexture) != 0) {
		fprintf(stderr, "SDL_SetRenderTarget error %s\n", SDL_GetError());
		exit(-1);
	}
	if (SDL_RenderCopy( WID_main_renderer, backgroundImage, NULL, NULL) != 0) {
		fprintf(stderr, "SDL_RenderCopy error %s\n", SDL_GetError());
		exit(-1);
	}

	if (SDL_SetRenderTarget(WID_main_renderer, NULL) != 0) {
		fprintf(stderr, "SDL_SetRenderTarget error %s\n", SDL_GetError());
		exit(-1);
	}

	if (SDL_RenderCopy( WID_main_renderer, drawingTexture, NULL, NULL) != 0) {
		fprintf(stderr, "SDL_RenderCopy error %s\n", SDL_GetError());
		exit(-1);
	}

	SDL_RenderPresent(WID_main_renderer);

    int loop = 1;
    while (loop) {
    	SDL_PollEvent(&sdlevt);

		if ( sdlevt.type == SDL_QUIT ) {
			loop = 0;
		}

		SDL_Delay(50);
		SDL_FlushEvent(SDL_MOUSEMOTION);

    }

    SDL_DestroyTexture(backgroundImage);
    SDL_DestroyRenderer(WID_main_renderer);
    SDL_DestroyWindow(WID_main_window);
    SDL_Quit();

	return EXIT_SUCCESS;
}

The image is here https://i.imgur.com/Gg4IBM2.jpg

Alain
Comment 1 Sylvain 2019-03-08 14:14:15 UTC
Just tried an it seems to happen at least with opengl, es, es2, but not with software renderer.

it seems that changing the target texture doesn't set "viewport_dirty" and so the projection isn't modified.

Changing (not a patch) :
data->drawstate.viewport_dirty = SDL_TRUE; 
to FALSE in SDL_render_gles2.c 
proves that it would work

Not sure where the best place to fix this, 
maybe setting viewport_dirty in GLES2_SetRenderTarget() ?


--- a/src/render/opengles2/SDL_render_gles2.c	Sat Feb 23 09:36:56 2019 +0100
+++ b/src/render/opengles2/SDL_render_gles2.c	Fri Mar 08 15:13:41 2019 +0100
@@ -1761,6 +1762,9 @@
             return SDL_SetError("glFramebufferTexture2D() failed");
         }
     }
+
+    data->drawstate.viewport_dirty = SDL_TRUE;
+
     return 0;
 }
Comment 2 Sylvain 2019-03-08 14:26:34 UTC
Could also be done at the beginning of GLES2_RunCommandQueue
like metal renderer does

--- a/src/render/opengles2/SDL_render_gles2.c	Sat Feb 23 09:36:56 2019 +0100
+++ b/src/render/opengles2/SDL_render_gles2.c	Fri Mar 08 15:21:30 2019 +0100
@@ -1206,6 +1207,7 @@
         return -1;
     }
 
+    data->drawstate.viewport_dirty = SDL_TRUE;
     data->drawstate.target = renderer->target;
     if (!data->drawstate.target) {
         SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh);
Comment 3 Sylvain 2019-03-08 14:30:44 UTC
Which makes me think there is big difference between Metal and GL renderers.

For Metal renderer: METAL_DrawStateCache is created and used only in RunCommandQueue

For GL renderers: GLES2_DrawStateCache belongs to the GLES2_RenderData and is available anytime. Which looks more complicated.
Comment 4 Anthony @ POW Games 2019-03-12 02:22:29 UTC
This happens for me on Windows openGL, but only with the current source (HG). The 2.0.9 DLL works OK on Windows.
Comment 5 Sylvain 2019-03-12 07:01:05 UTC
I have tested and fixed on linux for gl, gles2, gles
https://hg.libsdl.org/SDL/rev/6a60a1e87d10

Please try also on your side
Comment 6 Alain 2019-03-12 12:04:11 UTC
Hello,

Many many thanks for looking at this issue so quickly.
I greatly appreciate!

I need to ask my electronic board provider to rebuild it to be sure to test it in situation.

I hope I'll get back to you very soon.

Alain