Thread: Low FPS

  1. #1
    Registered User ~Kyo~'s Avatar
    Join Date
    Jun 2004
    Posts
    320

    Low FPS

    So I am drawing stuff to the screen and decide to check how fast my redraws are my fps sit around 90 with no lighting on the screen to something like 30 with ONE source on the screen.... This is my redraw routine:

    using allegro/msvc++.net/win xp

    I am looking for ways to trim it down to a nicer looking function. Any ideas are appreciated. Any questions just reply and I will post the relevant code or info. At this point switching to SDL GL DX etc can not happen this project is nearly done just tweaking what I can.

    I have heard of using dirty rectangles, just not sure about how to implement them in this case.
    Code:
    void RedrawScreen()
    {
    	if(FPSCNT == 0)FPSCNT = GetTickCount();
    	FPS++;
    	int mm,nm;
    	mm = player->GetY() - 10;
    	nm = player->GetX() - 14;
    	blit(LAYOUT,BUFFER,0,0,0,0,scrx,scry);
    	for(int m = 0;m < 21;m++)
    	{
    		for(int n = 0;n < 29;n++)
    		{
    			
    			if(mm+m < 0 || nm+n < 0 || mm+m >= mymap->GetY() || nm+n >= mymap->GetX())
    			{
    					
    			}else
    			{
    				blit(mymap->GetTile(nm+n,mm+m),BUFFER,0,0,48+(n<<5),48+(m<<5),32,32);
    				
    			}
    			if(m == 11 && n == 14)
    			{
    				try
    				{
    				if(!player->SwingingSword())masked_blit(player->GetImage(),BUFFER,(direction*3+step)*charx,0,(scrx/2)-(charx/2),(scry/2)-(chary/2)-16,charx,chary);
    				else masked_blit(player->GetImage(),BUFFER,(direction*3+step)*charx,chary,(scrx/2)-(charx/2),(scry/2)-(chary/2)-16,charx,chary);
    				}
    				catch(...)
    				{
    					
    				}
    			}
    			int p;//added by DarkAlloy(fixes scoping issue)
    			for(	p = 0;p < mymap->GetNumNPC();p++)
    			{
    				tmpNPC = mymap->GetNPC(p);
    				if(tmpNPC != NULL && tmpNPC->GetX() == nm+n && tmpNPC->GetY() == mm+m)
    				{
    					masked_blit(tmpNPC->GetIGPic(),BUFFER,(DOWN*3+tmpNPC->GetStep())*charx,0,48+n*32-(charx>>1)+16,48+m*32-(chary>>1),charx,chary);			
    				}
    			}
    			for(    p = 0;p < mymap->GetNumMonster();p++)
    			{
    				tmpMonster = mymap->GetMonster(p);
    				if(tmpMonster != NULL && tmpMonster->GetX() == nm+n && tmpMonster->GetY() == mm+m)
    				{
    					if(tmpMonster->IsDead())
    					{
    						masked_blit(tmpMonster->GetPic(),BUFFER,((GetTickCount()-tmpMonster->GetTick())/150)*charx,chary,48+n*32-(charx/2)+16,48+m*32-(chary/2),charx,chary);
    						if(GetTickCount()-tmpMonster->GetTick() > 450 )
    						{
    							mymap->DestroyMonster(p);
    						}
    					}else
    					masked_blit(tmpMonster->GetPic(),BUFFER,(DOWN*3+step)*charx,0,48+n*32-(charx>>1)+16,48+m*32-(chary>>1),charx,chary);			
    				}
    			}
    		}
    	}
    	int zmz;//added by DarkAlloy(fixes scoping issue)
    	for(	zmz=0;zmz<10;zmz++)
    	{
    		if(DamageFloats[zmz].damage != -1 && DamageFloats[zmz].x >= 0 && DamageFloats[zmz].y >= 0)
    		{
    			int mam;
    			mam = text_mode(-1);
    			int timedifferance = GetTickCount() - DamageFloats[zmz].time;
    			char damagestr[3];
    			damagestr[2] = '\0';
    			if(DamageFloats[zmz].damage / 10 > 0)
    			{
    				damagestr[0] = '0' + DamageFloats[zmz].damage / 10;
    			}
    			else damagestr[0] = ' ';
    			damagestr[1] = '0' + DamageFloats[zmz].damage%10;
    			textout(BUFFER,big_font,damagestr,DamageFloats[zmz].x,DamageFloats[zmz].y-((timedifferance/50)*1),makecol(255-(timedifferance/50)*2,255-(timedifferance/50)*2,255-(timedifferance/50)*2));
    			if(timedifferance >= 1500)DamageFloats[zmz].damage = -1;
    			text_mode(mam);
    		}
    	}
    	for(   zmz=0;zmz<15;zmz++)
    	{
    		if(Spells[zmz].Draw)
    		{
    			if(Spells[zmz].centered)
    			{
    				masked_blit(Spells[zmz].myspell->GetAnimation(),BUFFER,((GetTickCount()-Spells[zmz].StartTime)/Spells[zmz].myspell->GetFrameLength())*Spells[zmz].myspell->GetX(),0,(scrx/2)-Spells[zmz].myspell->GetX()/2,(scry/2)-Spells[zmz].myspell->GetY()/2,Spells[zmz].myspell->GetX(),Spells[zmz].myspell->GetY());
    				if(GetTickCount()-Spells[zmz].StartTime >= Spells[zmz].myspell->GetFrameLength()*Spells[zmz].myspell->GetNumFrames())
    					Spells[zmz].Draw = false;	//no delete b/c it is not ours to delete.
    			}else
    			if(Spells[zmz].x-nm > 0 && Spells[zmz].x-nm < 20 && Spells[zmz].y-mm > 0 && Spells[zmz].y-mm < 28)
    			{
    				masked_blit(Spells[zmz].myspell->GetAnimation(),BUFFER,((GetTickCount()-Spells[zmz].StartTime)/Spells[zmz].myspell->GetFrameLength())*Spells[zmz].myspell->GetX(),0,(48+(Spells[zmz].x-nm)*32)-Spells[zmz].myspell->GetX()/2,(48+(Spells[zmz].y-mm)*32)-Spells[zmz].myspell->GetY()/2,Spells[zmz].myspell->GetX(),Spells[zmz].myspell->GetY());
    				if(GetTickCount()-Spells[zmz].StartTime >= Spells[zmz].myspell->GetFrameLength()*Spells[zmz].myspell->GetNumFrames())
    					Spells[zmz].Draw = false;	//no delete b/c it is not ours to delete.
    			}
    		}
    	}
    	if(SHOWFPS == true)
    	{
    		char countstr[20];
    		strcpy(countstr,"FPS: ");
    		countstr[5] = '0' + (OLDFPS%1000)/100;
    		countstr[6] = '0' + (OLDFPS%100)/10;
    		countstr[7] = '0' +  OLDFPS%10;
    		countstr[8] = '\0';
    		textout(BUFFER,big_font,countstr,0,0,makecol(255,255,255));
    	}
    	masked_blit(HEALTHBAR,BUFFER,19,0,10,100+(player->GetHpLostAsPercent()*2)+scry/2,19,200-(player->GetHpLostAsPercent()*2));
    	masked_blit(HEALTHBAR,BUFFER,0,0,10,100+scry/2,19,200);
    	
    	masked_blit(HEALTHBAR,BUFFER,38,0,990,100+(player->GetManaLostAsPercent()*2)+scry/2,19,200-(player->GetManaLostAsPercent()*2));
    	masked_blit(HEALTHBAR,BUFFER,0,0,990,100+scry/2,19,200);
    	for(int m = 0;m < 21;m++)
    	{
    		for(int n = 0;n < 29;n++)
    		{
    			for(int p = 0;p < mymap->GetNumObj();p++)
    			{
    				tmpObject = mymap->GetObj(p);	
    				if(tmpObject->GetMapX() == nm+n && tmpObject->GetMapY() == mm+m)
    				{
    
    					masked_blit(tmpObject->GetImage(),BUFFER,tmpObject->GetX()*tmpObject->GetFrame(),0,48+(n*32)-(tmpObject->GetX()/2)+16,48+(m*32)-(tmpObject->GetY()/2),tmpObject->GetX(),tmpObject->GetY());	
    					if(DRAW_LIGHTS && tmpObject->IsLightSource())
    					{
    						int ax,ay;
    						ax = (n-tmpObject->GetMapX())*32;
    						ay = (m-tmpObject->GetMapY())*32;
    						OnTheFlyLighting(tmpObject,ax,ay);
    					}
    					
    				}
    			}
    			for(int procnt = 0;procnt < 25;procnt++)
    			{
    				if(Projectiles[procnt].X == nm+n && Projectiles[procnt].Y == mm+m && Projectiles[procnt].Image && Projectiles[procnt].draw)
    				{
    					masked_blit(Projectiles[procnt].Image,BUFFER,16*Projectiles[procnt].Direction,0,56+(n*32),40+(m*32),16,16);
    				}
    			}
    		}
    	}
    	if(DRAW_LIGHTS)
    	{
    		for(int aq = 0 ;aq < NUM_MAX_PARTICALS;aq++)
    		{
    			if(Particals[aq].draw)
    			{
    				if(Particals[aq].BlendLevel == 0)
    				{
    					fast_putpixel(BUFFER,Particals[aq].X,Particals[aq].Y,makecol((int)Particals[aq].Red,(int)Particals[aq].Green,(int)Particals[aq].Blue));
    					
    				}else
    				{
    					int tr,tb,tg;
    					int c = fast_getpixel(BUFFER,Particals[aq].X,Particals[aq].Y);
    						
    					tr=MyLightMap[(getr32(c))][Particals[aq].Red][Particals[aq].BlendLevel];
    					tg=MyLightMap[(getg32(c))][Particals[aq].Green][Particals[aq].BlendLevel];
    					tb=MyLightMap[(getb32(c))][Particals[aq].Blue][Particals[aq].BlendLevel];
    					fast_putpixel(BUFFER,Particals[aq].X,Particals[aq].Y,makecol32(tr,tg,tb));
    					
    				}
    			}
    		}
    	}
    	masked_blit(EXPBAR,BUFFER,0,7,(scrx/2)-75,scry-48,player->GetXPper()*1.5,7);
    	masked_blit(EXPBAR,BUFFER,0,0,(scrx/2)-75,scry-48,150,7);
    	
    	blit(BUFFER,screen,0,0,0,0,scrx,scry);
    	
    	if(GetTickCount()-FPSCNT >= 1000)
    	{
    		OLDFPS = FPS;
    		FPS = 0;
    		FPSCNT = 0;
    	}
    }
    Code:
    void OnTheFlyLighting(Object* temp,int ax,int ay)
    {
    	int cx,cy;
    	int tr,tg,tb;
    	cx = ax + 48 + (temp->GetX()>>1) + (temp->GetMapX()<<5);
    	cy = ay + 48 + (temp->GetY()>>1) + (temp->GetMapY()<<5);
    	for(int LCVY = cy-temp->GetBrightness(); LCVY < cy+temp->GetBrightness();LCVY++)
    	{
    		if( LCVY > 0 && LCVY < scry)
    		for(int LCVX = cx-temp->GetBrightness(); LCVX < cx+temp->GetBrightness();LCVX++)
    		{
    			if(LCVX < 0 || LCVX > scrx)
    			{
    
    			}
    			else
    			{
    				int grade = temp->GetLightInfo(LCVX-cx+temp->GetBrightness(),LCVY-cy+temp->GetBrightness());
    				int c = fast_getpixel(BUFFER,LCVX,LCVY);
    				if(grade != 0)
    				{
    					if(temp->GetR() == 0)
    					{
    						tr = (getr32(c));
    					}else
    					tr=MyLightMap[temp->GetR()][(getr32(c))][grade<<3];
    
    					if(temp->GetG() == 0)
    					{
    						tg = (getg32(c));
    					}else
    					tg=MyLightMap[temp->GetG()][(getg32(c))][grade<<3];
    
    					if(temp->GetB() == 0)
    					{
    						tb = (getb32(c));
    					}else
    					tb=MyLightMap[temp->GetB()][(getb32(c))][grade<<3];
    
    					fast_putpixel(BUFFER,LCVX,LCVY,makecol(tr,tg,tb));
    				}
    			}
    		}
    	}
    }
    Get and put pixel functions:
    Code:
    void fast_putpixel(BITMAP *bmp, int x, int y, int color)
      {
            ((long *)bmp->line[y])[x] = color;
      }
    Code:
    int fast_getpixel(BITMAP *bmp, int x, int y)
      {
            return ((long *)bmp->line[y])[x];
      }

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    With a structure like that, I'm surprised you are getting more than 10 FPS.


    1. Too many loops.
    2. Too many nested loops. A loop is bad enough, a nested loop with nested if's with more nested loops is really bad.

    A loop like this:

    Code:
    for (int i=0;i<10;i++)
    {
      for (int j=0;j<10;j++)
      {
        //Some if's here
      }
    }
    Is not going to be efficient.

    You need to break your system down into objects and classes and let each object/class handle it's own processing.

    Your main render for a 2D game would probably look like this:

    Code:
    void CEngine::Render(void)
    {
      //Render map, if applicable
      //Render sprites (animations)
      //Render active particles
      //Render active effects
      //Render GUI
      //Render any windowed views
    }
    Your update function might look like this:

    Code:
    void CEngine::Update(float fTimeDelta)
    {
      //Update animations
      //Update particles
      //Update effects
      //Update script actions/events
      //Update GUI
      //Update physics
    }
    So break your game down into manageable objects:

    Maps
    CMap2D
    - encapsulates all operations relative to maps
    CMap2DMgr
    - manages a collection of CMap2D objects

    Animations
    CAnimFrame
    - encapsulates one frame of animation
    CAnimSeq
    - a collection of CAnimFrame objects
    CAnimSeqMgr
    - manages a collection of CAnimSeq objects

    Particle system
    CParticle
    - encapsulates a particle (may not be needed)
    CParticleEffect
    - a collection of particles
    CParticleEffectMgr
    - manages a collection of CParticleEffect objects

    Scripting
    CScript
    - encapsulates a script object
    CScriptMgr
    - a collection of CScript objects

    Multi-player
    CSocket
    - a generic socket object
    CSocketMgr
    - manages a collection of CSocket objects
    CPacket
    - a generic packet object
    CPacketMgr
    - manages a collection of CPacket objects

    GUI
    CWindow
    - encapsulates a window object
    CButton
    - a button object derived from CWindow
    CRadio
    - a radio button object derived from CButton
    CCheckBox
    - a check box button object derived from CButton
    CListbox
    - a listbox object derived from CWindow
    CEditBox
    - an edit control derived from CWindow

    Physics
    CPhysicsEffect
    - a physics effect
    CPhysicsMgr
    - a collection of CPhysicsEffect objects - effects can be applied to any object to gain desired functionality

    Generic objects
    CObject
    - a generic base game object
    CAIObject
    - a generic base game AI object
    CPlayerObject
    - a genneric base player object

    Rendering
    CRenderObject
    - a generic rendering object
    CAlphaRenderObject
    - a generic rendering object requiring alpha blending
    CShaderRenderObject
    - a generic rendering object which uses a supplied shader

    Resources
    CResource
    - encapsulates a generic base resource object
    CResourceMgr
    - manages a collection of generic CResource objects
    CFileResource
    - a generic file resource object
    CMemoryResource
    - a generic memory resource object
    CTextureResource
    - a generic texture resource obejct
    CTextResource
    - a generic text resource object
    CScriptResource
    - a generic script resource object
    CNetResource
    - a generic internet resource object



    That's a good start. Break it down and it becomes much easier. Plus it allows you to work on your game as a completion of a series of 'systems' instead of a largely disconnected network of classes.

  3. #3
    Registered User ~Kyo~'s Avatar
    Join Date
    Jun 2004
    Posts
    320
    the reasoning for the nested loops is that I check section by section to see if something needs to be drawn NPC, Monster, player, Object. The map class contains all the relevant info for loading the map such as the tileset which is an array of tiles etc it isnt disconnected just as things were added some code was added to draw them.

    edit:
    Adding something like Map::rendor() would just break down what I currently have it wouldn't reduce the number of loops nor the depth of the loops it might look nicer but it would generally do the same thing would it not? The reasoning for multiple fors is to reduce the number of loops total since I can draw the tile then draw anything on top of the tile on the same pass. Using multiple functions would make me do the same loop three or four times just to draw tiles then npc then monsters etc I don't see how it can help the FPS in the long run doing that.
    Last edited by ~Kyo~; 06-11-2006 at 09:26 PM.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The reasoning for multiple fors is to reduce the number of loops ...
    Ok? I'm quite baffled at this statement.

    Using multiple functions would make me do the same loop three or four times just to draw tiles then npc then monsters etc I don't see how it can help the FPS in the long run doing that.
    And so you have answered your own question. Perhaps you need to re-think your design.
    Last edited by VirtualAce; 06-12-2006 at 05:32 AM.

  5. #5
    Computer guy
    Join Date
    Sep 2005
    Location
    I'm lost!!!
    Posts
    200
    Since you have multiple function with loops in it, the loops will not run all at once, therefore, faster processing. With nested loop, it will take more memory and time to run it over and over again. I've been in that situation once already so i know what it's like.
    Hello, testing testing. Everthing is running perfectly...for now

  6. #6
    Registered User ~Kyo~'s Avatar
    Join Date
    Jun 2004
    Posts
    320
    Making a RendorTile function or even a class would not help the FPS in my perspective. The multiple fors thing is better explained in a situation:

    Code:
    for(int x=0;...)  //now known as for(1) just for referance
    {
       for(int y=0;...)  //now know as for(2)
       {
            loop for all NPCS checking vs location(x,y)
            loop for all Monsters checking vs location (x,y)
            loop for all Spells checking vs location(x,y)
       }
    }
    ok so I have nested fors from hell I understand this but breaking it into functions doesn't help me since what would be done is check to see if it needs to be drawn then draw it if it indeed needs to be drawn. This is what my fors do it just steps through each of them checks vs current location and draws. Now maybe I could design someway to draw them vs the player location and only use one for loop if thats what you mean then yes that would probably be better and now that I have slept on it thats what I will probably try. A function like Draw_NPCs(int center_x,int center_y). If thats what you ment then sorry about argueing over that must not have been thinking very well.

    newer code idea:
    Code:
    void redraw_screen()
    {
       draw_tiles();   //rendors the map just tiles using a double for loop
       draw_npc(int,int);    //rendors the npcs using one for
       draw_Monsters(int,int);
       draw_whatever(int,int);
    }

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Not going into details. I don't know those. However, I don't think you understood exactly what has been advised...

    Quote Originally Posted by Bubba
    You need to break your system down into objects and classes and let each object/class handle it's own processing.
    (bold added by me)

    This is the key point
    Your code, while correct from the perspective of a procedural style of programming, is not a viable option for this type of processing.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Registered User ~Kyo~'s Avatar
    Join Date
    Jun 2004
    Posts
    320
    have many many classes and objects. everything from a tile class to a map class a scripting class that is functional etc just trying to draw it in a decent way is killing me although I did regain a couple fps just by using that new idea on just the NPCS. The effects will be felt even greater when the maps get beyond the testing size 50x50 and 3 NPCs and 2 Monsters. Although I still do not understand the reasoning to use a seperate class just to rendor everything maybe in a 3d world where drawing needs to check for an object partialy covering another etc but in a tiled world where only one NPC Monster or Player can be in one room at a time it would be overkill wouldnt it?

    edit for referance only

    Code:
    weapon.cpp
    Tile.cpp
    spell.cpp
    source.cpp
    Script.cpp
    Object.cpp
    NPC.cpp
    Monster.cpp
    map.cpp
    Item.cpp
    dialogue.cpp
    character.cpp
    
    other headers:
    linux.h for portability etc
    MYDATATYPES.h as follows
      struct FloatNumber
      struct Damage
      struct DrawSpell
      struct Projectile
      struct Partical
    Don't tell me you need to use classes because I do just not for rendoring since the rendoring process isn't horribly complex its just draw this sprite here. Also the lighting code kills FPS and I don't see a way to drop either for out of it suggestions(there is an option to turn lighting off)?
    Last edited by ~Kyo~; 06-12-2006 at 06:26 PM.

  9. #9
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Again, I don't wish to detract the thread from the main issue. I repeat, I don't know the specifics. You are way more advanced than I am in C++ programming. However, some things are just generic no matter the programming language one is used to.

    You should program for classes. Don't program with classes. That is the new paradigm I suggest you to consider.

    Instead of asking yourself if you should use a class to do this or that, take the next step and ask yourself how you should code a class to do this or that. Classes offer too many advantages. Processing speed, code maintenance, code readability, code structure, scalability, code reusability, encapsulation,... Only the most simple projects don't need classes.

    When your thought process leads to statements like " Don't tell me you need to use classes", your are shooting yourself in the foot in the short and long run. In the short because you just complicated your code structure, made debugging a much harder process and made it more difficult to alter the logic of parts of your code without affecting other parts. In the long run because you renounced the ability to quickly and effectively maintain, reuse and scale your code.

    The truth of the matter is that you are right. You don't need to use classes. But you should. And in any development process should always takes precedence over need.

    I'm not going to extend myself over this. Probably said too much already But do consider a change of paradigm. I believe that this is probably harder sometimes than a change in programming languages. Switching a way of thinking is not an easy task until that day arrives when suddenly something clicks inside our head and bang! We suddenly realize. But you can never achieve that bang if you don't grab oportunities like this project of yours and do it the hard way. i.e. forcing yourself the change, doing what I suggest on the third paragraph and struggle for it.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  10. #10
    the hat of redundancy hat nvoigt's Avatar
    Join Date
    Aug 2001
    Location
    Hannover, Germany
    Posts
    3,130
    loop for all NPCS checking vs location(x,y)
    loop for all Monsters checking vs location (x,y)
    loop for all Spells checking vs location(x,y)
    I don't think you need to check this per render. It will not change between two renders if no action is taken, player or AI action, so you should check this per action and cache the result. This way you only have to loop your cache for rendering.
    hth
    -nv

    She was so Blonde, she spent 20 minutes looking at the orange juice can because it said "Concentrate."

    When in doubt, read the FAQ.
    Then ask a smart question.

  11. #11
    vae victus! skorman00's Avatar
    Join Date
    Nov 2003
    Posts
    594
    The first question I would have is "Are you runnin in full screen mode?" Frames are usually a bit lower when running in windowed mode.

    As everyone has said, restructuring will probably help your frames, but you might want to do some timings to see how you should change it. See where most of your time is spent, and start from there.

  12. #12
    Registered User ~Kyo~'s Avatar
    Join Date
    Jun 2004
    Posts
    320
    Quote Originally Posted by nvoigt
    I don't think you need to check this per render. It will not change between two renders if no action is taken, player or AI action, so you should check this per action and cache the result. This way you only have to loop your cache for rendering.
    That helped alot although fps just around alot I don't see less than 100 often anymore. THNX. As for the other posts restructuring would make it look nicer but would not increase FPS. More portable sure easier to debug... ok thats done and over with and has been. As for the most FPS used that has to be the light sources...
    Thanks for the replys should come together nicely now.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. SwapBuffers & fps
    By hannibar in forum Windows Programming
    Replies: 0
    Last Post: 03-13-2006, 05:19 AM
  2. Game update...
    By jdinger in forum Game Programming
    Replies: 14
    Last Post: 11-08-2002, 07:10 AM
  3. FPS Counter Prob in OpenGL
    By brandonp in forum Game Programming
    Replies: 1
    Last Post: 07-16-2002, 02:49 PM
  4. SkyLock graphics demo (scrolling, etc.)
    By jdinger in forum Game Programming
    Replies: 9
    Last Post: 06-30-2002, 08:18 PM
  5. dramatic decrease in fps
    By DavidP in forum Game Programming
    Replies: 4
    Last Post: 06-27-2002, 09:05 AM