cross platform

Started by
37 comments, last by icecubeflower 15 years, 7 months ago
I have a game that I made on Slackware. It uses SDL, SDL_image, and SDL_mixer. It works great. I'm trying to make a version for windows. I'm using MS VC++ 6.0 or VC98 or whatever it's called. It's old. In project->settings I tell the link tab that I need these: SDL.lib SDLmain.lib SDL_mixer.lib SDL_image.lib opengl32.lib glu32.lib Then in the C/C++ tab under code generation I pick multi-threaded dll. Then in every .h and the main .cpp if it #includes either of these: #include <GL/gl.h> #include <GL/glu.h> Then before them I put #include <windows.h> Then I get the lib and include files for SDL, SDL_image, and SDL_mixer. Then I put those lib and include files in the VC++ lib and include directories. Then it compiles just fine! To get it to run I have to put copies of the lib files in the same directory as the executable. Then it runs! But it runs abysmally slow. WHY!? The windows machine I'm using is a dual core Athlon 64 X2 4400+ and the video card is an NVidia GeForce 8600 GT. It's the fastest computer I've ever used. The linux machine I use is a single core athlon something or other and it has a GeForce 6800 or something. But my game flies on that machine. What the hell is going on?

  while ( ! primo.done )
  {
    keys = SDL_GetKeyState(NULL);
    while ( SDL_PollEvent(&event) )
    {
      switch(event.type)
      {
      case SDL_VIDEORESIZE:
         screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 16, SDL_OPENGL|SDL_RESIZABLE);
         if ( screen )
         {
            reshape(screen->w, screen->h);
         }
         else
         {
            //we couldn't set the new video mode
         }
         break;
      case SDL_KEYUP:
         keyunbreak(primo, event);
         break;
      case SDL_KEYDOWN:
         keybreak(icemap, primo,picbox,event, nature);
         break;
      case SDL_QUIT:
         primo.done = 1;
         break;
      }//switch(event type)
    }//while SDL_PollEvent(&event)

    //update after a set time
    tickmaster(keys, &icemap, &lastmap, primo, iceblock, nature,boombox);

    draw(icemap, lastmap, typewriter, primo,picbox,heropics,monsterstore,nature,statbox);
  }//while not primo.done

Obviously I guess it's not getting to the end of that while loop as many times per unit of time on windows as it does on Linux. tickmaster and draw are getting called less. I don't know why.
Advertisement
I put this around my call to Draw

killme=SDL_GetTicks();
draw(icemap,lastmap,typewriter,primo,picbox,heropics,monsterstore,nature,statbox);
outfile<<int(SDL_GetTicks()-killme)<<"\n";

Then I played the abysmally slow game for a bit.

Here's a very small section of "killme.txt" (the outfile):
16
17
16
17
17
16
18
16
17

It goes on and on like that. The draw function executes in about 17 1/1000ths of a second. That's not slow, is it? My guy moves one pixel per cycle. A cycle happens every 1/1000 of a second (that's in my tickmaster function) assuming the drawing doesn't slow it down. My screen is 1024 pixels wide. It should take 1.024 seconds to cross the screen. It takes like 10 seconds!

Wait... oh wait 17 1/1000ths of a second is too slow, isn't it? That would make tickmaster execute only every 17/1000ths of a second instead of 1/1000th of a second. So my guy moves a pixel every 17/1000ths of a second (assuming my program spends about 100% of the time in the draw() function) and crosses the screen every 17 seconds (i didn't actually use a stopwatch, it seemed like 10).

So... that's it. My program draws too goddam slow on windows and I don't know why.
Okay... I just commented out my entire draw function. It doesn't do anything. I sat there and watched the black screen for a while. Then I opened my "killme.txt". The draw function still takes 17/1000ths of a second. Wtf.
void draw(MapBox *icemap, MapBox *lastmap, PrintBox &typewriter, BasicBox &primo,          ImageVault *picbox, ImageVault *heropics, PicBox &monsterstore, WeatherBox &nature,          GLuint statbox[]){  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  //a big commented out switch case is here  SDL_GL_SwapBuffers();}
You most likely have V-Sync enabled on the Windows machine but not on the Linux machine. V-Sync locks the framerate to a multiple of the monitor's refresh rate, which in your case is 60Hz. 1000 / 60 = 16.6 milliseconds per frame. To make the game run at the same speed on both machines you need to implement Framerate Independent Movement.

These topics are discussed very often on GameDev forums. Search and you will find your answers.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
It's probably syncing to the screen refresh. 17 ms is 60HZ, 60 frames per second. (1000/60=16.666)

Why are you trying to run your game at 1000 fps, anyway? That's way, way too high.
If anyone wants to see the Linux version of my game to see what I'm talking about then download it here:

http://www.icecubeflower.com/downloads.html

I cannot figure out how to make it run properly on windows.
Hey thanks guys, I'll look into it.

I don't why I tried 1000 fps. ...what should I go for?

I got rid of all my global variables a couple months ago. I guess now I gotta get rid of my magic numbers. Then if my graphics get more intense and I can only get 500fps or less I can just change one #define value and fix everything across the board. So my guy and all other guys move at a multiple of their former pixels per cycle and stuff.

Eventually I want to have the program test how fast the machine draws and adjust one multiplier so the whole program can adjust itself for different machines all on its own.
Are you sure about that Framerate Independent Movement? I think I already have that.

My tickmaster function is like this:
void tickmaster(Uint8 *keys, MapBox **icemap, MapBox **lastmap, BasicBox &primo, Iceint &iceblock,WeatherBox &nature, AudioBox *boombox){   if(SDL_GetTicks()-primo.elticko>=1)   {      primo.elticko=SDL_GetTicks();      //then update everything   }}


Draw and tickmaster are totally separate functions. Draw executes as many times as it can, tickmaster is timed. Draw draws. tickmaster updates everything.

So that makes everything update every 1/1000th of a second assuming I have better than 1000 fps. So really my only problem is that I expect too high of a framerate, right? If I want things to move faster I just have to increase pixels moved per cycle because I can't count on a cycle happening every 1/1000th of a second.

So I need do something like this:
if(SDL_GetTicks()-primo.elticko>=a number bigger than 1)
//then update everything

That's what Framerate Independent Movement is, isn't it?
Quote:So that makes everything update every 1/1000th of a second assuming I have better than 1000 fps

What if you have worse than 1000 fps?
e.g. if you're actually running at 500fps, then it's only going to update half as often as it needs to.

How about something like this - if your FPS is half of what you expect, then everything gets updated twice to compensate:
   unsigned int deltaTick = SDL_GetTicks()-primo.elticko;   for( unsigned int i=0; i<deltaTick; ++i )   {      //update everything   }   primo.elticko += deltaTick;


This is really just shoe-horning frame-rate independent movement into your current system though.

Quote:I don't why I tried 1000 fps. ...what should I go for?
Generally, you should go for whatever you want the graphics to run at. There's no point updating the game-state if no one is going to see the result.
So if most LCD screens can only refresh at 60hz, then there's really no need to update your internal game-state more than 60hz either.

Quote:Draw and tickmaster are totally separate functions. Draw executes as many times as it can, tickmaster is timed. Draw draws. tickmaster updates everything.
Unless draw and tickmaster are in different threads, then if one of them stalls, then the other is going to stall as well.
So if draw takes 16ms to complete (which it will if your graphics driver has enabled V-Synch), then by the time that tickmaster gets called again, at least 16ms will have gone by, but you will only update everything once.
If you change your loop like I showed above, then this will compensate by updating everything 16 times to get back to the 1000Hz that you're expecting.

Quote:Eventually I want to have the program test how fast the machine draws and adjust one multiplier so the whole program can adjust itself for different machines all on its own.
Rhetorical question - what if the machine draws at different speeds at different points in time? (e.g. the virus scanner kicks in, or the OS power saving decides to down-clock the CPU...).
The solution is to adjust this multiplier every single update ;)

Quote:If I want things to move faster I just have to increase pixels moved per cycle because I can't count on a cycle happening every 1/1000th of a second
The fact that you're measuring speed in pixels per cycle sets off alarm bells in frame-rate independence land.
This kind of system probably works for you at the moment because your expected frame-rate is so high. A 1px/cycle speed is much lower at 1000hz than at 100hz, giving you more control over speeds.
If you try lowering your simulation to 50hz using px/cycle you'll end up in trouble because 1px/cycle at 50hz is equivalent to 20px/cycle at 1000hz, meaning you've lost a lot of control over the speeds that everything can move at.
So basically, you cant drop down to a lower simulation Hz until you get yourself away from measuring speed in pix/cycle.


In the long run it will be better to switch over from pix/cycle to units/second.
For convenience you can define 1 unit to be equal to 1 pixel - the difference is that units are real numbers (float/double), but pixels are integers.

Now you can have precise control over speed at any frame-rate.
e.g.
float position_x = 10;//pixelsfloat speed = 0.1f;//pixels per secondfloat delta_time = 1/16.0f;//1/16th of a secondposition_x += speed * delta_time;int pixel_pos_x = (int)position_x;
You may have noticed - delta_time is your draw-speed multiplier from earlier!

Now you can use an update function like this:
   unsigned int deltaTick = SDL_GetTicks()-primo.elticko;   float delta_time = deltaTick / 1000.0f   //update everything using delta_time as a multiplier for speeds   primo.elticko += deltaTick;


[Edited by - Hodgman on September 22, 2008 1:48:12 AM]
Yeah, that's what I was saying. I was expecting an fps of 1000 which is too high. Draw takes 17ms if V-sync is enabled. So my

if(SDL_GetTicks()-primo.elticko>=1)

was meaningless. SDL_GetTicks()-primo.elticko always equals 17 and 17 ms goes by before tickmaster is ever called.

If I do what you are saying then instead of my guy moving one pixel per frame, he moves 16 pixels per frame. So the map scrolls 16 pixels per frame. It looks choppy as hell, I tried it. On SNES Zelda you just moved real slow and his legs ran in place real fast so you didn't notice. In my game I liked being able to run across the screen in 1 second and have it scroll real smooth. I don't see how I can do that with 60fps. It looks like my only options are to have choppy graphics or slow my guy down. :( Please tell me I'm missing the point.

This topic is closed to new replies.

Advertisement