How do I reclaim memory from sprites after removing them?

I am seeing an issue where if I add() a number of sprites I can see the memory used in the Malloc Log. If I then remove() those sprites and add another set, the sprites are removed but the Malloc Log shows the memory usage steadily increasing.

Here is the code I'm using. Is there something I'm missing about how to clean up sprites?

import 'CoreLibs/graphics'
import 'CoreLibs/sprites'

local gfx <const> = playdate.graphics
local geo <const> = playdate.geometry

-- 'ground' is a 66x66 gif, 316 bytes
local image = gfx.image.new('ground')
local spriteOffset <const> = 36
local testSprites = {}
local drawOffset = geo.vector2D.new(0, 0)
local count = 516

function addSprites(count)
  gfx.sprite.removeSprites(testSprites)
  testSprites = {}

  for i = 0, count do
    local sprite = gfx.sprite.new(image)
    sprite:moveTo(i * spriteOffset, 0)
    sprite:add()
    table.insert(testSprites, sprite)
  end

  print(string.format('%d sprites in display list', gfx.sprite.spriteCount()))
end

function load()
  playdate.display.setRefreshRate(50)
end

function  playdate.AButtonDown()
  addSprites(count)
end

function playdate.update()
  if playdate.buttonIsPressed(playdate.kButtonUp) then
    drawOffset.y += 5
  end
  if playdate.buttonIsPressed(playdate.kButtonDown) then
    drawOffset.y -= 5
  end
  if playdate.buttonIsPressed(playdate.kButtonLeft) then
    drawOffset.x += 5
  end
  if playdate.buttonIsPressed(playdate.kButtonRight) then
    drawOffset.x -= 5
  end

  gfx.setDrawOffset(drawOffset:unpack())

  gfx.sprite.update()
  playdate.drawFPS(10, 10)
end

load()

I would expect to be able to hit the A button over and over but instead I get an out of memory error after a few presses.

1 Like

If possible, I would probably pick a sane number of sprites for the entire game and manage a pool of them by myself. Note that system like the GBA have hardware support for sprites, but the number is limited.

Are you sure this is the correct code? I'm not able to reproduce the crash on simulator or device — but also it doesn't seem to draw anything other than the FPS counter.

@dwineman Thanks for taking a look. Silly question but did you add an image to use in place of the 'ground' sprite. Also do you have the Malloc Pool in the sim set to 16MB?

The video I posted uses that exact code and shows the memory usage growing with each iteration.

@sgeos Sure I can do pooling and try to work around what I’m seeing but my question is: is the behavior I’m seeing expected? Is it expected behavior of the SDK to not reclaim application memory after calling removeSprites?

I was going to just reply to this post, but I decided to post a bug report instead. It seems like removeAll() and removeSprites(array) might be bugged. If you use sprite:remove() instead, I think the issue doesn't happen.

for k, v in pairs(testSprites) do
    v:remove()
end
1 Like

@timhei Thanks very much for taking the time to test this.

@GloryFish Given the workaround, you should now be able to properly conduct your performance test. =)

My mistake — I didn't read carefully enough. Yes, there does seem to be a bug here. Thank you (and @timhei) for bringing it to our attention.