Memory and iOS 7

There was reference in this thread

http://codea.io/talk/discussion/4374/xcode-runtime-eventually-crashes

to the Codea runtime having issues with iOS 7. Is that right? I’ve been testing my game on Xcode with my iPad Air (running latest iOS 8) and an iPad 3 (running iOS7). Unsurprisingly the Air runs faster, but the big issue with the iPad 3 is that the memory gets completely out of control and the game slows down a lot over the course of a few minutes. The two seem to be related.

On the iPad Air the memory doesn’t go any higher than about 120MB and the collectgarbage() that I call periodically seems to work well. On the iPad 3 with the same code, the memory just balloons and the garbage collection barely makes a dent. It can head north of 500MB very easily.

Any suggestions, or is this a compatibility issue?

I’m going to give this a little bump in case the weekend crowd haven’t spotted it. @Simeon, any ideas?

@epicurus101 Does it slow way down when running the game on Codea on the iPad 3? I had some resolution problems with a Codea app in Xcode iOS 7 but nothing like this.

@Goatboy76 yes, seems to have same problem. Play it long enough and it just crashes

I seem to be closing in on the issue. The leaky memory seems to be related to drawing. One of the biggest culprits seems to be my timer countdown. TIMER starts out equal to 15 then I subtract delta time from it each frame and draw it (rounded to 1DP) on the screen with a text command. If I simply comment out the drawing of the timer (but still leave it running in the background) the memory leak is much slower but still noticeable.

Is it something to do with how text is buffered perhaps? Any workarounds?

@epicurus101Are you doing anything with meshes. I know there’s a memory leak with meshes that’s fix in the next release.

Is the content of the text changing every frame (ie is the timer displaying hundredths of a second)? If not, text to an image which you’ve set up as the texture of a rect on a mesh (you can have several rects on the mesh if you want to have fancy outline/ shadow effects, with very little extra overhead). This is what I do for scorelines etc. This is by far the fastest way to draw text on screen. text is not terribly quick, I would avoid calling it every frame if you possibly can.

Here’s my text box class:

TextBox = class()

function TextBox:init(str,x,y,col) 
    local x=x or WIDTH * 0.5 --work out dimensions
    local y=y or HEIGHT * 0.5
    fontSize(30)
    local w,h=textSize(str)

    self.img=image(w,h)
    
    self:update(str) --write text to image
    
    local m=mesh()
    m.texture=self.img
    
    --shadow effects (optional)
    local pos ={} --positions of shadows
    local deg=math.rad(100) --math functions 0 top, clockwise
    for i=1,8 do
        pos[i]=vec2(math.sin(deg),math.cos(deg))*7
        deg = deg + math.rad(8)
    end
   
    for i=1,#pos do
        local v = pos[i]
        m:addRect(x+v.x, y+v.y, w*1.01,h*1.02 ) --w*1.01, h*1.2) --shadow rects
        m:setRectColor(i, color(0,10) )
    end

    -- the main text
    local col=col or color(0, 167, 255, 255)
    local i = m:addRect(x,y,w,h) --the main rect
    m:setRectColor(i, col)
    self.mesh= m
end

function TextBox:draw() --call this every cycle
    self.mesh:draw()
end

function TextBox:update(str) --call this to change content of text. Don't call from a 3D projection. You must be in an orthogonal viewing mode.
    setContext(self.img)
    background(0,0)
    pushStyle()
    textMode(CORNER) --draw from 0,0
    fill(255)
    text(str)
    popStyle()
    setContext()
end

@yojimbo2000 I did some time tests on your TextBox code and a normal text line and the times were basically the same. One thing I noticed about your TextBox code is if you call TextBox:init() with some string and later call TextBox:update() with a larger string, the larger string gets truncated to the size of the original string.

Yeah I don’t bother resizing the image. It’s for things like scorelines that are predictable sizes. Interesting what you were saying about profiling, I’ll have a look at that.

Thanks for the thoughts guys. This is a really frustrating given how graphically simple my game is. I wasn’t expecting it to be a stretch for my iPad 3.

@dave1707 I am using meshes, yes, but not in the timer code. As I say, commenting out the drawing of the timer significantly reduces the memory leak. But I still need a timer! And the weird thing is that the memory leak only occurs when I’m running the game on iOS 7. Otherwise it works absolutely fine.

@yojimbo2000 my timer shows 1/10ths of a second, so it doesn’t change every frame but it’s not far off. Your text box code looks useful for text which is updated periodically (and I have some of that so I’ll see if it helps) but maybe not a timer like mine because I would still be calling a text draw 10 times a second, on the underlying texture img. I’ve considered reorganising my screen and designing a visual / non-text based timer. That’s doable but I like my screen the way it is right now so that’s a last resort for me. Having a timer that simply shows whole seconds should also make the issue more manageable, but I like the urgency of my timer!

@epicurus101 I’m not sure why text should have a memory leak or slow things down. I’ve never come across that. If you want the urgency but you want to cut down how often it changes, maybe above some time limit you show whole seconds but below the time limit you show 1/10 seconds.

@dave1707 that’s a pretty good compromise, nice idea! I may also bite the bullet and try to build a testflight version to see if any of the kind folks on here can give feedback of different os / device combos. I understand that involves submitting it to Apple, which is scary stuff (for me!)

When life gives you lemons, write a class that gives you lemonade.

While I like @yojimbo2000’s textbox class, I figured there was a solution for my numbers involving a numerical texture atlas and a single mesh for all the various on-screen counters I have and a way to avoid constantly writing text, even if that text gets written to a mesh. So I wrote this. Only uses text on setup. Thereafter it’s pure mesh magic! It works really fast on the iPad 3 and has practically eliminated my memory leak issues.

It’s not the most elegant coding (none of my stuff is) but it does what I need it to.

--# Main
displayMode(FULLSCREEN)

function setup()
    
    NumberMesh = mesh()
    NumberMesh.texture = createNumberSheet()
    points = NumberRects("52", 2, WIDTH/2, HEIGHT/2, 200)
    
end

function draw()
    
    background(120, 61, 61, 255)
    NumberMesh:draw()
    if math.random(99) > 95 then
        points:update(math.random(20))
    end
    
end

function createNumberSheet()
    
    pushStyle()
    local source = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", ""}
    local nsinfo = {}
    font("Futura-CondensedExtraBold")
    fontSize(150)
    local _, textheight = textSize("0")
    local textwidth = 0
    for i = 1,#source do
        local letter = source[i]
        nsinfo[i] = {}
        nsinfo[i].word = letter
        nsinfo[i].width = textSize(letter) + 4
        textwidth = textwidth + nsinfo[i].width
    end
    
    local img = image(textwidth, textheight)
    
    local startwidth = 0
    for i = 1, #nsinfo do
        nsinfo[i].startfrac = startwidth / textwidth
        nsinfo[i].sizefrac = nsinfo[i].width / textwidth
        startwidth = startwidth + nsinfo[i].width
    end
    
    setContext(img)
    background(255,0,0)
    textMode(CORNER)
    fill(255)
    local margin = 2
    for i = 1, #nsinfo do
        text(nsinfo[i].word, margin, 0)
        margin = margin + nsinfo[i].width
    end
    setContext()
    popStyle()
    
    NumberLookup = {}
    for i = 1, #nsinfo do
        NumberLookup[nsinfo[i].word] = {sizefrac = nsinfo[i].sizefrac, startfrac = nsinfo[i].startfrac}
    end
    
    return img
    
end



--# NumberRects

local ssub = string.sub
local tostring = tostring

NumberRects = class()

function NumberRects:init(str, len, x, y, size)
    -- you can accept and set parameters here
    self.len = len
    self.x = x
    self.y = y
    self.size = size
    self.rects = {}
    for i = 1,len do
        self.rects[i] = NumberMesh:addRect(self.x, self.y, self.size, self.size)
    end
    self:update(str)
end

function NumberRects:update( x )
    
    self.str = tostring(x)
    
    local mult = self.size
    local ratio = 1004/192
    
    local lookup = NumberLookup
    
    local sizefractot = 0
    for i = 1, #self.str do
        local letter = ssub(self.str, i, i)
        sizefractot = sizefractot + lookup[letter].sizefrac
    end
    
    local margin = self.x - (sizefractot * mult * ratio)/2
    local textheight = self.y
    local letterheight = mult
    for i = 1, #self.str do
        local letter = ssub(self.str, i, i)
        local sizefrac = lookup[letter].sizefrac
        local startfrac = lookup[letter].startfrac
        local letterwidth = mult * ratio * sizefrac
        NumberMesh:setRect(self.rects[i], margin - letterwidth/2, textheight, letterwidth, mult )
        NumberMesh:setRectTex(self.rects[i], startfrac, 0, sizefrac, 1)
        margin = margin + letterwidth
    end

    if #self.str < self.len then
        for i = (#self.str + 1), self.len do
            NumberMesh:setRect(self.rects[i], 0,0,0,0 )
        end
    end

    
end


Neat solution, thanks for sharing

@epicurus101 I compared your code to a text statement. I was displaying 100 text lines that were displaying a random number between 1 and 200. The 100 text statements were running at 15 FPS while 100 text displays using your code were running at 59 FPS. Good job.

When life gives you lemons, write a class that gives you lemonade.

@dave1707 I hadn’t even done a proper fps test like that. It seems astonishing that such a convoluted method would be so much quicker doesn’t it? In theory you could create a whole alphabet texture map in the same way. Might try one of these days.

@epicurus101 - that’s basically how font rendering was done in the “old” days on consoles, it took me ages to swap to using true type fonts and now it’s time to go back :slight_smile:

Actually if you are going to go down this route, then once you’ve generated your numbers / alphabet atlas add it as a single entity to a large texture atlas and keep all (or as many as is feasibly possible) of your images on a single sheet to get best performance.

Pro Tip : If you do have to separate out your images on to two (or more) atlases (assuming you’ve used up all of your 2048x2048 image) spend a few minutes organising the images based on the likely draw order and if needs be you can even replicate a couple of images across both atlases - the key thing is to minimise the number of texture changes

@TechDojo That’s a good pro-tip, I’ll look into it. It’s the headache of arranging the texture atlas that puts me off!

One question about meshes though - if you have a single static image you want to put on the screen (and for various reasons it’s not convenient to add to a texture atlas) is there anything to choose between a mesh and a basic sprite call (aside from simply using sprite for simplicity?)

@epicurus101 Here’s a simple way to create a sprite sheet of the ascii character set. You’ll have to vary some of the numbers depending on the font and fontsize you use. One reason to do it this way is you can calculate which sprite character to use based on the characters ascii value.

displayMode(FULLSCREEN)

function setup()
    fontSize(50)
    fill(255)    
    img=image(700,700)
    setContext(img)
    background(0, 217, 255, 255)
    s=32
    for a=0,12 do
        for b=0,9 do
            text(string.char(s),b*70+35,a*70+35)
            s=s+1
        end
    end
    setContext()
end

function draw()
    background(0)
    sprite(img,WIDTH/2,HEIGHT/2)
end