SDL/PS2 screen flickering - need help

Discuss the development of software, tools, libraries and anything else that helps make ps2dev happen.

Moderators: cheriff, Herben

Post Reply
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

SDL/PS2 screen flickering - need help

Post by protomank »

I am developing a project in SDL for PS2, but I noticed in my TV it looks very bad, blincking like hell, it hirts your eyes, mostly for white as if the frequency was very low, much like in this old topic: 640x512 PAL mode.

For the details, I have a TV that works both with PAL and NTSC, a NTSC PS2 and just initialize SDL as follows:

SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) < 0
game_screen = SDL_SetVideoMode(MAX_W, MAX_H, 32, SDL_SWSURFACE|SDL_FULLSCREEN);

I've hacked the gsKit and SDL to try different mode (PAL/NTSC) without any luck to fix this.

I just want to know, if this is a known problem with SDL that needs to be fixed (and I will gladly try) or happens only in my TV/PS2 console. In short, I need help, because this TV flickering thing is a major blocker for the project.
ragnarok2040
Posts: 202
Joined: Wed Aug 09, 2006 1:00 am

Post by ragnarok2040 »

You can try filtering the flicker, but it'll require modifying gsKit. I haven't done it because I've been busy modifying libgraph and libdraw to replace gsKit.

This should be all that's needed for enabling the flicker filter. You should keep the old code for non-interlaced/progressive modes, and just enable the new code using an if-else statement.
In gsKit_init_screen() in gsInit.c:

Code: Select all

  if&#40;flicker_filter&#41;  &#123;
	GS_SET_PMODE&#40;	1,		// Read Circuit 1
			1,		// Read Circuit 2
			1,		// Use ALP Register for Alpha Blending
			0,		// Alpha Value of Read Circuit 1 for Output Selection
			1,		// Blend Alpha with output of Read Circuit 2
			0x70&#41;;		// Alpha Value = 1.0 

	GS_SET_DISPFB1&#40;	0,			// Frame Buffer Base Pointer &#40;Address/2048&#41;
			gsGlobal->Width / 64,	// Buffer Width &#40;Address/64&#41;
			gsGlobal->PSM,		// Pixel Storage Format
			0,			// Upper Left X in Buffer
			0&#41;;

	GS_SET_DISPFB2&#40;	0,			// Frame Buffer Base Pointer &#40;Address/2048&#41;
			gsGlobal->Width / 64,	// Buffer Width &#40;Address/64&#41;
			gsGlobal->PSM,		// Pixel Storage Format
			0,			// Upper Left X in Buffer
			1&#41;;			// Upper Left Y in Buffer

	GS_SET_DISPLAY1&#40;gsGlobal->StartX,		// X position in the display area &#40;in VCK unit
			gsGlobal->StartY,		// Y position in the display area &#40;in Raster u
			gsGlobal->MagH,			// Horizontal Magnification
			gsGlobal->MagV,			// Vertical Magnification
			gsGlobal->DW - 1,	// Display area width
			gsGlobal->DH - 1&#41;;		// Display area height

	GS_SET_DISPLAY2&#40;gsGlobal->StartX,		// X position in the display area &#40;in VCK units&#41;
			gsGlobal->StartY,		// Y position in the display area &#40;in Raster units&#41;
			gsGlobal->MagH,			// Horizontal Magnification
			gsGlobal->MagV,			// Vertical Magnification
			gsGlobal->DW - 1,	// Display area width
			gsGlobal->DH - 2&#41;;		// Display area height
  &#125;
This enables both read circuits, sets the second read circuit's buffer down a line, removes the extra line we have in read circuit 2, and blends the even/odd lines together using 0x70 as the alpha value.

In gsCore.c:

Code: Select all

void gsKit_sync_flip&#40;GSGLOBAL *gsGlobal&#41;
&#123;
	if&#40;!gsGlobal->FirstFrame&#41;
	&#123;
		gsKit_vsync_wait&#40;&#41;;

		if&#40;gsGlobal->DoubleBuffering == GS_SETTING_ON&#41;
		&#123;
			GS_SET_DISPFB1&#40; gsGlobal->ScreenBuffer&#91;gsGlobal->ActiveBuffer & 1&#93; / 8192,
			gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 &#41;;

			if &#40;flicker_filter&#41;
				GS_SET_DISPFB2&#40; gsGlobal->ScreenBuffer&#91;gsGlobal->ActiveBuffer & 1&#93; / 8192,
				gsGlobal->Width / 64, gsGlobal->PSM, 0, 1 &#41;;
			else
				GS_SET_DISPFB2&#40; gsGlobal->ScreenBuffer&#91;gsGlobal->ActiveBuffer & 1&#93; / 8192,
				gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 &#41;;

			gsGlobal->ActiveBuffer ^= 1;
			gsGlobal->PrimContext ^= 1;
		&#125;

	&#125;

	gsKit_setactive&#40;gsGlobal&#41;;

&#125;
...
void gsKit_display_buffer&#40;GSGLOBAL *gsGlobal&#41;
&#123;
	GS_SET_DISPFB1&#40; gsGlobal->ScreenBuffer&#91;gsGlobal->ActiveBuffer & 1&#93; / 8192,
	gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 &#41;;
	if &#40;flicker_filter&#41;
		GS_SET_DISPFB2&#40; gsGlobal->ScreenBuffer&#91;gsGlobal->ActiveBuffer & 1&#93; / 8192,
		gsGlobal->Width / 64, gsGlobal->PSM, 0, 1 &#41;;
	else
		GS_SET_DISPFB2&#40; gsGlobal->ScreenBuffer&#91;gsGlobal->ActiveBuffer & 1&#93; / 8192,
		gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 &#41;;
&#125;
This switches the framebuffer for the read circuits, and does the offsetting when the flicker filter is enabled, when flicker_filter is false, gsKit should work like it normally did.

I'm not sure if this will help with your flickering problem, but it can't hurt to try, heh. I'm not sure if SDL will need to be modified, but it shouldn't if you hardcode gsKit into being flicker filtered. I haven't really tested with the flicker filter yet, but you should use FIELD mode for interlacing, as you can use a full-height buffer. Each frame would then be Even+Odd/Odd+Even/... FRAME mode's more useful for stretching out a half-height framebuffer or you're explicitly rendering two even/odd frames for displaying.
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

Post by protomank »

It did not work directly, in a *very* quick test. But I'll give another try later, with some printfs to see if this code is actually doing something, or forcing it to execute, and also test starting the video directaly with gsKit (but I have to find how to connect it with SDK).

Thanks ragnarok!
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

Post by protomank »

Well, yesterday I took the time to make experiments... and failed!
While I was able to reduce it, it is still very annoying to the eyes.
The funny part is that gsKit examples do not seem to have this issue, but I still could not figure what is causing the flickering on the TV screen for SDL, even after trying to make the video init and update as similar as possible to the examples.

The only thing I still did not tested is GS_ONESHOT. Well, actually I did, but it only shows a black screen. SO, before I loose time on it, can you tell me the diference between it and GS_PERSISTENT? Could using GS_ONESHOT help improve things in the flickering aspect?

Later today I plan on trying again to solve this mistery.

Thanks in advance.
ragnarok2040
Posts: 202
Joined: Wed Aug 09, 2006 1:00 am

Post by ragnarok2040 »

For the oneshot queue, you need to refill the dma pool between each gsKit_queue_exec() call with what you're drawing, since the queue is reset when it's executed. For the persistent queue, the queue isn't cleared when it's executed, so you don't need to redraw between gsKit_queue_exec() calls. It needs to be reset manually to draw something different, or redraw something with different information, though.

I'm not sure if switching between the queue types will help with flickering. I took a look at the SDL port to see the way it was implemented. If I understand it right, SDL makes surfaces in main ram on which to draw. When the SDL application is done drawing, the PS2 port uploads the surface to the GS as a textured primitive.

Maybe changing the primitive that the texture is mapped onto will help. Right now it's mapped to a striped sprite primitive, which is designed, I think, for animated sprites. Maybe using the normal sprite primitive would work.
Last edited by ragnarok2040 on Thu Dec 17, 2009 1:41 am, edited 1 time in total.
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

Post by protomank »

Yeah, for your description it will not make any difference for flickering. I wonder if SDL in PS2 is just too slow, and does not refresh the screen at a apropriate rate. :-P

I'll do some tests with basic (and small) SDL examples and check how it comes out.

EDIT: I think I found some clues:
first, SDL_UpdateRect() is intended to be used without page flipping
( page flipping = SDL_HWSURFACE+SDL_DOUBLEBUF, and the screen is updated
only when you call SDL_Flip() )

so, when using SDL_SWSURFACE with SetDisplayMode, the resulting surface
(surface) is not the physical screen, but
a sysram surface.
when you will blit a sprite on it, it will not be displayed.
to update the physical screen, you will have to call SDL_UpdateRect() (or
SDL_Flip(), which will itself call SDL_UpdateRect)
this is a double buffering, in other words

when SetDisplayMode is called with only SDL_HWSURFACE (no double buffer, nor
page flipping),
in this case, when you do a blit to the resulting surface, changes will be
showed immediatly...

so, SDL_UpdateRect() is usefull, this depend of the mode you created the
surface..
I am using SDL_Flip each loop of my program. Maybe using SDL_Update_Rect helps, because it have an advantage that is to only update one small part of the screen (like sprites) and not the whole thing.
ragnarok2040
Posts: 202
Joined: Wed Aug 09, 2006 1:00 am

Post by ragnarok2040 »

Oops, I accidentally hit submit instead of preview so I was still posting when you posted, heh :D.

I think that flickering is caused from using the primitive designed for sprite strips instead of for a single sprite.

In sdl/src/video/ps2sdk/SDL_ps2video.c in function PS2_SetVideoMode():

Code: Select all

        gsKit_prim_sprite_striped_texture&#40;gsGlobal, &gsTexture, device->hidden->center_x, device->hidden->center_y, // x1,y1
                                                                0, 0, // u1,v1
                                                                    &#40;int&#41;&#40;&#40;current->w * ratio&#41; + device->hidden->center_x&#41;, &#40;int&#41;&#40;&#40;current->h * ratio&#41; + device->hidden->center_y&#41;, //x2,y2
                                                                    gsTexture.Width , gsTexture.Height,
                                                                    1.0, TEXTURE_RGBAQ&#41;;
You can remove "striped" from the primitive name, so it's gsKit_prim_sprite_texture..., to see if that helps. It'll treat the surface texture as one entire texture, instead of splitting it up into 64-pixel wide strips.
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

Post by protomank »

At last I found the solution!
And man, that was actually pretty simple (well, maybe now that the SDL code is fixed). Just removed the stripped and now SDL_DOUBLEBUF started to work on SDL/PS2. Not only that, SDL_HWSURFACE too!

So, no more flickering, better speed and my project can really start working. Thanks a lot for your help Ragnarok, you rock! :)
The only problem I still have is clock/delay that seems to freeze, but I'm sure I can easily fix that, and is not related to gsKit.

When I return from vacations, I'll clean the code, do more testing and create a patch for submission.
cosmito
Posts: 307
Joined: Sun Mar 04, 2007 4:26 am
Location: Portugal
Contact:

Post by cosmito »

ragnarok2040 wrote:You can remove "striped" from the primitive name, so it's gsKit_prim_sprite_texture..., to see if that helps. It'll treat the surface texture as one entire texture, instead of splitting it up into 64-pixel wide strips.
That's odd... I always thought the splitting was a trick to improve performance... Why it works the other way in this case?
ragnarok2040
Posts: 202
Joined: Wed Aug 09, 2006 1:00 am

Post by ragnarok2040 »

I'm not sure if it helps improve performance, at least, the way I look at it. You have a constant number of pixels that need to be processed, and a constant amount of texture data that needs read. Doing it with multiple primitives, means the the GIF packet is larger and the GS has to start from the beginning with each strip, filter each strip, and then each strip will kick off drawing to the framebuffer. Each strip gets done faster, and that can trick you into thinking it's performing faster.

The GIF is half the speed of the EE, and the GS is half the speed of the EE when texture mapping. So once you send the GIF packet with all those strips, the GS/GIF is now lagging behind the EE, allowing you to set a vsync interrupt before the strips finish drawing. Doing one sprite, causes the GS to spend less time processing primitives, and more time processing texture data. Though, if the texture is large enough, the GS will still lag behind the EE, since it'll still be reading texture data.

The best method would be to upload the SDL screen surface to the framebuffer itself. As long as the texture and the framebuffer use the same storage mode, it should be fine. You wouldn't even need to draw a primitive, then. The pixel storage format will need to be 16 to 32-bit.

Putting:
gsTexture->Vram = gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1];
prior to uploading the texture and commenting out the sprite should do it.

I've never done it, so it might not work, or it might need to be processed or something if it looks garbled.
User avatar
jbit
Site Admin
Posts: 293
Joined: Sat May 28, 2005 3:11 am
Location: København, Danmark
Contact:

Post by jbit »

Drawing large prims as strips is much faster due to the way the GS' cache and rasterization works.
The GS will rasterize a maximum of 8x2 pixels per clock or 4x2 when texturing, this is the pixel pipeline layout, it can not for example render 2x8 pixels per clock or 4x4 pixels per clock.
It ALWAYS scans left to right, then top to bottom... and it can only operate on one page (which is 64x32 pixels with 32bpp) at a time, when switching from one page to another you get a "page break" penalty where the memory unit has to load from ram into internal cache.
So knowing this we can estimate how many page breaks will occur when we render...

For a 640x512 single prim at 0,0 without texturing the GS will render 8x2 pixels at the following locations:
0,0 ; 8,0 ; 16,0 ; 24,0 ; 32,0 ; 40,0 ; 48,0 ; 56,0 <Load a new page>
64,0 ; 72,0 ; 80,0 ; 88,0 ; 96,0 ; 104,0 ; 112,0 ; 128,0 <Load a new page>
When it gets to the end of the scanline it'll start at 0,2 and proceed in the same manner as above until all scanlines are rendered.

So for this 640x512 prim you end up having about (640/64)*512/2=~2560 page breaks... which is very expensive... For every eight cycles of drawing you have a page break which might take dozens to hundreds of cycles.

If you cut the prim up into ten 64x512 strips and properly align them to page boundaries you end up with a page break every 32nd scanline for each strip...

0,0 ; 8,0 ; 16,0 ; 24,0 ; 32,0 ; 40,0 ; 48,0 ; 56,0
0,2 ; 8,2 ; 16,2 ; 24,2 ; 32,2 ; 40,2 ; 48,2 ; 56,2
[...]
0,30 ; 8,30 ; 16,30 ; 24,30 32,30 ; 40,30 48,30 ; 56,30
<Load a new page>

So (512/32)* 10 = ~160 page breaks... this is sixteen times fewer page breaks than with a single primitive... This time we get to draw for for 128 cycles before each page break... this improves performance ALOT.

The extra command overhead is extremely negligible for the GS and the GIF since the setup engine will happily chew through one command every cycle at 150mhz and can overlap this with drawing... generating the strips may be slower on the EE side, but it shouldn't be that much...

Unfortunately I don't have my PS2 setup so I can't measure the different prim times, but i seem to remember a full screen 640x512 prim taking about 4milliseconds to draw, and ten 64x512 strips taking less than half a millisecond.

edit: also note this doesn't just apply to sprites, if you render large triangles that cross page boundaries then you will get huge performance penalties, the GS was designed to render many small primitives, not few large ones... A lot of "later generation" PS2 games will actually sub-divide triangles if they get too large (trading VU time for GPU time)

Also references:
http://www.technology.scee.net/files/pr ... rOfPS2.pdf "Keep polygons small - many small polys are much quicker than one large polygon."
http://www.technology.scee.net/files/pr ... ations.pdf "Wide Primitives will cause page misses, Use 32 Pixel wide strips to reduce page misses"

Second edit:
The reason the Sony guys recommend 32 pixel wide strips is that the GS only has one page buffer for the colour buffer and zbuffer. If you aren't zbuffering then 64pixel wide strips will not cause any unnecessary page breaks, but if you are zbuffering then due to the way the page buffer is structured you will get one page break every two scan lines.

A basic overview of why this is the case (the ordering is a simplified version, it's much more complex, but this gives a good idea)
Colour pages are ordered like:
<- 64 pixels ->
C0 C1 C4 C5
C2 C3 C6 C7

Depth pages are ordered like:
<- 64 pixels ->
Z6 Z7 Z2 Z3
Z4 Z5 Z0 Z1

So if you're drawing to blocks 0->3 the page cache will look like:
C0 C1 Z2 Z3
C2 C3 Z0 Z1

Which means it has the first 32x32 pixels of each the colour buffer and the depth buffer loaded into it, so if you draw a single primitive that is wider than 32 pixels it will page break every other scanline.
ragnarok2040
Posts: 202
Joined: Wed Aug 09, 2006 1:00 am

Post by ragnarok2040 »

I see, :D. Thanks for those references. This is actually great to know. The only thing I could find online about sprite strips was for animating sprites, lol. The method even looks similar.
User avatar
kouky
Posts: 48
Joined: Tue Sep 25, 2007 5:38 am
Location: London
Contact:

Post by kouky »

Protomank,

Did you had time to update the SDL lib on the SVN repository?
Will you amends give a general boost of performances when using SDL on the PS2? That would be great :)

I have installed the ps2 SDL port a long time ago, and don't really know how to update it... Should I just delete all doc related to the previous version of SDL and install a new one?

Best & happy new year to all
softwares for artists on video game systems - http://www.pikilipita.com
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

Post by protomank »

kouky wrote:Protomank,
Did you had time to update the SDL lib on the SVN repository?
No, I need to get a clean copy of the gsKit, PS2SDK and SDL, compile and test to see what changes are really needed to create the diffs to post here on the forum, then wait it to be commited. I hope I can have a spare time in the weekend to do so.
kouky wrote: Will you amends give a general boost of performances when using SDL on the PS2? That would be great :)
I do not think so, what would give SDL a performance boost, is having a better SDK, that I hope, will happen when ragnarok finishes his new libdraw. But then, we´ll have to port SDL to use it instead of gsKit.
But I'm fairy happy with SDL speed. It is slow only if you use some non-standard resolution (like 640x400 or 320x224).
kouky wrote: I have installed the ps2 SDL port a long time ago, and don't really know how to update it... Should I just delete all doc related to the previous version of SDL and install a new one?
Wait a bit until I commit fixes to SDL, then download a new version. It is good to be up to date, because gsKit received a lot of fixes.
cosmito
Posts: 307
Joined: Sun Mar 04, 2007 4:26 am
Location: Portugal
Contact:

Post by cosmito »

protomank wrote:
kouky wrote: Will you amends give a general boost of performances when using SDL on the PS2? That would be great :)
I do not think so, what would give SDL a performance boost, is having a better SDK, that I hope, will happen when ragnarok finishes his new libdraw. But then, we´ll have to port SDL to use it instead of gsKit.
But I'm fairy happy with SDL speed. It is slow only if you use some non-standard resolution (like 640x400 or 320x224).
Have you got into any conclusions regarding those performance issues with the gsKit_prim_sprite_texture?
I guess the optimal resolutions should be multiples of 64, right?
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

Post by protomank »

Well, the flickering problem was fixed by using double buffering, it does help your animation run a bit smoother but nothing major in terms of overall speed. It does help indeed the screen updating rate.

The resolution problem, I do not know the cause - I did not studied enought with PS2 hardware to understand how it works for resolution modes, but It does seems to follow the same logic as computer, it does not double 320x200 -> 640x480 instead of 640x400).

I am not a graphic hardware exper, I always program using a toolkit such as SDL that does abstract those kind of problems for me :)
Post Reply