Sdl Screen Tearing Issue


dgame

Active Member
Joined
Oct 1, 2006
Messages
945
Severe Screen Tearing In Games

Is there an update to the SDL screen Tearing issue?

I have cross-compiled GnGB, a Game Boy and Game boy Color emulator, and I am just about ready to release the PND for Beta testing.

GnGB uses SDL.

The source code is here: gngb-20060309.tar.gz

The emulator works fine but there is noticeable screen tearing.

What can I do in SDL the eliminate or minimize the screen tearing?

Thanks!
 
Last edited by a moderator:
dgame said:
Severe Screen Tearing In Games

Is there an update to the SDL screen Tearing issue?

I have cross-compiled GnGB, a Game Boy and Game boy Color emulator, and I am just about ready to release the PND for Beta testing.

GnGB uses SDL.

The source code is here: gngb-20060309.tar.gz

The emulator works fine but there is noticeable screen tearing.

What can I do in SDL the eliminate or minimize the screen tearing?

Thanks!

It is my understanding that the game update frequency must equal the monitor update frequency and they must be in perfect syncronisation in order to completely remove tearing. I seem to recall in some Amiga programming using AMOS there was a handy WaitForVerticleBlank function that helped keep games in sync with the TV...I wish I understood why there isn't an equivilent in SDL.
 
Last edited by a moderator:
I noticed some screen tearing on the regular Linux Cave Story port that I was playing on my laptop, yesterday, so it appears quite common.

From my general knowledge of 3D games, if you do get screen tearing you have to enable VSync, which has the side effect of decreasing performance.
 
SDL doesnt have any built in support for vsync. But there was some reports that some used the pandora's vsync 'manually' before the SDL_Flip and it worked.
 
EvilDragon said:

Would that work on desktop games with SDL as well then?
So, would your game loop look like this then?

while(loop)
DrawStuff()
ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
end

...and that would limit game refreshes to the frequency of the display? I.E. no need for sleeping the thread?

Or am I missing something terribly important...or just being stupid?
 
Last edited by a moderator:
Miner49er said:
So, would your game loop look like this then?

while(loop)
DrawStuff()
ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
end

...and that would limit game refreshes to the frequency of the display? I.E. no need for sleeping the thread?

Or am I missing something terribly important...or just being stupid?

try this:

while(loop)
DrawStuff()
while ( wait until ioctl(fbdev, FBIO_WAITFORVSYNC, &arg) is true )
SDL_Flip();
end
 
Last edited by a moderator:
So here an extract of what I tried:

#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)

#include <linux/fb.h>
#include <sys/ioctl.h>

int arg = 0;

Line 39: int fbdev = open("/fb/dev0", O_RDONLY);



while ((ioctl(fbdev, FBIO_WAITFORVSYNC, &arg)) != 1) {;}
SDL_Flip(gb_screen);

Returned this error:

video_std.c:39: warning: implicit declaration of function ‘open’
video_std.c:39: error: ‘O_RDONLY’ undeclared here (not in a function)
video_std.c:39: error: initializer element is not constant

Line 39 is: int fbdev = open("/fb/dev0", O_RDONLY);

Does anyone have some working code example of this FBIO_WAITFORVSYNC being implemented?

Thanks!
 
dgame said:
So here an extract of what I tried:

Does anyone have some working code example of this FBIO_WAITFORVSYNC being implemented?

Thanks!

your need
#include <fcntl.h>
 
Last edited by a moderator:
try adding
#include <fcntl.h>

edit: :ph34r: by quite a few minutes. Whoops
 
Thanks Guys,

I added #include <fcntl.h>

and now I get:

video_std.c:39: error: initializer element is not constant


Line 39 is: int fbdev = open("/fb/dev0", O_RDONLY);


What can I try now?
 
dgame said:
Thanks Guys,

I added #include <fcntl.h>

and now I get:

video_std.c:39: error: initializer element is not constant


Line 39 is: int fbdev = open("/fb/dev0", O_RDONLY);


What can I try now?

try :
int fbdev = 0;
fbdev = open("/fb/dev0", O_RDONLY);

PS: i suck at coding
 
Last edited by a moderator:
dgame said:
Thanks Guys,

I added #include <fcntl.h>

and now I get:

video_std.c:39: error: initializer element is not constant


Line 39 is: int fbdev = open("/fb/dev0", O_RDONLY);


What can I try now?

ok it seems from the type of failure you put this line outside of any functions. You need to call this in a function that is called once at the beginning of your application.
 
Last edited by a moderator:
Thanks for the help guys.


I got it to compile by placing fbdev = open("/fb/dev0", O_RDONLY) inside the init_video_yv12(Uint32 flag) function.

I can't seem to get it to work correctly. The screen goes blank and the games play blind.

I will post up the beta PND without the vsync modifications and hopefully someone can take a look at the source add vsync.


I can stop the video by not allowing SDL_DisplayYUVOverlay(overlay,&ov_rect) to be called.

This part of the code does not use SDL_Flip.

It uses YUV12 so I guess blit_screen_yv12(void) handles the blitting.

This is the unmodified code, if you can post some complete things to try I will try them. Thanks again.


Code:
#include <SDL.h>
#include "global.h"
#include "memory.h"
#include "video_yuv.h"
#include "vram.h"
#include "video_std.h"
#include "sgb.h"
#include "message.h"
#include "emu.h"
#include "interrupt.h"

void init_video_yv12(Uint32 flag)
{
  
  yuv_flag=SDL_HWSURFACE|flag|SDL_RESIZABLE; /* YUV mode are resizable */

  /* we need this because YUV may not support down scale */
  if (conf.gb_type&SUPER_GAMEBOY) {
    if (conf.res_w<SGB_WIDTH*2)
      conf.res_w=SGB_WIDTH*2;
    if (conf.res_h<SGB_HEIGHT*2)
      conf.res_h=SGB_HEIGHT*2;
  }

  gb_screen=SDL_SetVideoMode(conf.res_w,conf.res_h,BIT_PER_PIXEL,yuv_flag);
  if (conf.gb_type&SUPER_GAMEBOY) {
    overlay=SDL_CreateYUVOverlay(SGB_WIDTH*2,SGB_HEIGHT*2,SDL_YV12_OVERLAY,gb_screen);
  } else {
    overlay=SDL_CreateYUVOverlay(SCREEN_X*2,SCREEN_Y*2,SDL_YV12_OVERLAY,gb_screen);
  }
  /* Error check */
  if (gb_screen==NULL) {
    printf("Couldn't set %dx%dx%d video mode: %s\n",
	   SCREEN_X,SCREEN_Y,BIT_PER_PIXEL,SDL_GetError());
    exit(1);
  }
  if (overlay==NULL) {
    printf("Couldn't allocate overlay surface: %s\n", SDL_GetError());
    exit(1);
  }

  ov_rect.x=0;
  ov_rect.y=0;
  ov_rect.w=conf.res_w;
  ov_rect.h=conf.res_h;
  init_rgb2yuv_table();
  //  init_default_palette();
  init_message_yuv();
}

void blit_screen_yv12(void)
{
    int i,j;
  static Uint8 rumble=0;
  static Uint8 rb_time=0;
  
  Uint16 *bufy=(Uint16*)overlay->pixels[0];
  Uint8 *bufu=(Uint8*)overlay->pixels[1];
  Uint8 *bufv=(Uint8*)overlay->pixels[2];
  Uint16 *nbufy=(Uint16*)overlay->pixels[0]+(overlay->pitches[0]>>1);
  Uint16 *t=back->pixels;
  
  if (conf.gb_type&SUPER_GAMEBOY) {
    bufy+=overlay->pitches[0]*40+48;
    nbufy+=overlay->pitches[0]*40+48;
    bufu+=overlay->pitches[1]*40+48;
    bufv+=overlay->pitches[2]*40+48;
  }
  
  if (rb_on) {
    rumble=2-rumble;
    t+=rumble;
    rb_time++;
    if (rb_time>8) 
      rb_time=rb_on=0;
  }
  for(j=0;j<SCREEN_Y;j++){
    for(i=0;i<SCREEN_X;i++){
      nbufy[i]=bufy[i]=rgb2yuv[t[i]].y;
      bufu[i]=rgb2yuv[t[i]].u;
      bufv[i]=rgb2yuv[t[i]].v;
      
    }
    nbufy+=overlay->pitches[0];
    bufy+=overlay->pitches[0];
    bufu+=overlay->pitches[1];
    bufv+=overlay->pitches[2];
    t=&t[i];
  }


  SDL_DisplayYUVOverlay(overlay,&ov_rect);
  if ((!(LCDCCONT&0x20) || !(LCDCCONT&0x01)) && (conf.gb_type&NORMAL_GAMEBOY)) 
    clear_screen();

}

void blit_sgb_mask_yv12(void)
{
      int i,j;

  
  Uint16 *bufy=(Uint16*)overlay->pixels[0];
  Uint8 *bufu=(Uint8*)overlay->pixels[1];
  Uint8 *bufv=(Uint8*)overlay->pixels[2];
  Uint16 *nbufy=(Uint16*)overlay->pixels[0]+(overlay->pitches[0]>>1);
  Uint16 *t=sgb_buf->pixels;
  
  for(j=0;j<SGB_HEIGHT;j++){
    for(i=0;i<SGB_WIDTH;i++){
      nbufy[i]=bufy[i]=rgb2yuv[t[i]].y;
      bufu[i]=rgb2yuv[t[i]].u;
      bufv[i]=rgb2yuv[t[i]].v;
      
    }
    nbufy+=overlay->pitches[0];
    bufy+=overlay->pitches[0];
    bufu+=overlay->pitches[1];
    bufv+=overlay->pitches[2];
    t=&t[i];
  }

}

VIDEO_MODE video_yv12={
  init_video_yv12,
  reinit_video_yuv,
  //  draw_screen_col_yv12,
  //  draw_screen_wb_yv12,
  //  NULL,
  blit_screen_yv12,
  blit_sgb_mask_yv12
  //  set_pal_yv12,
  //draw_message_yv12,
  //NULL
};
 
I'm not an expert...but i think you could place it at the end of blitting function...inside the last routine
Code:
VIDEO_MODE video_yv12={
  init_video_yv12,
  reinit_video_yuv,
  //  draw_screen_col_yv12,
  //  draw_screen_wb_yv12,
  //  NULL,
  blit_screen_yv12,
  blit_sgb_mask_yv12

  // insert call to fbdev here !!!!!!

  //  set_pal_yv12,
  //draw_message_yv12,
  //NULL
};
 
Farox said:
I'm not an expert...but i think you could place it at the end of blitting function...inside the last routine

if the blit is drawing direct to the screen then wouldnt you want to put the vsync right before the blit?
 
Last edited by a moderator:
Well, I'm also still fighting with GnuBoy to get VSync working... maybe you could help me out here as well?

This is part of the code like I currently have changed it:

Code:
void vid_preinit()
{
}

void vid_close()
{
	if (overlay)
	{
		SDL_UnlockYUVOverlay(overlay);
		SDL_FreeYUVOverlay(overlay);
	}
	else SDL_UnlockSurface(screen);
	SDL_Quit();
	fb.enabled = 0;
}

void vid_settitle(char *title)
{
	SDL_WM_SetCaption(title, title);
}

void vid_begin()
{
  
	if (overlay)
	{
		SDL_LockYUVOverlay(overlay);
		fb.ptr = overlay->pixels[0];
		return;
	}
	SDL_LockSurface(screen);
	fb.ptr = screen->pixels;
}

void vid_end()
{
	if (overlay)
	{
		SDL_UnlockYUVOverlay(overlay);
		if (fb.enabled)
			int arg = 0;
			ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
			SDL_DisplayYUVOverlay(overlay, &overlay_rect);
		return;
	}
	SDL_UnlockSurface(screen);
	if (fb.enabled)
	{
	  SDL_Flip(screen);
	}
}

That does lead to:

Code:
sys/sdl/sdl.c: In function 'vid_end':
sys/sdl/sdl.c:440: error: expected expression before 'int'
sys/sdl/sdl.c:441: error: 'arg' undeclared (first use in this function)
sys/sdl/sdl.c:441: error: (Each undeclared identifier is reported only once
sys/sdl/sdl.c:441: error: for each function it appears in.)
make: *** [sys/sdl/sdl.o] Error 1

If I do it like this:

Code:
void vid_end()
{
	if (overlay)
	{
		SDL_UnlockYUVOverlay(overlay);
		if (fb.enabled)
			ioctl(fbdev, FBIO_WAITFORVSYNC, 0);
			SDL_DisplayYUVOverlay(overlay, &overlay_rect);
		return;
	}
	SDL_UnlockSurface(screen);
	if (fb.enabled)
	{
	  SDL_Flip(screen);
	}
}

I get no errors, but no VSync either...
Putting this in front of SDL_Flip also doesn't work.
I guess SDL_Flip is not even used here - if I remove it, GnuBoy still works fine.
If I remove SDL_DisplayYUVOverlay(overlay, &overlay_rect); however, there's no video anymore... so I guess the WAITFORSYNC is okay before that?
 
EvilDragon said:
Well, I'm also still fighting with GnuBoy to get VSync working... maybe you could help me out here as well?

This is part of the code like I currently have changed it:

Code:
SNIP
		if (fb.enabled)
			int arg = 0;
			ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
			SDL_DisplayYUVOverlay(overlay, &overlay_rect);
		return;
SNIP

That does lead to:

Code:
sys/sdl/sdl.c: In function 'vid_end':
sys/sdl/sdl.c:440: error: expected expression before 'int'
sys/sdl/sdl.c:441: error: 'arg' undeclared (first use in this function)
sys/sdl/sdl.c:441: error: (Each undeclared identifier is reported only once
sys/sdl/sdl.c:441: error: for each function it appears in.)
make: *** [sys/sdl/sdl.o] Error 1
SNIP

Place braces after that if. It should work without them but...try it with them.
like this.

Code:
if (fb.enabled){
			int arg = 0;
			ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
			SDL_DisplayYUVOverlay(overlay, &overlay_rect);
}
return;
It might have worked in the second snippet because the next line was a function call and not a variable definition.

Good luck ED!
 
Last edited by a moderator:
Back
Top