So I have this rain module for a game that I am developing, which is causing a massive system memory leak, which leads to lag and ultimately crash of the application. The function "t.start" is called with a timer every 50 ms.
Though I I've tried I can't really find the cause for this! Maybe I am overlooking something but I can't help it. As you see I niled out the graphics related locals...Does anyone notice something?
As a secondary issue : Does anyone have tips on preloading next scene for a smooth scene change? Because the loading itself is causing a short freeze when I put it in "scene:show()"...
Thanks for your help! Greetings, Nils
local t = {}
local composer = require("composer")
t.drops = {}
function t.fall(drops, group)
for i = 1, #drops, 1 do
local thisDrop = drops[i]
function thisDrop:enterFrame()
if aboutToBeDestroyed == true then
Runtime:removeEventListener("enterFrame", self)
return true
end
local randomY = math.random(32, 64)
if self.x ~= nil then
self:translate(0, randomY)
if self.y > 2000 then
self:removeSelf()
Runtime:removeEventListener("enterFrame", self)
self = nil
end
end
end
Runtime:addEventListener("enterFrame", drops[i])
thisDrop = nil
end
end
t.clean = function()
for i = 1, #t.drops, 1 do
if t.drops[i] ~= nil then
table.remove(t.drops, i)
t.drops[i] = nil
end
end
end
function t.start(group)
local drops = {}
local theGroup = group
for i = 1, 20, 1 do
local randomWidth = math.random(5, 30)
local dropV = display.newRect(group, 1, 1, randomWidth, 30)
local drop1 = display.newSnapshot(dropV.contentWidth , dropV.contentHeight * 3)
drop1.canvas:insert(dropV)
drop1.fill.effect = "filter.blurVertical"
drop1.fill.effect.blurSize = 30
drop1.fill.effect.sigma = 140
drop1:invalidate("canvas")
drop1:scale(0.75, 90)
drop1:invalidate("canvas")
drop1:scale(1, 1 / 60)
drop1:invalidate("canvas")
local drop = display.newSnapshot(drop1.contentWidth * 1.5, drop1.contentHeight)
drop.canvas:insert(drop1)
drop.fill.effect = "filter.blurHorizontal"
drop.fill.effect.blurSize = 10
drop:invalidate("canvas")
drop.alpha = 0.375
local randomY = math.random(-500, 500)
drop.y = randomY
drop.anchorY = 0
drop.x = (i - 1) * 54
drops[i] = drop
table.insert(t.drops, drop)
local dropV, drop1, drop = nil
end
composer.setVariable("drops", t.drops)
t.fall(drops, group)
drops = nil
t.clean()
end
return t
EDIT : I found out that it definitely has something to do with the nested snapshots, which are created for the purpose of applying filter effects. I removed one snapshot, so that I only have a vector object inside a snapshot and voila : memory increases way slower. The question is : why?
Generally, you don't need enterFrame event at all - you can simply do transition from start point (math.random(-500, 500)) to end point (2000 in your code). Just randomise speed and use onComplete handler to remove object
local targetY = 2000
local speedPerMs = math.random(32, 64) * 60 / 1000
local timeToTravel = (targetY - randomY) / speedPerMs
transition.to( drop, {
time = timeToTravel,
x = xx,
y = targetY,
onComplete = function()
drop:removeSelf()
end
} )
Edit 1: I found that with your code removing drop is not enough. This works for me:
drop:removeSelf()
dropV:removeSelf()
drop1:removeSelf()
Some ideas about memory consumption:
1) Probably you can use 1 enterFrame handler for array of drops - this will reduce memory consumption. Also don't add methods to local objects like 'function thisDrop:enterFrame()' - this is not optimal here, because you creating 20 new functions every 50 ms
2) Your code creates 400 'drop' objects every second and they usually live no more than ~78 frames (means 1.3 sec in 60fps environment). Better to use pool of objects and reuse existing objects
3) enterFrame function depends on current fps of device, so your rain will be slower with low fps. Low fps -> objects falls slower -> more objects on scene -> fps go down. I suggest you to calculate deltaTime between 2 enterFrame calls and ajust falling speed according to deltaTime
Edit 2 Seems like :removeSelf() for snapshot didn't remove child object. I modified your code and memory consumption drops a lot
if self.y > 2000 then
local drop1 = self.group[1]
local dropV = drop1.group[1]
dropV:removeSelf()
drop1:removeSelf()
self:removeSelf()
Runtime:removeEventListener("enterFrame", self)
self = nil
end