yokewang

BabylonJS render slower thanThreeJS?

Recommended Posts

I have written a simple demo to render 8000 cubes in BabylonJS. But found out it's quite slower than in ThreeJS.

Demo in BabylonJs:  https://jsfiddle.net/6ng7usmj/

Demo in ThreeJS: https://jsfiddle.net/pofq4827/

It does not make sense, because BabylonJS supports more performance related features like vao.

Any help would be greatly appreciated.

Share this post


Link to post
Share on other sites

Great demos. Can you share your FPS or performance stats?  On my machine with your jsfiddles:
ThreeJS:  ~21 FPS
BabylonJS: ~13 FPS

My BabylonJS: ~38 FPS

You are re-using your geometry in ThreeJS when you create your meshes, I think that is essentially equivalent to instancing in BabylonJS, causing a ~3x difference in FPS:
https://jsfiddle.net/6ng7usmj/10/

edit: BoxGeometry is used in Three.js, but not intended to be used as in your fiddle with BabylonJS.  Perhaps the BabylonJS docs could be updated to guide people coming from other ecosystems to best practices.  Hopefully this thread sheds some light for equivalence:
http://www.html5gamedevs.com/topic/38981-why-parameters-on-geometries-have-no-effect/?tab=comments#comment-222588

 

Share this post


Link to post
Share on other sites

Well, you don't really use the same features in both examples. In the ThreeJS one, you use the BufferGeometry. In 3JS, the BufferGeometry is under the hood a flat typed array to be passed directly to the GPU, what is faster than converting some JS objects in a first pass, and this geometry is shared among all the meshes.

Actually, in the BJS example that you provided, you are trying to port line by line the 3JS code to BJS. But, this doesn't work that way because the frameworks don't have the same way or philosophy to implement same features... Doing this will always lead to misunderstandings and wrong results.

As brianzinn suggested, you could investigate how BJS does to achieve the same result in other ways.

In BJS, you can use, for example, instances of a single mesh. In  this case, the geometry, and other properties like materials, are shared among instances : http://doc.babylonjs.com/how_to/how_to_use_instances

You could either use a Solid Particle System (SPS) : http://doc.babylonjs.com/how_to/solid_particle_system 

https://jsfiddle.net/6ng7usmj/32/       => 60 fps

If you want to tune things at buffer level, there's a way I just can remember now to to force to turn the buffers to be passed as flat ones (ie not related to indices ones)

EDIT : this http://doc.babylonjs.com/how_to/optimizing_your_scene#using-unindexed-meshes

Share this post


Link to post
Share on other sites
25 minutes ago, aWeirdo said:

60 fps on my pc

422K!!  My slow laptop is only 30FPS :(  maybe I should have got a gaming laptop :)  This XPS 9560 is nearly a year old - still looking for something not too big with an amazing screen and at least nvidia 1080...

Share this post


Link to post
Share on other sites

The frame rate improved by some optimizations from https://doc.babylonjs.com/how_to/optimizing_your_scene.

https://jsfiddle.net/6ng7usmj/57/  optimized version. But still slower than ThreeJS version.

https://jsfiddle.net/6ng7usmj original version.

BTW: We should not compare an ONE DRAWCALL instance version with a 8000 DRAWCALL version.  And ThreeJS supports instanced draw too.

It's seems BabylonJs spends more time than ThreeJS to evaluate active meshes. I will try octree to see if it will help.

Any help would be greatly appreciated.

Share this post


Link to post
Share on other sites
 for (var x = 0; x < gridSize; x++)
 for (var y = 0; y < gridSize; y++)
 for (var z = 0; z < gridSize; z++) {
                    var geometry = new THREE.BoxBufferGeometry(0.8, 0.8, 0.8); 
                    var boxMesh = new THREE.Mesh(geometry, material);
                      }


vs 

  var geometry = new THREE.BoxBufferGeometry(0.8, 0.8, 0.8); 
                  
 for (var x = 0; x < gridSize; x++)
 for (var y = 0; y < gridSize; y++)
 for (var z = 0; z < gridSize; z++) {
                    var boxMesh = new THREE.Mesh(geometry, material);
                      }

that is not good sample to test at all 

when we make a new Mesh we never use one GeometryBuffer for a lot mesh

https://jsfiddle.net/pofq4827/5/  

https://jsfiddle.net/6ng7usmj/68/

if you wanna make that type i think we do different in share geometryBuffer 

Share this post


Link to post
Share on other sites

Another lead for the comparison : check that the 3JS phong material has the same default settings than the BJS standard material, like specular, etc ... I'm quite sure there's some difference here because when rotating the cam to the dark side of the big cube, both examples render differently : fully opaque for 3JS whereas still separated boxes for BJS

Check also that the canvas in both samples have same size 

 

[EDIT] I changed the 3JS ambient light to a hemispereLight like in 3JS so the comparison is better : https://jsfiddle.net/pofq4827/11/

They still render differently in the dark side of the global cube, meaning there's still something different in the lights or in the materials, I suppose

Share this post


Link to post
Share on other sites

As Jerome stated, it is not a good idea to port directly from one framework to another.

Here is something more Babylonjs wise to compare with your original demo:

https://jsfiddle.net/6ng7usmj/89/

 

I also changed the hemi light (dynamic) by a ambient color like in original demo

 

Also please not that this demo works badly with uniform buffer as only world matrix change (thus using instances would be far better). In this case we update an entire uniform buffer for just a matrix which is counter productive

 

Share this post


Link to post
Share on other sites

The new version for 3JS: https://jsfiddle.net/pofq4827/16/   (disabled matrix auto updating and opaque objects sorting before rendering), its frame rate increased furtherly. The javascript computation time before indeed rendering(gl command)  is within 2ms comparing to 10ms in BabylonJS.

After inspecting the code for ThreeJS and BabylonJS,  _evaluateActiveMeshes method costs too much for 8000 meshes.

Share this post


Link to post
Share on other sites

Reducing the 3JS example at the same window size as the BJS one, I get quite the same framerate (30-33 fps) with your two last demos (dk and yokewang's ones) on my laptop. The 3JS example is even slightly slower.

Are you sure that you get still such a performance difference now ?

Share this post


Link to post
Share on other sites

Did you see my example? In my example evaluateActiveMeshes costs 0 (https://jsfiddle.net/6ng7usmj/89/)

On my computer, my example is faster than your last 3js example.

Here is the code of the _evaluateActiveMeshes: https://github.com/BabylonJS/Babylon.js/blob/master/src/babylon.scene.ts#L4190

Among all the code, the only things that could be slow are:

- Frustum clipping (you can turn it of with mesh.alwaysSelectAsActiveMesh = true)

- mesh.isReady (but we use the fatest version here as first parameter is set to false)

And you can completely avoid the evaluate stage by calling scene.freezeActiveMeshes() like in my example (http://doc.babylonjs.com/api/classes/babylon.scene#freezeactivemeshes)

Share this post


Link to post
Share on other sites

I have the impression to see a publicity for 3js that tries to say that babylon is less efficient than 3js.
Each engine has its characteristics and its way of doing things. Babylon is very effective if you use it with the tools that must. It still has other tools to optimize.

In addition each engine has its strengths and if we must compare the two engines, Babylon has a lot of assets and is much more modern and easy to use.

For my part, I never understood anything at 3js and the number of incompatible thing from one version to another made me totally abandon the 3D in browser until I found Babylon who has me to do much more than 3js. In fact with 3js I display a cube and I already had a lot of difficulty to create materials. with babylon i understood right away and is display a lot more stuff. when I imported a mesh it worked and in 3js, I had compatibility problems.  So 3js vs Babylon. Babylon with no hesitation.

Another important piece of information and the community. when i post questions on the 3js forum, i never got an answer. On babylon I had received in the minutes that followed my first posts because here there is an excellent community.

BriefA project with 8000 meshes is really a lot for a single scene. I can not believe that you need to create a project with 8000 meshes, but maybe you want to create a GTA V and you are surrounded by a team of 100 people. I do not know.
When a project already reaches 500 to 1000 meshes it is already a project that has content to present, if then you add instances, clones, you can create projects large enough without ever reaching 8000 meshes and get fps between 45 and 60 fps. 

Share this post


Link to post
Share on other sites

Freezing active meshes skip the step of checking 8000 mesh frustum clipping and state.

                if (mesh.alwaysSelectAsActiveMesh || mesh.isVisible && mesh.visibility > 0 && ((mesh.layerMask & this.activeCamera.layerMask) !== 0) && mesh.isInFrustum(this._frustumPlanes)) {
                    this._activeMeshes.push(mesh);
                    this.activeCamera._activeMeshes.push(mesh);
                    mesh._activate(this._renderId);
                    if (meshLOD !== mesh) {
                        meshLOD._activate(this._renderId);
                    }
                    this._activeMesh(mesh, meshLOD);
                }

this._activeMesh(mesh, meshLOD) costs time.

And I got a little performance boost after below code replacement(BUT IT IS NOT A WISE SOLUTION).

// this._totalVertices.addCount(mesh.getTotalVertices(), false);
this._totalVertices.addCount(mesh._geometry._totalVertices, false);
 
// if (mesh.alwaysSelectAsActiveMesh || mesh.isVisible && mesh.visibility > 0 && ((mesh.layerMask & this.activeCamera.layerMask) !== 0) && mesh.isInFrustum(this._frustumPlanes)) {
if (mesh.alwaysSelectAsActiveMesh || mesh.isVisible && mesh.visibility > 0 && ((mesh.layerMask & this.activeCamera.layerMask) !== 0) && mesh._boundingInfo.boundingSphere.isInFrustum(this._frustumPlanes)) {

Share this post


Link to post
Share on other sites

The frustum check  against boundingSpheres only is obviously faster but also less accurate.

 

Not sure, but I think that 3JS computes the culling only with the bounding spheres : https://github.com/mrdoob/three.js/issues/11291

Well, if you tune your BJS demo so it's the same as the 3JS one, you'll get same perfs 😉

Maybe we could add a parameter in BJS to let the final user to choose whether he wants to limit the culling tests to boundingSpheres only. This already exists in the solid particle intersections http://doc.babylonjs.com/how_to/solid_particle_system#particle-intersections    boundingSphereOnly : true

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.