Frame rate issues with ellipses.

The frame rate in my app is considerably low when I am drawing about 200-300 ellipses on the screen at once. About 15-20 fps. The ellipses are really tiny—with a diameter of 4 pixels in Codea. Is there a way to increase the frame rate? I have read past discussions, and enabled noSmooth() and even tried using pre-rendered images, but it doesn’t help as the frame rate is well below 30.

Here’s some background information:
Without drawing the ellipses my frame rate is at a full 60 fps, so I know that the frame rate drop is because of the ellipses. I am also using a for loop to iterate over the positions in the table, but every ellipse doesn’t have it’s own position in the table, but rather the sequence of ellipses have their own position. Any advise would be greatly appreciated!

I don’t think the code is the problem, but rather the ellipses being drawn in the screen at one time. However, I’ll post the code later as it is rather long and is scattered across my app.

@YoloSwag - are you using meshes? They should be faster than sprites when you have so many.

@YoloSwag I suggest you use one mesh with a circle texture, then add in rectangles using mesh:addRect(). This way it is possible to get over 1000 circles on the screen whilst retaining 50fps+

@Ignatz. I am not currently using meshes right now as I do not know how to implement it so that the circles will move with a variable.

@Luatee. Thank you for the advise, but I do not know how to do that. If you could possibly give an example, I would really appreciate that.

@YoloSwag happy to:


--# Main
function setup()
    local img = image(100,100)
    setContext(img)
        ellipse(50,50,100)
    setContext()
    m = mesh() 
    m.texture = img
    --Create empty table for rectangles
    rts = {}
    
    --Give each rectangle its properties
    for i=1,1000 do
        rts[i] = {}
        --set random position property
        rts[i].pos = vec2(math.random(10,WIDTH-10),math.random(10,HEIGHT-10))
        --set initial velocity
        rts[i].vel = vec2(math.sin(i/100),math.cos(i/100))*(math.random(10,40)/10)
        --set a random size
        rts[i].rad = math.random(20,50)*0.4
        --add the rectangle with the position and size properties
        rts[i].r = m:addRect(rts[i].pos.x,rts[i].pos.y,rts[i].rad,rts[i].rad)
    end
    --Table for running average of fps
    fpst={60,60,60,60,60,60,60,60,60,60,60}
end

function touched(t)
    for i=1,#rts do
        if vec2(t.x,t.y):dist(rts[i].pos)<70 then
            rts[i].vel = rts[i].vel+(rts[i].pos-vec2(t.x,t.y)):normalize()
        end
    end
end

function draw()
    background(40,40,50)
    for i=1,#rts do
        --Update position of the rectangle
        rts[i].pos = rts[i].pos + rts[i].vel
        local x,y = rts[i].pos.x,rts[i].pos.y
        --Use setRect(rect,x,y,w,h) to update the graphics position
        m:setRect(rts[i].r,rts[i].pos.x,rts[i].pos.y,rts[i].rad,rts[i].rad)
        --Check if position range is outside of the specified
        if x<rts[i].rad/2 or x>WIDTH-rts[i].rad then
            rts[i].vel.x=-rts[i].vel.x
        end
        if y<rts[i].rad/2 or y>HEIGHT-rts[i].rad then
            rts[i].vel.y=-rts[i].vel.y
        end
    end
    --Draw the mesh (this draws all the rectangles with one call)
    m:draw()
    text("FPS: "..(FPS()),100,100)
end

function FPS()
    local fps = math.floor((1/DeltaTime))
    local n = #fpst
    table.insert(fpst,fps)
    local fp = 0
    for i=0,10 do
        fp = fp + fpst[n-i]
    end
    table.remove(fpst,1)
    local txt = fp
    txt = math.ceil((txt/11))
    return txt
end

A basic program where balls bounce off the walls, there are 1000 balls running at ~45 FPS. If you want you can set yourself the exercise of trying to optimise it to 50fps+ by defining local variables for variables that gets used often.

@Luatee. Thanks. The movement of the balls on your program is much smoother than I expected.

It’s able to go faster, although the more you try to interact with the balls the slower the program will get. If you try to get them to collide with eachother you’ll end up with around 35 balls in total trying to maintain that speed

@Luatee. Is there a way to set the position of the balls in another function, and not in the setup?

@YoloSwag what sort of situation do you have in mind? The positions are set in the draw function too

@Luatee. I want to have small circles follow a bigger ellipse that I have already created, so that the position of the smaller balls will essentially follow the bigger one. I have already done this with the pre-rendered images, but your example with meshes proved to be better, so I was wondering how I could incorporate updating the mesh’s position in another function preferably by inserting and updating a table.

This is an example of me drawing a single pre-rendered image inside my for looped function using the variables posi.x, and posi.y

               sprite(circleImg2, posi.x, posi.y)

EDIT: I think I made this more complicated than it actually is. In my code, I don’t have the position set anywhere inside the setup function, but rather inside my for loop in the draw method. So when creating the ball, I don’t need all of them to be created in the setup because my loop takes care of that. I hope that makes sense!

@YoloSwag I don’t see why you’d initialise your positions in your for loop? The position of each circle is initialised in the setup function then set every frame, but I think this is what you were implying?

--# Main
function setup()
    local img = image(100,100)
    setContext(img)
        ellipse(50,50,100)
    setContext()
    m = mesh() 
    m.texture = img
    --Create empty table for rectangles
    rts = {}
    
    --Give each rectangle its properties
    for i=1,1000 do
        rts[i] = {}
        --set random position property
        rts[i].pos = vec2(math.random(10,WIDTH-10),math.random(10,HEIGHT-10))
        --set initial velocity
        rts[i].vel = vec2(math.sin(i/100),math.cos(i/100))*(math.random(10,40)/10)
        --set a random size
        rts[i].rad = math.random(20,50)*0.25
        --add the rectangle with the position and size properties
        rts[i].r = m:addRect(rts[i].pos.x,rts[i].pos.y,rts[i].rad,rts[i].rad)
    end
    rts[1].rad = 100
    --Table for running average of fps
    fpst={60,60,60,60,60,60,60,60,60,60,60}
end

function touched(t)
    for i=1,#rts do
        if vec2(t.x,t.y):dist(rts[i].pos)<70 then
            rts[i].vel = rts[i].vel+(rts[i].pos-vec2(t.x,t.y)):normalize()
        end
    end
end

function draw()
    background(40,40,50)
    for i=1,#rts do
        --Update position of the rectangle
        rts[i].pos = rts[i].pos + rts[i].vel
        if i>1 and rts[i].pos:dist(rts[1].pos)<300 then
            if rts[i].pos:dist(rts[1].pos) < rts[1].rad/2+rts[i].rad/2 then
                rts[i].vel = rts[i].vel+(rts[i].pos-rts[1].pos):normalize()*0.1
                rts[i].pos = rts[i].pos + (rts[i].pos-rts[1].pos):normalize()*0.1
                rts[i].vel = rts[i].vel*0.95
            else
                rts[i].vel = rts[i].vel+(rts[1].pos-rts[i].pos):normalize()*0.3
            end
            rts[i].vel = rts[i].vel*0.98
        end
        local x,y = rts[i].pos.x,rts[i].pos.y
        --Use setRect(rect,x,y,w,h) to update the graphics position
        m:setRect(rts[i].r,rts[i].pos.x,rts[i].pos.y,rts[i].rad,rts[i].rad)
        --Check if position range is outside of the specified
        if x<rts[i].rad/2 or x>WIDTH-rts[i].rad then
            rts[i].vel.x=-rts[i].vel.x
        end
        if y<rts[i].rad/2 or y>HEIGHT-rts[i].rad then
            rts[i].vel.y=-rts[i].vel.y
        end
    end
    --Draw the mesh (this draws all the rectangles with one call)
    m:draw()
    text("FPS: "..(FPS()),100,100)
end

function FPS()
    local fps = math.floor((1/DeltaTime))
    local n = #fpst
    table.insert(fpst,fps)
    local fp = 0
    for i=0,10 do
        fp = fp + fpst[n-i]
    end
    table.remove(fpst,1)
    local txt = fp
    txt = math.ceil((txt/11))
    return txt
end

@Luatee. Sorry I misjudged how many ellipses I was really drawing at one time. I was drawing about 2000-10000 on the screen at once. With about 2000 ellipses I’m getting 40-50 fps. But after that it goes down to 15-30 fps.

@YoloSwag There’s an example Simeon wrote on the forums (I’ll find it) that shows you how to use setRectTex in order to map one texture over 1000s of rectangles.
Edit: @Bortels website isn’t active anymore, so if you want I’ll make an example for you.