Transition.to is sloppy when executed after heavy load

Hello,

I noticed that the start of the transitions in Corona can get sloppy when they are executed after doing some heavy code before that.

For example in my game I execute a transition after my map is loaded. The loading of the map is quiet heavy which makes my map transition (2 rectangles sliding away) act laggy. If I put a timer.performWithDelay after the heavy function the lag is gone.

I made an example main.lua which is located below, what I want to know is why this is happening? The lua code is executed non-parralel right? Is this the garbage collector doing stuff in the background?

Ps. for this example it’s best to turn on 60fps, the green rect moving is WITH a timer.performWithDelay and the red rect is WITHOUT. So the red rect is the one acting sloppy in my simulator and phone. You can edit the amount in the for loop to work better on the pc you’re using.

local heavyLoad local rect = display.newRect(0, 0, display.contentWidth, display.contentHeight) rect.y = display.contentCenterY rect.x = display.contentCenterX + display.contentWidth - 10 local count = 0 local function doTransition() transition.from(rect, {x = display.contentCenterX, time = 300}) timer.performWithDelay(3000, heavyLoad) end heavyLoad = function() --Heavy load function count = count + 1 for i=1, 100000, 1 do local test = display.newRect(0, 0, 1, 1) display.remove(test) end if count % 2 == 0 then rect:setFillColor(1, 0, 0) doTransition() else rect:setFillColor(0, 1, 0) timer.performWithDelay(1000, doTransition) end end timer.performWithDelay(500, heavyLoad)

If you do too much work in any one frame, you will see laggy and jumpy visuals.

Sounds like you need to split up this ‘heavy load’ your’re talking about into smaller chunks if the transition is not dependent on something produced by it.

Note: I wouldn’t call this transition being ‘sloppy’, but rather Corona not getting a chance to do its work in a timely fashion because your workload has consumed more than a frame duration.

Remember, you can block Corona’s ability to proceed.  It will not preempt your code.

Ah yes, that is probably exactly what I am trying to do, block Corona’s ability to proceed (or am I wrong?). I don’t want the code to be preempting. 

What I want to achieve is something like this: 

setScreenToBlack() --By making a big rectangle the size of the screen

loadMap() --Pretty heavy function

transitionBlackScreenAway()

It does not matter that the loadMap takes 1ms or even 2 seconds, I want the loadMap to be completely done, and after that function is done, start the transition. 

Is there a way to be sure nothing of the loadMap() function is still processing? timer.performWithDelay(1, loadMap) seems to do the job most of the time, but I got the feeling that its not 100% of the processing done as completely not loading the map seems even smoother. 

Super-simple way would be to just set a flag and have a listener check periodically to see if the flag has been flipped:

local mapHasLoaded = false local transTimer local function loadMap() -- heavy lifting mapHasLoaded = true end loadMap() transTimer = timer.performWithDelay(100, function() debugPrint("mapHasLoaded = ", mapHasLoaded) if mapHasLoaded then timer.cancel(transTimer) transitionBlackScreenAway() end end, 0)

The below thread goes over some other concepts regarding flags and access to them:

https://forums.coronalabs.com/topic/56370-access-flag-value-from-one-class-to-another-class

@pouwelsjochem8

Your above example might also lend itself to some techniques I mentioned with coroutines, e.g. in the long-running processes section.

Very interesting article StarCrunch :slight_smile: I just adjusted my actual code to work with coroutines (divided into multiple yields), and it’s looks like it is a consistent clean transition after loading the map now ^^

Do you know why a single timer.performWithDelay(1, _onComplete) on the end of the execution of the code doesn’t do the job as consistent as coroutines do? Is it because of the difference that timer.performWithDelay just sets a flag that the function should be executed on the next frame, and a yield really stops with executing code until the next frame? Or is it probably because I release the thread multiple times now instead of just 1 time at the end?

Yes, the yields are key. Alternatively, you could do this with a repeating timer, simply doing a few rects (or whatever) at a time.

This is what roaminggamer was getting at with “blocking”. Corona itself isn’t able to proceed and do all of its update operations, so everything gets backed up, more or less. A coroutine gives control back to it, on the other hand, and picks up where it left off.

If you do too much work in any one frame, you will see laggy and jumpy visuals.

Sounds like you need to split up this ‘heavy load’ your’re talking about into smaller chunks if the transition is not dependent on something produced by it.

Note: I wouldn’t call this transition being ‘sloppy’, but rather Corona not getting a chance to do its work in a timely fashion because your workload has consumed more than a frame duration.

Remember, you can block Corona’s ability to proceed.  It will not preempt your code.

Ah yes, that is probably exactly what I am trying to do, block Corona’s ability to proceed (or am I wrong?). I don’t want the code to be preempting. 

What I want to achieve is something like this: 

setScreenToBlack() --By making a big rectangle the size of the screen

loadMap() --Pretty heavy function

transitionBlackScreenAway()

It does not matter that the loadMap takes 1ms or even 2 seconds, I want the loadMap to be completely done, and after that function is done, start the transition. 

Is there a way to be sure nothing of the loadMap() function is still processing? timer.performWithDelay(1, loadMap) seems to do the job most of the time, but I got the feeling that its not 100% of the processing done as completely not loading the map seems even smoother. 

Super-simple way would be to just set a flag and have a listener check periodically to see if the flag has been flipped:

local mapHasLoaded = false local transTimer local function loadMap() -- heavy lifting mapHasLoaded = true end loadMap() transTimer = timer.performWithDelay(100, function() debugPrint("mapHasLoaded = ", mapHasLoaded) if mapHasLoaded then timer.cancel(transTimer) transitionBlackScreenAway() end end, 0)

The below thread goes over some other concepts regarding flags and access to them:

https://forums.coronalabs.com/topic/56370-access-flag-value-from-one-class-to-another-class

@pouwelsjochem8

Your above example might also lend itself to some techniques I mentioned with coroutines, e.g. in the long-running processes section.

Very interesting article StarCrunch :slight_smile: I just adjusted my actual code to work with coroutines (divided into multiple yields), and it’s looks like it is a consistent clean transition after loading the map now ^^

Do you know why a single timer.performWithDelay(1, _onComplete) on the end of the execution of the code doesn’t do the job as consistent as coroutines do? Is it because of the difference that timer.performWithDelay just sets a flag that the function should be executed on the next frame, and a yield really stops with executing code until the next frame? Or is it probably because I release the thread multiple times now instead of just 1 time at the end?

Yes, the yields are key. Alternatively, you could do this with a repeating timer, simply doing a few rects (or whatever) at a time.

This is what roaminggamer was getting at with “blocking”. Corona itself isn’t able to proceed and do all of its update operations, so everything gets backed up, more or less. A coroutine gives control back to it, on the other hand, and picks up where it left off.