Sorry if this is a repost, I tried putting this up before but I don’t think it made it. The filters must
have not liked me attaching my coded tarred and zip.
Here’s the low down on what’s going on:
I've written a little sdl app that blits 64x48 tiles to screen and does scrolling by continuously
redrawing all the tiles in different spots based on the position of an SDL_Rect I call VRect (for View
Rectangle). It’s slow, painfully slow. But if I hold a key while scrolling it speeds up a lot. The scrolling
will take about half the time with a key down (I’ve measured it with SDL_GetTicks and can see with my own
eyes how much faster the scrolling is). That’s when the app has got focus. Here’s where it gets worse.
If the app is out of focus it’ll speed up; and if I hold a key in whatever app I just switched to my little
scroll app speeds up even more. At that point it looks like I’m getting a full
frame rate (30+).
What's even worse is I'm able to replicate the error in the test code
include with the api. I modified testsprite.c so that it dies with a mouse
down event instead of a keydown event (Just changed KEYDOWN to MOUSEBUTTONDOWN
in the event loop). Sure enough testsprite behaves exactly like my scroll app.
On the other hand I have the Raptor demo and don’t get this behavior, nor in
any other X widows game.
Here’s my system:
Redhat 6.1 with kernel 2.2.5-15
XFree86 4.01, Nvidia kernel driver v095 loading and running, GLX is broken
kde with kwm running
All surfaces appear to be software (At least that’s what testsprite reports).
Here’s what I’ve tried so far:
Made sure I’m only initializing the video subsystem, but I know it initializes the event polling mechanism.
Disabled loading of the GLX Driver, I’ve had problems with it so I thought it might be a problem
Ran a simple x server without a window manager with only and xterm and the
scroll app running (exec xterm in my .xinitrc)
Renice the scroll app to a higher priority
Removed ALL explicit input function calls (i.e. event polling). I can’t remove the implicit initialization of
the SDL event handling system done when the video subsystem is initialized, at least not right now since
I don’t know how.
Tried both the official nvidia driver for my tnt1 and the one that came with x4.0
I haven’t tried disabling DGA in X4.0, my tvcard needs it so it’s enabled right
now, but that seems like a long shot
At this point I'd like to know if there's some way to disable event
polling. It’s the only thing I can think of to try. I’m going to attach my
code at the end. If anyone has some free time and some clue about what’s
going on I’d appreciate it. If there’s anything I left out please let me know.
I’m pretty much at my wits end and I’m a sad to say I’m starting to miss
win32 and directx. Is Linux ever going to have an easy to configure multimedia
api?
/*Here’s my little 'ol app. spot_tile.bmp and brick.bmp are just 64x48 bitmaps I use for tiles
A Word or two about how it (suppositly) works: A retangle representing the area of the TileMap
that is visable on screen is maintained. I call this the ViewRectangle or VRect for short.It’s
passes to a function called DrawTiles() that uses the VRect to index the tile map and also to
figure out where to start drawing tiles. It’s a brute force approach but it works
*/
//includes
#include <stdio.h>
#include “SDL.h”
#include <SDL/SDL_thread.h>
//end includes
//typedefs
#define X_TILE_SIZE 64
#define Y_TILE_SIZE 48
#define SCREEN_EXTENT_X 640
#define SCREEN_EXTENT_Y 480
#define NUM_TILES_X 10
#define NUM_TILES_Y 40
#define WORLD_MAP_EXTENT_X (X_TILE_SIZE * NUM_TILES_X)
#define WORLD_MAP_EXTENT_Y (Y_TILE_SIZE * NUM_TILES_Y)
//end of typedefs
//function decs
SDL_Surface* LoadImage( char* file );
int DrawTiles(SDL_Rect* WorldCoordsRect);
int TileDrawThread(void *DrawTiles_Thread_Local);
//end function decs
//globals
SDL_Surface screen;
int done = 0;
int exit_flag = 0;
SDL_Event event;
char TileMap[NUM_TILES_X][NUM_TILES_Y];
SDL_Surface Tiles[2];
SDL_Rect VRect;
//end globals
int main(){
int initial_ticks;//I'll use this to mesure how long it takes to do the scroll
int final_ticks;
VRect.x = 0;
VRect.y = 0;//WORLD_MAP_EXTENT_Y - 640;
VRect.w = 640;
VRect.h = 640;//WORLD_MAP_EXTENT_Y;
/* Initialize SDL */
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
exit(1);
}
if ( (screen=SDL_SetVideoMode(640,480, 16, SDL_ANYFORMAT )) == NULL ) {
fprintf(stderr, "Couldn't set 640x480 video mode: %s\n",
SDL_GetError());
exit(2);
}
//load the tiles
Tiles[0] = LoadImage( “spot_tile.bmp” );
Tiles[1] = LoadImage( “brick.bmp” );
//initialize the tilemap with 0’s and 1’s to point to the two tiles I just loaded
for(int j = 0; j < 40; j++){
for(int i = 0; i < 10; i++){
TileMap[i][j] = i % 2;
}//end for loop i
}//end for loop j
initial_ticks = SDL_GetTicks(); //time how long it takes to scroll the map twice
//Here’s where the scrolling is done. VRect stands for view rectangle and is an SDL_Rect. VRect
//is the size of one screen (640x480) and it’s position is used to indicate which tiles are
//currently visable. By moving the VRect and Calling DrawTiles with VRect as and argument
//scrolling is done.
while ( VRect.y > 0 ){
DrawTiles(&VRect);
VRect.y -= 5;
VRect.h -= 5;
}//end while ( VRect.y > 0 )
while ( VRect.h < WORLD_MAP_EXTENT_Y ){
DrawTiles(&VRect);
VRect.y += 5;
VRect.h += 5;
}//end while ( VRect.h < WORLD_MAP_EXTENT_Y )
final_ticks = SDL_GetTicks();
printf("Time to do 2 scrolls: %d\n", final_ticks);
SDL_Quit();
return 0;
}//end main
SDL_Surface* LoadImage( char* file ){
SDL_Surface* temp = NULL;
/* Load an image */
temp = SDL_LoadBMP(file);
if ( temp == NULL ) {
fprintf(stderr, “Couldn’t load %s: %s\n”, file, SDL_GetError());////////
exit(1);
}
/* Convert image to video format */
temp = SDL_DisplayFormat(temp);
if ( temp == NULL ) {
fprintf(stderr, "Couldn't convert background: %s\n",
SDL_GetError());
exit(1);
}
return temp;
}//end methond LoadImage
//Right now this function only works for drawing a full screen of tiles, there are some overdraw issues
// to fix (i.e. drawing more tiles then it needs to). Also, right now it assumes if you pass it a Rect
// with tiles to draw that you want those tiles drawn, wheather the VRect is over them or not. This
// is an engine issue more than anything else
int DrawTiles(SDL_Rect* WorldCoordsRect){
SDL_Rect destpts, Blit_Tiles_Extents, SDL_Blit_Rect;
int first_destpts_x;
destpts.x = -(WorldCoordsRect->x % X_TILE_SIZE);
destpts.y = -(WorldCoordsRect->y % Y_TILE_SIZE);
first_destpts_x = destpts.x;
Blit_Tiles_Extents.x = WorldCoordsRect->x / X_TILE_SIZE;
Blit_Tiles_Extents.y = WorldCoordsRect->y / Y_TILE_SIZE;
Blit_Tiles_Extents.w = WorldCoordsRect->w / X_TILE_SIZE;
Blit_Tiles_Extents.h = WorldCoordsRect->h / Y_TILE_SIZE;
// The following lines of code make sure that when Blit_Tiles_Extents’ members are use to form
//Tile map indexes those indexes don’t overrun the bounds of the array
if ( Blit_Tiles_Extents.x < 0 )
Blit_Tiles_Extents.x = 0;
if ( Blit_Tiles_Extents.y < 0 )
Blit_Tiles_Extents.y = 0;
if ( Blit_Tiles_Extents.w > (NUM_TILES_X) )
Blit_Tiles_Extents.w = (NUM_TILES_X-1);
if ( Blit_Tiles_Extents.h > (NUM_TILES_Y) )
Blit_Tiles_Extents.h = (NUM_TILES_Y-1);
// The following lines of code make sure that partial tile get blited, SDL handles the clipping here
if( Blit_Tiles_Extents.x > 0 )
Blit_Tiles_Extents.x–;
if( Blit_Tiles_Extents.y > 0 )
Blit_Tiles_Extents.y–;
if( Blit_Tiles_Extents.w < (NUM_TILES_X) )
Blit_Tiles_Extents.w++;
if( Blit_Tiles_Extents.h < (NUM_TILES_Y) )
Blit_Tiles_Extents.h++;
for( int j = Blit_Tiles_Extents.y; j < Blit_Tiles_Extents.h; j++){
for( int i = Blit_Tiles_Extents.x; i < Blit_Tiles_Extents.w; i++){
SDL_Blit_Rect.x = destpts.x;
SDL_Blit_Rect.y = destpts.y;
//HERE'S THE BLIT
SDL_BlitSurface(Tiles[ TileMap[i][j] ], NULL, screen, &SDL_Blit_Rect );
destpts.x += X_TILE_SIZE;
}//end for loop i
destpts.y += Y_TILE_SIZE;
destpts.x = first_destpts_x;
}//end for loop j
//right now I’m just redrawing the whole screen, I’ll fix that when I start optimizing
SDL_UpdateRect(screen, 0,0,640,480);
return 0;
}//end of program main–
Jeremy Gregorio
jgreg at azstarnet.com
–
Jeremy Gregorio
jgreg at azstarnet.com