Board index FlightGear Development Canvas

Drastic performance drop from custom canvas HUD development  Topic is solved

Canvas is FlightGear's new fully scriptable 2D drawing system that will allow you to easily create new instruments, HUDs and even GUI dialogs and custom GUI widgets, without having to write C++ code and without having to rebuild FlightGear.

Drastic performance drop from custom canvas HUD development  

Postby omega95 » Fri Jul 18, 2014 2:06 pm

Hey,

I've just used a loop very similar to my VSD (any most of looping systems) for the canvas HUD but my frame-rate starts at around 40 and drops to 6 ins a few seconds (gradually) and continues to drop. :shock:

I have a feeling it has something to do with the loop but like I mentioned, I'm using the same loop I did for the VSD and with that running (and the HUD disabled), I still get good performance. I also tried using the type of loop used in the C-130J HUD but it's about the same.

So, could it be something to do with loading SVG elements, clipping and moving it around? Here's my code btw:

(The loop functions are all the way at the end)

Code: Select all
var hud = {
   new: func(obj_name, interface_props, svg_path, flight_params) {
      var t = {parents:[hud]};
      
      t.display = canvas.new({
         "name": "HUD",
         "size": [1024, 740],
         "view": [1024, 740],
         "mipmapping": 1
      });
      
      var font_mapper = func(family, weight)
      {
         if( family == "Liberation Sans" and weight == "normal" )
            return "LiberationFonts/LiberationSans-Regular.ttf";
      };
      
      t.props = interface_props;
      t.params = flight_params;
      t.display.addPlacement({"node": obj_name});
      t.symbols = t.display.createGroup();
      
      canvas.parsesvg(t.symbols, svg_path, {'font-mapper': font_mapper});
      t.display.setColorBackground(0.36, 1, 0.3, 0.02);
      
      # t.symbols.getElementById("wind_arrow").updateCenter();
      
      # Set Clips
      ## Central Horizon box
      t.symbols.getElementById("horizon_heading").set("clip", "rect(115,819,660,170)");
      t.symbols.getElementById("horizon_lines").set("clip", "rect(115,819,660,170)");
      t.symbols.getElementById("traj_marker").set("clip", "rect(115,819,660,170)");
      t.symbols.getElementById("traj_zeroroll").set("clip", "rect(115,819,660,170)");
      ## Speed Tape box - horizontal clipping is not important anymore
      t.symbols.getElementById("spd_tape").set("clip", "rect(200,1024,520,0)");
      t.symbols.getElementById("ap_spd").set("clip", "rect(200,1024,520,0)");
      t.symbols.getElementById("stall_tape").set("clip", "rect(200,1024,520,0)");
      t.symbols.getElementById("flaps_tape").set("clip", "rect(200,1024,520,0)");
      ## Altitude Tape box
      ## Altitude Marker Tape boxes
      t.symbols.getElementById("alt_tape_top").set("clip", "rect(360,1024,520,0)");
      t.symbols.getElementById("alt_tape_bottom").set("clip", "rect(200,1024,340,0)");
      t.symbols.getElementById("ap_alt").set("clip", "rect(200,1024,520,0)");
      ## Alitude 100s Tape box
      t.symbols.getElementById("alt_tape_100s").set("clip", "rect(325,1024,390,0)");
      ## Vertical Speed Indicator box
      t.symbols.getElementById("vs_pointer").set("clip", "rect(230,1024,490,0)");
      
      var c1 = t.symbols.getElementById("horizon_heading").getCenter();
      print(c1[0]);
      print(c1[1]);
      
      return t;
   },
   data: {},
   last_pitch: 0,
   last_heading: 180,
   last_alpha: 0,
   last_sideslip: 0,
   last_airspeed: 20,
   last_altitude: 0,
   last_ap_alt: 0,
   last_ap_spd: 20,      # Starts at 20 in SVG File
   last_altitude2: 0,
   update: func() {
   
      foreach(var data_prop; me.props) {
         me.data[data_prop.name] = getprop(data_prop.path);
         if(me.data[data_prop.name] == nil) {
            me.data[data_prop.name] = 0;
         }
      }
      
      # Translate for pitch
      me.symbols.getElementById("horizon_heading").setTranslation(0,39.3*(me.data.pitch-me.last_pitch));
      me.symbols.getElementById("horizon_lines").setTranslation(0,39.3*(me.data.pitch-me.last_pitch));
      me.symbols.getElementById("hdg_pointer").setTranslation(0,39.3*(me.data.pitch-me.last_pitch));
      me.symbols.getElementById("horizon_heading").setTranslation(-(10137/360)*(me.data.heading-me.last_heading),0);
      
      # Rotate for roll
      me.symbols.getElementById("horizon_heading").setCenter(512,740).setRotation(-me.data.roll*D2R);
      me.symbols.getElementById("horizon_lines").setCenter(512,740).setRotation(-me.data.roll*D2R);
      me.symbols.getElementById("roll_horizon").setRotation(-me.data.roll*D2R);
      me.symbols.getElementById("hdg_pointer").setCenter(512,740).setRotation(-me.data.roll*D2R);
      
      # Rotate trajectory marker according to roll rate
      me.symbols.getElementById("traj_marker").setCenter(512,740).setRotation(me.data.rollrate*D2R);
      
      # Move trajectory marker and zero-roll to <alpha, side-slip>
      me.symbols.getElementById("traj_marker").setTranslation(28.2*(me.data.sideslip-me.last_sideslip),39.3*(me.data.alpha-me.last_alpha));
      me.symbols.getElementById("traj_zeroroll").setTranslation(28.2*(me.data.sideslip-me.last_sideslip),39.3*(me.data.alpha-me.last_alpha));
      
      # Speed tape
      if(me.data.airspeed < 30) { # Limit value to 30 knots
         me.data.airspeed = 30;
      }
      me.symbols.getElementById("spd_tape").setTranslation(0,3*(me.data.airspeed-me.last_airspeed));
      me.symbols.getElementById("ap_spd").setTranslation(0,3*(me.data.ap_spd-me.last_ap_spd));
      
      
      # Altitude tape
      me.symbols.getElementById("alt_tape_top").setTranslation(0,0.2*(me.data.altitude-me.last_altitude));
      me.symbols.getElementById("alt_tape_bottom").setTranslation(0,0.2*(me.data.altitude-me.last_altitude));
      me.symbols.getElementById("ap_alt").setTranslation(0,0.2*(me.data.ap_alt-me.last_ap_alt));
      
      me.altitude1 = int(me.data.altitude/100);
      me.altitude2 = int(me.data.altitude - (me.altitude1*100));
      
      me.symbols.getElementById("alt_tape_100s").setTranslation(0,(me.altitude2-me.last_altitude2));
      
      # Set Text Values
      me.symbols.getElementById("radar_alt").setText(sprintf("%4.0f", me.data.radaralt));
      me.symbols.getElementById("mach_spd").setText(sprintf("%0.2fM", me.data.mach));
      me.symbols.getElementById("vs_text").setText(sprintf("%2.0f", me.data.vertspd*0.6));
      me.symbols.getElementById("qnhVal").setText(sprintf("%2.2f", me.data.qnh));
      me.symbols.getElementById("ap_spd_text").setText(sprintf("%3.0f", me.data.ap_spd));
      me.symbols.getElementById("ap_alt_text").setText(sprintf("%5.0f", me.data.ap_alt));
      me.symbols.getElementById("altitude_text").setText(sprintf("%3.0f", me.altitude1));
      if(me.data.radaralt == nil) {
         me.symbols.getElementById("radar_alt").hide();
      } else {
         if(me.data.radaralt>3000) {
            me.symbols.getElementById("radar_alt").hide();
         } else {
            me.symbols.getElementById("radar_alt").show();
         }
      }
      
      # Manage flaps and stall tapes
      
      
      # Manage speed trent tape
      
      
      me.last_pitch = me.data.pitch;
      me.last_heading = me.data.heading;
      me.last_alpha = me.data.alpha;
      me.last_sideslip = me.data.sideslip;
      me.last_airspeed = me.data.airspeed;
      me.last_altitude = me.data.altitude;
      me.last_ap_alt = me.data.ap_alt;
      me.last_ap_spd = me.data.ap_spd;
      me.last_altitude2 = me.altitude2;
   },
   init: func {
      me.UPDATE_INTERVAL = 0.03;
      me.loopid = 0;
      me.reset();
   },
   reset: func {
      me.loopid += 1;
      me._loop_(me.loopid);
   },
   _loop_: func(id) {
      id = me.loopid or return;
      me.update();
      settimer(func {me._loop_(id); }, me.UPDATE_INTERVAL);
   }


Thank you!
Merlion Virtual Airlines - the experience of a flight time...
Get high quality aircraft, airports, video tutorials or development tools from my hangar.
omega95
 
Posts: 1222
Joined: Sat Jul 30, 2011 1:59 am
Location: -unknown-
Callsign: MIA0001, OM-EGA
IRC name: omega95
Version: 2.12 git
OS: Ubuntu 13.04

Re: Drastic performance drop from custom canvas HUD developm

Postby Hooray » Fri Jul 18, 2014 2:57 pm

you can use debug.benchmark() to profile your update() method and individual parts of it.
Like we mentioned previously in the other thread, it is generally better not to use settimer, but instead the maketimer() API - and a less aggressive interval. Also, it would make sense to update elements conditionally, rather than unconditionally each 30ms.
Also, there are quite a few optimizations possible that you could use, see the ND code for examples - e.g. caching symbols in instance variables (me.foo) should be faster than having multiple hash lookups in a single line.
Besides, you could set the transformation once per element, instead of using separate API calls.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Drastic performance drop from custom canvas HUD developm

Postby Philosopher » Fri Jul 18, 2014 3:09 pm

See the note here: https://gitorious.org/fg/fgdata/source/ ... s#L365-366

Basically a better idea would be:
Code: Select all
    init: func() {
        ...;
        me.elements = {};
        foreach (var ename; ["horizon_heading",...])
            me.elements[ename] = me.symbols.getElementById(ename);
    },
    update: func() {
        me.elements["horizon_heading"].setTranslation(0,39.3*(me.data.pitch-me.last_pitch));
        ...;
    },
Philosopher
 
Posts: 1593
Joined: Sun Aug 12, 2012 7:29 pm

Re: Drastic performance drop from custom canvas HUD developm

Postby Hooray » Fri Jul 18, 2014 3:14 pm

right, but I'd prefer people to collaborate in order to contribute to a single unified framework, instead of us always having to give out the same optimization hints that we're already using in other places - we're using such constructs for a reason, and it doesn't help if people always need to re-discover certain findings, instead of applying what we told them ...
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Drastic performance drop from custom canvas HUD developm

Postby omega95 » Mon Jul 21, 2014 5:50 pm

but I'd prefer people to collaborate in order to contribute to a single unified framework


I think what would benefit developers the most would be a better API-like-thing to access and work with SVG elements. And maybe make some fail-safe/try-catch stuff to prevent some errors.
Merlion Virtual Airlines - the experience of a flight time...
Get high quality aircraft, airports, video tutorials or development tools from my hangar.
omega95
 
Posts: 1222
Joined: Sat Jul 30, 2011 1:59 am
Location: -unknown-
Callsign: MIA0001, OM-EGA
IRC name: omega95
Version: 2.12 git
OS: Ubuntu 13.04

Re: Drastic performance drop from custom canvas HUD developm

Postby Hooray » Mon Jul 21, 2014 6:05 pm

Philosopher once mentioned that, i.e. some kind of dedicated "animation" framework based on dealing with SVG elements and Nasal timers/listeners.
try/catch semantics can be easily implemented using existing Nasal means with 5-10 lines of code, see the call() API for details.

The real issue remains however - as can be seen here (and in other threads), people generally copy & adapt existing code without understanding its performance implications, which is exactly why I'd prefer to have a less API-centric workflow, and make it more focused on having actual frameworks for different purposes like handling NDs, PFDs, CDUs or HUDs - under the hood, those would obviously share quite a bit of overlapping functionalities, but at least that would free people from having to understand low-level details - we've seen a number of instances such as the extra500, the EFB or your recent work, that clearly demonstrates, that we shouldn't focus on lower-level APIs, but should really provide frameworks, analogous to MapStructure - those are easy to maintain and optimize, even if that should mean that they'll be partially (or even entirely) re-implemented through C++ extensions to provide better performance - but as long as people "dabble" with lower-level APIs, we have no way to holistically optimize things in terms of animation handling etc.

Coming up with a simple Nasal/Canvas animation framework that's designed around SVG elements, would not be very difficult - we have quite a few examples meanwhile, so that some APIs can be extracted, generalized and unified: http://wiki.flightgear.org/Canvas_Animation_Framework

That would allow us to provide a single back-end, and easily optimize that over time, instead of having to maintain a number of different code bases using Canvas. Frankly, any other suggestions simply don't scale at all - and your work demonstrates that fairly well, and all the mess that went on when the RM code was updated, is another excellent example for the underlying problem, i.e. why we literally need to SHIELD aircraft developers from implementation details, including lower-level APIs.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU


Return to Canvas

Who is online

Users browsing this forum: No registered users and 2 guests