SDL Graphics Tearing Problem
SDL Graphics Tearing Problem
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
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
Re: SDL Graphics Tearing Problem
You could add your own wait for vsync before any calls that update the screen (SDL_Flip, SDL_UpdateRect).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.
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.
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.
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.
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.
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?
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?
The tearing seems to go away with a waitvblank...
Although, maybe it would be better in the SDL_framerateDelay function... ?
Code: Select all
SDL_framerateDelay(&fpsMan); // Let frame frate manager do hi 'thang'
sceDisplayWaitVblankStart();
SDL_Flip(screen); // Update the screen
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?
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?
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.This is in tandem with most other SDL ports - none have an explicit wait for VBLANK.
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);
Code: Select all
sceDisplaySetFrameBuf(surface->pixels, 512,
this->hidden->pixel_format, PSP_DISPLAY_SETBUF_NEXTFRAME);
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.
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.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.
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.I think the PSP driver would be improved by putting off a flip until the next frame starts.
I just ran the following test with hardware surfaces:
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 :)
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);
}
This code should have prevented you from attempting to create an 8bit hardware surface:
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.
Code: Select all
case 8:
if (IS_HWSURFACE(flags)) {
SDL_SetError("8-bit surfaces are only supported on software surfaces.");
return NULL;
}
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.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?
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.
Agreed.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.
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.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.