Mirage Engine
Optimizing Graphic Output (Finished) - Printable Version

+- Mirage Engine (https://mirage-engine.uk/forums)
+-- Forum: Mirage Source (Nostalgia) (https://mirage-engine.uk/forums/forumdisplay.php?fid=61)
+--- Forum: Archive (2006-2011) (https://mirage-engine.uk/forums/forumdisplay.php?fid=18)
+---- Forum: Resources (https://mirage-engine.uk/forums/forumdisplay.php?fid=49)
+---- Thread: Optimizing Graphic Output (Finished) (/showthread.php?tid=357)



Optimizing Graphic Output (Finished) - William - 11-10-2006

By: William
Difficulty: 3/5 (Medium)
Improvement: Increases the speed in your game. The bigger maps and the more layers you have, the more this code will speed the game up.

Introduction
As it is now, the whole game screen is being covered with tiles every second. So I thought, wouldn't it be better if only the tiles that needs to be updated are redrawn. Thats basicly what this tutorial will do. It will blit the tiles around you, nothing else. If your game has a larger player/enemy size. Then you need to edit the xChange1-2 and yChange1-2. That might be a bit tricky if you dont understand the code. But just ask and I help you out.

Hopefully this will speed up the game Smile Tell me if it dont.

Backup your source before adding this, you never know what things can do Tongue

But in order for this system to work, these things need to be changed/added:
  • BltTile Change [Completed]
  • BltFringe Change [Completed]
  • Blt tile to cover the text (if your using the inbuilt system with the text on the screen. You dont need to change this if you use a textbox instead). Add [Completed]
  • Blt tiles around the enemies when they move. Add [Completed]
  • When saving the map - refill the screen. Add [Completed]
  • Need to blit around the other players when they move.Add
  • Refresh the screen when you enter a new map Add [Completed]
  • /Respawn command needs to have bltTile and bltFringe. Add [Completed]
So that list is pretty big. A few things needs to be changed and a lot of things need to be added. This tutorial will only cover the parts in MSE, it will not include the other layers (from GSD's tutorial), so no Mask1, Fringe2 and such.

Start with adding this at the top of Public Sub GameLoop():
Code:
Dim xChange1 As Byte, xChange2 As Byte, yChange1 As Byte, yChange2 As Byte
Dim x1 As Long, y1 As Long

Replace:
Code:
' Blit out tiles layers ground/anim1/anim2
For y = 0 To MAX_MAPY
                For x = 0 To MAX_MAPX
                    Call BltTile(x, y)
                Next x
            Next y
With this code below. This basicly checks were your player is and we now blit the tiles around him instead of on the whole map.
Code:
If GraphicInput1 = 0 Then
            GraphicInput1 = 1
            For y = 0 To MAX_MAPY
                For x = 0 To MAX_MAPX
                    Call BltTile(x, y)
                Next x
            Next y
        Else
        If InEditor Then
            For y = 0 To MAX_MAPY
                For x = 0 To MAX_MAPX
                    Call BltTile(x, y)
                Next x
            Next y
        Else
            yChange1 = 2
            yChange2 = 2
            xChange1 = 2
            xChange2 = 2
            
            If GetPlayerY(MyIndex) - 2 < 0 Then yChange1 = 1
            If GetPlayerY(MyIndex) + 2 > MAX_MAPY Then yChange2 = 1
            If GetPlayerX(MyIndex) - 2 < 1 Then xChange1 = 1
            If GetPlayerX(MyIndex) + 2 > MAX_MAPX Then xChange2 = 1
            
            If GetPlayerY(MyIndex) - 1 < 0 Then yChange1 = 0
            If GetPlayerY(MyIndex) + 1 > MAX_MAPY Then yChange2 = 0
            If GetPlayerX(MyIndex) - 1 < 0 Then xChange1 = 0
            If GetPlayerX(MyIndex) + 1 > MAX_MAPX Then xChange2 = 0
            
            For y = GetPlayerY(MyIndex) - yChange1 To GetPlayerY(MyIndex) + yChange2
                For x = GetPlayerX(MyIndex) - xChange1 To GetPlayerX(MyIndex) + xChange2
                   Call BltTile(x, y)
                Next x
            Next y
        End If
        End If

Now, change this:
Code:
' Blit out tile layer fringe
For y = 0 To MAX_MAPY
                For x = 0 To MAX_MAPX
                    Call BltFringeTile(x, y)
                Next x
            Next y
To the code below. This part is pretty much as the above one, but this does it for the fringe layer instead.
Code:
' Blit out tile layer fringe
        If GraphicInput2 = 0 Then
            GraphicInput2 = 1
            For y = 0 To MAX_MAPY
                For x = 0 To MAX_MAPX
                    Call BltFringeTile(x, y)
                Next x
            Next y
        Else
        If InEditor Then
            For y = 0 To MAX_MAPY
                For x = 0 To MAX_MAPX
                    Call BltFringeTile(x, y)
                Next x
            Next y
        Else
            yChange1 = 2
            yChange2 = 2
            xChange1 = 2
            xChange2 = 2
            
            If GetPlayerY(MyIndex) - 2 < 0 Then yChange1 = 1
            If GetPlayerY(MyIndex) + 2 > MAX_MAPY Then yChange2 = 1
            If GetPlayerX(MyIndex) - 2 < 1 Then xChange1 = 1
            If GetPlayerX(MyIndex) + 2 > MAX_MAPX Then xChange2 = 1
            
            If GetPlayerY(MyIndex) - 1 < 0 Then yChange1 = 0
            If GetPlayerY(MyIndex) + 1 > MAX_MAPY Then yChange2 = 0
            If GetPlayerX(MyIndex) - 1 < 0 Then xChange1 = 0
            If GetPlayerX(MyIndex) + 1 > MAX_MAPX Then xChange2 = 0
            
            For y = GetPlayerY(MyIndex) - yChange1 To GetPlayerY(MyIndex) + yChange2
                For x = GetPlayerX(MyIndex) - xChange1 To GetPlayerX(MyIndex) + xChange2
                   Call BltFringeTile(x, y)
                Next x
            Next y
        End If
        End If
Now, above this line:
Code:
' Blit out tiles layers ground/anim1/anim2
Add the code below. This will make you able to write to the chat. If your using textboxes for your chat (both the writing part and the text part) then you dont need to add this.
Code:
' Blit Tile to cover text
        For x = 0 To MAX_MAPX
            Call BltTile(x, MAX_MAPY)
        Next x

Now remove:
Code:
' Blit out the items
        For i = 1 To MAX_MAP_ITEMS
            If MapItem(i).Num > 0 Then
                Call BltItem(i)
            End If
        Next i

So now above this:
Code:
' Blit out the npcs
        For i = 1 To MAX_MAP_NPCS
            Call BltNpc(i)
        Next i
Add the code below. This will blit the tiles around the enemy, similar to the technique used for the player before.
Code:
' Blit out the tiles around the npc
        For i = 1 To MAX_MAP_NPCS
            ' Make sure that theres an npc there, and if not exit the sub
            If MapNpc(i).Num > 0 Then
                ' Blit tiles around the npc
                yChange1 = 2
                yChange2 = 1
                xChange1 = 1
                xChange2 = 1
            
                If MapNpc(i).y - 2 < 0 Then yChange1 = 1
                If MapNpc(i).y + 2 > MAX_MAPY Then yChange2 = 1
                If MapNpc(i).x - 2 < 1 Then xChange1 = 1
                If MapNpc(i).x + 2 > MAX_MAPX Then xChange2 = 1
            
                If MapNpc(i).y - 1 < 0 Then yChange1 = 0
                If MapNpc(i).y + 1 > MAX_MAPY Then yChange2 = 0
                If MapNpc(i).x - 1 < 0 Then xChange1 = 0
                If MapNpc(i).x + 1 > MAX_MAPX Then xChange2 = 0
            
                For y1 = MapNpc(i).y - yChange1 To MapNpc(i).y + yChange2
                    For x1 = (MapNpc(i).x - xChange1) To (MapNpc(i).x + xChange2)
                       Call BltTile(x1, y1)
                    Next x1
                Next y1
            End If
        Next i
        
        ' Blit out the tiles around the player
        For i = 1 To MAX_PLAYERS
            ' Make sure that theres a player here
            If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
                ' Blit tiles around the player
                yChange1 = 2
                yChange2 = 1
                xChange1 = 1
                xChange2 = 1
            
                If GetPlayerY(i) - 2 < 0 Then yChange1 = 1
                If GetPlayerY(i) + 2 > MAX_MAPY Then yChange2 = 1
                If GetPlayerX(i) - 2 < 1 Then xChange1 = 1
                If GetPlayerX(i) + 2 > MAX_MAPX Then xChange2 = 1
            
                If GetPlayerY(i) - 1 < 0 Then yChange1 = 0
                If GetPlayerY(i) + 1 > MAX_MAPY Then yChange2 = 0
                If GetPlayerX(i) - 1 < 0 Then xChange1 = 0
                If GetPlayerX(i) + 1 > MAX_MAPX Then xChange2 = 0
            
                For y1 = GetPlayerY(i) - yChange1 To GetPlayerY(i) + yChange2
                    For x1 = (GetPlayerX(i) - xChange1) To (GetPlayerX(i) + xChange2)
                       Call BltTile(x1, y1)
                    Next x1
                Next y1
            End If
        Next i
        
        ' Blit out the items
        For i = 1 To MAX_MAP_ITEMS
            If MapItem(i).Num > 0 Then
                Call BltItem(i)
            End If
        Next i
So now below this:
Code:
' Blit out the npcs
        For i = 1 To MAX_MAP_NPCS
            Call BltNpc(i)
        Next i
Add this code:
Code:
' Blit out the tiles around the npc
        For i = 1 To MAX_MAP_NPCS
            ' Make sure that theres an npc there, and if not exit the sub
            If MapNpc(i).Num > 0 Then
                ' Blit tiles around the npc
                yChange1 = 2
                yChange2 = 1
                xChange1 = 1
                xChange2 = 1
            
                If MapNpc(i).y - 2 < 0 Then yChange1 = 1
                If MapNpc(i).y + 2 > MAX_MAPY Then yChange2 = 1
                If MapNpc(i).x - 2 < 1 Then xChange1 = 1
                If MapNpc(i).x + 2 > MAX_MAPX Then xChange2 = 1
            
                If MapNpc(i).y - 1 < 0 Then yChange1 = 0
                If MapNpc(i).y + 1 > MAX_MAPY Then yChange2 = 0
                If MapNpc(i).x - 1 < 0 Then xChange1 = 0
                If MapNpc(i).x + 1 > MAX_MAPX Then xChange2 = 0
            
                For y1 = MapNpc(i).y - yChange1 To MapNpc(i).y + yChange2
                    For x1 = (MapNpc(i).x - xChange1) To (MapNpc(i).x + xChange2)
                       Call BltFringeTile(x1, y1)
                    Next x1
                Next y1
            End If
        Next i

Find this:
Code:
' ::::::::::::::::::::::
    ' :: Npc spawn packet ::
    ' ::::::::::::::::::::::
    If LCase(Parse(0)) = "spawnnpc" Then
In the end of that (before exit sub) add the code below. This will make it so that the map is updated when the enemies is set to be respawned.
Code:
' Blit Tiles
        For x = 0 To MAX_MAPX
            For y = 0 To MAX_MAPY
                Call BltTile(x, y)
                Call BltFringeTile(x, y)
            Next y
        Next x
Now add these in a module (modGlobal):
Code:
' Checks if the map has been updated
Public GraphicInput1 As Byte ' For the first layer (bltTile)
Public GraphicInput2 As Byte ' for the other layer (bltFringeTile)
Now find:
Code:
' Play music
        Call StopMidi
        If Map.Music > 0 Then
            Call PlayMidi("music" & Trim(STR(Map.Music)) & ".mid")
        End If
Below that, add the code below:
Code:
GraphicInput1 = 0
GraphicInput2 = 0
Now, below this:
Code:
' Blit out players
        For i = 1 To MAX_PLAYERS
            If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
                Call BltPlayer(i)
            End If
        Next i
Add this code:
Code:
' Blit out the tiles around the player
        For i = 1 To MAX_PLAYERS
            ' Make sure that theres an player there
            If IsPlaying(i) And GetPlayerMap(i) = GetPlayerMap(MyIndex) Then
                ' Blit tiles around the player
                yChange1 = 2
                yChange2 = 1
                xChange1 = 1
                xChange2 = 1
            
                If GetPlayerY(i) - 2 < 0 Then yChange1 = 1
                If GetPlayerY(i) + 2 > MAX_MAPY Then yChange2 = 1
                If GetPlayerX(i) - 2 < 1 Then xChange1 = 1
                If GetPlayerX(i) + 2 > MAX_MAPX Then xChange2 = 1
            
                If GetPlayerY(i) - 1 < 0 Then yChange1 = 0
                If GetPlayerY(i) + 1 > MAX_MAPY Then yChange2 = 0
                If GetPlayerX(i) - 1 < 0 Then xChange1 = 0
                If GetPlayerX(i) + 1 > MAX_MAPX Then xChange2 = 0
            
                For y1 = GetPlayerY(i) - yChange1 To GetPlayerY(i) + yChange2
                    For x1 = (GetPlayerX(i) - xChange1) To (GetPlayerX(i) + xChange2)
                       Call BltFringeTile(x1, y1)
                    Next x1
                Next y1
            End If
        Next i

That covers 8 of the 8 points in the begining of the topic. Thats it Smile Should be working fine.


- Jack - 11-10-2006

hmm....would work good because the engine otherwise needs to redo everything a lot


- William - 11-10-2006

I think it will speed it up a bit. Eventhough it might seem like there are more things that needs to be blitted, thats not really the case. Since instead of blitting everything at one time all the time. Pieces are being blitted at different times.

The larger maps you have, the more effective it will be compared to the old system.

Still I want one of you guys who knows a lot about programming to answer if it's noticeable difference. Smile


- William - 11-10-2006

Well I don't really understand how to do the surfaces stuffs. But you said that the system I created checked if there should be a change.

My system only checks how far it should blt. For example, you can't blit outside the map, so when your standing on the very left side of the map, xChange1 will be 0 and not blit on the left side of him.

But I got a feeling that you dont like the idea. So is it a waste of time then to complete it?

Edit: If you try it out with a blank MSE you will notice how it works.


- William - 11-10-2006

Okay, so you are currently working to optimize the graphic output? Sounds good.

Well I might finish my system, just to prove to myself I can Tongue And then I might release it since it abviously helping some Tongue


- William - 11-10-2006

Verrigan Wrote:I think you should. It is a great idea!

Okay well I finish it then Smile Thanks for all the information Tongue


- William - 11-10-2006

Dave Wrote:You have two loops For i = 1 To MAX_MAP_NPCS, consolidate them to one, you don't need to do those loops twice.

Well, the first one is for bltTile and the other one is after the bltNpc, that is bltFringeTile.

And two of those are needed so it works like this:
bltTile - Normal, mask around the npc
bltNPC - Blits the NPC
bltFringeTile - Blits the upper layer around the npc.

If I combine them to one the npc will walk on fringe or below normal.

The further I came to complete this system, the more I started to thinking if this will actually improve the speed. Cause it all became so much coding, compared to so little before (the default).

So the code is ready for testing, I might have missed something. But I dont think so. Also, it would be good if anybody could go through it and compare it with the old to see if it is faster now than before. So I can geet a true statement that this tutorial is good or if it sucks.


- William - 12-10-2006

Well, the code you posted is wrong. So please make sure you post the correct code before posting it.

You still using two For Loops.. I think this is what you mean:
Code:
For i = 1 To MAX_MAP_NPCS
' Make sure that theres an npc there, and if not exit the sub
            If MapNpc(i).Num > 0 Then
                ' Blit tiles around the npc
                yChange1 = 2
                yChange2 = 1
                xChange1 = 1
                xChange2 = 1
          
                If MapNpc(i).y - 2 < 0 Then yChange1 = 1
                If MapNpc(i).y + 2 > MAX_MAPY Then yChange2 = 1
                If MapNpc(i).x - 2 < 1 Then xChange1 = 1
                If MapNpc(i).x + 2 > MAX_MAPX Then xChange2 = 1
          
                If MapNpc(i).y - 1 < 0 Then yChange1 = 0
                If MapNpc(i).y + 1 > MAX_MAPY Then yChange2 = 0
                If MapNpc(i).x - 1 < 0 Then xChange1 = 0
                If MapNpc(i).x + 1 > MAX_MAPX Then xChange2 = 0
          
                For y1 = MapNpc(i).y - yChange1 To MapNpc(i).y + yChange2
                    For x1 = (MapNpc(i).x - xChange1) To (MapNpc(i).x + xChange2)
                       Call BltTile(x1, y1)
                    Next x1
                Next y1
            End If

Call BltNpc(i)

            ' Make sure that theres an npc there
            If MapNpc(i).Num > 0 Then
                ' Blit tiles around the npc
                yChange1 = 2
                yChange2 = 1
                xChange1 = 1
                xChange2 = 1
          
                If MapNpc(i).y - 2 < 0 Then yChange1 = 1
                If MapNpc(i).y + 2 > MAX_MAPY Then yChange2 = 1
                If MapNpc(i).x - 2 < 1 Then xChange1 = 1
                If MapNpc(i).x + 2 > MAX_MAPX Then xChange2 = 1
          
                If MapNpc(i).y - 1 < 0 Then yChange1 = 0
                If MapNpc(i).y + 1 > MAX_MAPY Then yChange2 = 0
                If MapNpc(i).x - 1 < 0 Then xChange1 = 0
                If MapNpc(i).x + 1 > MAX_MAPX Then xChange2 = 0
          
                For y1 = MapNpc(i).y - yChange1 To MapNpc(i).y + yChange2
                    For x1 = (MapNpc(i).x - xChange1) To (MapNpc(i).x + xChange2)
                       Call BltFringeTile(x1, y1)
                    Next x1
                Next y1
            End If
Next i

I think I tested that, and it didn't work. But I can test it when I get home. Cause if it works, then I can optimize the MAX_PLAYERS too.


- William - 12-10-2006

That code does not work. It will blit it wrong. Here is the source with it installed. Feel free to try it out. You wont notice a different with the new system. Only a small speed difference.

http://www.key2heaven.net/MSEOptimizeGFXOutput.rar

Investigation To Prove the Optimization
Okay, so imagine this: MAX_MAPX * MAX_MAPY
In the unedited version of MSE, the MAX_MAPX = 15 and the MAX_MAPY = 11.

11*15=165 That value shows us how many tiles that are being blit out all the time from bltTile.
165+165=330 That is the value for both (bltTile and bltFringe) of them. And this happens all the time the GameLoop runs.

So I think that value is pretty high. So we will compare 330 with the new value:

First we have: ' Blit Tile to cover text
That is 1*MAX_MAPX = 15.
After that we have:
Code:
For y = GetPlayerY(MyIndex) - yChange1 To GetPlayerY(MyIndex) + yChange2
                For x = GetPlayerX(MyIndex) - xChange1 To GetPlayerX(MyIndex) + xChange2
                   Call BltTile(x, y)
                Next x
            Next y
The largest range on that code is -2 to 2 which is 4 and we have two loops which makes it 4*4=16.

So far, we have 15+16=31. Thats still below 330.
After that we have the same loop 4 times. Which makes it 16*4=64
Now we have 64+31= 95. Still a lot less than 330.

And for the final, we have one more of that loop. That makes it 95+16= 111.

So the difference is 330-111=219 in difference. Thats pretty much, isn't it?

So, now we need to take into account that the code is a little bit longer, and this might slow it down a tiny bit. So lets say it slows it down with 69.

That still gives us a difference of 150. Which is a very good result. Smile

And the more layers you have of mask and fringe the more it will help. And also for a bigger map, it will help even more!


- Spodi - 12-10-2006

I dont really understand how you got your numbers. Tiles * Layers is not how much you are drawing, and the speed really wont slow down much if you arn't drawing. Most optimizations in a game such as this come from the drawing, since it sure as hell ain't going to come from the calculations (rare you'd ever need an intensive calculation in such a game).


- William - 12-10-2006

Well I simply just used the for loops for it. To show that they got a lot smaller Tongue

Guess the code doesn't really do much anyway then ..


- BigRed - 12-10-2006

Verrigan, I currently use the same system you stated in a project of mine. I did this just for the simplicity of having the entire map drawing routines in one sub.

I have LowerBuffer which would hold the mirage ground & mask, a Middlebuffer which would hold mirages items, players, & npcs, and a UpperBuffer which would hold mirages Fringe layer. I then blt each of them in order to the BackBuffer which in turn is blted to the primary surface.

It keeps everything so much neater, and even better, you only need to update the lower and upper buffer (depending on what you draw to it) whenever you enter a new map. I then designed the middle buffer to only update if there is a change to that specific surface which I believe is somewhat equivalent to Williams system.

I haven't tested your system william, but the one Verrigan stated does help a lot and to me is the ideal way of gaining some speed when it comes to the graphical side of an engine/game.


- William - 12-10-2006

I understand exactly how yours work. Although I can't code it. it would be good if you could provide with some code from it Wink

My system is kinda the easy way to do a thing. And it simply just makes the For Loops smaller so less is being blitted..


- Rian - 12-10-2006

William, you might wanna go back through your tutorial. You've left a couple instances of graphicinput, which I'm pretty sure need to be either graphicinput1 or graphicinput2


- William - 12-10-2006

Okay, ill check it out.

Edit: yes you were right. I fixed it now. Thanks Smile


- BigRed - 26-10-2006

Hey william, I created a text tutorial for what I was talking about (very little code), would you care to test the tutorial for me? It makes sense to me, but just wanted to make sure it makes sense to everyone else. If so, contact me on any of my instant messengers below, or GTalk (same as my MSN).


- William - 26-10-2006

Of course I can. I think I have you already. So add me instead on msn:
mailto:johansson_tk@hotmail.com


Re: Optimizing Graphic Output (Finished) - William - 21-09-2007

Brining this topic back to life
I just tested to see what FPS change there was in adding this system. I used 4 full layers of ground, mask, anim and fringe. And removed the fps lock. And the fps on a unedited MSE1 was around 300. When my system was added, the FPS was around 900. So I'd say that a pretty big difference.

This optimization do help, however there might be a few glitches using it so it might need a little bit more programming.


Re: Optimizing Graphic Output (Finished) - Robin - 21-09-2007

I got that without this tutorial :P


Re: Optimizing Graphic Output (Finished) - William - 21-09-2007

The surface tutorial? Tongue


Re: Optimizing Graphic Output (Finished) - Robin - 21-09-2007

Surface takes the default mirage to about 600 or 700.

I sorted out some more things to get it to 900.

There's also a few more things I'm gonna work on.


Re: Optimizing Graphic Output (Finished) - William - 21-09-2007

Your improvements on the fps can only be seen when its unlocked right?


Re: Optimizing Graphic Output (Finished) - Robin - 21-09-2007

Yup, but it's really useful for when adding huge features like AI and alphablending, because you can allow a larger FPS drop if you have 900 rather than 300 :P


Re: Optimizing Graphic Output (Finished) - William - 21-09-2007

Yeah, I havn't added my own tut to my game yet, but I will rewrite my tutorial soon cause there are many improvements to be done to it.