multi threading for resource heavy functions?

Hello guys,

I’ve been working on this cloud function today , and it seems to work as intended, however I have a problem with its loading. After initiating one cloud, the app will pause for about half a second. Is there a way to do heavy resource functions on a different thread, or having the function spread over a longer amount of time so that it doesnt lag the app?

The cloud function draws a few thousand meshes with 30k? vertices every time its called using a self feeding loop.

cloudcontainer = {}
cloudcontainer2 = {["locx"] = {}, ["locy"] = {}, ["speedx"] = {}, ["speedy"] = {}}
rWIDTH = 1000
rHEIGHT = 1000
globalScale = 1


function setup()

end

function draw()
background(40,40,40)

 for i = 1, #cloudcontainer do
     sprite(cloudcontainer[i],cloudcontainer2["locx"][i],cloudcontainer2["locy"][i])
end

 if CurrentTouch.state == BEGAN then 
        table.insert(cloudcontainer, cloudini(math.random(0,1.0)))
        table.insert(cloudcontainer2["locx"], math.random(rWIDTH*globalScale))
        table.insert(cloudcontainer2["locy"], math.random(rWIDTH*globalScale))
 end

end

function cloudini()
    
    local objectcloudtemp = {}
    local w = 40
    local cWIDTH = 300
    local cHEIGHT = 300

    objectcloudtemp = clouddup(w,cWIDTH,cHEIGHT)

    local objectcloudtemp2 = {}
    for i = 1, #objectcloudtemp do
        table.insert(objectcloudtemp2, ellipsemesh(objectcloudtemp[i][1], objectcloudtemp[i][2], objectcloudtemp[i][3]))    
    end
    
    local cloudtemp1 = {}
    for i = 1, #objectcloudtemp2 do
        for j = 1, #objectcloudtemp2[i] do
            table.insert(cloudtemp1, objectcloudtemp2[i][j])
        end        
    end
    
    local cloudtemp2 = mesh() 
    cloudtemp2.vertices = cloudtemp1
    cloudtemp2:setColors(255,255,255,12)
    
    local cloudImage = image(600,600) 
    setContext(cloudImage)
    cloudtemp2:draw()
    setContext()
    return cloudImage
    
end

function clouddup(w,cWIDTH,cHEIGHT)
    local containertemp = {}
    local w2 = 40
    local limit = 5
    
    while w > limit do
        for i = 1 , 2 do
        local tempval = math.random(8)
        local cloudradius = w - tempval
        w = w - tempval
            local cloudduptemp = {}
            cloudlocvarx = math.random(-w2, w2) 
            cloudlocvary = math.random(math.ceil(-w2/2), math.ceil(w2/4))
            cloudduptemp = clouddup(w,cWIDTH+cloudlocvarx,cHEIGHT+cloudlocvary)
            for i = 1, #cloudduptemp do
                table.insert(containertemp, cloudduptemp[i])
            end
        
        if cloudradius < limit then cloudsradius = limit end
        table.insert(containertemp, {cWIDTH+cloudlocvarx, cHEIGHT+cloudlocvary, cloudradius})  
        end            
    end    
    return containertemp
end

function ellipsemesh(locx,locy,size)
    
    local x,y,i,angle
    local cos,sin,rad = math.cos,math.sin,math.rad
    local vertices = {}
    
    for i = 1,18 do
        table.insert(vertices,vec3(locx+0,locy+0,0))
        angle = rad(20*(i-1))
        x = size*cos(angle) 
        y = size*sin(angle)
        table.insert(vertices,vec3(locx+x,locy+y,0))
        angle = rad(20*i)
        x = size*cos(angle) 
        y = size*sin(angle)
        table.insert(vertices,vec3(locx+x,locy+y,0))
    end
    return vertices
end

About the only timesharing device available is coroutines

@ignatz hmmm looks complicated, ill give it a crack after I grab some dinner :wink:

@architudent - for something like clouds, you should learn about shaders

@ignatz hello, would you know how I could work out the optimum amount of time between coroutine.resumes for initiating the cloud function above?

At the moment, I have put coroutine.yield into the cloudini function, and having it resume every 0.X seconds, but perhaps there is a better method?

Very simply, each frame is ideally 1/60 of a second apart (or no more than 1/30), and your aim is to build your clouds in the unused time between each frame. How much that is will depend on what else your program is doing.

Rather than resume every X seconds, I would rather do X loops in clouding and then pause and resume next frame, ie spread the loops between frames. How many loops is trial and error.

@ignatz I see, very good thats seems logical. Because the clouddup function is self looping, i am going to add a counter into the self loop code to pause and restart, and hopefully it all works.

I am having some trouble returning a value back to the main loop though, I cant figure out where to get the/put the return value.
For example:

  1. There is one coroutine.create, which initiates the cloud code. But this is activitated at the start, so cant return here.

  2. There is the coroutine.resume, the return value cant go here because I just want the final return value.

So how do I put them return value into a table only after the coroutine has finished running? I’ve tried coroutine.status == “dead” but that doesnt seem to work either…

I wouldn’t use the coroutine to return anything

Just put the pause inside your loop, and resume in the draw. Then when your cloud function is finished, it will return the finished cloud using your existing code

@archistudent I was playing with your code and I notice just a slight delay from the time I touched the screen until the clouds appear. The larger delays were for large clouds, but that’s expected. Even then there was just maybe 1/10 sec delay or less. I’m running on an iPad Air, so maybe that’s why I don’t see that big of a delay. I made the changes below to your code. I placed the variables that were above setup() into setup(). I set rWIDTH and rHEIGHT to WIDTH and HEIGHT because some of the clouds were created off the screen. I also noticed that you weren’t using rHEIGHT, but was using rWIDTH twice in the touch code. I took your CurrentTouch code out of draw() and put it in the touched() function and got rid of CurrentTouch. One thing about using CurrentTouch.state. When you compare it to BEGAN, when you touch the screen it stays true until you lift your finger. So it’s possible to call the code to create the clouds several times before you lift your finger.

function setup()
    cloudcontainer = {}
    cloudcontainer2 = {["locx"] = {}, ["locy"] = {}, ["speedx"] = {}, ["speedy"] = {}}
    rWIDTH = WIDTH
    rHEIGHT = HEIGHT
    globalScale = 1
end

function draw()
    background(40,40,40)
    for i = 1, #cloudcontainer do
        sprite(cloudcontainer[i],cloudcontainer2["locx"][i],cloudcontainer2["locy"][i])
    end
end

function touched(t)
    if t.state == BEGAN then 
        table.insert(cloudcontainer, cloudini(math.random(0,1.0)))
        table.insert(cloudcontainer2["locx"], math.random(rWIDTH*globalScale))
        table.insert(cloudcontainer2["locy"], math.random(rHEIGHT*globalScale))
    end
end

@ignatz this is mind boggling stuff - i love it.

@dave1707 Ahhhh i’ve never understood why people used touched functions, yes I’ve had to write a lot of convoluted code to compensate for the ‘true’ in the past, now I know. i’ll make the changes, thank you.

@archistudent When using the touched function, BEGAN is true only once when you touch the screen. ENDED is true only once when you lift your finger from the screen. And MOVING is true as long as your finger is moving on the screen.

@archistudent - here is something I wrote on touch

https://coolcodea.wordpress.com/2014/12/28/188-understanding-touch/

@ignatz haha you guys would laugh if I posted the amount of code that I wrote to compensate for not using touched, lots of true/false switches, counters, stuff like

if CurrentTouch.state == BEGAN and CurrentTouch.state ~= MOVING and touchswitch1 == false and touchswitch2 == true then 

etc, sometimes with nested touch indicators…

we all do that, it’s what happens when there aren’t any official guides or manuals

Is there a way to donate to the mods of this forum to help support them?

We don’t want money, but you can help people with problems, share code, and generally support this great app.

@ignatz okay I will forward the generosity forward. :slight_smile:

I’ve got the coroutine to work !

There is a slight lag when generating the cloud in setcontext(), is there way to put a coroutine into the setcontext so it doesnt try to run all at once? I am not sure how to delve deeper into the setContext() code

If all you’re doing in setContext is drawing a mesh, no, there is nothing you can do, except maybe adding another pause just before setContext to make sure all the loops are finished first

or use smaller clouds

hmm I see, I’ll see what I can do…

I found the clouds interesting, so I thought I’d try my hand at it just to get an idea of how it worked. Tap the screen for another cloud. You can vary all the x,y values along with the for loop values to get varying cloud sizes and density.

displayMode(FULLSCREEN)

function setup()
    cx,cy=WIDTH/2,HEIGHT/2
    create()
end

function create()
    tab1={}
    local tab={}
    local x1,y1=120,60
    local x2,y2=20,10
    local x3,y3=100,50
    for z=1,30 do
        local x,y=math.random(-x3,x3),math.random(-y3,y3)
        table.insert(tab,vec2(cx+math.random(-x1,x1)+x,cy+math.random(-y1,y1)+y))
        for z1=1,120 do
            x,y=math.random(-x3,x3),math.random(-y3,y3)
            table.insert(tab1,vec2(tab[z].x+math.random(-x2,x2)+x,tab[z].y+math.random(-y2,y2)+y))
        end
    end 
end

function draw()
    background(0)
    fill(255,255,255,8)
    for a,b in pairs(tab1) do
        ellipse(b.x,b.y,50)
    end    
end

function touched(t)
    if t.state==BEGAN then
        create()
    end
end