SDL Graphics Tearing Problem

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Garak
Posts: 46
Joined: Wed Jul 27, 2005 1:59 am

SDL Graphics Tearing Problem

Post by Garak »

Greetings,

I have been using the SDL to develop some “vaporware”, and have come across a problem I cannot seem to solve. When I draw my background image to the screen (a full 480x272 pixels) there is a very noticeable tearing effect. Here is my setup:

I have a very large background image (960x272). Each pass through the main loop, a different portion of that image is drawn to the screen (along the x axis) to give the effect that the background is moving. This is when I experience the tearing effect.

I can only guess this has something to do with the SDL not drawing the image during the Vertical Blank period. I have examined the SDL video source file SDL_pspvideo.c and it appears that it uses sceGuDrawArray to paint images. I do not see any calls to sceDisplayWaitVblankStart as I would expect to synchronize the drawing of the graphics. Maybe the sceGuDrawArray command when used with sceGuSwapBuffers does not need to wait for a VBlank, I am no expert here. All I know is a tearing effect is occurring.

I have tried about every combination of setting my screen mode that is possible: varying color depths, converting my background to the screen’s surface format, etc. While certain combinations do often speed up graphics rendering, they do not solve the tearing problem. The link below points to a very simple demo that illustrates the problem I am having. If anyone has an idea why my background tears as I draw it to the screen, any help you can offer will be greatly appreciated!

Demo Link: http://www.savefile.com/files/1672479

Garak
jimparis
Posts: 1145
Joined: Fri Jun 10, 2005 4:21 am
Location: Boston

Re: SDL Graphics Tearing Problem

Post by jimparis »

Garak wrote:I can only guess this has something to do with the SDL not drawing the image during the Vertical Blank period. I have examined the SDL video source file SDL_pspvideo.c and it appears that it uses sceGuDrawArray to paint images. I do not see any calls to sceDisplayWaitVblankStart as I would expect to synchronize the drawing of the graphics.
You could add your own wait for vsync before any calls that update the screen (SDL_Flip, SDL_UpdateRect).
Garak
Posts: 46
Joined: Wed Jul 27, 2005 1:59 am

Post by Garak »

I have allready tried this and I get the same effect. I imagine I could go in and alter the SDL source files as well, but I have allready done so many trial and error scenerios, I would like to see if anyone has any new input.

I am guessing my calls to Vblank placed before SDL_Flip and Update Rec failed becasue so much seems to go on in these SDL function calls. There is probably a specific place in the SDL updaterec function where such a call should be placed, like by a swap buffer call or something. Any input is appreciated.
rinco
Posts: 255
Joined: Fri Jan 21, 2005 2:12 pm
Location: Canberra, Australia

Post by rinco »

The GU stuff in SDL_pspvideo is only used for a software surface, which is only useful to those working with non-PSP resolutions (ie: not you).

I'm going to take a guess that you are using a hardware surface without double buffering. Try using double buffering.

If that doesn't work, try loading your 960x272 image in vram.

And then confirm that the colour format for the image is the same as that of the screen.
Garak
Posts: 46
Joined: Wed Jul 27, 2005 1:59 am

Post by Garak »

I have been using double buffering. I have used double buffering with my buffer in VRAM and in system memory. In both cases, I have converted the background image to the screen's format (I am using 16 bit mode). Neither scenerio has worked.

For that matter I have tried using single buffers, located in both hardware and software RAM. Same effect, usually slighty worse when using VRAM. In my demo code, I have commentd out a few variations I have tried when seting up the screen mode.

Also, I did try loading my BG image into VRAM, but I could never get it to actually do so. It allways claims the image was loaded into system RAM. But none the less, I don't figure it should make a difference where my BG is loaded. If I am using double buffering (in VRAM), the BG will ultimatly be coppied to a buffer in VRAM. After the entire image is there (in VRAM) and I call Flip(), it should be sent to the screen, at least that was my understanding.

Is there any reason the SDL_pspvideo does not call upon any waits for VBLANK?
mrbrown
Site Admin
Posts: 1537
Joined: Sat Jan 17, 2004 11:24 am

Post by mrbrown »

Garak wrote:Is there any reason the SDL_pspvideo does not call upon any waits for VBLANK?
This is in tandem with most other SDL ports - none have an explicit wait for VBLANK.
rinco
Posts: 255
Joined: Fri Jan 21, 2005 2:12 pm
Location: Canberra, Australia

Post by rinco »

The tearing seems to go away with a waitvblank...

Code: Select all

    SDL_framerateDelay(&fpsMan);  // Let frame frate manager do hi 'thang'
    sceDisplayWaitVblankStart();
    SDL_Flip(screen);             // Update the screen
Although, maybe it would be better in the SDL_framerateDelay function... ?
mrbrown
Site Admin
Posts: 1537
Joined: Sat Jan 17, 2004 11:24 am

Post by mrbrown »

Isn't that what sceDisplayWaitVblankStart() does? You use it and you will run at 60 (or 30, I forget). I would think that if you use a manual delay and then call it you will drop frames.
urchin
Posts: 121
Joined: Thu Jun 02, 2005 5:41 pm

Post by urchin »

Yes. I had that problem before. My program was calculating when the next frame should start and delaying the thread. The frame rate I got was accurate, but I was getting tearing appearing in different places (presumably because SDL_Flip was being called mid screen)

I added in a vsync wait and the frame rate dropped as my delay was pushing the wait to the next vsync. So I removed the delay and the tearing reappeared, but was consistently in the same place about 1/3 down the screen!

I've tried using a software surface too, but then I get stepped tearing across the top of the screen (as the screen is painted as several textured vertical stripes).

I'm currently using 8 bit surfaces. Could that be the source of my problems?
User avatar
Nige
Posts: 10
Joined: Thu Sep 29, 2005 10:30 am
Contact:

Post by Nige »

This is in tandem with most other SDL ports - none have an explicit wait for VBLANK.
This isn't quite true - some do and some don't. For example, the SVGA driver definately does wait for a vblank, and the DirectX5 driver will depending on the video driver settings.

I think the PSP driver would be improved by putting off a flip until the next frame starts. This could easily be achieved by changing the following line in PSP_FlipHWSurface:

Code: Select all

sceDisplaySetFrameBuf(surface->pixels, 512,
				this->hidden->pixel_format, PSP_DISPLAY_SETBUF_IMMEDIATE);
to:

Code: Select all

sceDisplaySetFrameBuf(surface->pixels, 512,
				this->hidden->pixel_format, PSP_DISPLAY_SETBUF_NEXTFRAME);
This would, in theory, make all flips occur exactly at the start of the next frame, therefore solving the main issue with the SDL driver as it stands - that you can't get double-buffering without some sort of screen tearing.

I haven't tried this change yet but if anyone who does try it would let me know of its effects (in this thread) I'd be grateful.
jimparis
Posts: 1145
Joined: Fri Jun 10, 2005 4:21 am
Location: Boston

Post by jimparis »

Nige wrote:This isn't quite true - some do and some don't. For example, the SVGA driver definately does wait for a vblank, and the DirectX5 driver will depending on the video driver settings.
The SDL docs say that SDL_Flip() should set up a flip but return immediately, so having the flip wait for vblank would be wrong. SDL seems somewhat poorly specified in this regard, so yes, there are a bunch of drivers that do it differently.
I think the PSP driver would be improved by putting off a flip until the next frame starts.
Yes, _NEXTFRAME sounds like it's exactly what we want (flip the buffer at the next frame), but I recall that there were some problems with it when it was last tried. My guess is that the problem lies in the fact that we only have two buffers: if a client fills buffer 2, calls SDL_Flip, then starts writing buffer 1 before the vsync, they'll be writing directly to the displayed screen. Three buffers could potentially fix this, if we have the VRAM.
urchin
Posts: 121
Joined: Thu Jun 02, 2005 5:41 pm

Post by urchin »

I just ran the following test with hardware surfaces:

Code: Select all

	int col_index = 0;
	int col[] = { 0x0, 0xff };

	SDL_Init(SDL_INIT_VIDEO);
	SDL_ShowCursor(0);

	SDL_Surface* screen = SDL_SetVideoMode(480, 272, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);

	while(true) {
		SDL_FillRect(screen, NULL, col[col_index]);
		col_index ^= 1; 	
		sceDisplayWaitVblankStart();
		SDL_Flip(screen);
	}
and it worked fine for 32 and 16 bit surfaces. For 8 bit, the flip always happens 1/3 the way down the screen. btw, code is pruned from my demo :)
rinco
Posts: 255
Joined: Fri Jan 21, 2005 2:12 pm
Location: Canberra, Australia

Post by rinco »

This code should have prevented you from attempting to create an 8bit hardware surface:

Code: Select all

    case 8:
        if (IS_HWSURFACE(flags)) {
            SDL_SetError("8-bit surfaces are only supported on software surfaces.");
            return NULL;
        }
But it sounds like tearing. I suspect SDL is missing a sceDisplayWaitVblankStart and/or mutex for double buffered screens, and this could be done in PSP_LockHWSurface.
mrbrown
Site Admin
Posts: 1537
Joined: Sat Jan 17, 2004 11:24 am

Post by mrbrown »

Mutex? SDL video code is not thread safe, you can only use it from a single thread.
urchin
Posts: 121
Joined: Thu Jun 02, 2005 5:41 pm

Post by urchin »

rinco> ah, my bad. i was logging the message that it had failed, but carrying on regardless ;)

Just reading some other comments on SDL and I've read that if SetVideoMode is called with a bit depth that the hardware doesn't support, a shadow surface is created. Would this be worth looking into?
User avatar
Nige
Posts: 10
Joined: Thu Sep 29, 2005 10:30 am
Contact:

Post by Nige »

urchin wrote:Just reading some other comments on SDL and I've read that if SetVideoMode is called with a bit depth that the hardware doesn't support, a shadow surface is created. Would this be worth looking into?
This is the thing - if you want a hardware video mode then you must use 480x272 at 15, 16 or 32 bits. If you want any other bitdepth or resolution then you must use a software surface. Usually, but not always, hardware surfaces are faster. The exception is generally if you do a lot of reading from the screen, software surfaces might be faster.

Shadow surfaces are software surfaces. They support double-buffering. At flip() time they will convert their image to the display format and blit it to the screen. On the PSP driver, all blitting is unaccelerated. I'd expect this to be a whole lot slower than any other type of video mode.

From the SDL source:
There are three conditions under which we create a shadow surface:
1. We need a particular bits-per-pixel that we didn't get.
2. We need a hardware palette and didn't get one.
3. We need a software surface and got a hardware surface.
User avatar
Nige
Posts: 10
Joined: Thu Sep 29, 2005 10:30 am
Contact:

Post by Nige »

jimparis wrote:The SDL docs say that SDL_Flip() should set up a flip but return immediately, so having the flip wait for vblank would be wrong.
Agreed.
Yes, _NEXTFRAME sounds like it's exactly what we want (flip the buffer at the next frame), but I recall that there were some problems with it when it was last tried. My guess is that the problem lies in the fact that we only have two buffers: if a client fills buffer 2, calls SDL_Flip, then starts writing buffer 1 before the vsync, they'll be writing directly to the displayed screen. Three buffers could potentially fix this, if we have the VRAM.
Ah yes, I hadn't thought of that. Perhaps triple-buffering would be a good idea. How much VRAM is generally available? We'd need.. 1530K to triple-buffer at 32bpp.
Post Reply