Love2d stops running after awhile

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: Love2d stops running after awhile

Post by pgimeno »

No. I've had less than 0.2 fps without crashing.

If it freezes, it's possible that you're doing something that causes the memory to grow a lot in one go. In that case, the lowmemory callback won't have a chance to run, because it's not run in a separate thread, so it has to wait for your events to finish.
User avatar
Gunroar:Cannon()
Party member
Posts: 1088
Joined: Thu Dec 10, 2020 1:57 am

Re: Love2d stops running after awhile

Post by Gunroar:Cannon() »

Hmm?Like creation of alot of objects at one go or...( What are other examples of things that cause high memory use?)
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
MrFariator
Party member
Posts: 512
Joined: Wed Oct 05, 2016 11:53 am

Re: Love2d stops running after awhile

Post by MrFariator »

Graphics and sound are some of the most memory consuming things, but game data like large maps, or tons of repeated objects can also add up as well if you don't consider memory usage much. If you can help it, try to reuse assets (like textures) across all instances of use, instead of every one of them loading it separately.
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: Love2d stops running after awhile

Post by pgimeno »

I suspect it's a bug that is causing perhaps exponential growth of memory use, but it's hard to tell without the code.
User avatar
Gunroar:Cannon()
Party member
Posts: 1088
Joined: Thu Dec 10, 2020 1:57 am

Re: Love2d stops running after awhile

Post by Gunroar:Cannon() »

...I should mention that it doesn't(really) stop on the tablet anymore after I tried something but still stops on the older phone after a while. Can it be device specific or ...no?(Yes, it's the same löve version and apk)
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
MrFariator
Party member
Posts: 512
Joined: Wed Oct 05, 2016 11:53 am

Re: Love2d stops running after awhile

Post by MrFariator »

It could be, though I'm pretty doubtful, but it's hard to tell with the vague device information and sample size you are giving ("the tablet" and "the older phone"). For all we know, from our perspective, the issue is that the game is, one way or the other, too heavy to run on the older phone, while the tablet has enough power inside it to not cause any (immediate) issues.
User avatar
Gunroar:Cannon()
Party member
Posts: 1088
Joined: Thu Dec 10, 2020 1:57 am

Re: Love2d stops running after awhile

Post by Gunroar:Cannon() »

Nice, i narrowed down the problem and found out it comes from my Puffs when they get destroyed,
but the main cause is from my class object pooling(which is strange and means I must have fixed the other problem or that this is a new problem why it closes). So I posted those two classes in code because I find it easier to post code:

puff.lua

Code: Select all

--[[
-- Puff Class modified from bump.lua's example
-- Represents a Puff of smoke, created either by explosions or by the Player's propulsion
-- Puffs don't interact with anything (they can be displaced by explosions)
-- They gradually change in shape & color until they disappear (lived & lifeTime are used for that)
-- Since puffs continuously change in size, we keep adding and removing them from the world (this is ok,
-- it's the same thing that bump does internally to move things around)
--]]
warn = null
local lume = lume

--in conf.lua externalstorage = true
_LOG_FILE = love.filesystem.newFile("game_log.txt","w")


local function log(_str,file)

    local file = file or _LOG_FILE
    local str = lume.wordwrap(inspect(_str,1),90)
    file:write(str.."\n")
    return true
end

local Puff = class:extend("Puff",5100)--if you don't pool it doesn't close
--around 5212 puffs are made in total before it closes

--function that returns current updates loops in game
local getUpdateCount = getUpdateCount

local rad = math.rad
local game = game


local defaultVx      = 0
local defaultVy      = -10
local defaultVa      = 10
local defaultMinSize = 2
local defaultMaxSize = 10

function Puff:__init__(kwargs)
  self.active = true
    
  kwargs = kwargs or {}
  self._kwargs = kwargs
    
  self.uuid = kwargs.uuid or lume.uuid()
  self.updateCount = getUpdateCount()

  self.map = kwargs.map
  self.x = kwargs.x
  self.y = kwargs.y
  
  self.passive = kwargs.passive
  if not self.passive then
    --hitchhiking
    self.map:storeBullet(self)
    --[[map:storeBullet(ent)
            self.bullets[ent.uuid] = ent
        end
    ]]

  end
  
  vx, vy = kwargs.vix or defaultVx, kwargs.viy or defaultVy
  self.va = kwargs.va or math.random(-defaultVa,defaultVa)
  minSize = kwargs.minSize or defaultMinSize
  maxSize = kwargs.maxSize or defaultMaxSize

  self.w = math.random(minSize, maxSize)
  self.h = math.random(minSize, maxSize)
  self.angle = 0
  
  self.parent = kwargs.parent or nil
  self.source = kwargs.source or "dust.png"
  
  --set to 0 if smoke bullet and user
  self.opacity = kwargs.opacity or 100/255
  --set to 0.95 if smoke bullet and user
  --????use 0.08 for smoke
  self.opacity2 = kwargs.opacity2 or kwargs.outline or 1
  if self.source and self.source~="explosion.png" then self.opacity2=100/255 end
  
  --so that the player who caused the puff from a Smokescreen Bullet
  --can still see
  if self.parent == game.map.player then
    self.opacity2 = .95
    self.opacity = 0
    if self.source then
        self.opacity2 = .02
    end
  end
  
  
 
  if self.opacity>1 then
      self.opacity = self.opacity/255
  end
  
  if self.opacity2>1 then
      self.opacity2 = self.opacity2/255
  end
  
  if kwargs.favor then
      self.opacity = 0
      self.opacity2 = .95
  end
  
  local l = kwargs.lifeTime or kwargs.life or 0.1
  self.lifeTime = l + math.random()
  self.lived = 0
  self.vx, self.vy = vx, vy
  self.dead = false

  self.isBackground = true
  
  if self.source then
      self._w, self._h = resizeImage(game:getSource(self.source),self.w,self.h)
  end
end

function Puff:getCenter()
    return self.x + self.w / 2,
         self.y + self.h / 2
end

function Puff:expand(dt)
  local cx,cy = self:getCenter()
  local percent = self.lived / self.lifeTime
  if percent < 0.2 then
    self.w = self.w + (200 + percent) * dt
    self.h = self.h + (200 + percent) * dt
  else
    self.w = self.w + (20 + percent) * dt
  end

  self.x = cx - self.w / 2
  self.y = cy - self.h / 2
end

function Puff:destroy()
    log(self.destroyed)
    assert(not self.destroyed)
    log("desiiing")
    self.map:kill(self)
    self.dead = true
    self.destroyed = true
    log("destroyed")
end

function Puff.checkCollision(item, other)
    if other then
        return nil
    end
end


function Puff:update(dt,c)
    c = c or getUpdateCount()
    
    if c-1<self.updateCount then --set to <= if shadow
        return
    elseif c-1>self.updateCount then
        self.updateCount = c-1
    end
    self.updateCount = self.updateCount + 1
    log("source")
  if self.source then
      self._w, self._h = resizeImage(game:getSource(self.source),self.w,self.h)
  end
  
  self.lived = self.lived + dt

  if self.lived >= self.lifeTime  then
    assert(not self.dead)
    self.dead = true
  else
    self:expand(dt)
    local next_l = self.x + self.vx * dt
    local next_t = self.y + self.vy * dt
    --self.angle = self.angle+self.va

    self.x, self.y = next_l, next_t
  end
  
  if self.dead then
      --problem comes from destroy because after awhile
      --when the game closes last thing seen is dest
      log("dest")
      self:destroy()
      log("done")
  end
end

function Puff:getColor()
  local percent = math.min(1, (self.lived / self.lifeTime) * 1.8)
  --return colors.brown[1],colors.brown[2],colors.brown[3]
  return  (math.floor(155*percent))/255,
          (255 - math.floor(155*percent))/255,
          100/255
end

function Puff:draw()
  if not self.source then
        local rr,gg,bb,a= love.graphics.getColor()
        local r,g,b = self:getColor()
        util.drawFilledRectangle(self.x, self.y, self.w, self.h, r,g,b,self.opacity,self.opacity2)
        love.graphics.setColor(rr,gg,bb,a)
        if ty then love.graphics.setColor(colors.brown[1],colors.brown[2],colors.brown[3],25) end
  else
        local rr,gg,bb,a= love.graphics.getColor()
        love.graphics.setColor(255,255,255,self.opacity2)
        local so = game:getSource(self.source)
        local vw,vh = so:getDimensions()
        local ang = rad(self.angle)
        if self.angle~= 0 then
            love.graphics.draw(game:getSource(self.source),self.x,self.y,ang,self._w,self._h,
            vw/2,vh/2)
        else
            love.graphics.draw(game:getSource(self.source),self.x,self.y,0,self._w,self._h)
        end
        love.graphics.setColor(rr,gg,bb,a)
  end
  
end

return Puff
class.lua
(problem is probably from here so pls help me and fix it then post the code
if you can because I can't really download now(using data bundle that doesn't allow downloading))

Code: Select all

--calss.lua modified from rotlove's class

local BaseClass = {timeMade=-1}
CLASSES = {BaseClase=BaseClass}
POOLS = {}

local function destroyFunc(self,...)
  --  log(name.." pool at "..self.__id.." is being freed.")
    local notActive = POOLS[self.__class].notActive
    notActive[#notActive+1] = self.__id
    return self._destroy(self,...)
end

local function oldNew(self,...)
    local t = setmetatable({}, self)
    t:__init__(...)
    local kw = ...
    t._kwargs = t._kwargs or t.kwargs or (kw and type(kw)=="table" and kw)
    t.timeMade = love.timer.getTime()
    if store then
        store(t)
    end
    return t
end

function BaseClass:new(...)
    local t = setmetatable({}, self)
    t:__init__(...)
    local kw = ...
    t._kwargs = t._kwargs or t.kwargs or (kw and type(kw)=="table" and kw)
    t.timeMade = love.timer.getTime()
    if store then
        store(t)
    end
    return t
end

function BaseClass:extend(name, t, tt)
    assert(name, "Class must have a name!")
    
    local p = t
    if type(t)~="table" then
        t = nil
    else
        p = tt
    end
    
    t = t or {}
    if type(p)=="number" then
        t.poolSize = p
    end
    t.__index = t
    t.__class = name
    t.class = name
    t.super = self
    t.__data = t.__data or t.data or {
        x=0,y=0,w=0,h=0,_w=0,_h=0,source="none.png",
        active=false,attack=0,life=0,passive=true
    }
    
    t.poolSize = t.poolSize or 1000
    t.pool = p
    
    local tclass = setmetatable(t, { __call = self.new, __index = self })
    tclass.__lt = self.__lt

    CLASSES[name] = tclass
    t["is"..name] = true
    
    if not p then
        return tclass
    end
    t.__data.map = game.map
    --log("pooling "..name)
    

    t = tclass
    
    local poolSize = t.poolSize
    local notActive = {}
    local pool = {}
    if not self.__taggezd then
        --so won't get repeatedly added to notActive
        --from inheritance though does not work if 
        --super form is not called
        
        t.__tagged = true
        local destroy = t.destroy or null
        function t:destroy(...)
            --log(name.." pool at "..self.__id.." is being freed.")
            if self.isPuff then log(string.format("trying to destroy...%s",#notActive)) end
            notActive[#notActive+1] = self.__id
            if self.isPuff then log("done trying now...") end
            return destroy(self,...)
        end
        
    end
    
    --populating pool
    local pp
    local notYet = true
    for i = 1, poolSize do
        pool[i] = t:new(t.__data)
        pool[i].__id = i
        pool[i]:destroy()
        pool[i].passive = false
        pp=i
    
    end
    local nonTotal = #notActive
    
    local function new(self,...)
        assert(#notActive>1,"No space left in pool:size "..nonTotal.."!")
        if self.class~=name then
            return oldNew(self,...)
        end
        local r = table.remove(notActive,1)
        --log(name.." is making at "..r)
        local obj = pool[r]
        obj:__init__(...)
        
        obj._destroy = obj.destroy
        obj.destroy = destroyFunc
        
        local kw = ...
        obj._kwargs = obj._kwargs or obj.kwargs or (kw and type(kw)=="table" and kw)
        obj.timeMade = love.timer.getTime()
        if store then
            store(obj)
        end
        return obj
    end
    
    t.new = new
    
    
    
    POOLS[name] = {pool=pool,notActive=notActive}
    
    return tclass
end

function BaseClass:__init__()
end


function BaseClass.__lt(self,b)
    return self.timeMade<b.timeMade
end

function BaseClass:__le(self,b)
return true end

function BaseClass:callSuper(func,...)
    if not self.super[func] then
        return
    end
    return self.super[func](self,...)
end

return BaseClass
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
Gunroar:Cannon()
Party member
Posts: 1088
Joined: Thu Dec 10, 2020 1:57 am

Re: Love2d stops running after awhile

Post by Gunroar:Cannon() »

Oh, I use Hump's game state too ,so I don't know if that should help ( https://love2d.org/forums/viewtopic.php?t=85098. I don't think that's the problem tho , as I downloaded hump in 2019/2020.)
pgimeno wrote: Mon Dec 28, 2020 6:59 pm ...

Maybe you're still lucky enough that someone looks into it in its current state.
I really want it my way so if anyone can that will help. thnx for the links tho.
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
User avatar
slime
Solid Snayke
Posts: 3132
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Love2d stops running after awhile

Post by slime »

At this point I don't think anyone will be able to guess what your code is doing wrong without being able to see and run it in its entirety. People want to help but you're making it hard to do so.

One thing (among many) that you can do on your own would be to look at how much resources (RAM, VRAM, etc) your game uses over a period of time, using Task Manager or other external tools - love.graphics.getStats can also help track texture memory usage over time.

You should be able to reasonably correlate resource usage increases and decreases with the flow of your game, and if anything looks suspicious or unexpected you can work through debugging it starting with that knowledge.
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 229 guests