FlightGear Newsletter May 2014: Difference between revisions

Jump to navigation Jump to search
m
→‎Canvas Performance: fix some typos & spelling/grammar
m (Protected "FlightGear Newsletter May 2014": Newsletter ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)))
m (→‎Canvas Performance: fix some typos & spelling/grammar)
Line 32: Line 32:
Recently, it has been two years since [[Canvas]], our 2D rendering API, got integrated. And even today, there are people claiming that aircraft using Nasal and Canvas are generally slow and don't provide sufficient performance, suggesting not to adopt Canvas yet, so here are some comments about that, to help put some context around such statements:
Recently, it has been two years since [[Canvas]], our 2D rendering API, got integrated. And even today, there are people claiming that aircraft using Nasal and Canvas are generally slow and don't provide sufficient performance, suggesting not to adopt Canvas yet, so here are some comments about that, to help put some context around such statements:


Canvas is a new technology in FlightGear, and it not just a single technology, it's built on top of other technologies, like the [[Property Tree]], ShivaVG/OpenVG, OSG and OpenGL. As you may have noticed, we didn't even mention [[Nasal]] yet - that's because Nasal scripting, strictly speaking, isn't even required to use [[Canvas]] - the whole thing can be used without touching any Nasal - just by using the property tree, the built-in httpd or even the [[Telnet usage|telnet]] interface.
Canvas is a relatively new technology in FlightGear, and it not just a single technology, it's built on top of other technologies, like the [[Property Tree]], ShivaVG/OpenVG, OSG and OpenGL. As you may have noticed, we didn't even mention [[Nasal]] yet - that's because Nasal scripting, strictly speaking, isn't even required to use [[Canvas]] - the whole thing can be used without touching any Nasal - just by using the property tree, the built-in httpd or even the [[Telnet usage|telnet]] interface.


In theory, you could create a canvas just by setting properties, and even add placements (GUI/scenery/aircraft) by doing just that.
In theory, you could create a canvas just by setting properties, and even add placements (GUI/scenery/aircraft) by doing just that.
Line 40: Line 40:
But whenever we've seen dead-slow Nasal/Canvas code it was usually not the problem of Nasal or Canvas per se, but due the original code/implementation in the first place, including our own code by the way  :D  
But whenever we've seen dead-slow Nasal/Canvas code it was usually not the problem of Nasal or Canvas per se, but due the original code/implementation in the first place, including our own code by the way  :D  


Just because there are means available to add scripted graphics to FlightGear, it doesn't mean that people automatically know how to use this technology properly.
Just because there are means available to add scripted graphics to FlightGear, doesn't mean that people automatically know how to use this technology properly.


For example, the very first version of the airport selection dialog was much slower than necessary, simply because it would redraw stuff unnecessarily, and not even use separate layers for different features like airports, taxiways, runways etc.
For example, the very first version of the airport selection dialog was much slower than necessary, simply because it would redraw stuff unnecessarily, and not even use separate layers (canvas groups) for different features like airports, taxiways, runways etc.


Since then, we've learned a lot about using Canvas properly, but also about the combination of Nasal and Canvas, including other subsystems.
Since then, we've learned a lot about using Canvas properly, but also about the combination of using Nasal and Canvas, including other subsystems.


So, as Canvas is a fairly recent feature, there's sometimes some really -sorry- stupid stuff done in various areas, that makes people think that Nasal and/or Canvas are generally slow.  
So, as Canvas is a fairly recent feature, there's sometimes some really naive stuff done in various areas, that makes people think that Nasal and/or Canvas are generally slow.  


However, that is not generally true. Aircraft like the m2000-5 or the extra500 are indeed fairly slow - not primarily because of Nasal/Canvas, but due a combination of factors, including several other FG technologies. And the 747 or 777 are slow even without any Nasal/Canvas code running; we can actually measure now how little impact our Nasal/Canvas code has, and how significant the impact is due to complex cockpit modeling (many polygons and high resolution textures). Understandably, under such circumstances, improper use of Nasal and/or Canvas may cause additional performance issues.
However, that is not generally true. Aircraft like the m2000-5 or the extra500 are indeed fairly slow - not primarily because of Nasal/Canvas, but due a combination of factors, including several other FG technologies. And the 747 or 777 are slow even without any Nasal/Canvas code running; we can actually measure now how little impact our Nasal/Canvas code has, and how significant the impact is due to complex cockpit modeling (many polygons and high resolution textures). Understandably, under such circumstances, improper use of Nasal and/or Canvas may cause additional performance issues. And people may easily draw false conclusions under these circumstances.


Anyway, nobody should simply "port/convert" stuff like -for example- the Garmin196 to use Canvas, because that's exactly where all those problems come from: Canvas and Nasal themselves are simply too low-level for these purposes, people need to be very experienced to come up with fast code here, very familiar with FG and certain Nasal technologies like timers and listeners (or the whole process inevitably involves lots of trial & error). Typically, that really only applies to core developers still, or fgdata committers who have extensively worked with both Nasal and Canvas over the years.
Anyway, nobody should simply "port/convert" stuff like -for example- the Garmin196 to use Canvas, because that's exactly where all those problems come from: Canvas and Nasal themselves are simply too low-level for these purposes, people still need to be very experienced to come up with fast code here, very familiar with FG and certain Nasal technologies like timers and listeners (or the whole process inevitably involves lots of trial & error). Typically, that really only applies to core developers still, or fgdata committers who have extensively worked with both Nasal and Canvas over the years.


To use Nasal and Canvas properly, there are several layers that need to be understood, such as:
To use Nasal and Canvas properly, there are several layers that need to be understood, such as:
Line 60: Line 60:
# OpenGL
# OpenGL


Most people really only understand #1 (well, partially), even though writing really fast code may very well involve looking at several layers in combination to see how they affect each other.
Most people only understand #1 (well, partially), even though writing really fast code may very well involve looking at several layers in combination to see how they affect each other.


So writing "fast" Nasal/Canvas code still is quite an undertaking despite things being accessible from scripting space these days.
So writing "fast" Nasal/Canvas code still is quite an undertaking despite things being accessible from scripting space these days. And even our most experienced Nasal/Canvas contributors are still learning new things each day.  


Which is exactly the point of having [[MapStructure]] and the [[NavDisplay]] frameworks: those are designed to handle things efficiently, e.g. by using smarter queries, smarter updates, and techniques like caching or selective/delta searches.  
Which is exactly the point of having [[MapStructure]] and the [[NavDisplay]] frameworks: those are designed to handle things efficiently, e.g. by using smarter queries, smarter updates, and techniques like caching or selective/delta searches.  


Imagine it like "Ruby on Rails" for aviation charts - that's what the whole MapStructure framework is all about: maps and charts. The NavDisplay framework also used to be fairly inefficient in its early days - since then, we've significantly reworked it to use MapStructure. That first introduced some ugly hacks because we didn't want to rewrite it completely, but now its layers are mostly using MapStructure. And most of the original ND stuff has been refactored, generalized and integrated with the MapStructure framework, so that this very code can be used in other places, not just other aircraft, but also GUI dialogs. 
Imagine it like "Ruby on Rails" for aviation charts - that's what the whole MapStructure framework is all about: maps and charts.  


Very fast Nasal/Canvas code will typically do very little at runtime/frame rate, but instead use pre-created data structures and dynamically toggle canvas layers on/off (show/hide groups) and update the underlying data selectively, while using cached images instead of lots of OpenVG paths.  
The NavDisplay framework also used to be fairly inefficient in its early days - since then, we've significantly reworked it to use MapStructure. That first introduced some ugly hacks because we didn't want to rewrite it completely, but now its layers are mostly using MapStructure. And most of the original ND stuff has been refactored, generalized and integrated with the MapStructure framework, so that this very code can be used in other places, not just other aircraft, but also GUI dialogs. 
 
Very fast Nasal/Canvas code will typically do very little at runtime/frame rate, but instead use pre-created data structures and dynamically toggle canvas layers on/off (show/hide groups) and update the underlying data selectively, while using cached images instead of having lots of identical OpenVG paths.  


We've arrived at these conclusions based on testing and lots of profiling.
We've arrived at these conclusions based on testing and lots of profiling.


Frame rates >= 60 fps are not impossible to achieve like this - but coding such a design is obviously much more involved than simply drawing/updating once per frame, which people will typically do. This is very similar to people running timer callbacks at frame rate instead of use using split-frame loops or listeners to do certain computations and processing selectively. A simple design can get you only so far, but an efficient design requires much more thinking, and time to get right.
Frame rates >= 60 fps are not impossible to achieve like this - but coding such a design is obviously much more involved than simply drawing/updating once per frame, which is what people will typically do. This is very similar to people running timer callbacks at frame rate instead of using using split-frame loops or listeners to do certain computations and processing selectively. A simple design can get you only so far, but an efficient design requires much more thinking, and time to get right.


For instance, D-LEON has done quite some Nasal/Canvas benchmarking and confirmed that MapStructure/Canvas are sufficiently fast already (and we're still exploring additional options for making it even faster). Also, we're looking into augmenting/re-implementing certain features through native code additions, such as having dedicated extension functions, Nasal library functions or new Canvas extensions to make things even faster.
For instance, D-LEON has done quite some Nasal/Canvas benchmarking and confirmed that MapStructure/Canvas are sufficiently fast already (and we're still exploring additional options for making it even faster). Also, we're looking into augmenting/re-implementing certain features through native code additions, such as having dedicated extension functions, Nasal library functions or new Canvas extensions to make things even faster.


The key point here is that having a set of generic abstraction layers allows us to easily generalize, but also optimize, things - without having to rewrite all the places where Nasal/Canvas are used for mapping purposes. Things like the Avidyne don't use our framework at all currently - which also means that the code doesn't benefit from recent MapStructure optimizations unfortunately.
The key point here is that having a set of generic abstraction layers allows us to easily generalize, but also optimize, things - without having to rewrite all the places where Nasal/Canvas are used for mapping purposes. Things like the [[Avidyne Entegra R9]] don't use the [[MapStructure]] framework at all currently - which also means that the code doesn't benefit from recent MapStructure optimizations unfortunately.


Imagine we were to optimize positioned queries (NavDB stuff like VORs, NDBs, airports etc) to become 50% faster - all instruments and dialogs using the MapStructure framework would get this speedup for free, while custom solutions would need to separately implement the optimized query.
Imagine we were to optimize positioned queries (NavDB stuff like VORs, NDBs, airports etc) to become 50% faster - all instruments and dialogs using the MapStructure framework would get this speedup for free, while custom solutions would need to separately implement the optimized query. And that applies to pretty much any effort that doesn't favor a framework-centric development approach.


By the way, this is exactly the reason why the MapStructure framework lives under $FG_ROOT/Nasal, to help grow a shared library of '''fast''' components that can be easily reused, but also maintained - freeing aircraft and GUI developers from such chores and obligations, so that they don't have to track development and update all their own work in order to avoid breakage, but still benefit from recent optimizations. This is a lesson that we've learned from all the feedback that some of the most active airliner developers, like omega95 and redneck, have provided over time.
By the way, this is exactly the reason why the MapStructure framework lives under $FG_ROOT/Nasal, to help grow a shared library of '''fast''' components that can be easily reused, but also maintained - freeing aircraft and GUI developers from such chores and obligations, so that they don't have to track development and update all their own work in order to avoid breakage, but still benefit from recent optimizations. This is a lesson that we've learned from all the feedback that some of the most active airliner developers, like omega95 and redneck, have provided over time.
Line 84: Line 86:
Unless someone is a really experienced programmer, working with "low-level" Nasal & Canvas for mapping purposes is probably not a good idea, because we already have a handful of people working on a single framework that is dedicated to just that, and we actually do regularly profile things and we're talking to the Canvas guys to sneak out more performance, better speed, less lag etc.  
Unless someone is a really experienced programmer, working with "low-level" Nasal & Canvas for mapping purposes is probably not a good idea, because we already have a handful of people working on a single framework that is dedicated to just that, and we actually do regularly profile things and we're talking to the Canvas guys to sneak out more performance, better speed, less lag etc.  


But whenever someone comes up with a custom mapping solution -solving the same problem that MapStructure and Gijs' ND frameworks solve- that doesn't use those frameworks, they need to do all the hard work from scratch, and code sharing/reuse and maintenance is made increasingly difficult, too.
But whenever someone comes up with a custom mapping solution -solving the same problem that MapStructure and Gijs' ND frameworks solve- that doesn't use those frameworks, they need to do all the hard work from scratch, and code sharing/reuse and maintenance is made increasingly difficult, too. Basically, we end up "competing" without meaning to, by working on fairly similar projects, without coordinating our work to generalize and reuse existing solutions. 


Which is why we talked with the Avidyne developers to team up with us: their code is much more elegant than the original ND/MapStructure code, but the latter is much more generic (aircraft/instrument agnostic), and also fairly optimized already.
Which is why we talked with the [[Avidyne Entegra R9]] developers to team up with us: their code is much more elegant than the original ND/MapStructure code, but the latter is much more generic (aircraft/instrument agnostic), and also fairly optimized already.


Admittedly, we already spent a while examining all the Avidyne code there - we were kinda surprised seeing OOP code using design patterns solving a problem that we had been working on for several months. Actually, had we seen that code earlier, it would have been a much more mature foundation for a generic ND/MapStructure framework, because of its OO nature. But in the meantime, we've come up with something fairly generic - mostly thanks to Philosopher's MapStructure framework.
Admittedly, we already spent a while examining all the [[Avidyne Entegra R9]] code there - we were kinda surprised seeing OOP code using design patterns solving a problem that we had been working on for several months. Actually, had we seen that code earlier, it would have been a much more mature foundation for a generic ND/MapStructure framework, because of its OO nature. But in the meantime, we've come up with something fairly generic - mostly thanks to Philosopher's MapStructure framework.


As mentioned elsewhere, if we could have had a look at the Avidyne code it would have been a great/better foundation for MapStructure and the ND framework - but we had to work off a completely different code base (several actually),  so things are a little inconsistent and less elegant - but those frameworks are '''really''' designed to be generic, not just intended to be used with different aircraft, but also different GUI dialogs. And whenever we add a new optimized feature, all people/aircraft/dialogs using those frameworks will benefit automatically (backward compatibility is handled by US).
As mentioned elsewhere, if we could have had a look at the Avidyne code it would have been a great/better foundation for MapStructure and the ND framework - but we had to work off a completely different code base (several actually),  so things are a little inconsistent and less elegant - but those frameworks are '''really''' designed to be generic, not just intended to be used with different aircraft, but also different GUI dialogs. And whenever we add a new optimized feature, all people/aircraft/dialogs using those frameworks will benefit automatically (backward compatibility is handled by US).


We literally depend on having many different use cases and front-end scenarios, i.e. for these two frameworks to evolve properly, we need many different aircraft developers to adopt them, do regular testing and provide feedback - and GUI use-cases are just as important. In fact, just for the sake of generalizing things, both frameworks contain support for being not just driven by the main/FDM aircraft, but even by AI aircraft.
We literally depend on having many different use cases and front-end scenarios, i.e. for these two frameworks to evolve properly, we need many different aircraft developers to adopt them, do regular testing/profiling and provide feedback - and GUI use-cases are just as important. In fact, just for the sake of generalizing things, both frameworks contain support for being not just driven by the main/FDM aircraft, but even by AI aircraft.


Technically, MapStructure layers are a bit more sophisticated than having pre-created SVG/OpenVG groups, because symbols can be cached in a separate canvas via a texture map, so that there's true "instancing" support for each cached symbol. This is something that we've been working on in the last two weeks. Also, positioned queries are handled by an abstraction layer to ensure that things are sufficiently fast using selective delta-updating. Besides, the ND/MapStructure code is intended to be reusable.
Technically, MapStructure layers are a bit more sophisticated than having pre-created SVG/OpenVG groups, because symbols can be cached in a separate canvas via a texture map, so that there's true "instancing" support for each cached symbol. This is something that we've been working on in the last two weeks. Also, positioned queries are handled by an abstraction layer to ensure that things are sufficiently fast using selective delta-updating. Besides, the ND/MapStructure code was intended to be reusable right from the beginning.


Quite a few of the problems people are likely to encounter are already solved via MapStructure, i.e. identical paths for different symbols are simply instanced by using a raster image (canvas) as a cache.
Quite a few of the problems people, interested in mapping, are likely to encounter are already solved via MapStructure, i.e. identical paths for different symbols are simply instanced by using a raster image (canvas) as a cache.


The bottom line is: if there's someone who actually IS experienced enough to know how to write good Nasal/Canvas code, that person should better team up with us, to help improve the generic framework, rather than develop some niche instrument or dialog that will only ever be used by a few aircraft.
The bottom line is: if there's someone who actually IS experienced enough to know how to write good and fast Nasal/Canvas code, that person should better team up with us, to help improve the generic framework, rather than develop some niche instrument or dialog that will only ever be used by a few aircraft, work that may never make it into fgdata.


Nasal & Canvas are really just tools, and tools can be misused obviously. Using them properly still requires certain knowledge, or slow code may result from it. We've been trying to make this easier by providing a few wrappers on top of Nasal/Canvas, that are intended to help with the creation of mapping displays, so that unnecessarily slow code can be avoided, while also allowing front-end code to directly benefit from optimized features.
Nasal & Canvas are really just tools, and tools can be misused obviously. Using them properly still requires certain knowledge, or slow code may result from it. We've been trying to make this easier by providing a few wrappers on top of Nasal/Canvas, that are intended to help with the creation of mapping displays, so that unnecessarily slow code can be avoided, while also allowing front-end code to directly benefit from optimized features.


Badly written code will remain slow no matter how optimized underlying technologies like Nasal or Canvas are, even if they should be completely replaced. Those are algorithmic issues, and MapStructure/NavDisplay are intended to help people focus on non-algorithmic stuff.
Badly written code will remain slow no matter how optimized underlying technologies like Nasal or Canvas are, even if they should be completely replaced at some point. Those are algorithmic issues, and MapStructure/NavDisplay are intended to help people focus on non-algorithmic stuff.


=== A Canvas-based Map dialog ===
=== A Canvas-based Map dialog ===

Navigation menu