[osg-users] Maximizing rendering throughput

15 views
Skip to first unread message

Sebastian Messerschmidt

unread,
Oct 22, 2015, 12:36:11 PM10/22/15
to OpenSceneGraph Users
Hi,

I have a couple of elements in the scene which data variance is set to
DYNAMIC to change them thread safe.
This will effectively kill performance as cull and draw are no longer
executed in parallel. So if I'd set those elements to STATIC, where is
the safe place to update them?
For instance I have some osg::Text which has to be changed every frame.
Is it safe to change it in between the update and renderingTraversals?
Also, what about updateOperation? Is considered to modify drawables from
there?

Cheers
Sebastian
_______________________________________________
osg-users mailing list
osg-...@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Jannik Heller

unread,
Oct 22, 2015, 2:17:21 PM10/22/15
to osg-...@lists.openscenegraph.org
Hi Sebastian,

You may be interested in this topic, where I discuss some workarounds to setting objects to DYNAMIC: http://forum.openscenegraph.org/viewtopic.php?t=14849

------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=65408#65408

Sebastian Messerschmidt

unread,
Oct 22, 2015, 2:54:12 PM10/22/15
to osg-...@lists.openscenegraph.org
Hi Jannek,

That's a great approach, but my dynamic part of the scene is
unfortunately rather big, so there are some concerns on having the
structure twice (I guess by deep copy you really mean copying drawables
etc. as well).
I've just stumbled upon the osgtext example which interestingly enough
uses the dynamic data variance while employing an update operation to
update the text. This seems contra-intuitive, which why I simply removed
it, and voila: I've got parallel cull, draw and gpu while updating the
stuff in an operation thread, without crashes.
So basically I need to reformulate the question: Can we use the update
operations to induce more parallelism?
P.S. While your approach might not fit anyone's need, is there some code
for an example which might be shared with the community? My biggest
concern with OSG right now is performance, or the fact that performance
loss is obfuscated.

Cheers
Sebastian

Robert Osfield

unread,
Oct 23, 2015, 4:55:52 AM10/23/15
to OpenSceneGraph Users
Hi Sebastian,

On 22 October 2015 at 19:54, Sebastian Messerschmidt <sebastian.m...@gmx.de> wrote:
 
So basically I need to reformulate the question: Can we use the update operations to induce more parallelism?

The update operation runs as part of the update traversal so is really just an alternative to update callbacks placed in the scene graph.  You could possible merge in data created by a background thread in an update operation or update callback, but this is the glue to help other background stuff work rather than creating an parallelism,
 
P.S. While your approach might not fit anyone's need, is there some code for an example which might be shared with the community? My biggest concern with OSG right now is performance, or the fact that performance loss is obfuscated.

There are many different ways to optimize scene graphs for better performance, but to optimize you first need to know what the bottlenecks are.

Without knowing more specifics about the nature of your scene graph, the performance characteristics you are getting there isn't much we can do to help.  My expectation is that the performance problems aren't simply an issue of DYNAMIC/STATIC and DrawThreadPerContext usage, my expectation is that this is only an issue because other parts of the scene graph are poorly optimized.

Robert.

Robert Milharcic

unread,
Oct 23, 2015, 7:36:56 AM10/23/15
to osg-...@lists.openscenegraph.org
On 22.10.2015 18:36, Sebastian Messerschmidt wrote:
> Hi,
>
> I have a couple of elements in the scene which data variance is set to DYNAMIC to change them thread safe.
> This will effectively kill performance as cull and draw are no longer executed in parallel. So if I'd set those elements to STATIC, where is the safe place to update them?
> For instance I have some osg::Text which has to be changed every frame. Is it safe to change it in between the update and renderingTraversals?
> Also, what about updateOperation? Is considered to modify drawables from there?
>
> Cheers
> Sebastian

Hi Sebastian,

First of all, I didn't know that cull and draw traversal can execute in parallel on a single scene. I always thought that cull and draw can only execute sequential (serial) in all available threading models. Anyway, what I know for sure is that update and draw traversal can indeed execute in parallel within some threading models, and that is the reason why we need DYNAMIC variance, to tell drawing thread it must process dynamic elements first, and then immediately allow execution of the update traversal in a main thread while STATIC elements are still being rendered in a draw thread. I also suspect that next frame cannot start before all the static+dynamic elements are rendered. If I'm correct on this one, then few DYNAMIC elements should not affect frame rate at all, because there is plenty of time to do the processing while STATIC elements are still being rendered.

You might also want to have a look at osgBullet project where calculations are done in its own thread and then the results are synchronised with a scene graph in an update traversal through a triple buffering mechanism.

I also did a benchmark recently that shows what is the effect of different threading models on a geometry intensive scene. If someone is interested in results let me know...

Best Regards,
Robert Milharcic

Robert Osfield

unread,
Oct 23, 2015, 7:46:08 AM10/23/15
to OpenSceneGraph Users
Hi Robert,

On 23 October 2015 at 12:36, Robert Milharcic <robert.m...@ib-caddy.si> wrote:
First of all, I didn't know that cull and draw traversal can execute in parallel on a single scene. I always thought that cull and draw can only execute sequential (serial) in all available threading models. Anyway,  what I know for sure is that update and draw traversal can indeed execute in parallel within some threading models, and that is the reason why we need DYNAMIC variance, to tell drawing thread it must process dynamic elements first, and then immediately allow execution of the update traversal in a main thread while STATIC elements are still being rendered in a draw thread. I also suspect that next frame cannot start before all the static+dynamic elements are rendered. If I'm correct on this one, then few DYNAMIC elements should not affect frame rate at all, because there is plenty of time to do the processing while STATIC elements are still being rendered.

With the DrawThreadPerContext and DrawThreadPerContextCullThreadPerCamera threading models the static part of the rendering can be done in parallel with the next frame.  You guess this correct.

The one thing I'd add is that the OSG itself doesn't attempt to sort DYNAMIC objects so that are drawn first.  You can set up your StateSet::RenderBinDetails to force the dynamic objects to be drawn first, but you can only do this for objects that don't affect the rendering of other objects, or are affected by what is the fame buffer already. 

In the case of text it has to be placed in the depth sorted bin which is drawn after the main opaque bin, so if there are text objects set to DYNAMIC then you stop the next frame from start till the end of dispatch of the last depth sorted dynamic object.  This may well be very near the end of the draw dispatch so you come pretty close to nullifying all the capacity for running the draw thread in parallel with the next frames' update and cull traversals.  This is likely the situation for Sebastian.

Using double buffering of Text object's is probably the best way to avoid updating a Text object while it's being drawn, allowing the Text DataVariance to remain STATIC.  Such double buffering could be done a custom Node that has two Text objects, one for current frame being updated, and one for the previous frame still being rendered.

Robert.


Robert Milharcic

unread,
Oct 23, 2015, 8:22:01 AM10/23/15
to osg-...@lists.openscenegraph.org
Hi Robert Osfield,

Thank you very much for the more in-depth explanation. I pretty sure that this additional info can benefit Sebastian and others as well (including me).

Cheers,
Robert Milharcic

Sebastian Messerschmidt

unread,
Oct 23, 2015, 1:07:36 PM10/23/15
to OpenSceneGraph Users
Hi Robert,

Thanks for the explanation. I'm still puzzled by the question which elements can be updated in the update-phase without setting them to dynamic. I always was under the impression, that the update is performed before cull/draw are actually executed.
Right now I need some thread safe "time slot" to change the scene graph in terms of inserting nodes, updating transforms etc. I guess it is totally okay to do this in the update callback/operation.
But for changes to images, text, an arrays of drawables I need to set them to DYNAMIC if I understood you correctly. So basically what I got is, that I could put the draw of those elements as far to beginning of the draw as possible.

As for the double buffering: Can it be done at drawable level? Like swapping the front/back drawable back and forth, effectively doubling the geometry/image space needed?

Cheers
Sebastian

Glenn Waldron

unread,
Oct 23, 2015, 1:16:04 PM10/23/15
to OpenSceneGraph Users
Sebastian, here is my understanding.

StateSets and Drawables must be marked as DYNAMIC is you plan to change them. That's because they are used by the rendering stage, which can overlap the next frame's update.

Everything else (scene graph structure, etc.) is safe to change during the Update traversal/callbacks.

Hope this helps.

Glenn Waldron

Sebastian Messerschmidt

unread,
Oct 23, 2015, 1:44:14 PM10/23/15
to OpenSceneGraph Users

Hi Glenn,
Sebastian, here is my understanding.

StateSets and Drawables must be marked as DYNAMIC is you plan to change them. That's because they are used by the rendering stage, which can overlap the next frame's update.
Okay, thank you for your insights.


Everything else (scene graph structure, etc.) is safe to change during the Update traversal/callbacks.

Hope this helps.
Okay, there is some idea growing, how to get the maximum out of my use case.

Robert Osfield

unread,
Oct 23, 2015, 3:36:54 PM10/23/15
to OpenSceneGraph Users
Hi Sebastian,

On 23 October 2015 at 18:07, Sebastian Messerschmidt <sebastian.m...@gmx.de> wrote:
Thanks for the explanation. I'm still puzzled by the question which elements can be updated in the update-phase without setting them to dynamic. I always was under the impression, that the update is performed before cull/draw are actually executed.
Right now I need some thread safe "time slot" to change the scene graph in terms of inserting nodes, updating transforms etc. I guess it is totally okay to do this in the update callback/operation.
But for changes to images, text, an arrays of drawables I need to set them to DYNAMIC if I understood you correctly. So basically what I got is, that I could put the draw of those elements as far to beginning of the draw as possible.

Have a look through the archives, I've written a lot about the DrawThreadPerContext model that I introduced back in the OSG-2.0 days.
 
As for the double buffering: Can it be done at drawable level? Like swapping the front/back drawable back and forth, effectively doubling the geometry/image space needed?

Yes, you'd do the double buffering at the StateSet and Drawable level.  Essentially you'd have Node's in the scene graph manage two alternatives and have each update, cull traversal for a frame work on one version, and draw to work on the other. You could double buffer whole subgraphs if you wanted. 

Within these double buffered StateSet and Drawable you can share all the arrays/stateattributes that are constant and just have the changing parts duplicated.
 
This is advanced OSG usage though, it may well be that this added complexity isn't required at all.  You haven't really told us anything about your application usage and confirmed bottlenecks to know what is the best route for your application to get better performance.

Robert.

Sebastian Messerschmidt

unread,
Oct 23, 2015, 4:03:18 PM10/23/15
to OpenSceneGraph Users
Hi Robert,
Thanks for your insights. I know the biggest concern is the structure of the scene-graph but at some point one will get stuck or will simply have to live with the sheer number of drawables spit out by external tools (I also cannot merge certain geometries due to additional attributes that need to be per-primitive)
In my scenario I have a extremely high number of nodes in the scene, which need to stay where they are in terms of structure. Let's assume that I have purely static scene. It will perform with a decent framerate (100Hz for instance).
As soon as I add some HUD-Camera containing osgText::Text nodes it will almost drop to 50Hz, as the cull and draw are no longer able to execute in parallel, by simply adding one HUD-text node.
Right now I add them as post-render camera/child of the main camera, so maybe the problem is my camera arrangement.
Apart from this, there might be the use case, where the scene is mainly static with some elements drawn on top, which are dynamic, which cause the same frame-rate degradation.
In my case, the scene loaded with the osgviewer has double the frame-rate from my viewer, which adds some dynamic text on top, which is not really satisfying. If you want some example illustrating this, I can totally provide some as a "DON'T DO THIS" example.

I'm simply was wondering, why the update overlaps the draw/cull. I was under the impression, that cull/draw can be parallelized, as long as the data rendered is updated in the update phase. But of course, there are changes which need to sent down the pipe.
I guess the optimizations I'm looking for are simply restrained by the way OpenGL works and might be relaxed with Vulkan etc

Cheers
Sebastian

Robert Osfield

unread,
Oct 26, 2015, 6:14:53 AM10/26/15
to OpenSceneGraph Users
Hi Sebastian,

On 23 October 2015 at 21:03, Sebastian Messerschmidt <sebastian.m...@gmx.de> wrote:
Thanks for your insights. I know the biggest concern is the structure of the scene-graph but at some point one will get stuck or will simply have to live with the sheer number of drawables spit out by external tools (I also cannot merge certain geometries due to additional attributes that need to be per-primitive)

Tools that "generate" data for us to render often provide the data in a form that is very poorly optimized for real-time work.  Sometimes for an easy life you just have to pull in the data and put up with it.  Sometime the peformance requirements are so strict on the rendering side you are forced to be creative and process the data into a more rendering friendly form.  The various different types of shaders we can write these days can help in one be quite creative on just how you do the final rendering.  For things like per-primitive data one can use things like textures or uniform arrays to store the data to avoid the need for separate primitives.  This can't be achieved in all cases.  Custom drawables can be another route for passing high frequency attribute data.

Whether any of these routes might be practical in your case I can't say as I don't know enough about the specifics of the nature of the data you are working with.

 

In my scenario I have a extremely high number of nodes in the scene, which need to stay where they are in terms of structure. Let's assume that I have purely static scene. It will perform with a decent framerate (100Hz for instance).
As soon as I add some HUD-Camera containing osgText::Text nodes it will almost drop to 50Hz, as the cull and draw are no longer able to execute in parallel, by simply adding one HUD-text node.
Right now I add them as post-render camera/child of the main camera, so maybe the problem is my camera arrangement.
Apart from this, there might be the use case, where the scene is mainly static with some elements drawn on top, which are dynamic, which cause the same frame-rate degradation.
In my case, the scene loaded with the osgviewer has double the frame-rate from my viewer, which adds some dynamic text on top, which is not really satisfying. If you want some example illustrating this, I can totally provide some as a "DON'T DO THIS" example.

I'm simply was wondering, why the update overlaps the draw/cull. I was under the impression, that cull/draw can be parallelized, as long as the data rendered is updated in the update phase. But of course, there are changes which need to sent down the pipe.
I guess the optimizations I'm looking for are simply restrained by the way OpenGL works and might be relaxed with Vulkan etc

Vulkan will give us a bit more flexibility to pre-prepare data before passing on to the drive so should help with dynamic data, however, this future a little while off yet.

On the OSG/OpenGL side there is still lots of things that can be done.  Even on the text side.  The osgText::Text implementation was written for OpenGL 1.1 hardware and drivers, and does a few things quite crudely because of old bottlenecks that we used to have to deal with on the hardware/driver side, alas this bring it's own bottlenecks.  Personally I'd like to be able to deprecate the OSG's Text implementation and start afresh, but backwards compatbility/time available rather restricts this.

This however needn't restrict you, if you have quite limited needs for text rendering capability you might be able to roll your own light weight text rendering implementation that copes much better with dynamic data. Either double buffering at the scene graph level or internally in a custom drawable might be a route forward.  Text is essentially just a bunch of textured quads.  As the text you want to render changes you need to provide a different set of quads positions and texcoords, but this could easily be managed by double buffering two osg::Vec3Array+Vec2Array respectively within a custom drawable.

Robert.
 
Reply all
Reply to author
Forward
0 new messages