Tilemap with polyvector strips

Previous topic - Next topic

TomG

Hello,

Me and a friend have been toying around with GLBasic for last couple of days, it's pretty nicely done. I made some tests regarding the iPhone exporter and I'm pretty worried about performances on 2G/3G iPhones.

After an extensive search and lots of reading on the forum, I made a very basic tilemap test using Polyvector (strip mode) as it seems to be the fastest way to deal with that kind of stuff on iOS devices.

The tilemap size is 8x6, with a 60pixels tile size. That's 48 tiles, which, you'll agree, is definitly not a lot. On my 2G iPhone, I get a very variable framerate, going from 39 to 51 fps max (let say a 45fps average).

I'm not very confortable with GLB at the moment, so maybe I made a huge mistake... Well, here's the full source code :

Code (glbasic) Select

// Setup
SETCURRENTDIR("Media")

SETSCREEN 480, 320, 1
LIMITFPS 60


// Tilemap
TYPE TileMap

width% = 8
height% = 6
tilesize% = 60
_width% = self.width * self.tilesize
_height% = self.height * self.tilesize
tx%
ty%

FUNCTION draw:
STARTPOLY 1, 2
FOR x = 0 TO self._width STEP self.tilesize
FOR y = 0 TO self._height STEP self.tilesize
self.tx = RND(1) * self.tilesize
self.ty = RND(1) * self.tilesize

POLYVECTOR     x,    y,  self.tx+0,  self.ty+0, RGB(255,255,255)
POLYVECTOR     x, y+60,  self.tx+0, self.ty+60, RGB(255,255,255)
POLYVECTOR  x+60,    y, self.tx+60,  self.ty+0, RGB(255,255,255)
POLYVECTOR  x+60, y+60, self.tx+60, self.ty+60, RGB(255,255,255)

POLYNEWSTRIP
NEXT
NEXT
ENDPOLY
ENDFUNCTION

ENDTYPE


// Image loading
LOADFONT "font.png", 0
LOADSPRITE "tiles.png", 1


// Vars
LOCAL dtime
LOCAL fps
LOCAL showfps
LOCAL delay

LOCAL map AS TileMap


// Main loop
WHILE TRUE

// Draw the map
map.draw()

// FPS
dtime = GETTIMER()
fps = ((1000 / dtime) + fps) / 2
delay = delay + dtime

IF delay > 500
delay = 0
showfps = fps
ENDIF

PRINT "FPS: " + showfps, 2, 2

// Display
SHOWSCREEN
WEND


The "tiles.png" is just a 120x120 image with four 60x60 color squared. No alpha. I can zip the full package if someone wants to try it.

So, am I missing something important or am I doing something wrong ? Or is it just the most I can get from my iPhone 2G with GLBasic ?

Thanks !

Moru

For more exact timing you might want to use GETTIMERALL() instead but I'm no iPhone programmer so YMMV

Qube

As you are filling the entire iPhone screen with graphics each frame then the default action after SHOWSCREEN (to clear the backbuffer) is not needed, so to gain a few FPS, place the command CLEARSCREEN -1 after your LIMITFPS command.

TomG

#3
Quote from: Qube on 2011-Jan-15
As you are filling the entire iPhone screen with graphics each frame then the default action after SHOWSCREEN (to clear the backbuffer) is not needed, so to gain a few FPS, place the command CLEARSCREEN -1 after your LIMITFPS command.
Thanks for the tip, unfortunatly it doesn't make any difference :(

The strange thing is my code does the same thing over and over but the frame rate is very irregular... I'll change the fps counter to use GETTIMERALL() but I don't think it'll make a difference.

Edit: Yep, GETTIMERALL() is better, the framerate is more stable. I'm getting a constant 39-40fps. Now, about making it faster... Any idea ? :)

Scott_AW

You should limit the draw update to 30FPS, in other words skip every other frame.
Current Project, Orbital Contract Defense
http://gamejolt.com/games/adventure/code-name-ocd/9887/

BlackShadow now open source/resource(requires duke3d)
http://gamejolt.com/games/adventure/black-shadow-3d/9885/

Moru

What is the refresh speed of a display on an iphone and is it possible to lock the framerate to that?

TomG

The iPhone screen refresh rate is 60Hz.

Yes, I can limit the framerate to 30 but that's not really the point here. If I only get 40 fps with 48 tiles, what will happen when I add an animated player sprite, some collision detection and some enemies on screen ?

Moebius

One polyvector strip is capable of creating a tiled rectangle...  If you use a power of 2 sized texture (scaling up 60x60 to 64x64 should be fine) then you can just use a single strip with texture coords 'self._width' and 'self._height'.  If you want to preserve 60x60 tiles using this method, use a 64x64 image and use 'self._width * 60 / 64' and 'self._height * 60 / 64' for texture coords and the texture size will automatically be scaled.
This way should be faster (using polyvector for a parallax effect background is working fine for me...), but I would think that your method would be faster - polyvector strips have allowed me to render over a thousand particles with alpha at 60 FPS on my iPod 2G, but you only get 40 FPS with 48 strips...
I would make a quick example and test, but sry I really don't have any time ATM...
Endless Loop: n., see Loop, Endless.
Loop, Endless: n., see Endless Loop.
- Random Shack Data Processing Dictionary

TomG

Quote from: Serpent on 2011-Jan-15
One polyvector strip is capable of creating a tiled rectangle...  If you use a power of 2 sized texture (scaling up 60x60 to 64x64 should be fine) then you can just use a single strip with texture coords 'self._width' and 'self._height'.  If you want to preserve 60x60 tiles using this method, use a 64x64 image and use 'self._width * 60 / 64' and 'self._height * 60 / 64' for texture coords and the texture size will automatically be scaled.

I'm not sure I get what you mean, but if I'm correct, your method is good if you want to repeat the tiling pattern to a single strip. In my case, I'm trying to draw something like a platformer tilemap, using a tilesheet (well in my code it's drawing random tiles , but that's for testing purposes before I use something like TileD), it won't be a looping pattern.

Or did I miss your point ?

QuoteThis way should be faster (using polyvector for a parallax effect background is working fine for me...), but I would think that your method would be faster - polyvector strips have allowed me to render over a thousand particles with alpha at 60 FPS on my iPod 2G, but you only get 40 FPS with 48 strips...

If you can draw thousands of strips at 60fps on your iPhone 2G, then there's definitly something wrong with my code. I guess that's good news :good:

Note : about the CLEARSCREEN command... I lose 4 fps if I use it. Strange, heh ?

Moebius

You got my point exactly but I was hoping that this wouldn't be true:
QuoteIn my case, I'm trying to draw something like a platformer tilemap, using a tilesheet
In this case you will indeed need a new strip for each tile  :(
Hopefully something in your code can be changed, but unfortunately I can't see anything that would slow it down...

QuoteIf you can draw thousands of strips at 60fps on your iPhone 2G
Actually iPod Touch 2G (actually slightly faster than iPhone 3G apparently)...  It starts to lag after about 1500 10px sized particles but I can only get a bit less than 50FPS with your code on it...
The only difference in rendering between my particles and your code (apart from size) really is I'm not using anything other than variables inside my particle type...
Endless Loop: n., see Loop, Endless.
Loop, Endless: n., see Endless Loop.
- Random Shack Data Processing Dictionary

Moebius

[Continued from end of last post]
...Actually there is a 'major' difference - I've been using alpha (mode '1').
Sticking an 'ALPHAMODE 1' at the start of the 'draw' function seems to make it run at 60 FPS for me...

Very strange that enabling alpha blending seems to make it considerably faster...   One would think that it would require many more calculations than simply replacing pixels with others...

If you're forced to use alpha, you could just draw a black rectangle before using polyvector...  but again it's strange that this would cause any sort of an increase in speed  :blink:
Endless Loop: n., see Loop, Endless.
Loop, Endless: n., see Endless Loop.
- Random Shack Data Processing Dictionary

TomG

Yep, using ALPHAMODE 1, I get 54fps. Thanks Serpent ! That's better, but still O_O

I tried pre-computing every data in some DIMs but I get the same frame rate, so graphics are indeed the bottleneck in this case... That's really strange.

Considering I'm using POLYNEWSTRIP (which adds an invisible triangle every time according to the doc), to draw my 48 tiles I need about 150 triangles. How is 150 triangles too much, especially when drawn in one batch ?

I'd be curious to see the generated C++ code, is there any way to do that ?

Another quick question : is there a way to use VBOs in GLBasic for non-animated polyvectors ? That would give a huge performance boost.

Dark Schneider

#12
- Usually it is not good to draw STRIPS of 2 triangles only, strips are made for larger ones, in your case use better triangles, when you call POLYNEWSTRIP this generates more triangles (degenerated ones) than simply reusing 2 vectors (that are cached before was used recently) for a new complete triangle. So you should use triangle list mode and then 6 POLYVECTORs per tile. About ALPHAMODE, use -1 if not you have additive alpha.

- About FPS, it is very important if you are using an old device (iPhone or iPod Touch) or a newer one (iPhone 3G or iPod Touch 2 or 3G, I don't remember exactly), at some moment Apple changed the GPU for a new version of PowerVR much faster than the older one, and the old one is not able to draw all screen pixels at 60FPS.
Moreover, I don't know but in the case of newer devices, like iPhone 4 or iPad, I am not sure if they are able to draw all screen pixels at 60 FPS in their high resolution modes (640x960 with Retina and 1024x768), this should be confirmed by someone that has these ones.

I should recommend you to limit fps at 30, at this rate you have speed to draw multiple layers with no worry about it at all in any device. Anycase in those small devices LCDs 30 and 60 FPS are not very different.

If you want to keep your code or if it is needed (for better collision detection) keep the 60 FPS, then simply Update twice but Draw only once.

Code (glbasic) Select

LIMITFPS 30

...

for update_loop%=0 to 1
   //...updates
next

//...draws
SHOWSCREEN


You know... ;)

TomG

Quote from: Dark Schneider on 2011-Jan-15
- Usually it is not good to draw STRIPS of 2 triangles only, strips are made for larger ones, in your case use better triangles, when you call POLYNEWSTRIP this generates more triangles (degenerated ones) than simply reusing 2 vectors (that are cached before was used recently) for a new complete triangle. So you should use triangle list mode and then 6 POLYVECTORs per tile. About ALPHAMODE, use -1 if not you have additive alpha.
Thanks for the tips, I'll try them tonight !

Quote- About FPS, it is very important if you are using an old device (iPhone or iPod Touch) or a newer one (iPhone 3G or iPod Touch 2 or 3G, I don't remember exactly), at some moment Apple changed the GPU for a new version of PowerVR much faster than the older one, and the old one is not able to draw all screen pixels at 60FPS.
Moreover, I don't know but in the case of newer devices, like iPhone 4 or iPad, I am not sure if they are able to draw all screen pixels at 60 FPS in their high resolution modes (640x960 with Retina and 1024x768), this should be confirmed by someone that has these ones.

I know it'll run way faster on 3GS/4 iPhones, but your statement about 2G/3G iPhone is not true. Let me explain.

How come I can play Mirror's Edge (a full 3D and beautiful game) on my 2G without any FPS trouble ? And what about Canabalt, running 60fps too on my 2G which draws generated tilemaps with lots of 32x32, 16x16 tiles (and bigger ones for background layers) ? Canabalt is open source now, you can take a look at the source code (Objective-C + OpenGL). Last month I did some tests with Cocos2D/Objective-C, and rendering a tilemap was no problem, although I hate the Objective-C language and I can't get used to it (that's why I'm here, GLBasic is fun =D).

This is what I don't understand, I know I won't be able to create a very fast action game with lots and lots of special effects and 3D models everywhere with GLBasic, but still, GLBasic compile to C++ which is supposedly faster than Objective-C and yet I can't get a very basic tilemap to render properly... Well, it renders properly, but there's almost nothing on the screen and I can't even get to 60fps.

QuoteI should recommend you to limit fps at 30, at this rate you have speed to draw multiple layers with no worry about it at all in any device. Anycase in those small devices LCDs 30 and 60 FPS are not very different.
Sure, but when I'll add four or five animated sprites and the frame rate will go below 30 fps, it'll be the same problem.

QuoteIf you want to keep your code or if it is needed (for better collision detection) keep the 60 FPS, then simply Update twice but Draw only once.
Actually, this tilemap code I made was a performance test. My game will run @30fps. I'm just worried that a so simple test is already stressing my iPhone... That's why I'm trying to find out if the speed problem is coming from my code or from GLBasic.

Anyway, thanks for your answer :)

TomG

Some progress :

- 60px tiles =  48 tiles on screen : >60 fps (vsync limit)
- 48px tiles =  70 tiles on screen : >60 fps (vsync limit)
- 32px tiles = 150 tiles on screen :  58 fps
- 16px tiles = 600 tiles on screen :  51 fps

That's way better. I might be able to get some more fps by using triangle strips without POLYNEWSTRIP, and thus skipping degenerated triangles.

Something I discovered while testing : RND() is slow on the iPhone. 150 RND() per frame = 10fps lost. So be careful with it.