Board index FlightGear Development Canvas

SVG parser performance (from Cessna C182T)

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.

SVG parser performance (from Cessna C182T)

Postby Hooray » Sun May 31, 2020 1:36 pm

Some lower end RPi/Intel based computers are taking ~15 seconds to show the device.

So there's currently a discussion taking place to optimize/port or re-implement Canvas SVG handling - could some more people please report here how long initialization of the FG1000 is taking for them ? Ideally, by using just the debug menu and checking how much time it takes for the PDF to show up ?

Okay, here's an update that should be pretty promising actually:
Hooray wrote:after a bit of googling, it actually turns out that there is already a dedicated plugin for SVG handling via OSG - so that would probably be the least amount of work, given that we already have Canvas::Image using the same ReaderWriter mechanism under the hood.

I don't know anything about the plugin's state - but I suppose it'll we much more feature-complete than our own little parser.
And it being an OSG plugin, it will automatically be able to run in the background - so we won't have to add our own threading code.

Also, it's not a totally new dependency, rather just another OSG plugin - so comparatively easy to support via the existing build system (according to the OSG website, the plugin only needs librsvg)


https://github.com/openscenegraph/OpenS ... lugins/svg

In general, this would probably be best added as a new dedicated "svg-image" canvas element - rather than touching svg.nas/Canvas.Path directly, because those are used for other things that will not be easily supported by the OSG library without updating all sorts of existing work using the current method.

I guess this should be coordinates with James - but it should be in line with his intention of deprecating Shiva VG - i.e. an OSG plugin is implicitly compatible with OSG - and most of our use-cases will have to do with SVG handling anyway, so that updating Canvas.Path in the future, will be more straigthforward once we have a dedicated canvas element for SVG handling
[...]
Actually, it only just occurred to me that we may already have native SVG support anyway, due to the way how OSG ReaderWriter plugins work, it's very well possible that all that we need to do is build/add the SVG plugin, because at that point Canvas.Image might automatically be able to work "as is", without any C++ changes, all that is needed is specifying an SVG file - how does that sound? :D

(note this bypasses Nasal/svg.nas and the XML parser entirely, and runs inside a background thread handled by OSG, and it only takes a few ms to load/display)

http://wiki.flightgear.org/Canvas_SVG
Image

To see for yourself, use an SVG-enabled OSG build and execute the following snippet via the Nasal console, it will by-pass svg.nas/Canvas.Path (Shiva VG) entirely:
Code: Select all
 var (width, height) = (512,512);
                                        # Create a standalone Canvas (not attached to any GUI dialog/aircraft etc)
                                        var myCanvas = canvas.new({
                                          "name": "Canvas.Image SVG Test",   # The name is optional but allow for easier identification
                                            "size": [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]
                                              "view": [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]
                                                                      # which will be stretched the size of the texture, required)
                                                                        "mipmapping": 1       # Enable mipmapping (optional)
                                                                        });

                                                                        # set background color
                                                                        myCanvas.set("background", canvas.style.getColor("bg_color"));

                                                                        # creating the top-level/root group which will contain all other elements/group
                                                                        var root = myCanvas.createGroup();

                                                                        var window = canvas.Window.new([width,height],"dialog");
                                                                        window.setCanvas(myCanvas);

                                                                        # path is relative to $FG_ROOT (base package)
                                                                        var path = "Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.svg";
                                                                        # create an image child for the texture
                                                                        var child = root.createChild("image")
                                                                            .setFile(path)
                                                                            .setSize(512, 512);




Obviously, we will need to work out a way to access SVG elements via the Canvas, or simply use separate SVG files in the meantime.
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: Cessna C182T with FG1000 glass panel

Postby Puffergas » Mon Jun 01, 2020 12:38 am

The code in the PM produced an error. The above code instantly produced an all white "Canvas Dialog" box. I did not see a SVG plugin in my OSG plugin folder.
Puffergas
 
Posts: 56
Joined: Thu Jan 02, 2020 2:09 am

Re: Cessna C182T with FG1000 glass panel

Postby Hooray » Mon Jun 01, 2020 7:10 am

I only had to install librsvg and its dependencies to make this work (package manager), if available, the OSG plugin is loaded via Canvas.Image automatically and will convert the SVG to a raster image - to see for yourself whether the plugin is available, run something like $ locate osgdb_svg.so , which should return the location of the plugin:

/usr/lib/x86_64-linux-gnu/osgPlugins-3.4.1/osgdb_svg.so

Thanks for reporting back here !

PS: There should be an OSG error shown in the console, unless I managed to post a snippet of code with typos in it ...in which case some Nasal parsing errors would be shown
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: Cessna C182T with FG1000 glass panel

Postby Puffergas » Mon Jun 08, 2020 6:27 pm

Raspberry Pi OS 64-bit
Image

There was no long wait for the appearance of the dialog box. I can time it, if you want.

librsvg2-dev did the trick for me. Later, I'll update the Wiki.

I should learn how to compile just the OSG plugin, not the whole shebang.

Did that small amount of code produce that instrument, or is it just an image?

EDIT: Looks like I'm not allowed to post and image. Link below:
https://imgur.com/hz94MJt
Puffergas
 
Posts: 56
Joined: Thu Jan 02, 2020 2:09 am

Re: Cessna C182T with FG1000 glass panel

Postby wkitty42 » Mon Jun 08, 2020 6:47 pm

Puffergas wrote in Mon Jun 08, 2020 6:27 pm:Raspberry Pi OS 64-bit
Image

you have to use the image link, not the page link... so follow these steps...

1. post the image
2. view the posted image on the page
3. right click and view the image only
4. right click and select "copy image location"
5. use the image location in the tags

eg:
Code: Select all
[img]https://i.imgur.com/hz94MJt.png[/img]

Image
"You get more air close to the ground," said Angalo. "I read that in a book. You get lots of air low down, and not much when you go up."
"Why not?" said Gurder.
"Dunno. It's frightened of heights, I guess."
User avatar
wkitty42
 
Posts: 9146
Joined: Fri Feb 20, 2015 4:46 pm
Location: central NC, USA
Callsign: wk42
Version: git next
OS: Kubuntu 20.04

Re: Cessna C182T with FG1000 glass panel

Postby Hooray » Tue Jun 09, 2020 1:59 pm

Puffergas wrote in Mon Jun 08, 2020 6:27 pm:There was no long wait for the appearance of the dialog box. I can time it, if you want.
[...]
Did that small amount of code produce that instrument, or is it just an image?


Thanks for reporting back here - actually, it's not the full instrument that is rendered here, just one of the most complex SVG files that is treated as a raster image by loading it via OSG instead of going through svg.nas and our own little SVG/XML parsing routines, because those show up considerably when profiling - thus, the whole point of the exercise was to independently verify whether the speed-up can be reproduced or not.

I suppose Stuart will be interested in those findings to work out a way to use native code instead of Nasal (svg.nas) whenever possible - in addition, this makes our SVG support much more feature complete, too. Obviously, some issues remain - but given the performance improvement, it'll be worthwhile to investigate further

Also, to be really clear about it: This isn't "just" a speedup in terms of loading/displaying the image itself, but the other major benefit is that this can be handled by native OSG code, which means ReaderWriter Plugins, i.e. asynchronously in background/worker thread, which means that other CPU cores can be used. So this is indeed very interesting to pursue given that the whole SVG/Inkscape workflow has become pretty common to create MFDs from scratch.
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: SVG parser performance (from Cessna C182T)

Postby stuart » Tue Jun 09, 2020 5:40 pm

I had a quick chat with James about this. We suspect just shifting more of the svg.nas code into C++ would provide good enough "bang for the buck" to improve the load time without needing anything more complicated.

However this isn't something either of us see as a priority right now (WS3.0 and various other things being higher priority), so we're not going to spend any time investigating further or implementing an improvement. If anyone is interested, they would be welcome to submit a patch.

I also suspect that 15s compares favourably to the startup time for a G1000 :)

-Stuart
G-MWLX
User avatar
stuart
Moderator
 
Posts: 1629
Joined: Wed Nov 29, 2006 10:56 am
Location: Edinburgh
Callsign: G-MWLX

Re: SVG parser performance (from Cessna C182T)

Postby Hooray » Tue Jun 09, 2020 5:51 pm

Do you realize that we can have our cake and eat it by using the built-in SVG plugin provided by OSG ?
I mean, what's the point of porting svg.nas to C++ if OSG comes with readily-available SVG reading support ?

Again, the screen shots shown above were created by a renderer that does not go through svg.nas at all (or Nasal in general) - and not even a single line of C++ code had to be modified, at the obvious expense of requiring another dependency (that is transparently handled by OSG).

In other words, if you'd like to see patches related to this - how would you want this to be dealt with, i.e. dynamic elements (say anything referenced via its SVG id and SVG text nodes in particular) ?

Finally, this isn't just about loading time, performance at run time is also much better if we can avoid svg.nas/Nasal and use native OSG machinery (aka ReaderWriter plugins) for this sort of thing.
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: SVG parser performance (from Cessna C182T)

Postby Puffergas » Wed Jun 10, 2020 1:48 am

Not easy to time, using my cell phone. In the DC-3, the dialog pops up before my hand can move to the cell phone. In the 172, about a second or I'm getting better at moving my hand from the mouse to the phone. In the UFO, less than a second. I'll just say one second. After closing the dialog box and after executing your code, it seems like it pops up faster. Did not notice much of a FPS hit in the DC-3, however there seemed to be a fps hit in the 172.
Puffergas
 
Posts: 56
Joined: Thu Jan 02, 2020 2:09 am

Re: SVG parser performance (from Cessna C182T)

Postby Hooray » Wed Jun 10, 2020 1:59 pm

there's debug.benchmark() to profile a Nasal function - to profile a whole code block, just wrap it inside an anonymous function, as per:

debug.benchmark(
func() {
CODE_BLOCK;}
()
};

This can then also be used to compare the performance of svg.nas vs. the native approach - i.e. by adapting the code snippet to go through parsesvg, along these lines:

Code: Select all
debug.benchmark(

func() {
var (width, height) = (512,512);
                                     
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc)
var myCanvas = canvas.new({
                                          "name": "Canvas parsesvg() benchmark",   # The name is optional but allow for easier identification
                                            "size": [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]
                                              "view": [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]
                                                                      # which will be stretched the size of the texture, required)
                                                                        "mipmapping": 1       # Enable mipmapping (optional)
                                                                        });

# set background color
myCanvas.set("background", canvas.style.getColor("bg_color"));

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

var window = canvas.Window.new([width,height],"dialog");
window.setCanvas(myCanvas);
                                                                       
 # path is relative to $FG_ROOT (base package)
 var path = "Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.svg";
 var svg_symbol = root.createChild('group');
canvas.parsesvg(svg_symbol, path);

}()
);


This would go through the conventional parsesvg/svg.nas code paths and not use any native OSG machinery, but instead use Canvas.Path/Shiva VG respectively.
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: SVG parser performance (from Cessna C182T)

Postby jsb » Sat Jun 13, 2020 9:42 am

Maybe I missed something but you use an image element which IIRC is a "bitmap". That is completly different from what the SVG parser does, e.g. putting the vector data into the property tree so you can animate each object separately. I do not see how this would be possible with the OSG plugin? *IF* it renders the SVG into a texture, the vector data is gone -> no animation of elements -> useless for canvas instruments.
jsb
 
Posts: 285
Joined: Sat Oct 25, 2014 9:17 pm
Location: Hamburg, Germany
Callsign: D-JSB
Version: next
OS: Win7/Linux

Re: SVG parser performance (from Cessna C182T)

Postby Hooray » Sat Jun 13, 2020 10:39 am

No, you didn't miss anything - you hit the nail on the head, because that's exactly the kind of discussion I was hoping to have here. So it's really appreciated !

First of all, there are elements that don't need to be dynamic at all - think the "fascia" of the FG1000 - that doesn't need to be dynamically "animated". And there are other MFD elements that can be treated as "static", which means less init/runtime overhead.

But like you say, the real issue is dealing with dynamic elements - since those are currently represented as a scene graph internally, each of which is addressable via a corresponding Canvas.Element and its helper methods (properties) - i.e. to set translations, scaling etc

Right now, this is accomplished by parsing each SVG file in scripting space, using a sub-classed variant of our own XML parser, which then builds the corresponding scene graph hierarchy by traversing all elements in turn (text nodes, paths, raster images).

I have been profiling parsesvg/svg.nas extensively, using different SVG files - and especially the really complex ones (several 100 kbytes in size) add up considerably, due to hundreds of context switches between Nasal and C++.

Stuart and James suggested to move parts of svg.nas back into C++ to reduce the runtime footprint - which is why I pointed out that OSG already comes with SVG handling support by using librsvg - in fact, people only need to install librsvg-dev to see for themselves how much of a difference that makes, by going through ReaderWriter instead.

This is because:

Like you said, the issue remains how to deal with SVG elements that need to be dynamically animated, which is what is currently accomplished by using a handle for each dynamic element (its SVG id) and then associating a Canvas.Element instance with it (Canvas.Text, Canvas.Path or Canvas.Image)

Thus, the first step would be to annotate those elements that can indeed by treated as static raster images (think the fascia) - this could be accomplished using a hash/vector respectively, so that the parser creates a Canvas.Image instead of a Canvas.Path/Canvas.Text for such elements.

Note: SVG <text> nodes going through librsvg/cairo will internally not go through osgText at all - which also has certain ramifications, i.e. much greater flexibility for text handling than what we currently provide/support in svg.nas or Canvas.Text specifically.

So, getting back to your question: The reader/writer plugin's code is really straightforward currently, because it only returns a single osg::Image:

https://github.com/openscenegraph/OpenS ... G.cpp#L105
Code: Select all
osg::Image* createImage(RsvgHandle *handle, unsigned int width, unsigned int height) const
        {
                RsvgDimensionData dimensionData;
                rsvg_handle_get_dimensions( handle, &dimensionData);
                // If image resollution < 128, cairo produces some artifacts.
                // I don't know why, but we check the size...
                if (width < 128) width = 128;
                if (height < 128) height = 128;
                //width = osg::Image::computeNearestPowerOfTwo(width);
                //height = osg::Image::computeNearestPowerOfTwo(height);
                osg::Image *image = new osg::Image();
                image->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
                image->setPixelFormat(GL_BGRA);

                // make sure the image data is cleared before we write to it.
                memset(image->data(), 0, image->getTotalSizeInBytesIncludingMipmaps());

                cairo_surface_t *cairo_surface = cairo_image_surface_create_for_data(image->data(),
                                        CAIRO_FORMAT_ARGB32, width, height, image->getRowSizeInBytes());
                cairo_t *cr = cairo_create(cairo_surface);
                cairo_scale(cr,((float)width)/dimensionData.width, ((float)height)/dimensionData.height);
                rsvg_handle_render_cairo(handle, cr);

                cairo_destroy(cr);
                cairo_surface_destroy(cairo_surface);

                image->flipVertical();
                return image;
        }


(only the last 10 lines are relevant here)

The point being, cairo is told to generate an image using its rsvg_handle_render_cairo() API

This will take the whole DOM and render it into the corresponding raster image.

Next, let's look at the cairo docs: https://developer.gnome.org/rsvg/stable ... nder-cairo
Draws a loaded SVG handle to a Cairo context. Drawing will occur with respect to the cr 's current transformation: for example, if the cr has a rotated current transformation matrix, the whole SVG will be rotated in the rendered version.

This function depends on the RsvgHandle's DPI to compute dimensions in pixels, so you should call rsvg_handle_set_dpi() beforehand.

Note that cr must be a Cairo context that is not in an error state, that is, cairo_status() must return CAIRO_STATUS_SUCCESS for it. Cairo can set a context to be in an error state in various situations, for example, if it was passed an invalid matrix or if it was created for an invalid surface.



However, we can also selectively re-render only a certain part of the DOM using the concept of a "layer", let's look at this: https://developer.gnome.org/rsvg/stable ... nder-layer

Renders a single SVG element in the same place as for a whole SVG document.

This is equivalent to rsvg_handle_render_document(), but it renders only a single element and its children, as if they composed an individual layer in the SVG. The element is rendered with the same transformation matrix as it has within the whole SVG document. Applications can use this to re-render a single element and repaint it on top of a previously-rendered document, for example.

Element IDs should look like an URL fragment identifier; for example, pass "#foo" (hash foo) to get the geometry of the element that has an id="foo" attribute.


This means, this API could be used to selectively re-render only a certain part of the SVG document.

Thus, we could use this machinery to patch the current SVG plugin to hook it up to the sc::Element machinery to selectively update/re-render only certain parts of the document.

As far as I know, FlightGear already comes with custom/forked ReaderWriter plugins for FlightGear specific files, so we could use the same approach to customize the SVG ReaderWriter plugin to return a Canvas::Group instead, which would mean that we'd retain the flexibility of a scene graph and of the Canvas in particular.

That is why I asked Stuart how we'd like to see this structured, when he suggested that people interested in working on this, are encouraged to provide patches.

Personally, I don't see much of a benefit in re-implementing svg.nas in C++ if we can use a "proper" OSG-enabled SVG parser that comes with better performance/conformity (and threading support), at the mere cost of adding librsvg to our dependencies - but maybe, I am missing something ?

Basically, we can sub-class sc::Element to create something like sc::SVGImage which goes through OSG's ReaderWriterSVG plugin but instead of returning an osg::Image it returns a sc::Group structure, i.e. a proper osg scene graph consisting of sc::Image elements that can be addressed via the sc::Element API, to selectively update/re-render parts of the raster image as needed, without causing any Nasal overhead whatsoever, i.e. all handled in a background thread using native code.


For some features, it simply doesn't make sense to represent them in the form of low-level building blocks like OSG text nodes, OpenVG paths or osg::Image raster images assembled inside the main loop - either because of the corresponding Nasal overhead, or because of the required property I/O (and possibly even both)

For instance, a synthetic terrain view, loading/translating 3D models or PDF files would also not be implemented using these low level building bocks, but instead new dedicated Canvas elements should be used:


http://wiki.flightgear.org/Howto:Extend ... _3D_models
Image

http://wiki.flightgear.org/Canvas_Sandbox#CanvasPDF
Image

http://wiki.flightgear.org/Canvas_View_Camera_Element
Image

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 4 guests