Log In  


I'm wondering if there's any difference in CPU usage when we manually detect situations where something has changed and then call methods in the _draw individually only in those cases.
I'm working on a game with many static elements and I'm considering whether it makes sense to constantly redraw everything or only the elements that have changed, for example in picross game.

1


if you are calling cls() you are already drawing everything each frame.. if you don't call cls() you will persist the last frame and have to clear what you need redrawn manually.. the drawing operations themselves, unless you run a lot of them or loop many times, are not generally going to use too much CPU


I didn't use cls() at all. Let's discuss an example. Half of the screen is the player's interface with information, and it doesn't change very often. The other half is the game, where the action happens. Each frame, I draw a black rectangle only in the area where the action takes place, leaving the interface untouched. The interface will only be redrawn if I set a specific flag. When I decide that the interface needs to change, I redraw only the interface once, with the new values.


@Porkite this might indeed save some CPU but really, unless you're doing some really complex drawing were taking about 1 or 2 % so it's up to you if it's worth that for your game.


1

In general not doing a thing is going to be faster than doing that thing so yes, you can certainly squeeze some extra performance out of your game by making as few draw calls as you can.

This info is out of date but you can see here (http://pico8wiki.com/index.php?title=CPU#Functions_that_add_Lua_cycles) that draw calls are comparatively quite expensive compared to other kinds of calls. That's not surprising since their cost is mostly based on how many pixels are being drawn. It's not necessarily just minor savings either.

I drew some random junk into the map and made these two examples:

Code:


Note that map ignores empty tiles so if you don't draw anything on the map then both of these methods will run at pretty much the same speed because they're both doing basically nothing.

function _init()
   y = 0

   -- copy 2 chunks of the map to upper memory
   --map()
   --memcpy(0x8000, 0x6000, 0x2000)
   --map(0, 16)
   --memcpy(0xA000, 0x6000, 0x2000)
end

function _update()
   -- moves the camera to scroll the background.
   y += 1
   camera(0, y)

   -- changes the memory offset to scroll the background.
   --y += 32
end

function _draw()
   -- drawing with map()
   map()
   print('\#1'..stat(1), 0, y)

   -- drawing with memcpy()
   --memcpy(0x6000, 0x8000+y, 0x2000)
   --print('\#1'..stat(1), 0, 0)
end

The first just calls map() and updates the camera and you can see that it's using about 12% of the CPU. The second one draws a chunk of the map and copies the screen buffer into upper memory. Then instead of calling map() the drawing is done with memcpy. That one only uses about 2% of the CPU. Pretty big savings and map is the only draw call being made in that first example.

memcpy, technically, isn't a draw call even though I'm using it to write to the screen buffer every frame. So even though both are moving backgrounds this is basically the difference between drawing something and drawing nothing. So kind of an extreme example. Still, drawing less can have an impact but it comes with a few caveats. Drawing everything every frame is simple. Managing dirty draw updates is going to be more complex and that means more code and more (Lua) memory dedicated to keeping track of it all. Depending on how you're handling it, managing all that extra state could create its own performance problems. It probably also presents a bunch of new opportunities for weird bugs or visual glitches.

As for whether it makes sense or not, that depends on you and your game. You mention picross games and those don't exactly strike me as the kind of games which need super optimized draw systems. But you know what you're trying to do and I don't so really you're the only one who can judge whether it's needed or not. In general I'd say don't optimize until you need to. That said, part of the fun of Pico-8—at least for me—is seeing how you can push it. So if you want to try optimizing the draw system, just because, then by all means try to optimize the draw system. For something more complex than a simple scrolling background I don't know how much extra performance you'll be able to squeeze out of it but it might be fun to find out.


1

This screen shows the dungeon view, life (top), UI, and map.
The dungeon background, 3D objects, and breathing circle are constantly updated, but each UI is redrawn when it receives an update flag.

If there is only one type of 3D object, this will keep the frame rate at 60fps. (Displaying three of the same object was acceptable.)

When the player moves, the map configuration of the drawn background is updated and the floor scrolls.
If there are two types of 3D objects with this map configuration, the frame rate may drop.

In other words, if you distribute the update timing, you can display a lot of things, but if the update timing is concentrated in one frame, frame skipping will occur.

I hope this gives you a sense of it.


Thank you very much for your help. I know what to look out for and how to check CPU usage. I’ll keep an eye on the resources and take action when necessary :).
Thanks a lot!



[Please log in to post a comment]