A quick comparison profiling of 3 ways of drawing meshes

Here’s a quick profiler comparing different ways of drawing meshes. Similar tests have been done before of course, so I think it’s quite well known how much faster the setRect method is than repeated calls to translate. Unfortunately, method 2 here, moving a single mesh around, is only slightly faster than the slowest method, method 3, having an individual mesh for each object.

Although this is for 2D, I’m interested in the implications for 3D. I think that for an acceptable voxel system, you’d need to write an addCube, setCube function. Or if anyone can make method 2 run faster…


--# Main
-- 2D Mesh Profiling

function setup()
    methods = {Box, Box2, Box3}
    parameter.integer("number",200,5000,500)
    parameter.integer("method", 1,3,1)
    parameter.action("INITIALISE", initialise)
    profiler.init()
    for i=1,#methods do
        print("Method "..i..": "..methods[i].doc)
    end
    initialise()
end

function draw()
    background(40, 40, 50)
    for i=1, #object do
        object[i]:draw()
    end
    if method==1 then Box.mesh:draw() end --just one draw operation with rectangle method
    profiler.draw()
end

function initialise()
    object={}
    Box.mesh:clear()
    collectgarbage()
    for i=1,number do
        object[i]=methods[method]()
    end
end

profiler={}

function profiler.init()    
    profiler.del=0
    profiler.c=0
    profiler.fps=0
    profiler.mem=0
    parameter.watch("profiler.fps")
    parameter.watch("profiler.mem")
end

function profiler.draw()
    profiler.del = profiler.del +  DeltaTime
    profiler.c = profiler.c + 1
    if profiler.c==10 then
        profiler.fps=profiler.c/profiler.del
        profiler.del=0
        profiler.c=0
        profiler.mem=collectgarbage("count", 2)
    end
end
--# Box
Box = class()
Box.doc = "The objects are drawn as rectangles on a single mesh using setRect. No translation used"

local size=20
Box.mesh=mesh()
Box.mesh.texture=readImage("Platformer Art:Block Brick")

function Box:init()
    self.pos = vec2(math.random(WIDTH),math.random(HEIGHT))
    self.angle = math.random(2*math.pi)
    self.vel = vec2(math.random(11)-6,math.random(11)-6)
    self.angleVel=(math.random()-0.5)*.02
    self.col=color(math.random(255), math.random(255), math.random(255))
    self:add(self.pos, self.angle)   
end

function Box:draw() --nb no need for translate or rotate
    self:move()
    self.mesh:setRect(self.rect, self.pos.x, self.pos.y, size, size, self.angle)
end

function Box:move()
    self.pos = self.pos + self.vel
    if self.pos.x > WIDTH + size then self.pos.x = - size
    elseif self.pos.x < - size then self.pos.x = WIDTH + size
    end
    if self.pos.y > HEIGHT + size then self.pos.y = - size
    elseif self.pos.y < - size then self.pos.y = HEIGHT + size
    end
    self.angle = self.angle + self.angleVel
end

function Box:add(pos,ang)
    self.rect=self.mesh:addRect(pos.x,pos.y,size,size,ang)
    self.mesh:setRectTex(self.rect,0,0,1,1)
    self.mesh:setRectColor(self.rect, self.col)
end

--# Box2
Box2 = class(Box) --a subclass, just changes add and draw
Box2.doc = "A single mesh with a single rectangle is drawn repeatedly over the screen using translate/ rotate/ setColors to set positions/ angle/ colour"

local size = 20
Box2.mesh=mesh()
Box2.mesh.texture=readImage("Platformer Art:Block Brick")
Box2.mesh:addRect(0,0,size,size) --a single rectangle this time

function Box2:draw()
    self:move()
    pushMatrix()
    translate(self.pos.x,self.pos.y)
    rotate(math.deg(self.angle))
    self.mesh:setColors(self.col) --self.mesh here is actually Box2.mesh. Weird, huh?
    self.mesh:draw()
    popMatrix()
end

function Box2:add()
    -- empty function so that we don't inherit the add function from Box
end

--# Box3
Box3 = class(Box)
Box3.doc = "Each object has its own mesh, drawn using translate/ rotate to set positions/ angle."

function Box3:draw() --just one line different (no need to set colour) compared to Box2
    self:move()
    pushMatrix()
    translate(self.pos.x,self.pos.y)
    rotate(math.deg(self.angle))
    self.mesh:draw()
    popMatrix()
end

function Box3:add()
    self.mesh=mesh() --a separate mesh for each instance
    self.mesh.texture=readImage("Platformer Art:Block Brick")
    Box.add(self,vec2(0,0),0)
end

If you comment out the mesh:draw line in the Box2 class, the FPS is just as high as method 1. So it’s all the calls to draw, rather than the translate/rotate calls, that slow it down.

My 3D library has an addBlock function that can also be used to reset a previously defined block in a mesh.

Is that library on Codea Community? Or is there a gist of it somewhere?

Ok, I found your shapes library and addBlock on GitHub. For others reading, it’s here https://github.com/loopspace/Codea-Shapes

Thanks for the tip!

The voxel thing would be some point in the future, a bit too much on my plate at present