if(($ACT == 'edit' || $ACT == 'preview') && $INFO['editable']){ ?> } else { ?> } ?>
This shows you the differences between two versions of the page.
wiki:ardour_windows_gdk_and_cairo [26.10.2014 01:52] rgareus |
wiki:ardour_windows_gdk_and_cairo [28.10.2014 15:24] (current) 81.57.94.90 [Ardour, Windows, Gdk/GTK and Cairo] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Ardour, Windows, Gdk/GTK and Cairo ====== | ====== Ardour, Windows, Gdk/GTK and Cairo ====== | ||
- | Lab Notes. | + | Lab Notes.\\ |
- | Analysis the //slowness// and high CPU-usage of the Ardour3 GUI on the windows platform. | + | Analysis the //slowness// and high CPU-usage of the [[http://ardour.org|Ardour3]] GUI on the Windows platform. |
===== Findings ===== | ===== Findings ===== | ||
Line 8: | Line 8: | ||
First a bit of background: | First a bit of background: | ||
- | * All of GDK/WIN32 is using a Windows Drawing Context (DC).\\ | + | * All of GDK/WIN32 is using a Windows Drawing Context (DC).\\ |
- | * When a cairo-surface is used, GdkDrawables use ''cairo_win32_surface_create(HDC*)''.\\ | + | * When a cairo-surface is used, GdkDrawables use ''cairo_win32_surface_create(HDC*)''.\\ |
- | * That cairo-surface has the geometry of the GC (not the size of the GdkDrawable).\\ | + | * That cairo-surface has the geometry of the DC (not the size of the GdkDrawable).\\ |
- | * operations on the cairo-surface are flushed to the DC. Gdk does not use an extra copy step (cairo does that). | + | * operations on the cairo-surface are flushed to the DC. Gdk does not use an extra copy step (cairo does that). |
==== A: Cairo BitBlt ==== | ==== A: Cairo BitBlt ==== | ||
Line 17: | Line 17: | ||
When using a cairo-surface backed by Windows Drawing Context: | When using a cairo-surface backed by Windows Drawing Context: | ||
- | - The first cairo-operation on that surface which need an Alpha-Channel creates a fallback-surface (RGB32). This is done because windows DC does not properly support 32bit RGBA. | + | - The first cairo-operation on that surface which need an Alpha-Channel creates a fallback-surface (ARGB32). This is done because windows DC does not properly support 32bit RGBA. |
- | - Then, cairo calls [[http://msdn.microsoft.com/en-us/library/windows/desktop/dd183370%28v=vs.85%29.aspx|BitBlt]] to copy the whole GC area to that new surface. | + | - Then, cairo calls [[http://msdn.microsoft.com/en-us/library/windows/desktop/dd183370%28v=vs.85%29.aspx|BitBlt]] to copy the whole DC area to that new surface. |
- Cairo continues to operate on the fallback surface. | - Cairo continues to operate on the fallback surface. | ||
- Eventually, Cairo ''BitBlt''s back only the changed parts when the surface is flushed or destroyed. | - Eventually, Cairo ''BitBlt''s back only the changed parts when the surface is flushed or destroyed. | ||
- | - A surface_flush also invalidates and destroys the fallback-surface (next cairo operation on that surface: go back to ''[1]''). | + | - A surface_flush also invalidates and destroys the fallback-surface (the next cairo operation on that surface go back to **1.**). |
Line 31: | Line 31: | ||
and more specifically, how Ardour uses GTK for its cairo canvas. | and more specifically, how Ardour uses GTK for its cairo canvas. | ||
- | In a lot of cases in Ardour the BitBlt (2.) is not even needed. Ardour draws its own background. | + | In a lot of cases in Ardour the BitBlt (**2.**) is not even needed. Ardour Canvas Items draw their own background. |
- | Everything that is ''BitBlt'' during step 2 above is overwritten. This is in particular true for the largest area (main canvas) of ardour. | + | All pixels that are ''BitBlt'' during step 2 above are overwritten. This is in particular true for the largest area (main canvas) of Ardour. |
- | In that case it would be helpful to have an API to tell cairo to skip the initial ''BitBlt'' -- or rather tell gdk to not use a win32 surface in the first place. | + | In that case it would be helpful to have an API to tell cairo to skip the initial ''BitBlt'' -- or rather tell Gdk to not use a win32 DC backed surface in the first place. |
- | For other cases it is however essential to initially copy the DC to the fallback surface in cases where there's composition (gtk-box background, ardour-cairo foreground with round-edges (alpha) or text). In ardour's case that's mostly static things like buttons, toolbar when the actual DC geometry is small. | + | In cases where there is composition, it is however essential to initially copy the DC to the fallback surface (e.g. gtk-box background, ardour-cairo foreground with round-edges (alpha) or text) in order to properly display |
+ | edges or round corners. In ardour's case this is only relevant for mostly static widgets like buttons or the toolbar when the actual DC geometry is small. | ||
- | Ideally Gdk should be changed to always use a cairo image/software-surface (never GC backed) and only synchronize the modified-areas to the GC as a last step (GdkWindow uses a cairo backing store already). | + | Ideally Gdk should be changed to always use a cairo image/software-surface (never DC backed) and only synchronize the modified-areas to the DC as a last step (GdkWindow uses a cairo backing store already). |
- | The cairo/pixmap software implementation beats various HW acceleration methods for 2D drawing performance (also on other platforms) and the cairo image/software surface works reliably regardless of underlying hardware. | ||
===== Other Findings ===== | ===== Other Findings ===== | ||
+ | |||
+ | While the above issue has been analyzed in detail, (code study, gdb and print'ing) the facts below | ||
+ | were established using //printf-debugging// for the most part. | ||
==== B: Gdk Backing Store ==== | ==== B: Gdk Backing Store ==== | ||
Every GdkWindow has a backing store (gdkwindow.c): | Every GdkWindow has a backing store (gdkwindow.c): | ||
- | '' | ||
- | gdk_window_begin_paint_region()'' creates a new temporary cairo-surface (using GdkDrawable API), it is destroyed again in ''gdk_window_end_paint_region()''. | ||
- | Some operations on the backing-store are RGB region-copies only (no alpha) and hence do not have the BitBlt problem, but when ardour is active the vast majority or expose-event do trigger the issue. | + | ''gdk_window_begin_paint_region()'' creates a new temporary cairo-surface (using GdkDrawable API), it is destroyed again in ''gdk_window_end_paint_region()''. |
+ | |||
+ | Some operations on the backing-store are RGB region-copies only (no alpha) and hence do not have the BitBlt problem, but when ardour is active the vast majority or expose-events do trigger the issue. | ||
-> BitBlt the complete window. zzzZZZ. | -> BitBlt the complete window. zzzZZZ. | ||
+ | |||
+ | (possible workaround: unset [[https://developer.gnome.org/gtk2/stable/GtkWidget.html#gtk-widget-set-double-buffered|double buffering]]) | ||
==== C: Gdk/Windows Drawing Context Size ==== | ==== C: Gdk/Windows Drawing Context Size ==== | ||
- | The windows DC size is a region-combine of all invalidated widgets for a given expose. e.g. When invalidating a 16x16 widget top-left and one bottom-right: the drawing-context and hence the fallback-surfaces needed to BitBlt is the complete window. | + | The windows ''DC'' geometry is a region-combine of all invalidated widgets for a given expose. e.g. When invalidating a 16x16 widget top-left and one bottom-right: the drawing-context and hence the fallback-surfaces needed to BitBlt is the complete window. |
==== D: Compositing ==== | ==== D: Compositing ==== | ||
- | GdkDrawable background and pixmaps, etc are painted with GC, text is done with pango-cairo. There are often multiple surface-flushes for every GdkDrawable expose, each of which implies a ''BitBlt''. | + | GdkDrawable background and pixmaps, etc are painted with ''GC'', text is done with pango-cairo. There are often multiple surface-flushes for every GdkDrawable expose, each of which implies a ''BitBlt''. |
==== E: Surface Allocation ==== | ==== E: Surface Allocation ==== | ||
Line 83: | Line 87: | ||
{{ :wiki:a3_no_bitblt.png?600 |}} | {{ :wiki:a3_no_bitblt.png?600 |}} | ||
+ | |||
+ | compare to the image on the left (with BitBlt). | ||
+ | |||
+ | {{:wiki:a3_win_3386.png?100 }} | ||
Various hacks would be possible to address the issue. | Various hacks would be possible to address the issue. | ||
- | However there's not a single simple point to easily work around this. | + | <del>However there's not a single simple point to easily work around this</del>. |
- | Probably the easiest way to mitigate the performance hog is to provide a custom GdkDrawable implementation for the main canvas (which does not use a hardware backed surface). | + | Probably the easiest way to mitigate the performance hog is to provide a custom GdkDrawable implementation for the main canvas (which does not use a hardware backed surface) and keep using default GdkDrawable for all other widgets. A similar solution would be to make the backing-store of GdkWindow persistent and access it directly. |
+ | |||
+ | A quick hack has been prototyped. The cairo-surface user-data API is used to flag select cairo/win surfaces to not BitBlt (ardour's main canvas & meters): [[https://gist.github.com/x42/ef0b0f6e70d416b3af17|patch for ardour, libcairo]] ((In this case a dedicated user-data key - known to ardour and libcairo - is used to tunnel the information though gdk/gtk which remains unchanged.)). | ||
- | Still, while performance on Linux & OSX is OK, the expose strategy (see B -> F above) is abysmal and unsuitable for Ardour in general. It only works because CPUs are fast :) | + | Still, while performance on Linux & OSX is OKish, the expose and invalidation strategy (see B -> F above) is abysmal and unsuitable for Ardour in general. It only works because CPUs are fast :) |
The proper way forward: **get rid of GdkDrawable on _all_ platforms.** | The proper way forward: **get rid of GdkDrawable on _all_ platforms.** | ||
Line 96: | Line 106: | ||
Use a single cairo [ARGB32] surface for the whole GUI and directly map its data to e.g an openGL texture. | Use a single cairo [ARGB32] surface for the whole GUI and directly map its data to e.g an openGL texture. | ||
- | The ardour canvas already has its own event management and puGL can provide the rest. That'll still leave window-management, box (and table?) layout packing, to be done, but those are manageable. | + | Cairo's ''...surface_mark_dirty()'', damage-reduction and surface-flushing algorithms are very efficient. |
+ | |||
+ | The [[http://ardour.org/canvas.html|ardour canvas]] already has its own event management and [[http://drobilla.net/software/pugl/|puGL]] can provide the rest... That will still leave window-management, box (and table?) layout packing to be done, but those are manageable. | ||
The hard part will be tree-views, file-manager (and maybe menu), maybe some hybrid solution can be done for those cases (or code copy/paste of select GTK implementations sans Gdk). | The hard part will be tree-views, file-manager (and maybe menu), maybe some hybrid solution can be done for those cases (or code copy/paste of select GTK implementations sans Gdk). | ||
+ | |||
+ | The cairo/pixmap software-surface implementation beats various Hardware-accelerations for 2D drawing performance (on any platform with most graphics chipsets/drivers). And more importantly, the cairo image/software surface works reliably regardless of underlying hardware. | ||
+ | Until cairo 2D HW accelleration improves in a way useful to ardour ((Internally ardour uses a lot of cairo surfaces and patterns for waveform image cache, meter-gradients, button insets. They are currently not backed by a hardware.)), using a software image-surface for the complete top-level window will be most efficient. | ||
+ | |||
+ | ===== Versions Used ===== | ||
+ | |||
+ | * Ardour 3.5-3386-gec92524 (ardour-build-tools 11919e4, x86_64-w64-mingw32-gcc (GCC) 4.9.1) | ||
+ | * glib-2.42.0 | ||
+ | * cairo-1.14.0 + [[https://gist.github.com/x42/c875f8910c9186c013fb|BitBlt debug diff]] | ||
+ | * gtk+-2.24.24 , gtkmm-2.24.4 | ||
+ |