Jump to content

Photo

Is 10 FPS a too low framerate for a LYNX tile based game?


84 replies to this topic

#1 Nop90 OFFLINE  

Nop90

    Space Invader

  • 49 posts
  • Location:Italy

Posted Wed Jan 16, 2019 5:59 AM

I'm working on the port of Xump for the lynx and at the moment I have a working alpha with one level and without music/sfx.

 

The first working build had about 4 FPS during gameplay. Optimizing the code I reached steady 7 FPS. Now I'm moving things in a cart filesystem to load title screen and menu backgrounds only when needed, so I can have other memory to make more optimizazions. I think I can reach about 10 FPS, but no more, mostly because to maintain the original level fomat I have to use small 8x7 px tiles.

 

Kojote, the Retroguru team leader, thinks this is a too low FPS for an official release, but it seems to me that some commercial games have worst FPS.

 

What do you think about this? Is it worth to continue the project with such a performance limit? 



#2 42bs OFFLINE  

42bs

    Moonsweeper

  • 288 posts
  • Location:Germany/Southest West

Posted Wed Jan 16, 2019 6:11 AM

Still using C? Switch to Assembly and get another 20FPS ;-)

 

And yes, 10FPS is far to slow IMHO.
 

I wonder, why it is so slow. There is not much action on the screen, right?



#3 enthusi OFFLINE  

enthusi

    Dragonstomper

  • 531 posts
  • Location:Potsdam, Germany

Posted Wed Jan 16, 2019 7:01 AM

What might have been ok with original LCDs screens will look (and in particular feel) very very bad on emulators and with McWill mod.

Emus + mod are probably the way people will experience games the most these days.



#4 42bs OFFLINE  

42bs

    Moonsweeper

  • 288 posts
  • Location:Germany/Southest West

Posted Wed Jan 16, 2019 7:45 AM

I wonder, the game play looks rather static. Only the player moves and some animated sprites ...

 

*hmm* I need to check the FPS of Invaders some day ... :-)



#5 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Wed Jan 16, 2019 9:37 AM

Assembly is an option, but my ASM skills are very rusty and I should rewrite all the game engine. Probably I'll try to optimize a little more the C code first.

 

The screen is very static, there is only the player sprite moving and some animates tiles. The alpha version I have is not bad IMHO, getting some more speed could be enough.

 

At the moment I'm testing it with mednafen on a Linux VM on my PC, and with the online emulator on atarigamer. I'm in the waiting list for a SD cart, so I can test it on my Lynx, but I don't expect big differences.



#6 LordKraken OFFLINE  

LordKraken

    Chopper Commander

  • 144 posts
  • Location:Sverige

Posted Wed Jan 16, 2019 10:37 AM

I have approx. 15 fps (12 to 20 depending on the action) using a 15*13 map with 12*12 pixels tiles.

Are you using linked sprites when you render your level? (i.e. one single only to DrawSprite)


Edited by LordKraken, Wed Jan 16, 2019 10:38 AM.


#7 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Wed Jan 16, 2019 3:18 PM

I'm using 3 sprites. One for the game header (where score and time are placed), one for text chars and one for the tiles (about 20 tiles).

 

Here is a screenshot of the game screen:

xumplynx.png

 

Please don't judge the gfx quality, they are only a fast rescaling of the original tiles. If this project reach an acceptable level of quality we will hire a gfx artist to make better graphics.

 

At the moment I'm testing the code to find FPS bottlenecks.

 

Commenting out the tiles drawing loop and the animation logic (i.e. drawing only the header, the topscreen numbers and the player tile) I have 50 FPS. I can't go faster than this even if I remove all the drawings. Maybe an emulator limit?

 

Simply using tgi_clear() the framerate drops to 25 FPS. I'm avoiding to use it.

 

Both the tiles drawing loop and the game logic are very CPU intensive, so I', going to work  them now.

 

I also tryed using a single buffer, drawing only changes to the screen, but the speed is still to low and I have a lot of flickering, so at the moment this isn't  a good solution.

 

 

 



#8 enthusi OFFLINE  

enthusi

    Dragonstomper

  • 531 posts
  • Location:Potsdam, Germany

Posted Wed Jan 16, 2019 4:08 PM

tgi_clean() probably clears the screen (by drawing a large pixel I assume).

Also you dont need to draw all tiles each 'frame'.

Just draw those that changed (but in a double buffer, that is fine).

That should work in C as well. How tight is your memory currently?



#9 42bs OFFLINE  

42bs

    Moonsweeper

  • 288 posts
  • Location:Germany/Southest West

Posted Wed Jan 16, 2019 10:51 PM

I'm using 3 sprites. One for the game header (where score and time are placed), one for text chars and one for the tiles (about 20 tiles).

 

 

 

Why this complicated? Why not drawing the tiles and all this directly? Now you are "drawing" into some buffer to use it as sprite data to use Suzy to draw it to the screen?

 

BTW: Atari recommends to use 60Hz screen refresh. 50Hz flickers a lot on the old LCDs.
 



#10 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Thu Jan 17, 2019 12:37 AM

 

Why this complicated? Why not drawing the tiles and all this directly? Now you are "drawing" into some buffer to use it as sprite data to use Suzy to draw it to the screen?

 

BTW: Atari recommends to use 60Hz screen refresh. 50Hz flickers a lot on the old LCDs.
 

 

In the site structure I'm only changing the pointer do the pixel data, the I let Suzy draw it. I suppose this is faster than writing directy to the video buffer.

 

I set the screen refresh at 75FPS, 50FPS is what i get o the emulator doing almost nothing.



#11 Fadest OFFLINE  

Fadest

    Moonsweeper

  • 491 posts
  • Location:France

Posted Thu Jan 17, 2019 2:06 AM

 

Both the tiles drawing loop and the game logic are very CPU intensive, so I', going to work  them now.

 

 

 

 

 

Tiles drawing loop ?

Do you mean you draw each tile individually inside a loop ?

If so, you'd better create a list a chained sprite, it uses a bit more memory but is far more efficient.

If your level is never changing, you only declare it once at the beginning and only have to make a DrawSprite in your game logic loop.

If it change sometimes (tile disapearing for example), you just have to change the pointer to pixel data of the sprite when needed.



#12 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Thu Jan 17, 2019 3:07 AM

Chained sprite. This is new to me, but now I understand that *next in the sprite struct.

 

So I can declare an array of sprites and initialize them with the game map (position, pixels data pointers and the chain) when loading the map.

 

Thanks, I'll try this way.



#13 Fadest OFFLINE  

Fadest

    Moonsweeper

  • 491 posts
  • Location:France

Posted Thu Jan 17, 2019 3:52 AM

This should improve a lot your FPS.

We made a test with Anata years ago on a Mario like level with scrolling, it was unplayable without sprite chaining and smooth once sprite chaining was used.

 

You can create an array of sprites (just like your array of tiles - but think of setting visible flag to no to the item not to be displayed) or create a list of sprites, and declare only visible tiles. This is more efficient in memory, but is a bit more difficult to make the link between sprite and tile if needed.

The first sprite must a be a "real and complete" sprite declaration, but the next one can bypass zoom end color definition to gain some space if every definition is the same (look at Mathias or Bastian source code for old newcc65 for example).



#14 enthusi OFFLINE  

enthusi

    Dragonstomper

  • 531 posts
  • Location:Potsdam, Germany

Posted Thu Jan 17, 2019 4:01 AM

For Lacim's Legacy I generate speedcode/sprite chains on the fly :)

It might be faster to plot a single empty sprite rather than disabling it in the chain (since you modify pointers already you dont have to deal with the skip-sprite flag code).
At least that was the the case for me :)
You can also speed up the process alot if for example the top and/or bottom part of the chain isnt updated very often. Just enter the chain at a later point and modify it to return earlier.



#15 LordKraken OFFLINE  

LordKraken

    Chopper Commander

  • 144 posts
  • Location:Sverige

Posted Thu Jan 17, 2019 1:09 PM

 

 

Are you using linked sprites?

 

Apparently the answer is no :)

 

And yes, that's the problem!!!

 

It's easy to solve luckily :)

1) Define an initial SCB with a size, position, data, palette, scaling,

2) Then link it to a second SCB that only define data and position (as you reuse the scaling and the palette of the first SCB - you need to set the CTL flags correctly - check the docs for that).

3) GOTO 2) for all the tile of your level

4) Just draw your first SCB and - automagically - the whole level will be drawn with this single call :)

 

edit: for instance here are the SCB definition for my level (also tile-based). Note that I'm using the old compiler, so it might look a bit different in the new compiler but you get the idea. Note also that I need to define an array of size X to store X copy of _SCBLevelNext.

 

 

_SCBLevel: dc.b $c0, $10, $20 ; CTL0, CTL1 = zoom X/Y, NO COLLISION

dc.w _g_arrSCBLevel ; NEXT (pointer to the linked list)
dc.w 0 ; DATA (dummy sprite)
dc.w 0, 0 ; X & Y position
dc.w $100, $100 ; Width and height (256 = normal)
dc.b $01, $23, $45, $67, $89, $ab, $cd, $ef ; Palette definition
          
_SCBLevelNext: dc.b $c0, $08, $20 ; CTL0, CTL1 = reuse existing palette, NO COLLISION
dc.w 0, 0 ; NEXT, DATA
dc.w 0, 0 ; X & Y position

Edited by LordKraken, Thu Jan 17, 2019 1:22 PM.


#16 LordKraken OFFLINE  

LordKraken

    Chopper Commander

  • 144 posts
  • Location:Sverige

Posted Thu Jan 17, 2019 1:19 PM

Also, you might consider:

- to use Enthusi trick of entering the SCB chain a bit later, and exiting it a bit earlier, updating only the part that has been actually modified,

- OR to increase the size of your sprites a little bit.

 

Right now, even with linked sprites, you still have a fair amount of sprite to draw (260 to cover the screen whil I only need 100 with 12 * 12 pixels tiles)


Edited by LordKraken, Thu Jan 17, 2019 1:20 PM.


#17 42bs OFFLINE  

42bs

    Moonsweeper

  • 288 posts
  • Location:Germany/Southest West

Posted Thu Jan 17, 2019 11:26 PM

Also, you might consider:

- to use Enthusi trick of entering the SCB chain a bit later, and exiting it a bit earlier, updating only the part that has been actually modified,

- OR to increase the size of your sprites a little bit.

 

Right now, even with linked sprites, you still have a fair amount of sprite to draw (260 to cover the screen whil I only need 100 with 12 * 12 pixels tiles)

 

260 sprites? Why? The backdrop, title, title bar, score (10), player and the field (< 80): ~ 100 sprites


Edited by 42bs, Thu Jan 17, 2019 11:31 PM.


#18 LordKraken OFFLINE  

LordKraken

    Chopper Commander

  • 144 posts
  • Location:Sverige

Posted Fri Jan 18, 2019 9:36 AM

He wrote that his sprite are 8*8, so 160 / 8 = 20 sprites for a line and 102 / 8 = 12 sprites for a row, so 20 * 12 = 240 sprites to cover the screen (and you have to add a line and a row for scrolling on top of that - if you want scrolling oc).

So even if you remove the upper part with the title and the score, it's still a bunch of sprites.


Edited by LordKraken, Fri Jan 18, 2019 9:37 AM.


#19 enthusi OFFLINE  

enthusi

    Dragonstomper

  • 531 posts
  • Location:Potsdam, Germany

Posted Fri Jan 18, 2019 10:08 AM

I think the approach is not suitable :)

If I were going for such a layout, I might end up doing draw-loops in code actually but only those that were marked 'dirty' by moving objects.

Ignoring a few you can still easily ,x index a list of tiles and check for dirty (or also other) flags.



#20 42bs OFFLINE  

42bs

    Moonsweeper

  • 288 posts
  • Location:Germany/Southest West

Posted Fri Jan 18, 2019 10:15 AM

He wrote that his sprite are 8*8, so 160 / 8 = 20 sprites for a line and 102 / 8 = 12 sprites for a row, so 20 * 12 = 240 sprites to cover the screen (and you have to add a line and a row for scrolling on top of that - if you want scrolling oc).

So even if you remove the upper part with the title and the score, it's still a bunch of sprites.

 

So the design needs to be re-considered. As from what I have seen so far, the "playfield" are the stones the player walks on (BTW, anyone remember Oxygen on ST or Enigma, similar game). The rest is "background". No need to split up the background in tiles.

If the background is static, then it would be even easier, as you only need to draw the modified parts.

I wonder what FPS Loopz or Lemmings has ...



#21 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Fri Jan 18, 2019 11:56 AM

I'm restarting the port from scratch. Drawing tiles is one of the reasons for low FPS, but the other one seems to be the use of an array of struct for the gamemap. I'll code something faster to address.

 

At the moment I created a new cart structure with the intro, the main code (for now only the menu,the pause screen and the gameover screen), the different backgrounds in separate cart files, and the first bg music (also in a cart file).

 

Now I'm adding the 48 levels data in separated cart files. I think I'll load them in the back buffer because I need them only to setup the level map at the beginnig of every level. This way I can save another 1kB of memory.

 

If I don't use animated tiles I can draw the full level only once, than I can draw less than 20 chained sprites every frame. This should give a good result.

 

But to do this I have to understand how to use a single buffer. Using two buffers I need to maintain the two buffers aligned (every frame I have to draw the previous changes than draw the new changes) and this doubles the number of sprites to draw.

 

Can you give me an hint about this? If I can use on lynx a smart trick, a code snippet would be apreciated.



#22 42bs OFFLINE  

42bs

    Moonsweeper

  • 288 posts
  • Location:Germany/Southest West

Posted Fri Jan 18, 2019 12:15 PM

Before you start to write code for the whistles and bells, I'd study what is possible to draw in one VBL. Maybe you can "animate" by color cycling?
 

Using two static buffers means, you just have to redraw the parts that change, but in both. If this is quicker than redrawing everything needs to be tested.

 

There is a book about r-type for the Spectrum. IIRC the coder used to redraw only the modified parts but with double buffering.

 

I just checked: In Invaders, I use a single buffer and just remove the aliens at old position and draw at the new one. I had to do this because the alien-shots destroy the bunker.



#23 karri OFFLINE  

karri

    River Patroller

  • 2,561 posts
  • Location:Espoo, Finland

Posted Fri Jan 18, 2019 12:27 PM

In Solitaire I used a static buffer for the table and all the cards and drew only the zoomed up pile while playing. Erase was copying areas from background and drawin was directly to screen, But now I am simplifying the cards and going back to normal dbl buffering. It looks so much nicer.



#24 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Fri Jan 18, 2019 1:11 PM

Thanks. I know the basic concepts of single and double buffering, have only to make some experience with them on the Lynx.

 

Finished adding the levels to the cart structure, now I'm going to implement the data structure for the level map and the loading code, than the new drawing logic.



#25 Nop90 OFFLINE  

Nop90

    Space Invader

  • Topic Starter
  • 49 posts
  • Location:Italy

Posted Sun Jan 20, 2019 4:03 PM

I created the cart structure using the cart template and almost everything went fine, but the game freezes at startup if I use in the code the clock() function or the CLOCKS_PER_SEC value;

 

The same code works loading the whole code in ram (without using the cart structure), but while in a cart structure the code freezes even if the call to clock() is in a part of code not loaded (at startup I load the intro, but the call to clock() is in the game section loaded from the cart after the intro).

 

Could be a problem of the linker? Does somebody else had this problem? Can you find me to find a solution?






0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users