I made a minimal test that exercises features that I use in my application...
This scene is quite fast:
http://danceliquid.com/docs/threejs/material-test/index.html?many-materials=false
But sadly, I can't individually control the colors of each cube. (Because there are only 2 materials in the scene)
This scene is slower:
http://danceliquid.com/docs/threejs/material-test/index.html?many-materials=true
In this case, I've given each cube it's own material. (The materials share the textures)
In the second case, I'm able to tint (change the color) of each cube individually, which is what I need to do in my real application. But the performance is degraded. I have two machines that I can use for testing here, and the framerates on the first are 53/46 (15% drop) and the framerates on the other are 60/33 (45% drop).
Ideally, I'd like to have a large number of meshes all made from the same geometry and material, but to be able to tint (color) them individually. Perhaps when the material is setting the color uniform, it could see if there was an override provided by the mesh and use that instead?
Thanks! I love three.js. It's my hero.
Additional details in my SO question, in case that's helpful:
http://stackoverflow.com/questions/14145275/
I do notice the degradation. It's about 5fps slower here.
What it's interesting is that in both tests there is only 1 program being generated (which all the materials share).
I appreciate you looking at this so quickly.
Your observation is super-interesting. That would be great if the system could optimize similar materials in that way.
Can you explain how you're verifying that about the number of programs? I'm not very familiar with the internals of three.js.
Separately, what do you think about the idea of a mesh-level override for the diffuse color uniform? I believe it would be good for my case, but I don't know a lot about how others use the library. Thanks again!
I just opened the console and typed:
renderer.info.memory.programs
Neat, I didn't know about renderer.info. Thanks.
(You should try instancing, try per-vertex color with THREE.VertexColors / THREE.FaceColors, or try using a custom shader).
Summary: There are two textured Phong materials. The fast case creates a grid of meshes and assigns alternating materials. The slow case does the same, but assigns a .clone of the original materials.
With r72dev and a 64 x 64 grid, I'm getting
However, I can remove the textures and use a single reference material instead of two and I still get basically the same fps.
The problem is caused by the large number for cloned materials, regardless of their complexity.
Using the WebGL Inspector plugin, this is the GL call log for a 2x2 grid, using a single non-textured material (fast case)
An this is the same for the slow case (each mesh has a clone of the original material)
And these are the logs for the textured Phong material, using two different material ("snow" and "disco")
Fast (2 materials total):
Slow (4 materials total):
The slow case, using a lot of cloned materials, makes a lot more redundant calls. (I assume that if the properties of the material were more different, there would be less redundancy).
Could/Should WebGLState track those redundant state changes?
Could/Should
WebGLStatetrack those redundant state changes?
The more WebGLState can cache the better. But, at the same time, trying to keep the code simple/manageable.
More appropriately, WebGLProgram should be responsible for caching the uniforms, not WebGLState.
Each program would do is own monitoring, starting at useProgram until the program is switched off. Anytime the active program is switched, the uniform cache should be reset (at least in the initial implementation).
Perhaps when the material is setting the color uniform, it could see if there was an override provided by the mesh and use that instead?
mesh.onBeforeRender() should solve this.
I am not working in this area any more but I am glad to see progress is being made.
If there's an easy way to make the cases I was trying fast, then that is a huge improvement, and could mean massive perf improvements for many common instancing workflows. Kudos!
@WestLangley
mesh.onBeforeRender() should solve this.
This does not solve the problem.
Here's a pen using one material instance per mesh (the slow case) with different colors:
https://codepen.io/anon/pen/JMydzb
and here's an attempt to use only a single material shared with all meshes, and using onBeforeRender to change the color of each mesh, but what you will notice happens is that all meshes are rendered the same color, probably that of the mesh closest to the camera after sorting (because notice the color changes when moving the camera):
https://codepen.io/anon/pen/zpdGWx
The performance improvement when using a single material is fairly significant. I too was trying to figure out how to use a single material instance and change the color for different objects, which landed me here.
Can you please re-open this? (Or it should be a new issue?)
Maybe there's opporrunity to detect certain scenarios and do auto-instancing? Is that possible? Does that cause problems with sorting?
Looks like that was attempted in https://github.com/mrdoob/three.js/pull/10093.
@trusktr Apparently the uniforms are not being updated.
If we want to support updating material properties in onBeforeRender(), we may need something like a material.uniformsNeedUpdate flag to force it.
should be a new issue?
If you want to pursue this.
@WestLangley So before every draw call of every mesh, it'll update the uniform? Seems like an edge case that should be handled. This also would not work with any of the proposed auto-instancing by @Benjamin-Dobell, right?
@trusktr are you still mainly drawing planes...?
@mrdoob I'm making a general purpose HTML interface for 3D. I'd like anyone to be able to make anything with it, so I'm experimenting with ways to improve performance without any specific use case in mind.
The Planes are for the specific "mix DOM with WebGL" use case.
@trusktr have you done performance profiles that shows that this is an issue in your use case?
Of those two pens, the second one is definitely faster (disregarding color not working as I hoped).
https://codepen.io/anon/pen/zpdGWx
I do have other performance problems in my lib. I'm trying to make faster any way that I can.
Why are you creating 1000 identical geometries??? You should create just one and reuse it.
Here's your fiddle with same output with, theoretically, 1000x memory reduction, initialises much faster and renders much faster:
https://codepen.io/anon/pen/NXayKj
There is no way the renderer could optimise your code.
Why are you creating 1000 identical geometries??? You should create just one and reuse it.
I wanted to have them be different sizes _and_ have differing amounts of animated scale, so it was easy to do it this way (without thinking much about it, because the API lends to it being easy that way).
What I'd like is a scene graph that has both size and scale, decoupled from each other, like DOM+CSS.
I see how this can be done with Three.js' scene graph, with some book keeping on top of it. But it's not as ideal as DOM+CSS where width/height is decoupled and separate from transform:scale() without me having to write a layer on top of the DOM+CSS in order to have both separate size and scale.
There's much good stuff to learn from DOM. F.e., imagine, various "people" in a game, they are different heights, and all have a scale of 1. Then, they all take some mushrooms, and now their scales start wobbling, but their size numbers haven't changed. (weird example, but valid!). It can be done in Three, but the layer of numbers (size + scale) has to be implemented on top of Three.
In my lib, I will create a non-coupled concept of scale + size, and under the hood, yes, I will re-use geometries.
There is no way the renderer could optimise your code.
Something like https://github.com/mrdoob/three.js/pull/10093 may help! I know, it's a lot to maintain for one person. How can that maintenance problem be solved? CoughsponshorshipfromGoogleCough?
In my lib, I will create a non-coupled concept of scale + size, and under the hood, yes, I will re-use geometries.
馃憣
Something like #10093 may help! I know, it's a lot to maintain for one person. How can that maintenance problem be solved? CoughsponshorshipfromGoogleCough?
Responding to conversations like this one is what leaves me with no time/energies left to implement these kind of features.
Please, try to use this section mindfully.