I’ve found that spatial.updateGeometricState() is called regardless of the cullHint mode. This means that the object geometry is updated even when it is not visible. I think this is pointless. I’ve also found that this method is called on each update in application. Is that really needed? Shouldn’t be it executed only when object transform (scale, rotation, translation) changes? Otherwise this is a big overhead when there are many static objects in scene.
Meshes can change shape though which then changes the bounding box without modifying the transform.
Not sure about the cullHint - since I expect cullHint to always cull to be rare I guess the check for it would be more expensive than the savings… or did you mean something else?
updateGeometricState is responsible of updating and computing the world transforms of an object. Objects that are outside of the camera can move and eventually come in the field of view so they definitely have to be updated.
When an object is transformed, we have a bit mask system that flags this objects to be updated.
The method is called every time, but If the object was not transformed at all, it just checks the bit mask and early exits.
In other words, how do you know if it should be culled if you don’t know where it is?
Thank You for Your replies.
@pspeed If I set “setLocalTranslation” to an object attached to a root node - it’s world translation becomes the same as it’s local translation (if the root node translation wasn’t changed before) - so the outcome is that I know where the object is and I can cull him appropriately.
And the question is: why should the transform be updated on every frame, when no one can say if the transform changed that time? I think it should be computed only when the object parent transform has changed or its own transform has changed by explicit call to a methods like “setLocalTranslation” or “setLocalRotation” etc.
The other thing is that - why should bounds be updated every frame? I think it shoud by default, but there should be a way to set some flag on a Spatial like "setUpdateBounds(boolean)’ which says to the object if it should update its bounds every frame or not depending on parameter passed.
Looking into the body of updateGeometricState I saw also the “updateWorldLightList”. This also is irrelevant when the object is culled because it cant be seen so the lights at this moment are not used for rendering, because there is no rendering at the time.
That is how I understand the “updateGeometricState”, correct me if I’m wrong.
My problem is that I had a serious performance issuess made by two Spatial methods: updateLogicalState (but that is a different story), and updateGeometricState. With both active, having around 13000 objects in scene the performance was (from what I was able to see) around 20 fps. When I overrided Node with my own “GameObject” class, where the updateGeometricState is only called when a “visible” field is set to true, and reimplemented the updateLogicalState, the framerate grow above thirty (it could be 35 but also 60 or ninety). Also the CPU usage dropped from 50-60 to 20.
But I know that isn’t a long term solution.
Especially I don’t like the fact that the “updateGeometricState” is called every frame, not when the object transform is changed, this looks a lot of like somebody was afraid that something could go wrong so he decided put it there. This get’s worse, because this method is recursive on it’s children in the scene graph. From what I can see it flows from the rootNode to the deepest child.
This seriously creates some bottleneck on the CPU.
No it does not create a “serious bottleneck” and if you think this is not the way to go look for another game engine, you will find out that most work this way in some respect.
Well as You can see 40% drop in CPU usage is an improvement. Read my post first, then write a response that makes any sense. If Your argument is “because other does the same in some respect” then please don’t speak out in this thread.
@luke1985 said: Thank You for Your replies. @pspeed If I set "setLocalTranslation" to an object attached to a root node - it's world translation becomes the same as it's local translation (if the root node translation wasn't changed before) - so the outcome is that I know where the object is and I can cull him appropriately.And the question is: why should the transform be updated on every frame, when no one can say if the transform changed that time? I think it should be computed only when the object parent transform has changed or its own transform has changed by explicit call to a methods like “setLocalTranslation” or “setLocalRotation” etc.
The other thing is that - why should bounds be updated every frame? I think it shoud by default, but there should be a way to set some flag on a Spatial like "setUpdateBounds(boolean)’ which says to the object if it should update its bounds every frame or not depending on parameter passed.
I’m not even going to read any farther… did you actually look at the code at all? It’s not hard to find:
http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/core/com/jme3/scene/Spatial.java
[java]
public void updateGeometricState() {
// assume that this Spatial is a leaf, a proper implementation
// for this method should be provided by Node.
// NOTE: Update world transforms first because
// bound transform depends on them.
if ((refreshFlags & RF_LIGHTLIST) != 0) {
updateWorldLightList();
}
if ((refreshFlags & RF_TRANSFORM) != 0) {
updateWorldTransforms();
}
if ((refreshFlags & RF_BOUND) != 0) {
updateWorldBound();
}
assert refreshFlags == 0;
}
[/java]
These things are only recalculated if they change.
@luke1985 said: Well as You can see 40% drop in CPU usage is an improvement. Read my post first, then write a response that makes any sense. If Your argument is "because other does the same in some respect" then please don't speak out in this thread.
Then something else is going on… like you really are moving things every frame. Otherwise, updateGeometricState() is nearly a no-op.
We have trouble responding because you are communicating to us from some other planet where the source code is not available or something and you are making wildly inaccurate assumptions. Lighten up on the attitude, maybe.
Alternately, if you have 10s of thousands of objects in your scene then you will also hit traversal-related performance issues… but this will be dwarfed by the thrashing the graphics card is enduring with all of that draw dispatch. So hopefully the scene is properly optimized at least.
I got curious so I read the rest of your post…
@luke1985 said: ...having around 13000 objects in scene the performance was.....
This is a problem. JME is not designed to handle that many separate geometries and neither is your graphics card. I can hear your bus screaming from here.
Is there a list of limits somewhere what one can “reasonably” expect?
13k geometries doesn’t sound THAT many to me, so I might have run into the same trap.
I know that no accurate numbers are possible, and that they can vary wildly across even within a platform, much more so between desktop and mobile.
Still, it would be nice to have some ballpark figures for each class of platforms. Say, something for low-performance and high-performance, both on Android and desktop.
@pspeed
Ok. It seems that these noops were the problem. I could have come to this by myself. Sorry for confusion.
If You want to know the source code, it’s nothing special about. Only there are many, many spatials with many controls attached to them. Each of this objects had at least 4 child nodes attached, so there were 13000 objects to update. That is a lot. My wrong I misinterpreted the profiler results.
These are results before optimisation:
Doing this solved the problem, this is a code fragment from GameObject.java, a class that simply extends jme3.Node:
[java]
private boolean isVisible = true;
public void hide() {
setCullHint(CullHint.Always);
isVisible = false;
}
public void show() {
setCullHint(CullHint.Never);
isVisible = true;
}
@Override
public void updateGeometricState() {
if (isVisible) {
super.updateGeometricState();
}
}[/java]
I rather think the main problem is that you should rethink your scenegraph.
As you can see in pspeeds code fragment only the needed upadates are done, but to decide if they are done or not you first have to check if they need to be done, which also costs a little bit of processor time. Altho i realy doubt that those times from your profiler come from those checks. I guess you have some influence on a lot of the nodes so they realy need an update.
Worldbounds and transforms are also needed for more then just displaying it on the screen. So in several cases these need to be updated even if the geometry is not drawn in your viewport. Just think about AI controlled entities, if moving geometries are not updated any AI you give them will work with world bounds and world positions that are not up to date. While it might be funny to look at a badass robot run into a wall this probably is not its intended behavior.
Take a look at all the different nodes, some are optimized for different tasks. For example if you have static geometries in your scene you can batch them in a batch node seriously reducing the ammount of nodes your scene graph contains.
The code you presented also lets me assume that you have some kind of check if a node is visible and you are calling the show and hide functions accordingly. This is another spot where you could optimize. If those nodes are not needed for anything else then displaying you could just remove them from the scenegraph and later when they are close to becomeing visible add them back to the scenegraph. That way you also keep the ammount of nodes in your scene graph at a low ammount. Rule of thumb i follow is allways only have nodes in the scenegraph that really need to be on it. If they are needed for physics or AI then sure they need to be there, but if not they only need to be there if they are actualy displayed (or better if they are either displayed or could be displayed in the next few update cycles).
<cite>@luke1985 said:</cite> Doing this solved the problem, this is a code fragment from GameObject.java, a class that simply extends jme3.Node:[java]
private boolean isVisible = true;public void hide() { setCullHint(CullHint.Always); isVisible = false; } public void show() { setCullHint(CullHint.Never); isVisible = true; } @Override public void updateGeometricState() { if (isVisible) { super.updateGeometricState(); } }[/java]</blockquote>
Ahh, as I expected you are abusing cullhint.
Why not just do:
[java]
private Node parent;public void hide() { parent.detachChild(this); } public void show() { parent.attachChild(this); }
[/java]
Which would be better done in a control, and then you don’t need to subclass Node at all.
Rough rule of thumbs for you:
- If you are subclassing Node you are probably doing something wrong.
- If you are using Cullhint NEVER or ALWAYS then you are probably doing something wrong.
Those facilities are there for people who know exactly what and why they are doing, but in 99.9% of cases are not useful.
See, to remove this update you will have to change the whole engine, it depends on this functionality working. All controls expect the spatial to be updated, all changes made to the position need to be distributed. The impact is totally negligible compared to the impact or the GPU update, hence its not a “serious bottleneck”. The comparison to other engines was made to point to the fact that this is the usual solution for scenegraphs, you update them each frame. So to me it makes no sense for you using this engine if you feel like changing it at this level. Write your own scenegraph library or use opengl directly. Also what exactly was I supposed to see?
Well I understand what You mean but I’m afraid that this won’t fit into my case.
The problem is that the objects must be invisible for performance but their controls must be active. This means that I can’t detach them from parent, because then the update loop won’t update their controls. (for example a car that is out of screen - for this example I don’t need bounds or transforms updated, simply nothing that is in updateGeometricState). So the only way to do this is to use Cullhint. The GameObject in my code which extends Node is a top-level container of controls (like Unity’s GameObject container of Components). There is a custom “culling system” defined in an appstate which shows/hides objects that are outside of a specified range from a camera (works by simply calculating distance between Vectors). The other reason I’ve extended node is for this “culling system”. Only objects that are instance of GameObject are the ones that are taken into consideration by the culling system, not terrain or water.
I have 13000 trees on a 256 x 256 terrain, which have to be GameObjects because they have something like “ObstructionControl” which controls for example if something can be placed on top of them, or if they can be placed on a certain place. Those for example are not updated. But there is another way to handle the empty updates:
I’ve extended the abstractControl with a class called “GameControl” and I add them in another method of GameObject:
[java]
private void addControls(GameControl[] gameControls) {
for (GameControl gameControl : gameControls) {
this.gameControls.add(gameControl);
addControl(gameControl);
if (gameControl instanceof Updateable) {
Updateable updateable = (Updateable) gameControl;
updateableControls.add(updateable);
}
}
}[/java]
So the only controls that are updated are the ones which implement updateable:
[java]
@Override
public void updateLogicalState(float tpf) {
for (Updateable updateable : updateableControls) {
updateable.update(tpf);
}
}
[/java]
Worldbounds and transforms are also needed for more then just displaying it on the screen. So in several cases these need to be updated even if the geometry is not drawn in your viewport. Just think about AI controlled entities, if moving geometries are not updated any AI you give them will work with world bounds and world positions that are not up to date. While it might be funny to look at a badass robot run into a wall this probably is not its intended behavior.
I understand this but what in situation, when the bounds have to be updated only? The only way to do so is to extend Node and override the only needed methods called from updateGeometricState (like updateWorldLights).
@ryukajiya
About the robot and things like this - back to the example of a car - when the car will be outside of visible area, actions that include bounds and geometry location won’t count. For example overtaking and stopping at red light will be only as an eyecandy.
My problem is that there is a situation when object has to be updated by it’s control so it must persist in scene graph while being invisible, and I don’t need the updated bounds, geometry. And this is the only solution I came to.
If You want to know where my problems come from - I’m developing a game similar to openttd (http://www.openttd.org/en/) with some own ideas.
So currently I have a map of a size 256 by 256 with lot’s of objects (trees), which eats 0.8 gb of RAM, so You can imagine the scale and complexity behind this :).
@normen
I understand this. Well we weren’t thinking about the same thing. I know that the scene graph is a composite pattern where updates flow from the top to bottom. What I wanted to say is that, it would be good if there were more options for customization of this execution path, in my examples You can see that I had to do stop the propagation down the hierarchy by extending Node. Also I think that conscequences of this could be mentioned somewhere in documentation.
This: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:updategeometricstate
Is really not nice. Its like saying: You dont have to know this. Well I think that someone may need info about the updateGeometricState, like me. I’m not sure if I don’t get into trouble someday because I’m not into the engine, and to be honest I don’t like working with & and | operators, and guessing is not the way to go. Also reading one method documentation didn’t told me that objects that geometric state didn’t changed are not updated with “updateGeometricState”.
I think one “problem” with your approach is that you are trying to use the scenegraph (which is the view) as the model of your game.
Ideally when coding a non trivial game it should be runnable without the view layer, that is you should be able to run your game simulation entirely without jME (or at least it is a good rule of thumb when thinking about it).
The tutorials/documentation is a bit misleading since they promote code patterns which is not very suitable for larger games/systems.
@luke1985 said: @normen I understand this. Well we weren't thinking about the same thing. I know that the scene graph is a composite pattern where updates flow from the top to bottom. What I wanted to say is that, it would be good if there were more options for customization of this execution path, in my examples You can see that I had to do stop the propagation down the hierarchy by extending Node. Also I think that conscequences of this could be mentioned somewhere in documentation. This: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:updategeometricstate Is really not nice. Its like saying: You dont have to know this. Well I think that someone may need info about the updateGeometricState, like me. I'm not sure if I don't get into trouble someday because I'm not into the engine, and to be honest I don't like working with & and | operators, and guessing is not the way to go. Also reading one method documentation didn't told me that objects that geometric state didn't changed are not updated with "updateGeometricState".
This comment is due to the history of jME, in jME2 you had to mess with this call all the time so we inform users they don’t have to anymore. We cannot possibly conceive all ways people might think the functionality is inadequate and state that its not but you are free to update the documentation, this is an open project.
I agree to what kwando says though. You cannot store an object as big as a spatial in memory for all locations of your world, you have to construct them based on the user view and use another model for your world.