CompositeViewer support: Difference between revisions

Added note about being able to configure extra view windows using properties.
(Added note about being able to configure extra view windows using properties.)
(13 intermediate revisions by one other user not shown)
Line 21: Line 21:


=== Status updates ===
=== Status updates ===
* 2020-12-12: One can now [https://sourceforge.net/p/flightgear/mailman/message/37174948/ configure extra view windows using properties].


* 2020-11-21: Merged into next: {{flightgear commit|f62e5b9ce3462758b48f4c711eb7c3bf4bcc7061|CompositeViewer:Support for multiple view windows using osgViewer::CompositeViewer}} <ref>https://sourceforge.net/p/flightgear/mailman/message/37158652/</ref>
* 2020-11-21: Merged into next: {{flightgear commit|f62e5b9ce3462758b48f4c711eb7c3bf4bcc7061|CompositeViewer:Support for multiple view windows using osgViewer::CompositeViewer}} <ref>https://sourceforge.net/p/flightgear/mailman/message/37158652/</ref>
Line 47: Line 49:
* for the time being, use of reset/re-init and aggressive OSG threading options seems to cause stability problems even without having cloned any views <ref>https://forum.flightgear.org/viewtopic.php?f=6&t=38334</ref>
* for the time being, use of reset/re-init and aggressive OSG threading options seems to cause stability problems even without having cloned any views <ref>https://forum.flightgear.org/viewtopic.php?f=6&t=38334</ref>
* Using OpenSceneGraph-3.6 causes problems elsewhere: [[OSGText Issues]] (under investigation as of 11/2020) <ref>https://sourceforge.net/p/flightgear/mailman/message/37157550/</ref>
* Using OpenSceneGraph-3.6 causes problems elsewhere: [[OSGText Issues]] (under investigation as of 11/2020) <ref>https://sourceforge.net/p/flightgear/mailman/message/37157550/</ref>
* the [[PUI]] based fps/frame spacing counter implemented in $FG_ROOT/Nasal/gui.nas could probably be replaced by a Canvas implementation rendering to the [[Canvas_Snippets#Accessing_the_Canvas_Desktop|Canvas desktop]] using the [[Tooltips]] backend <ref>https://sourceforge.net/p/flightgear/mailman/message/37170351/</ref>
* for some people there seem to be [[Compositor]] related event handling regressions which we should keep track of once we begin supporting events per view <ref>https://sourceforge.net/p/flightgear/mailman/message/37170307/</ref>


=== Current limitations ===
=== Current limitations ===
Line 72: Line 76:


== Background  ==
== Background  ==
<!--
{{WIP}}
{{WIP}}
-->
The natural way to manage a application that was two views on to two different scenes is to use a osgViewer::View for each separate scene, and then a osgViewer::CompositeViewer to manage these two scenes.  These two views can share the same GraphicsWindow, or have their own.  They may even be added/removed from the CompositeViewer, or have their rendering toggled on/off via NodeMask's on the master Camera for each View.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg24466.html</ref>
The natural way to manage a application that was two views on to two different scenes is to use a osgViewer::View for each separate scene, and then a osgViewer::CompositeViewer to manage these two scenes.  These two views can share the same GraphicsWindow, or have their own.  They may even be added/removed from the CompositeViewer, or have their rendering toggled on/off via NodeMask's on the master Camera for each View.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg24466.html</ref>


Line 181: Line 187:
thread one two, the state for each context should be kept local to
thread one two, the state for each context should be kept local to
each one.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg03788.html</ref>
each one.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg03788.html</ref>
running multiple windows multi-threaded will give you the
best performance, the OSG is designed for this usage model, and most
easily set up using the native windowing support that the OSG
provides.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg16632.html</ref>
As long as your run the viewer multithreaded the OSG will use a
barrier so that each graphics thread waits at the end of draw
dispatch, then once all the threads join this barrier then all move on
together and then call swap buffers.  This is done to try and achieve
synchronized swapping, however, it's not a full proof scheme as it
doesn't use any low level driver and hardware synchronization.
Extensions to some OpenGL drivers exist to enable the low level
synchronisation, such as swap groups, swap ready and gen lock.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg54571.html</ref>


* osgViewer::CompositeViewer is designed for applications that have multiple Views. The only thing to be careful of is when you are adding and removing View's from the CompositeViewer you should do is calling stopThreading() on the viewer prior to adding or removing views, then call startThreading() afterwards.  If you are running SingleThreaded or CullDrawThreadPerContext you won't need to worry about stop and starting threads.<ref>https://groups.google.com/d/msg/osg-users/7OojxLpBGdw/mzorZe3rKwEJ</ref>
* osgViewer::CompositeViewer is designed for applications that have multiple Views. The only thing to be careful of is when you are adding and removing View's from the CompositeViewer you should do is calling stopThreading() on the viewer prior to adding or removing views, then call startThreading() afterwards.  If you are running SingleThreaded or CullDrawThreadPerContext you won't need to worry about stop and starting threads.<ref>https://groups.google.com/d/msg/osg-users/7OojxLpBGdw/mzorZe3rKwEJ</ref>
Line 188: Line 210:
* as long as you have two GPU's the most efficient way to drive them should be multi-threaded - there is a caveat though, hardware and drivers aren't always up to scratch, and even then they should be able to manage the multi-threads and multi-gpus seemless they fail too.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg18332.html</ref>
* as long as you have two GPU's the most efficient way to drive them should be multi-threaded - there is a caveat though, hardware and drivers aren't always up to scratch, and even then they should be able to manage the multi-threads and multi-gpus seemless they fail too.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg18332.html</ref>
* Cull and draw can only run in a parallel once all the dynamic geometry has been dispatched, otherwise the draw will be dispatching data that is being modified by the next frames update and cull traversals. Perhaps you have some dynamic geometry or StateSet's that are holding back the next frame. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg36238.html</ref>
* Cull and draw can only run in a parallel once all the dynamic geometry has been dispatched, otherwise the draw will be dispatching data that is being modified by the next frames update and cull traversals. Perhaps you have some dynamic geometry or StateSet's that are holding back the next frame. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg36238.html</ref>
 
* if you are using a single graphics card for best performance one usually tries to use a single graphics window and have two cameras or more share this context.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg54512.html</ref>


The ThreadPerCamera is just shorthand for
The ThreadPerCamera is just shorthand for
Line 204: Line 226:


=== Sharing scenes ===
=== Sharing scenes ===
The osgViewer has a mechanism for avoid multiple traversals of shared
scene graphs if mutlple View's share the same root node of the scene
graph.  If shared component isn't the topmost node then the OSG has no
straight forward way to know whether a subgraph has been traversed or
not that frame.  One could implement a mechanism to avoid this
visiting a node multiple times in one frame but it would be really
costly to do, an expense that would only be a benefit for a very small
number of users, but would slow performance for everyone else.
If you have a shared subgraph that you don't want traversed multiple
times per frame then use an UpdateCallback that has a frameNumber
member variable that keep track of the the frameNumber (use
NodeVisitor::getFrameStamp()'s FrameNumber) of the last traversal,
when a traversal calls the update callback you only traverse the
subgraph if the frameNumber is different and then set the frameNumber
to the present frame,  if the frameNumber is the same then you just
return immediately.  This custom UpdateCallback you'd place as high as
you can in your scene graph to make sure the traversal stops as soon
as possible.
Another approach is to move this frameNumber tracking into your
existing update callbacks, and simple return right away with the
frameNumber is the same.  This requires a small tweak to the callbacks
but is such a small change it's generally pretty easy to integrate.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg74476.html</ref>
* Each View has one scene graph, and can share its scene graph between other instances of View. The View can also share the same GraphicsWindow, or have its own GraphicsWindow. The View also has a master Camera, and an optional list of slave Camera so you can scale from simple views up to complete distortion correction or multiple display output setups. Each View has its own event handlers and cameras handlers. Its extremely flexible and configurable. <ref>https://groups.google.com/d/msg/osg-users/kmSNMm6w008/zBBunL-VRJsJ</ref>
* Each View has one scene graph, and can share its scene graph between other instances of View. The View can also share the same GraphicsWindow, or have its own GraphicsWindow. The View also has a master Camera, and an optional list of slave Camera so you can scale from simple views up to complete distortion correction or multiple display output setups. Each View has its own event handlers and cameras handlers. Its extremely flexible and configurable. <ref>https://groups.google.com/d/msg/osg-users/kmSNMm6w008/zBBunL-VRJsJ</ref>
* sharing a scene between View's is OK within one CompositeViewer as they will Views on the same scene will share the same FrameStamp i.e. there will be all at the same point in time. Sharing one scene between multiple Viewers will hit up against the problem that in one set of traversals the scene graph is one time and then the traversals from the other viewer will try to change the time back - and likely to cause a mess.  This timing issue isn't likely to cause problems with high level rendering though - it should just mess up things like particle systems and sequences.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg07305.html</ref>
* sharing a scene between View's is OK within one CompositeViewer as they will Views on the same scene will share the same FrameStamp i.e. there will be all at the same point in time. Sharing one scene between multiple Viewers will hit up against the problem that in one set of traversals the scene graph is one time and then the traversals from the other viewer will try to change the time back - and likely to cause a mess.  This timing issue isn't likely to cause problems with high level rendering though - it should just mess up things like particle systems and sequences.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg07305.html</ref>
Line 223: Line 269:
trivial, but with encapsulating all this functionality it has to make
trivial, but with encapsulating all this functionality it has to make
some assumptions about the way it's used<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg36853.html</ref>
some assumptions about the way it's used<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg36853.html</ref>
A sharing OpenGL contexts doesn't mean actually sharing of the
context, its just sharing some data between contexts, so you don't
have a "common OpenGL context", you have two separate OpenGL contexts
that are sharing display lits/texture objects etc.
Each GraphicsWindow "is a" GraphicsContext which maps directly to a
single OpenGL graphics context.  Each OpenGL graphics context has its
own state machine which is mapped by a single osg::State object -
which you'll find on the GraphicsContext.
Sharing of display lists/texture objects between contexts on the OSG
just requires you to set the State::ContextID to same value.  If the
GraphicsWindow implementation is set up correctly then it'll
automatically assign the same ContextID for each of the seperate
osg::State objects.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg02505.html</ref>


* It's possible to share contexts in the OSG [...]  As for general desirability of share GL objects between contexts, yes it can reduce memory usage, but it forces you to use the OSG single threaded otherwise two contexts will be contended for the same resources that deliberately aren't mutex locked for performance reasons.  There is also on a limited set of cases where drivers/hardware will actually share OpenGL contexts. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg32676.html</ref>
* It's possible to share contexts in the OSG [...]  As for general desirability of share GL objects between contexts, yes it can reduce memory usage, but it forces you to use the OSG single threaded otherwise two contexts will be contended for the same resources that deliberately aren't mutex locked for performance reasons.  There is also on a limited set of cases where drivers/hardware will actually share OpenGL contexts. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg32676.html</ref>
Line 230: Line 292:
* Sharing contexts is also something the forces a few limits on how you use the graphics contexts, such as it's only really safe to use them single threaded. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg16791.html</ref>
* Sharing contexts is also something the forces a few limits on how you use the graphics contexts, such as it's only really safe to use them single threaded. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg16791.html</ref>
* If you are creating new graphics contexts and applying and old scene graph to it then you can't use the Texture::setUnRefImageDataAfterApply(true) feature of osg::Texture as this will discard the imagery once it's applied to all the graphics contexts that it knows about. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg32658.html</ref> The typical problem is that the scene graph has been set up to unref texture images after apply so when it comes to reloading the texture images there aren't the to download. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg34284.html</ref>
* If you are creating new graphics contexts and applying and old scene graph to it then you can't use the Texture::setUnRefImageDataAfterApply(true) feature of osg::Texture as this will discard the imagery once it's applied to all the graphics contexts that it knows about. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg32658.html</ref> The typical problem is that the scene graph has been set up to unref texture images after apply so when it comes to reloading the texture images there aren't the to download. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg34284.html</ref>
=== Swap Buffers ===
OpenGL drivers have a FIFO, your app fills the fifo with tokens and
data, at the end of the frame you send in a swap buffers token and
this goes into the FIFO with everything else.  Normally the swap
buffers call itself doesn't block (although some implementations do
this), but the FIFO itself can only be cleared at the rate for one
swap buffers call per frame so it'll fill and once filled up it will
effectively block until previous frame was begun dispatching.  The
driver may allow several frames worth data in the fifo before block,
this is driver dependent, and also dependent on just how data you have
to pass to OpenGL- if you have massive models the CPU will be block on
the FIFO right on the same frame rather than more than one frame begin
backed in the FIFO.
The end result of this is simpler though - put vsync on, and your
frame loop will block and should iddle while its waiting for the FIFO
to begin accepting new data. <ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg04323.html</ref>
The driver will be queuing up multiple frames in the FIFO, something it
does to help improve the framerate, but increases frame latency.
We aren't powerless in this though, modern drivers and hardware support
putting fences into the pipeline and waiting on these to be completed on
the GPU.  In the svn/trunk vesion of the OSG you'll find a swap buffers
SyncSwapBuffersCallback implementation that does this for you.  You can
enable this via the env var OSG_SYNC_SWAP_BUFFERS=ON, or --sync on the
command line for examples like osgviewer.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg68525.html</ref>
There is an OpenGL extension that supports syncronizing of swap
buffers across multiple graphics contexts that allows you to assigns
contexts to swap groups<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg32951.html</ref>
some graphics drivers will do swap buffers in sequence if you have
multiple windows being rendered too, with each swap doing a vsync,
which ends up with each window blocking till the end of each screen
refresh.  Use of the swap groups extension would be one way around
this issue<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg22273.html</ref>
The GraphicsContext::swapBuffers() is
normally what does the swap buffers and then calls
GraphicsContext::clear(), with GraphicsWindowEmbedded::swapBuffers()
it's a non op, because there is no way it can do a swap buffers as it
doesn't actually know about a real graphics context.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg21395.html</ref>


=== Image Sharing ===
=== Image Sharing ===
Line 238: Line 344:


=== Multiple Viewers ===
=== Multiple Viewers ===
One possible
solution would be to have two separate viewers, each running their own
frame() when required - you can't mix scene graphs or graphics contexts
in this case though.<ref>https://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg30983.html</ref>
If you don't want the main rendering loop to wait for the rendering of all these extra views then
If you don't want the main rendering loop to wait for the rendering of all these extra views then
you'll need to use a separate viewer(or compositeviewer) with it's own
you'll need to use a separate viewer(or compositeviewer) with it's own
226

edits