jacquesr

Controlling the render loop

Recommended Posts

Hi,

 

I'm writing an app that essentially only needs to render when the user does something.

 

There are a few edge cases that cause one-time animations, all other stuff is a one time render situation.

 

The main reason to avoid the render loop is that there is nothing generally going on that requires visual updates so we can save a lot of resources by not rendering all the time

 

 

 

I currently try to handle the render loop the following way:

 

Some consumer code calls a render on my babylon js wrapper object that handles everything

In this moment, a pendingFrames variable is increased (default 0) and startRenderLoop is called with a callback doing nothing apart from scene.render()

In onAfterRender, I look for further pending frames ( pendingFrames > 0). If there is nothing, I call stopRenderLoop.

For animations, there is a similar approach, with the difference that the pendingAnimations variable is increased whenever an animation starts and decreased in every end animation callback.

So, let's take away the animation case.

I have a mouse move interaction that calls my render call on my wrapper object. It increases the pendingFrames by one every time. This is my first problem, because there is no 1:1 relation between mouse move and rendering, so I can increase more render calls than I actually need.

Before, I did not use "pendingFrames" but instead had a simple flag that managed to render the scene using scene.render if anyone requested a frame. Then I did not have the relation problem BUT: positioning changes that were handled in onBeforeRender did not work even though I changed positions on depending meshes before the render call. Upon the next mouse move, without any further change, it then suddenly was correct again. So, that may also have something to do with the timing.

 

In the end, my question now is, how do I correctly take full control so that I do not render anything I do not need.

 

It was also noticable that, against a general render loop, my mouse move based enabling and disabling of rendering was 7 fps slower (against 37) which I currently don't really have an answer for.

EDIT:

I should note that another noticable thing is that the movement is not really smooth when I use my enable/disable render loop logic. The camera movement tends to jump a little. I must do something seriously wrong. Maybe some technical background helps :-)

Share this post


Link to post
Share on other sites

Hey,

 

you are not doing anything wrong :) and this is pretty smart actually. 

 

The first problem that I can foresee is that you won't have camera movement inertia with your system because this requires several rendering to achieve it

 

One idea here: Your stop condition should be: no pending frames AND no running animations (ie: scene.._activeAnimatables.length === 0) AND camera.cameraDirection === Vec3(0) AND camera.cameraRotation === 0 as well

Share this post


Link to post
Share on other sites

Hey Deltakosh,

 

we have set the inertia to 0.1 because we also did not want that and we are aware of that problem. Well, inertia 0.1 is not 0 but I learned that babylon "does not like zero values" :D

 

I will try improving the check in the renderloop management.

Anyway, do you have an idea why my manual start/stop costs me 7 fps? 30 to 37 (when we once start the renderloop and let it run)

 

I first thought it might be the mousemove, but I guess that mouse move interrupts are a lot more often than 60 fps (at least in theory without logic inside) in the end...

 

Now I tend to think that it is due to the binding and unbinding of the renderloop callback or the general approach with initializing and deinitializing a renderloop. I guess that internally, you are working on requestAnimationFrame

Share this post


Link to post
Share on other sites

Hey @Deltakosh

I recently added the changes you suggested. Running on babylon 2.3.

 

if (sceneObjects.pendingAnimationCount > 0 || 
                        sceneObjects.pendingFrames > 0 ||
                        sceneObjects.forceKeepRendering ||
                        cam.cameraDirection.x !== 0 || cam.cameraDirection.y !== 0 || cam.cameraDirection.z !== 0 ||
                        cam.cameraRotation.x !== 0 || 
                        cam.cameraRotation.y !== 0)
                    {}
                    else
                        this.stopRenderLoop(); 

yet, that doesn't work since cameradirection/rotation seems to be always 0... I have an arcrotatecamera here

Share this post


Link to post
Share on other sites

Making the renderer stop rendering when there is nothing to do - this is something I would like to implement as well. Do you have any PG for this? I cannot find any full example above. Thanks lot!

Share this post


Link to post
Share on other sites

Thank you, this is a nice and simple example, but the functionality I a bit limited. How could I extend it to render as long as the camera or some object is moving?

Share this post


Link to post
Share on other sites
2 hours ago, Hanesu said:

How could I extend it to render as long as ..... some object is moving?

Mmmmmmmmmmmmmm what is going to start the object moving? I know I will set a random time T milliseconds after which the object will move. Mmmmmmmm how will I know T milliseconds have passed? I know I will write a routine that that runs once every sixtieth of a second and see if the time has passed yet? Mmmmmmmmmm! No I wont I will just use new BABYLON.Animation and leave the rendering alone.

Mmmmoral is I better have a very good reason to want to stop rendering else I am just going to give myself a lot of work for no good reason! :wacko::D

Does it really matter that if nothing is going on then the BabylonJS engine is rendering the same screen upto 60 times a second?

Share this post


Link to post
Share on other sites
8 hours ago, JohnK said:

Mmmmoral is I better have a very good reason to want to stop rendering else I am just going to give myself a lot of work for no good reason! :wacko::D

Does it really matter that if nothing is going on then the BabylonJS engine is rendering the same screen upto 60 times a second?

There is a very good reason why I want to stop rendering if there is nothing new to render: I have complicated meshes (scientific data) to visualize with millions of triangles. If they are re-rendered 60 times a second, the cpu load goes up to 100%, my laptop gets freaking loud, crazy hot, and runs out of battery after one hour!

If I have means to stop rendering, like for example with the simple solution by @aWeirdo above, the cpu load goes down below 5%, my laptop is quiet, keeps cool, and runs for eight hours on battery!

Share this post


Link to post
Share on other sites
6 hours ago, Hanesu said:

There is a very good reason why I want to stop rendering if there is nothing new to render: I have complicated meshes (scientific data) to visualize with millions of triangles. If they are re-rendered 60 times a second, the cpu load goes up to 100%, my laptop gets freaking loud, crazy hot, and runs out of battery after one hour!

If I have means to stop rendering, like for example with the simple solution by @aWeirdo above, the cpu load goes down below 5%, my laptop is quiet, keeps cool, and runs for eight hours on battery!

That is a very good reason and that will start the rendering if the user moves the pointer and so moves the camera. However you then said

17 hours ago, Hanesu said:

 

Thank you, this is a nice and simple example, but the functionality I a bit limited.

 

and asked to extend this

17 hours ago, Hanesu said:

to render as long as .... some object is moving

The question then becomes what event starts and stops the object moving. If it is an external event such as a key press or pointer move then capturing that event works in the same way as aWeirdo showed.

The following PG will also render when the key g is pressed and stop when s is pressed. NOTE click on scene before pressing key so scene has focus.

http://playground.babylonjs.com/#3A2PUF#3.

If it is a timed event then use setTimeout http://playground.babylonjs.com/#3A2PUF#4

Share this post


Link to post
Share on other sites

Thanks for the examples, very helpful. I will check out the actionManager stuff.

One thing that still bothers me is when I zoom with the arcRotateCamera through mouse scroll. When I scroll the mouse wheel nothing happens (of course, because the mouse button is not pressed). Then, when I press the mouse button, the scrolling motion happens, it is somehow remembered and the action is delayed. This behavior is not useful, so one way around it would be an "onScroll" event analogous to the "onPointerDown", but this event does not seem to exist. That is why I thought it would be simpler and more general to check for motions in the camera, like camera.alpha/beta/radius changes. How could I check for changes in those parameters and only render in case of change?

 

Share this post


Link to post
Share on other sites

As far as I know you can only rotate or move the camera when you have a pointer or key down and aWeirdo's method will detect this. As far as scrolling goes then I think any zoom is a browser zoom not a BJS zoom and so there is no change of radius.

IMHO without rendering and using scene.registerAfterRender you could only check for an camera.alpha  change using setInterval to check every few milliseconds if oldAlpha was currentAlpha but alpha will only change if you are are moving the mouse with pointerdown so back to above.

An alternative would be to use BABYLON.GUI and change alpha, beta and radius values on the click of a button, but then as you are clicking the button this event is captured and you can then switch rendering on during the onclick function.

If you have other ways of checking for alpha, beta and radius values let us know.

Share this post


Link to post
Share on other sites

The inputs to arc rotate camera don't modify the alpha/beta/radius directly, but rather modifies inertialAlphaOffset, inertialBetaOffset and inertialRadiusOffset, which in turn sets the alpha, beta and radius of the camera (adds the offset, then multiplies the offset by inertia, repeat until the offset is ~0, see here).

If you just want to make sure movement hasn't been buffered while the render is stopped, you can just set these offset to 0 before the render.

https://www.babylonjs-playground.com/#AFPV4K#2

If you just want to allow rendering while the camera is moving, you can however just use a render loop that checks if these values are not 0.

https://www.babylonjs-playground.com/#AFPV4K#5

@Deltakosh When inertia is set to 0 in the above pg, the movement seems to be a frame behind (if zoom in then out, the first frame after the zoom out scroll wheel tick will zoom in). Something to do with render order? Also if you zoom, then just click move to rotate normally, it will do the final zoom on that render.

Share this post


Link to post
Share on other sites
On 6/24/2017 at 4:37 PM, JohnK said:

As far as I know you can only rotate or move the camera when you have a pointer or key down and aWeirdo's method will detect this. As far as scrolling goes then I think any zoom is a browser zoom not a BJS zoom and so there is no change of radius.

By monitoring camera.radius you immediately see that scrolling directly changes the radius.

Share this post


Link to post
Share on other sites
On 6/26/2017 at 7:58 PM, sable said:

The inputs to arc rotate camera don't modify the alpha/beta/radius directly, but rather modifies inertialAlphaOffset, inertialBetaOffset and inertialRadiusOffset, which in turn sets the alpha, beta and radius of the camera (adds the offset, then multiplies the offset by inertia, repeat until the offset is ~0, see here).

If you just want to make sure movement hasn't been buffered while the render is stopped, you can just set these offset to 0 before the render.

https://www.babylonjs-playground.com/#AFPV4K#2

If you just want to allow rendering while the camera is moving, you can however just use a render loop that checks if these values are not 0.

https://www.babylonjs-playground.com/#AFPV4K#5

That's it, the second PG does exactly what I need. Thank you so much, you made my week! :D

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.