Canvas troubleshooting: Difference between revisions
mNo edit summary |
|||
Line 443: | Line 443: | ||
.set("font", "LiberationFonts/LiberationSans-Bold.ttf") | .set("font", "LiberationFonts/LiberationSans-Bold.ttf") | ||
.set("fill", "#ff0000"); | .set("fill", "#ff0000"); | ||
group = group.createChild('group', 'level:'~i); | group = group.createChild('group', 'level:'~i); | ||
} | } |
Revision as of 13:53, 17 February 2015
The FlightGear forum has a subforum related to: Canvas |
Objective
This article is intended to be a collection of tips, code snippets and patches for helping troubleshoot Canvas related issues, i.e. debugging, profiling and optimizing Canvas based efforts. While there's usually plenty of optimization potential left in scripting space, at the end of the article, you'll also find a few C++ patches for SG/FG adding features to the Canvas system for exposing additional functionality to each Canvas/element, i.e. to help disable Canvas entirely, to track update/rendering times per Element or to get comprehensive OSG stats for each active Canvas camera.
While these features are currently intended to be a just toolbox for better understanding Canvas internals, the hope in the mid-term is also that these can be used for determining future extensions/improvements to the Canvas system as a whole.
Everybody is invited to help contribute to this article, be in the form of corrections, patches, advice or code snippets.
Background
if we don't look at improving performance right now, we cannot realistically replace the map dialog or the navdisplay
|
I'm currently working on this one (lazy updates/rendering). I have some working code, but it's very hackish. So if anyone has experiences in render-to-texture on demand in OSG any hint is appreciated
|
Canvas itself is a great piece of technology, but using it to implement complex MFDs is like asking someone to create an object-oriented and multi-threaded application using assembly language: it is possible, but very tedious and requires tons of experience and expertise. This is basically the reason why the most generic Nasal/Canvas code was typically written by people already familiar with FG/SG and OSG internals (i.e. core developers).
— Hooray (Mon Feb 02). Re: Project Farmin [Garmin Flightdeck Frame work].
(powered by Instant-Cquotes) |
Debugging Canvas code
Note For the sake of simplicity, we are going to work with a simple code snippet showing a Canvas GUI dialog. |
The following snippet is taken from Canvas Snippets, it can be copied/pasted into the Nasal Console for testing purposes.
# create a new window, dimensions are 320 x 160, using the dialog decoration (i.e. titlebar)
var window = canvas.Window.new([320,160],"dialog");
# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
# Using specific css colors would also be possible:
# myCanvas.set("background", "#ffaac0");
# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();
Canvas Status
Modifying the Property Browser to show a Canvas preview
The property browser GUI dialog can be easily extended to contain a Canvas widget for previewing/inspecting canvas texture at run-time using the following changes:
You can also create a Canvas dialog showing a preview of all active Canvas textures using 10-15 lines of code, for example see:
var (width, height) = (640,480);
var window = canvas.Window.new([width,height],"dialog");
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
var root = myCanvas.createGroup();
var (x,y) = (30,30);
var size = 128;
var padding = 30;
foreach(var c; props.globals.getNode('canvas/by-index').getChildren('texture') ) {
if (c.getPath() == myCanvas.getPath()) continue; # skip own Canvas
var child=root.createChild("image")
.setFile( 'canvas://'~c.getPath() )
.setTranslation(x,y)
.setSize(size,size);
x += size + padding;
if (x >= width) {
y += size;
x = padding;
}
}
Inspecting a Canvas Group
To inspect the structure of the root
group, all you need to do is using debug.dump()
:
debug.dump(root._node);
to write the structure of the property sub-tree to a file, you can use io.write_properties()
like this:
var filename = getprop('/sim/fg-hime')~'CanvasRootGroup.xml';
io.write_properties(filename, root._node);
Inspecting a whole Canvas
Equally, you can inspect the structure of the whole canvas by using something like this:
props.dump( myCanvas._node );
The output (shown on the console) will look something like this:
texture[2] {NONE} = nil texture[2]/status {INT} = 1 texture[2]/status-msg {STRING} = Creation pending... texture[2]/blend-source-rgb {STRING} = src-alpha texture[2]/view {DOUBLE} = 320 texture[2]/view[1] {DOUBLE} = 160 texture[2]/size {DOUBLE} = 320 texture[2]/size[1] {DOUBLE} = 160 texture[2]/placement {NONE} = nil texture[2]/placement/type {STRING} = window texture[2]/placement/id {STRING} = 1 texture[2]/blend-destination-rgb {STRING} = one-minus-src-alpha texture[2]/blend-source-alpha {STRING} = zero texture[2]/blend-destination-alpha {STRING} = one texture[2]/data-focused {LONG} = 1 texture[2]/background {STRING} = rgba(242,241,240,1) texture[2]/group {NONE} = nil
to write the structure of the whole Canvas sub-tree to a file, you can use io.write_properties()
like this:
var filename = getprop('/sim/fg-home')~'/Export/WholeCanvas.xml';
io.write_properties(filename, props.wrap(myCanvas._node));
The output, written to $FG_HOME/Export/WholeCanvas.xml, would look like this:
<?xml version="1.0"?>
<PropertyList>
<status type="int">1</status>
<status-msg type="string">Creation pending...</status-msg>
<blend-source-rgb type="string">src-alpha</blend-source-rgb>
<view type="double">320</view>
<view n="1" type="double">160</view>
<size type="double">320</size>
<size n="1" type="double">160</size>
<placement>
<type type="string">window</type>
<id type="string">1</id>
</placement>
<blend-destination-rgb type="string">one-minus-src-alpha</blend-destination-rgb>
<blend-source-alpha type="string">zero</blend-source-alpha>
<blend-destination-alpha type="string">one</blend-destination-alpha>
<data-focused type="long">1</data-focused>
<background type="string">rgba(242,241,240,1)</background>
</PropertyList>
Do note that this is an entirely valid PropertyList encoded XML file, which means that it could be also used to unserialize the Canvas, i.e. reading it back into FG to create a new valid Canvas and display the texture there. The main limitation here being that this would be a "passive" Canvas only, without any listeners/timers etc being bound obviously, because just the visual aspects of the original Canvas texture would be re-created, but none of the logics used to originally create/animate the Canvas (think PFD, ND, CDU or Map):
var filename = getprop('/sim/fg-home')~'/Export/WholeCanvas.xml';
io.read_properties(filename, '/canvas/by-index/texture[100]');
However, even with those restrictions, serializing a Canvas to an XML file may still be useful - i.e. to make bug reports attached with with reproducible settings using a simple XML file.
Profiling Canvas code
Optimizing Canvas code
Useful C++ changes
Note The following patches are intended to help better understand what's going on behind the scenes. They are intended to be regularly rebased onto SG/FG 'next'. In the mid-term, our hope is to ensure that people working on Nasal/Canvas related features get a better understanding about when, where and why Canvas performance is affected, and how to restructure their code accordingly to make better use of Canvas - as well as identify potential C++ extensions to further improve Canvas performance. |
Adding support for wireframe mode
To enable wireframe mode, we only need to change the osg::StateSet accordingly. Internally, each Canvas element contains a osg::MatrixTransform
[2] node - there's a virtual method getOrCreateStateSet()
exposed returning the StateSet for each element, so that we can directly it:
osg::StateSet *state = getOrCreateStateSet();
osg::PolygonMode *polyModeObj;
polyModeObj = dynamic_cast< osg::PolygonMode* >
( state->getAttribute( osg::StateAttribute::POLYGONMODE ));
if ( !polyModeObj ) {
polyModeObj = new osg::PolygonMode;
state->setAttribute( polyModeObj );
}
polyModeObj->setMode( osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
This change could be applied globally for each Canvas, or optionally per Canvas Element - the latter is more useful for troubleshooting things without affecting all active Canvas textures.
The corresponding diff for $SG_SRC adding this as a property-controlled option, looks like this:
Adding support for disabling textures
Analogous to the previous example, we can modify the osg::StateSet for a Canvas/CanvasElement to disable texturing, too:
osg::StateSet *state = getOrCreateStateSet();
state->setTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::PROTECTED | osg::StateAttribute::OFF);
The diff adding this as a run-time option to the Canvas system, looks like this:
Serializing a Canvas to disk (as raster image)
The following C++ code can be used to easily serialize a Canvas to disk - this can be useful for making bug reports, without having to take/edit a full screen shot, it's based on the existing screen shot code that's been slightly adapted:
Dumping Canvas scene graphs to disk
Here's the required SimGear changes to add a new method for dumping the scene graph for each Canvas element to a file (simply invoke the method on the top-level group to dump the whole canvas):
diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx
index 6d8c930..6e0dc9b 100644
--- a/simgear/canvas/elements/CanvasElement.cxx
+++ b/simgear/canvas/elements/CanvasElement.cxx
@@ -222,6 +222,14 @@ namespace canvas
}
}
+ void Element::dumpSceneGraph(const std::string&filename)
+{
+ osgDB::writeNodeFile(*_transform.get(), filename);
+ SG_LOG(SG_GENERAL, SG_ALERT, "dumping Canvas scene graph to file:"<<filename);
+
+}
+
+
//----------------------------------------------------------------------------
void Element::onDestroy()
{
diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx
index 37e12de..5a2cec8 100644
--- a/simgear/canvas/elements/CanvasElement.hxx
+++ b/simgear/canvas/elements/CanvasElement.hxx
@@ -25,6 +25,8 @@
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/misc/stdint.hxx> // for uint32_t
+#include <osgDB/WriteFile>
+
#include <osg/BoundingBox>
#include <osg/MatrixTransform>
@@ -212,6 +214,8 @@ namespace canvas
return ElementPtr( new Derived(canvas, node, style, parent) );
}
+
+ void dumpSceneGraph(const std::string& filename);
protected:
enum Attributes
@@ -276,6 +280,7 @@ namespace canvas
return false;
}
+
/**
* Register a function for setting a style specified by the given property
*
Here's the FlightGear part for the SimGear changes above exposing the new API via ELEMENT.dumpSceneGraph()
:
diff --git a/src/Scripting/NasalCanvas.cxx b/src/Scripting/NasalCanvas.cxx
index 4fa7652..ffa8211 100644
--- a/src/Scripting/NasalCanvas.cxx
+++ b/src/Scripting/NasalCanvas.cxx
@@ -492,7 +492,8 @@ naRef initNasalCanvas(naRef globals, naContext c)
.method("setFocus", &sc::Element::setFocus)
.method("dispatchEvent", &sc::Element::dispatchEvent)
.method("getBoundingBox", &sc::Element::getBoundingBox)
- .method("getTightBoundingBox", &sc::Element::getTightBoundingBox);
+ .method("getTightBoundingBox", &sc::Element::getTightBoundingBox)
+ .method("dumpSceneGraph", &sc::Element::dumpSceneGraph);
And here's the Nasal code to test the new API via the Nasal Console:
var window = canvas.Window.new([320,160],"dialog");
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
var root = myCanvas.createGroup();
var myGroup = root.createChild('group', 'my group');
var textNode = myGroup.createChild("text")
.setText("Some text")
.setTranslation(20, 200)
.set("alignment", "left-center")
.set("character-size", 15)
.set("font", "LiberationFonts/LiberationSans-Bold.ttf")
.set("fill", "#ff0000");
textNode.dumpSceneGraph('textnode.osg');
This is what a simple Canvas sub-scenegraph may look like:
MatrixTransform {
nodeMask 0xffffffff
cullingActive TRUE
StateSet {
rendering_hint DEFAULT_BIN
renderBinMode OVERRIDE
binNumber 0
binName PreOrderBin
}
referenceFrame RELATIVE
Matrix {
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
}
num_children 1
Geode {
nodeMask 0xffffffff
cullingActive TRUE
num_drawables 1
osgText::Text {
UniqueID Text_0
StateSet {
UniqueID StateSet_1
rendering_hint TRANSPARENT_BIN
renderBinMode USE
binNumber 10
binName DepthSortedBin
}
supportsDisplayList FALSE
useDisplayList FALSE
useVertexBufferObjects FALSE
color 1 0 0 1
font /home/hooray/sources/fgroot/Fonts/LiberationFonts/LiberationSans-Bold.ttf
fontResolution 32 32
characterSize 15 1
characterSizeMode OBJECT_COORDS
alignment LEFT_CENTER
rotation 1 0 0 6.12303e-17
layout LEFT_TO_RIGHT
position 0 0 0
drawMode 1
BoundingBoxMargin 0
BoundingBoxColor 0.94902 0.945098 0.941176 1
text "Some text"
backdropType NONE
backdropHorizontalOffset 0.07
backdropVerticalOffset 0.07
backdropColor 0 0 0 1
backdropImplementation NO_DEPTH_BUFFER
colorGradientMode SOLID
colorGradientTopLeft 1 0 0 1
colorGradientBottomLeft 0 1 0 1
colorGradientBottomRight 0 0 1 1
colorGradientTopRight 1 1 1 1
}
}
}
To dump the scene graph of the whole Canvas, just invoke the dumpSceneGraph()
method on the top-level root node like this:
root.dumpSceneGraph('my-canvas-scenegraph.osg');
Now, let's consider another example - this time actually building a little scene graph hierarchy with multiple groups and nodes, using a for loop:
var window = canvas.Window.new([320,160],"dialog");
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
var root = myCanvas.createGroup();
var group = root.createChild('group', 'level:0');
for (var i=0;i<=5;i+=1) {
var textNode = group.createChild("text")
.setText("text node in group #1"~i)
.setTranslation(20, 200)
.set("alignment", "left-center")
.set("character-size", 15)
.set("font", "LiberationFonts/LiberationSans-Bold.ttf")
.set("fill", "#ff0000");
group = group.createChild('group', 'level:'~i);
}
This will create a new group for each text node, added to its parent group. Once we inspect the scene graph, we'll see that it contains quite a bit of redundant state. However, this is where osg::Optimizer can shine, by optimizing our scene graph to get rid of redundant state sets and flatten the scene graph.
Work in progress This article or section will be worked on in the upcoming hours or days. See history for the latest developments. |
Next, we're going to explore dumping complex scene graphs to disk, i.e. those created by Canvas MFDs like the NavDisplay - so that we can check what the scene graph looks like after optimizing/simplifying it using osg::Simplifier and osg::Optimizer respectively, while also investigating which additional OSG properties may be helpful to expose for the optimizer/simplifier to provide satisfying results.
Tracking update frequency per Canvas/Element
This is intended to help people better understand how frequently a Canvas is being updated, which happens automatically once a property in the sub-tree is modified (usually involving timers and listeners):
Tracking update/rendering duration per Canvas/Element
This is a fairly useful thing to do, while also being pretty easy - all that is needed are two SimGear classes:
- PropertyObject<>
- SGTimeStamp
These two can be used to time the duration of each method invocation and automatically write it to the property tree. Which in turn can then be viewed/processed by the property browser and/or Nasal respectively to identify comparatively expensive canvas elements/groups.
Adding draw masks for Canvas
There's so called "draw-masks" which are property-controlled switches for enabling/disabling rendering of certain scene details, including 1) scenery/terrain, 2) aircraft, 3) models, 4) clouds. This can be used for troubleshooting performance issues - you can basically toggle individual scene graphs on/off, to see if/how performance is affected. For example, if performance improves dramatically by disabling the terrain, you are mainly affected scenery complexity. Equally, disabling the (main) aircraft, will tell you if it's the complexity of the 777 3D model (cockpit).
This would work analogous to our existing "draw-masks", (as per the minimal startup profile detailed on the wiki) - basically, more and more features would get dedicated properties with draw-masks to disable/enable rendering and optionally customize things - this isn't too difficult to do, and it doesn't necessarily involve touching tons of STG/BTG files - it's mainly a change that would involve SimGear/FG, so that buildings (and other heavy stuff) would be loaded into dedicated sub-scene graph that can be easily disabled using an osg::switch node - here's a code snippet demonstrating the concept by disabling rendering of the sun/sky.
We've used these for doing some more profiling on the netbook that I mentioned to help better understand where performance is spent, not just in terms of subsystems, but also at the rendering level (PUI!) -this is line with James' original "draw-masks" commit and uses the same property location for new draw masks - I think this could be a useful extension for various reasons, because we can simply keep stuff disabled while booting - and also use it for troubleshooting.
diff --git a/src/Viewer/renderer.cxx b/src/Viewer/renderer.cxx
index 2c4d5c7..d3ba9a6 100644
--- a/src/Viewer/renderer.cxx
+++ b/src/Viewer/renderer.cxx
@@ -82,7 +82,10 @@
#include <simgear/scene/tgdb/pt_lights.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/structure/OSGUtils.hxx>
+
#include <simgear/props/props.hxx>
+#include <simgear/props/propertyObject.hxx>
+
#include <simgear/timing/sg_time.hxx>
#include <simgear/ephemeris/ephemeris.hxx>
#include <simgear/math/sg_random.h>
@@ -380,9 +383,60 @@ public:
static bool scenery_enabled;
};
-
bool FGScenerySwitchCallback::scenery_enabled = false;
+// update callback for the sky switch node
+struct FGSkySwitchCallback : public osg::NodeCallback {
+ FGSkySwitchCallback() : _enabled("/sim/rendering/draw-mask/sky") {}
+
+ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+ {
+ assert(dynamic_cast<osg::Switch*>(node));
+ osg::Switch* sw = static_cast<osg::Switch*>(node);
+
+ sw->setValue(0, _enabled);
+ if (!_enabled)
+ return;
+ traverse(node, nv);
+ }
+private:
+simgear::PropertyObject<bool> _enabled;
+};
+
+// update callback for the GUI (old) switch node
+struct FGOldGUISwitchCallback : public osg::NodeCallback {
+ FGOldGUISwitchCallback() : _enabled("/sim/rendering/draw-mask/old-gui") {}
+ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+ {
+ assert(dynamic_cast<osg::Switch*>(node));
+ osg::Switch* sw = static_cast<osg::Switch*>(node);
+
+ sw->setValue(0, _enabled);
+ if (!_enabled)
+ return;
+ traverse(node, nv);
+ }
+private:
+simgear::PropertyObject<bool> _enabled;
+};
+
+// update callback for the GUI (old) switch node
+struct FGSunlightSwitchCallback : public osg::NodeCallback {
+ FGSunlightSwitchCallback() : _enabled("/sim/rendering/draw-mask/sunlight") {}
+ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+ {
+ assert(dynamic_cast<osg::Switch*>(node));
+ osg::Switch* sw = static_cast<osg::Switch*>(node);
+
+ sw->setValue(0, _enabled);
+ if (!_enabled)
+ return;
+ traverse(node, nv);
+ }
+private:
+simgear::PropertyObject<bool> _enabled;
+};
+
FGRenderer::FGRenderer() :
_sky(NULL),
_ambientFactor( new osg::Uniform( "fg_SunAmbientColor", osg::Vec4f() ) ),
@@ -1483,7 +1537,8 @@ FGRenderer::setupView( void )
lightSource->setLocalStateSetModes(osg::StateAttribute::ON);
lightSource->setUpdateCallback(new FGLightSourceUpdateCallback);
_viewerSceneRoot->addChild(lightSource);
-
+
+
// we need a white diffuse light for the phase of the moon
osg::ref_ptr<LightSource> sunLight = new osg::LightSource;
sunLight->setName("sunLightSource");
@@ -1501,7 +1556,11 @@ FGRenderer::setupView( void )
sunLight->addChild(skyGroup);
if ( _classicalRenderer ) {
- _root->addChild(sunLight);
+ osg::Switch *sw = new osg::Switch;
+ sw->setName("sunLightSwitch");
+ sw->setUpdateCallback( new FGSunlightSwitchCallback);
+ sw->addChild(sunLight);
+ _root->addChild(sw);
}
osg::Group* sceneGroup = globals->get_scenery()->get_scene_graph();
@@ -1531,19 +1590,25 @@ FGRenderer::setupView( void )
stateSet->setAttributeAndModes(fog);
stateSet->setUpdateCallback(new FGFogEnableUpdateCallback);
+
// plug in the GUI
osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
if (guiCamera) {
+ osg::Switch* sw = new osg::Switch;
+ sw->setName("OldGUISwitch");
+ sw->setUpdateCallback(new FGOldGUISwitchCallback);
+
osg::Geode* geode = new osg::Geode;
geode->addDrawable(new SGHUDDrawable);
geode->addDrawable(new SGPuDrawable);
+ sw->addChild(geode);
+ sw->addChild( FGPanelNode::create2DPanelNode() );
// Draw first (eg. before Canvas GUI)
- guiCamera->insertChild(0, geode);
- guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
+ guiCamera->insertChild(0, sw);
}
- osg::Switch* sw = new osg::Switch;
+ osg::Switch *sw = new osg::Switch;
sw->setName("scenerySwitch");
sw->setUpdateCallback(new FGScenerySwitchCallback);
sw->addChild(_root.get());
@@ -1552,8 +1617,13 @@ FGRenderer::setupView( void )
// because, in theory, they don't want the same default state set
// as the rest of the scene. This may not be true in practice.
if ( _classicalRenderer ) {
- _viewerSceneRoot->addChild(_sky->getCloudRoot());
- _viewerSceneRoot->addChild(FGCreateRedoutNode());
+ sw = new osg::Switch;
+ sw->setName("skySwitch");
+ sw->setUpdateCallback(new FGSkySwitchCallback);
+ sw->addChild(_sky->getCloudRoot());
+ sw->addChild(FGCreateRedoutNode());
+
+ _viewerSceneRoot->addChild(sw);
}
// Attach empty program to the scene root so that shader programs
@@ -1625,7 +1695,8 @@ FGRenderer::update( ) {
: osg::Vec4(0, 0, 0, 1);
camera->setClearColor(clear_color);
- updateSky();
+ if (fgGetBool("/sim/rendering/draw-mask/sky",true))
+ updateSky();
// need to call the update visitor once
_frameStamp->setCalendarTime(*globals->get_time_params()->getGmt());
diff --git a/preferences.xml b/preferences.xml
index dbbbe2a..bc35ef3 100644
--- a/preferences.xml
+++ b/preferences.xml
@@ -224,10 +224,13 @@ Started September 2000 by David Megginson, david@megginson.com
non-cockpit + aircraft elements. Use draw-mask instead. -->
<draw-otw type="bool">true</draw-otw>
<draw-mask>
+ <sunlight type="bool">true</sunlight>
<terrain type="bool">true</terrain>
<models type="bool">true</models>
<aircraft type="bool">true</aircraft>
<clouds type="bool">true</clouds>
+ <sky type="bool">true</sky>
+ <old-gui type="bool">true</old-gui>
</draw-mask>
<shadows-ac type="bool" userarchive="y">false</shadows-ac>
We are probably going to add a few more draw-masks, also for Canvas - not because I believe that we need to run FG without Canvas :lol: , but to provide stats/evidence for benchmarking purposes, i.e. we can tell people to use those draw masks to disable all CanvasGUI/CanvasScenery/CanvasAircraft rendering and watch their frame rate/spacing accordingly. The following patch is intended to help determine the rendering overhead of Canvas by adding optional draw-masks for disabling rendering of Canvas textures via their corresponding placements (aircraft, scenery and GUI):
Under the hood, the corresponding Nasal/C++ code updating Canvas textures would obviously still be running - it would just be rendering that is explicitly disabled here.
For the FGCanvas mode, this just means that sky/sunlight and PUI rendering can be completely disabled for even better performance/appearance, i.e. Nasal/Canvas are up and running in under 5 seconds here normally. Likewise, this is a good thing for debugging and regression testing, i.e. to keep certain rendering features completely disabled - for example so that only Canvas related OSG/OpenGL calls show up in the gDebugger profile, i.e. much more fine-grained info, without having to patch FG.
Hooking up Canvas to the Osg Stats
Using Osg::Optimizer for complex Canvases
Once a complex Canvas texture (MFD) has been set up, the following patch can be used for invoking osgUtil::Optimizer() to finalize and optimize the scene graph:
Experimenting with Osg::Simplifier
We can extend each Canvas element inheriting from the CanvasElement base class to have its own osg::Simplifier sub-class implementing 2D simplification using CGAL. Such nodes (CanvasGroups) could in turn be used for procedurally creating LOD nodes for different viewer distances.
Tracking RAM utilization per Canvas
Tracking update/cull and draw per Canvas or Element
Patching CanvasMgr to use CompositeViewer internally
With Canvas textures typically not rendering any scene/scenery data (terrain), we don't necessarily need those Cameras to render within the main viewer. This is intended to help better leverage OSG-level concurrency support by using a separate CompositeViewer instance to render Canvas textures, without having to change the main OSGViewer and its threading mode: