Log In  

I'm working on a project where I want to relegate some of the simpler sprites to being generated by the code, rather than occupying valuable space in the sprite sheet and map areas. However, it just dawned on me that I am running many loops of line(), rect(), etc. every single _draw() call, which I have a gut feeling isn't a very efficient way of doing things. Aside from the obvious "just use the sprite sheet" solution, is there a way in which I can continue to generate images from code BUT only run this one time (eg. during _init()), and then load the buffered images during _draw() somehow?

Even if the answer is just simply that this isn't possible, knowing so would at least put my mind at rest!


EDIT: Misremembered my implementation somewhat!
-"single _update() call" => "single _draw() call";
-"to use this image generation code BUT only call it one time" => "to generate images from code BUT only run this one time".

P#75890 2020-05-05 21:34 ( Edited 2020-05-05 22:45)

You could write a function that creates a table in lua RAM and fills it with your data, and draw to screen from this using another function. It may not be quite as fast or easy as using the Sprite sheet and the built-in functions, but it would be possible.

If you did just redraw lots of shapes each frame, though, Pico 8 can handle a lot of those. My game Cloud Bounce draws about a thousand filled circles each frame while running at 60fps.

P#75895 2020-05-05 22:11 ( Edited 2020-05-05 22:19)

Thanks for your reply! When you refer to Lua RAM, are you saying just to create a table with the image's pixel values in (and to draw from that), or is there a way to specifically allocate memory? Just because I'm confused how the former would be any more efficient than my current implementation, and I imagine it would use more tokens. I'm fairly new to Pico8 and Lua, so apologies for the gaps in my knowledge and if I'm misunderstanding anything!

It's more the knowledge that it's wasteful than the actual performance being hindered, but that anecdote's good to know!

P#75899 2020-05-05 22:41

I'm fairly new to Pico-8 myself (and to programming in general), so I can't claim to be an expert. I mentioned the idea because I've made a couple functions that create and fill an array of 6,400 values and constantly transfer data from it to map memory. They only take 180 tokens and about 5% of system memory resources. For my usage case this method is very advantageous, but it's possible that for yours it wouldn't be.

P#75905 2020-05-05 23:17 ( Edited 2020-05-05 23:23)

Yes, creating a table is extremely efficient. Here are two additional hints:

  • start indexing the table at 1, not 0, even if Lua allows it; you’ll get better memory efficiency
  • whenever possible, load/save 4 bytes (8 pixels) at a time using poke4/peek4 (or the new $ operator for peek4)

Some sample code to save and restore the screen:

    local save={}
    for i=1,2048 do save[i]=$(0x5ffc+4*i) end

    ... do things ...

    for i=1,2048 do poke4(0x5ffc+4*i,save[i]) end
P#75906 2020-05-05 23:24

@JadeLombax

Oh okay, I getcha! Either way, that sounds like it could work for my project (or at least an important tool to consider for others in the future!). How exactly do you go about storing the array as a map?

I've also just realised that, ultimately, regardless of how an image is stored, the process of drawing anything (shape, sprite, etc.) probably boils down to a group of pset()s at an internal level. So I guess the only things I can strive to cut out from each tick are colour and position calculations, which means storing things in a table after a one-time generation phase is almost certainly an improvement.

The only other dream solution I can imagine is a selective cls() which avoids areas of the screen that are not to be redrawn each tick. Again, this is probably similarly constrained to specifying which pixels one wants to wipe each tick in yet another 2d array.

Both of these tables are probably going to eat up a lot more storage and tokens than my current on-the-fly implementation, although the performance might be better... But I stress that this is just me thinking out loud!


@samhocevar

Oo I had no idea those commands existed, but I'll definitely consider those! Out of curiosity, how could I find out which address to peek/poke from? Or are you using "0x5ffc" in your example for a reason?

P#75909 2020-05-05 23:45

@ggiboihmis
I don't actually store the array as a map, I just use mset to take tile values from it and put them in the map area. You could use pset in the same way to take pixel values from an array and draw them to the screen, and I've been thinking about doing this myself as a buffer for decompressed sprite data so I don't have to leave spritesheet or map area empty for the same purpose. I'll see about getting you some example code.

@samhocevar
Are you saying it's possible to use peek (and multi-peek) functions to get values from a table in RAM? If so, how would that be done?

P#75910 2020-05-06 00:02 ( Edited 2020-05-06 04:34)

don’t use pset to draw many sprites or anything big.
(and no - under the hood there is no ‘pset everything ‘!)
For your problem, couple of options:

  • easy: pack your in-memory sprite in spritesheet format (eg look at the gfx memory layout) and copy the necessary images to sprite sheet as needed.
    Using memcpy or poke4 is really fast. Then use standard spr/sspr routines to draw on screen
  • difficult: make your own memory-to-screen routine using bit shifting and but masking to copy to screen memory. This can be done, it is quite fast but requires a very good understanding of the memory layout
P#75935 2020-05-06 05:55

@ggiboihmis 0x5ffc comes from 0x6000 (the address of the screen in memory) minus 4 (because variable i starts at 1, not 0)

@JadeLombax I am not sure I understand your question; the sample code I posted above shows one way to load/save 4 bytes at a time from/to a table of 32-bit numbers.

P#75945 2020-05-06 10:46

[Please log in to post a comment]