From FlightGear wiki
Jump to navigation Jump to search
Canvas Subsystem
Started in 05/2012 (Available since FlightGear 2.8)
Description Dynamic 2D drawing at runtime using the property tree and scripting (for instruments, HUDs, GUIs)
Maintainer(s) TheTom
Contributor(s) TheTom (since 02/2012)
Status Under active development as of 02/2013

In FlightGear, a Canvas is a dynamically created image (OpenGL texture) that can be created and modified (drawn to) at runtime by using the Property Tree, i.e. via the built-in scripting language Nasal and setting a few properties via setprop() or its object-oriented wrapper props.nas.

Canvas is all about rendering to a texture and updating it dynamically at run-time by modifying a sub-tree in the property tree that represents the texture (RTT) - its primary rendering primitives are:

  • text (via osgText)
  • vector graphics (via shivaVG/OpenVG, subject to change as of 03/2022, see Shiva Alternatives)
  • static raster images (or dynamic images via osg::Image represented as another Canvas)
  • groups/maps - for grouping/nesting elements to arbitrary depths, and selectively controlling/transforming/clipping each sub-tree

Canvas itself maintains a FBO (frame buffer object ) for each texture, which is also the mechanism in use by Rembrandt. In other words, Canvas is an abstraction mechanism for RTT/FBO management via conventional FlightGear properties. A Canvas texture can optionally also respond to common GUI events to support keyboard/mouse interactions (e.g. drag&drop, hot-keys).[1]

It is a good idea to learn more about OOP (object oriented programming) and how to use that in Nasal.

Next, you will probably want to look at some of the existing Canvas sources - i.e. $FG_ROOT/Nasal/canvas/* (especially api.nas)

And then, you need to understand that Canvas works in form of primitives that form the primitives/ "building blocks" for all instruments.

Namely: text handling, raster images, OpenVG Paths

Everything else is built on top of these primitives (well, except for Maps, but that's a different topic). For learning purposes, you could play with the examples that can be found in the Canvas snippets article on the wiki: Canvas Snippets Note that you need to understand OOP, and especially method chaining, to understand how those examples work behind the scenes.[2]

Canvas textures can be used for a number of different purposes, such as creating fully-scripted avionics (airliner/bizjet -style/MFD glass cockpits) but also custom HUDs and custom GUI textures, even fully interactive GUI widgets (scheduled for FG 4.2+) and interactive scenery elements (e.g. VDGS).

You can use any arbitrary shape to place a canvas onto. For a HUD you just need to create an arbitrarily shaped polygon with your favorite 3d modeling software and place a canvas onto it. As everything drawn onto the canvas goes to an offscreen texture first, only the parts placed onto the 3d model are shown.

You can also draw transparent paths. With the correct blending mode you can just replace all existing contents. It's not named mask, but you can use every canvas element as a mask:

# replace everything already drawn with the pixels of the path
path.set("blend-source", "one")
    .set("blend-destination", "zero");

Currently only rectangular clipping regions are possible (and will be for the next time). You can still get rounded corners if you just draw them in black (or any other background color) on top of the clipped region.

The canvas subsystem is entirely implemented on top of the FlightGear Property Tree, it makes use of property listeners to watch a sub tree of the property tree for canvas-related events (e.g. drawing commands or events to load an image from disk), so that textures can be dynamically instantiated and modified by setting properties in the property tree. This method is not specific to Nasal scripting, the same system can be used by other subsystems (such as the Telnet or HTTP daemons) to create and modify textures without directly using Nasal. The Canvas makes use of OpenVG for 2D rendering.

As of 08/2012, the Canvas system is still under active development and nothing is set in stone yet. See Canvas Properties for further information.

The first prototype of the canvas system became available in FlightGear 2.8, this lacked some features currently under development (notably the Nasal API, support for raster and vector images)- it is expected that a more feature-rich version will be available in the following release, i.e. FlightGear 3.0+ - possibly replacing the entire existing PUI GUI with a fully Canvas-driven implementation in scripting space (one of the primary long-term goals is to modernize the FlightGear GUI).

Future FlightGear versions will contain reimplementations of currently hard-coded instruments such as the wxradar, tcas or navdisplay using the canvas system, so that these can be easily maintained as part of the base package, and so that the same backend code can be also used for creating sophisticated dialogs using the Canvas system, such as ATC displays like ATC-FS, sharing a single backend - without having to re-implement logic for otherwise identical purposes (currently, we have code that implements a navigational display, while another piece of code renders a conceptually identical Map dialog).

Among other advantages, this will also provide for an opportunity to run Canvas-based instruments and GUI elements in another process, or even on another computer, analogous to FGPanel (see FGCanvas).

The canvas system allows you to use SVG images drawn with Inkscape, and easily use raster images and even commpletely custom drawing commands using OpenVG paths.

However, please do note that the canvas cannot currently be run in a standalone/FGPanel-fashion - there's been some talk about that, but that will at least take another 1-2 releases probably. That being said, you could certainly come up with a canvas instrument panel, and then simply use in a dedicated fgfs instance, which is slaved to a corresonding master instance.

Also, there's currently work ongoing to port the old 2D panel and HUD systems to the new Canvas system, using custom Nasal wrappers reimplementing the old behavior and turning the textures into canvas properties.

  1. Hooray (Sun Oct 19). Re: Orbital Makes the Sky Black.
  2. Hooray (Apr 4th, 2016). Re: Best way to learn Canvas?.

Requirements and Limitations

Beginning with 2.8, up to 2.99+, all FlightGear versions require FBO support for the canvas system to work properly [1]. In particular, this means that very old GPUs such as Intel 910/915 cards are currently not supported (FBO support is obviously also required for Rembrandt to work), people without FBO support are likely to just get "white" Canvas textures[2].

Unless render-to-texture (RTT) support can be provided through some form of fallback mode for hardware without FBO support, we we might need to consider officially un-supporting such old hardware from 3.0 (since we can already detect the vendor as Intel).

It seems 940-class chips are okay, and the 2000/3000/4000HD versions seem to work, but the earlier 9xx and before are going to be problematic for the time being [3]. At the moment, it isn't clear if telling the od_gauge/OSG backend code to use pbuffer rendering instead of FBOs would already help solve the problem [4].


Canvas is just a piece of technology - there are some early adopters/attempts who began using Canvas at a time when things were very much still in flux, which is why we have code snippets/instruments (and aircraft) where Canvas related functionality is significantly contributing to lag - I think this did apply to the m2000 and the extra500.

I haven't looked at any of those aircraft in months, so I cannot say if those problems have been addressed/fixed meanwhile - but there are a handful of examples for efficient Nasal/Canvas-based features, without them causing/contributing to significant lag. We have provided a bunch of "best practices" in various threads, and also offered 1:1 help to the efforts/people affected by this. Some have refused to accept such advice, others simply disappeared over time. So I cannot say much about any particular aircraft. But Nasal and Canvas are just tools that can be misused - and they're "interpreted" tools, both of which are basically using the concept of a "virtual machine" internally, so there's a certain overhead due to all the indirection taking place - which is to say that you don't need to be an experienced C/C++ or OpenGL/OSG programmer to use Nasal and Canvas to create sophisticated avionics - which isn't unlike the YASim/JSBSim FDM engines (or the autopilot system), which also, can be mis-used to create inefficient constructs that end up slowing down the simulator significantly. Then again, sometimes, there simply are very real bugs - like those in the property tree/effects code that TorstenD fixed a while ago, or the memory leak that Jabberwocky brought up on the forum, and that sanhoay identified - as long as people can provide actionable bug reports and reproducible test scenarios, there's a fairly good chance for bugs (and performance issues) to get fixed within 1-2 release cycles. But just coming here and saying that feature XYZ is broken or slows down the simulator isn't helping anybody, unless you can provide all the information that is needed that people who understanding the underlying subsystems can nail down the problem and see if there's an actual problem or not. This also involves stating exactly what hardware you are on, and what your other startup/runtime settings are - as of now, Canvas doesn't provide native support for shaders, so it will be using OSG/shivaVG for all hardware accelerated graphics, which should usually be sufficiently fast - but there are known constructs/usage patterns that are infamous for triggering unnecessary updates/re-drawing, which should be avoided. The other obvious issue is that Canvas does require FBO support. People writing Nasal/Canvas code should ideally understand that Canvas is not just a VM but also a state machine to some extent, so even redundant/unnecessary state changes (all of which are going through the property tree usually) will trigger updates/re-drawing under certain circumstances, which is to say that this should be avoided - and which is why I, and a handful of others, have been trying to enforce a framework-centric development philosophy when it comes to Canvas development, so that people don't use Canvas directly, but instead come up with frameworks for functionality that they need, to encapsulate any Canvas specific functionality - that we- can help review/improve over time, without having to touch tons of code files, instruments, aircraft etc The underlying idea can be seen in the ND and MapStructure efforts, neither of which needs to be touched by aircraft/GUI developers to add/create an ND or chart/map. In summary, Canvas certainly isn't being maintained as actively as it once used to be, but -like Nasal- Canvas is just a technology enabler, and people should introduce abstraction layers to use the system, so that possibly problematic usage patterns can be more easily encapsulated, but also reviewed/edited and optimized over time.

But please don't expect any of the people involved in Canvas related efforts to answer such broad questions with a single "yes" or "no", without also providing a proper context.
— Hooray (Jul 7th, 2015). Re: Developing a Canvas Cockpit for the CRJ700.
(powered by Instant-Cquotes)

Latest Canvas Efforts

Canvas and Nasal

Cquote1.png Canvas is not all about Nasal - it's probably safe to say that 95-99% of all Nasal code using Canvas ends up being "just" property I/O i.e. properties being read/written, or updated via timers/listeners. Usually, the idea is to initialize a Canvas texture by setting up all important state early on, and then "update" and "animate" things using timers/listeners - or even property rules later on. That will usually help minimize most Nasal-induced overhead.
— Hooray (Fri Apr 03). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)
Cquote1.png Canvas can definitely also be used without using Nasal - to see for yourself, just use the property tree browser to create/display canvas textures, or even the telnet/httpd protocols.

For the time being Nasal merely happens to provide the most abstract interface/mechanism to deal with Canvas - but you could just as well use the Canvas system from C++ by doing the corresponding property I/O using the C++ property interface directly - which would eliminate all Nasal overhead, even timers/listeners could then implemented in C++ space for animation/update logic.

— Hooray (Fri Apr 03). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)
Cquote1.png That being said, this isn't necessarily the smartest/best thing to do - primarily, Canvas is all about exposing hardware-accelerated 2D rendering to fgdata space, so that non-core developers can easily address their 2D rendering needs, without having to rebuild FG from source and without having to be intimately familiar with C++ and OSG/OpenGL. Which is where the performance overhead is coming from, i.e. Canvas APIs being modeled on top of the property tree and wrapped using Nasal bindings - which adds two fairly massive layers of abstraction in terms of property I/O and Nasal<->C++ marshalling.
— Hooray (Fri Apr 03). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)

Using the Canvas in non-FG projects

The canvas system has been refactored such that it can be more easily re-used in other programs, this included moving the Canvas component from FlightGear to SimGear and releasing the canvas code under SimGear's license (LGPL).

It is now possible to use the Canvas in projects unrelated to FlightGear, but you will obviously benefit from using certain FlightGear-related technologies, such as an * property tree

  • SGSubsystem/Mgr-based main loop
  • an osgviewer-based main window
  • Nasal scripting

Icecode's FGRadar project is such a completely separate code base from FG, which uses SG/FG components to simplify code reuse, without re-inventing the wheel.

For this very purpose, FGRadar uses a custom "SGApplication" framework - so that existing FlightGear subsystems can be more easily reused in different projects. All that's needed is deriving your own class from "SGApplication" and implementing its interface. The whole idea behind "SGApplication" is to provide a scriptable framework for OpenGL applications, fully based on 1) OSG, 2) SGSubsystems, 3) Nasal scripting and 4) the Canvas.

Cquote1.png the whole purpose of SGApplication is to provide a SGSubsystem-based "architecture" so that Nasal + Canvas and the property tree code can be easily reused.
— Hooray (Thu Apr 02). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)
Cquote1.png the SGApplication stuff isn't really needed, we just came up with it to simplify working with the SG code. But at least it demonstrates how the whole SGSubsystemMgr stuff works.
— Hooray (Thu Apr 02). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)
Cquote1.png you can also use SGSubsystem/SGSubsystemMgr directly - no need for "SGApplication" at all, it's just a convenicence helper.

"porting" isn't strictly necessary - you would primarily need int main() with a top-level SGSubsystemMgr, where you can then add the property tree, Nasal, Canvas, events etc (a handful of subsystems). To use Canvas outside FG, you would also need to look at the FGCanvasSystemAdapter in $FG_SRC/Canvas and provide your own wrapper for your own app (trivial).

— Hooray (Thu Apr 02). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)
Cquote1.png "fgpanel" is a code base that is using the "copy & paste" approach - so could serve as another example (beyond fgradar).
— Hooray (Thu Apr 02). Re: Simgear-based subsystem with Canvas support?.
(powered by Instant-Cquotes)

For additional info, see:


Boeing 747-400 PFD and ND approach EHAM 18R.png

A short video demonstrating a possible usecase (C-130J AMU):

By using alphablending it can also be used to render the contents of a HUD:

In my branch there is now also support for using the canvas in a gui widget:

Using the Canvas as a GUI widget

With the new map element it is also very easy to draw maps:

Simple Map of KSFO

Using the new canvas.parsesvg function to use an SVG file to simulate an EICAS display:

Demonstrating how the new Canvas GUI wrapper can be used to create entirely custom, scripted GUI widgets using XML, SVG and Nasal:

Experimenting with selecting text and positioning a cursor inside the text. The visualization is all done using Nasal.

Since 07/2012, the canvas system also provides support for full window-drawing:

Omega95 has reimplemented the CDU of the ATR72 using the Canvas system:

Another video demonstrating window stacking and nested canvases:

Using Canvas mouse events to create animation of slider, wheel and knob