wx:gtk potential drawing optimisation when using non-native controls.

24 views
Skip to first unread message

O'Neill, Owen

unread,
Nov 23, 2009, 10:38:03 AM11/23/09
to wx-...@googlegroups.com


Hi everyone,

Apologies in advance for the length of this post, Im trying to describe something rather complex.

There are a couple of possibilies, either I have grabbed completely the wrong end of the stick in the interaction between widgets and gtk,

or Ive identified potential for a significant painting optimisation on wxWidgets GTK when using non-native controls and buttons (possibly only with gtk on Xserver).

Before I launch into the post below in anger as a minor aside is there any reason that freeze() and thaw() are not implemented for gtk since the underlying GDK has them ?

In considering the bulk of the post below then please let me know if Ive found something valid, and also the prospects of the proposed fix working.

right ready ? here we go.


This is going to get really complicated to explain, but I believe this is the most significant cause of our ‘slow screen draw’ problem

Reading this page is fundamental to understanding the problem, along with understanding how wxWidgets maps onto GTK:gdk

http://library.gnome.org/devel/gtk/stable/chap-drawing-model.html

Note the major difference in how child ‘window’ and ‘non-window’ widgets are redrawn and that for hierarchical redrawing “This process is applied recursively for all the GTK_NO_WINDOW descendants of the original widget.”

And that….” When you draw on a GdkWindow, your drawing gets clipped by any child windows that it may intersect. Sometimes you need to draw over your child windows as well; for example, when drawing a drag-handle to resize something. In this case, turn on the GDK_INCLUDE_INFERIORS subwindow mode for the GdkGC which you use for drawing”

Also pay attention to the section on double buffering and in particular automatic double buffering.

Now lets turn to wxWidgets. The base window class has several requirements that don’t map too well onto gdk, which are that it can be a container for further windows and controls and that the client area can be drawn on, this resulted in the creation of the gtk_pizza class which performs these tasks. At a GTK level this derives from GtkScolledWindow – So on GTK on Xserver this means every control is a separate child window.

This must have been seen to be a problem, since some classes derived from wxWindow (e.g. wxButton) use a ‘native’ gtk button instead of a gtk_pizza, hence at a GTK level they are ‘non-window’ widgets.

(still with me ? – said this was going to get complicated)

….now our control (which is essentially a custom bitmap button) is derived from wxWindow, not wxBitmapButton. This means at a GTK level it is a SEPARATE CHILD WINDOW, not a ‘non-window’ widget.

So what I believe is happening is that when a window is sent an ‘expose’ the background (clipped to it’s children) is painted – the duration of this call being double buffered – and after this expose call returns it is shown – so we get an outer frame with ‘holes’ where all the buttons are showing the previous dialog through it

Each child gtk WINDOW in turn then gets sent an expose – each one of these individual ‘exposes’ being double buffered, but there is no overall double buffering of the entire process. (I believe I have proved this is the case by switching on visual updates in the GTK runtime and it looks like this)

So the short term fix is to derive from wxBitmapButton which has the additional bonus of shoving the bitmap right down to the gtk layer. (there is a flag on the style to have 'no border' and some other gtk level styles we might want to tinker with)

I have knocked up a demo of this and it partially works - I think it falls apart because I'm re-generating the bitmap in the 'onsize' handler and the 'set bitmap' method on the wxBitmapButton currently queues up a resize. - it does demonstrate the faster repainting - and I have a tool that hilights and delays the redrawing of the invalidated regions by manipulating the gtk update flag ( updates =

 Visual feedback about window updates  http://library.gnome.org/devel/gtk/unstable/gtk-running.html )

This does not solve the more general situation where we have other ‘custom’ controls which are made up of several other wxWindows. If the custom controls all do their own painting then we can blit to a device context under our own control in the wxWidgets layer, however a better general solution would be to carry out the fix at the widget/gtk interface layer so it works for all controls.

On the surface of it I’m thinking that in the ‘parent’ control we turn on ‘GDK_INCLUDE_INFERIORS’ during the processing of the expose event – then we see which children (both window and non-window) gtk widgets are in the region to be painted (or maybe the area)(area is a simple single rectangle, region is a collection). Then for each of these widgets we redirect it’s output to the parent gtk window using

gdk_window_redirect_to_drawable
http://library.gnome.org/devel/gdk/unstable/gdk-Windows.html#gdk-window-redirect-to-drawable
http://library.gnome.org/devel/gtk/stable/GtkWidget.html#gtk-widget-intersect
 (a gtkWindow derives from a gtkDrawable)

then prod the child window to paint itself using gtk_widget_draw to perform an immediate update, then cancel the redirection.

- or it may be better to allow the ‘normal’ processing of the non-window gtk widgets. I’m not entirely sure how the return value from the expose callback controls further processing.

Problems still remain in that (1) the theory is good but I have not tested it (2) how do we block the subsequent expose events sent to the children so they do not draw twice (3) the ordering of parent / children expose calls may change depending on whether we are showing an entire window – or uncovering (part of) an existing window by the user moving the window on top of it.


Many thanks

Owen


Robert Roebling

unread,
Nov 23, 2009, 11:14:05 AM11/23/09
to wx-...@googlegroups.com

O'Neill, Owen wrote:

> Before I launch into the post below in anger as a minor aside is there
> any reason that freeze() and thaw() are not implemented for gtk since
> the underlying GDK has them ?

This has been implemented for GTK+ (in TRUNK at least).


> Now lets turn to wxWidgets. The base window class has several
> requirements that don’t map too well onto gdk, which are that it can
> be a container for further windows and controls and that the client
> area can be drawn on, this resulted in the creation of the gtk_pizza
> class which performs these tasks. At a GTK level this derives from
> GtkScolledWindow

In TRUNK it only does that if it is supposed to have scrollbars,
but that is secondary here.

> – So on GTK on Xserver this means every control is a separate
> child window.
>

> This must have been seen to be a problem, since some classes derived
> from wxWindow (e.g. wxButton) use a ‘native’ gtk button instead of a
> gtk_pizza, hence at a GTK level they are ‘non-window’ widgets.

This is fundamentally wrong. Most native GTK+ widget are "window"
widgets and this includes GtkButton. Just look at gtk_button_realize
in gtkbutton.c

> ….now our control (which is essentially a custom bitmap button) is
> derived from wxWindow, not wxBitmapButton. This means at a GTK level
> it is a SEPARATE CHILD WINDOW, not a ‘non-window’ widget.

Just as the native counterpart.

> So what I believe is happening is that when a window is sent an
> ‘expose’ the background (clipped to it’s children) is painted – the
> duration of this call being double buffered – and after this expose
> call returns it is shown – so we get an outer frame with ‘holes’ where
> all the buttons are showing the previous dialog through it

> Each child gtk WINDOW in turn then gets sent an expose – each one of
> these individual ‘exposes’ being double buffered, but there is no
> overall double buffering of the entire process. (I believe I have
> proved this is the case by switching on visual updates in the GTK
> runtime and it looks like this)

Cannot be different when using native controls.

> On the surface of it I’m thinking that in the ‘parent’ control we turn
> on ‘GDK_INCLUDE_INFERIORS’ during the processing of the expose event –
> then we see which children (both window and non-window) gtk widgets
> are in the region to be painted (or maybe the area)(area is a simple
> single rectangle, region is a collection). Then for each of these
> widgets we redirect it’s output to the parent gtk window using
>
> gdk_window_redirect_to_drawable
> http://library.gnome.org/devel/gdk/unstable/gdk-Windows.html#gdk-window-redirect-to-drawable
> http://library.gnome.org/devel/gtk/stable/GtkWidget.html#gtk-widget-intersect
> (a gtkWindow derives from a gtkDrawable)
>
> then prod the child window to paint itself using gtk_widget_draw to
> perform an immediate update, then cancel the redirection.
>
> - or it may be better to allow the ‘normal’ processing of the
> non-window gtk widgets. I’m not entirely sure how the return value
> from the expose callback controls further processing.

I haven't thought about the redirect to drawable code, but please
bear in mind that the entire drawing system of GDK has been rewritten
in GTK+ 2.18 [1] and every trick we do now may have different effects
for later GTK+ versions. But please, try using GTK+ 2.18 first before
designing such fundamental changes.

This doesn't mean at all that wxGTK drawing and child window
clipping cannot be improved...

Robert

[1]
http://library.gnome.org/devel/gtk/2.18/gtk-migrating-ClientSideWindows.html


Paul Cornett

unread,
Nov 23, 2009, 12:33:51 PM11/23/09
to wx-...@googlegroups.com
Robert Roebling wrote:
> O'Neill, Owen wrote:
...

> > This must have been seen to be a problem, since some classes derived
> > from wxWindow (e.g. wxButton) use a ‘native’ gtk button instead of a
> > gtk_pizza, hence at a GTK level they are ‘non-window’ widgets.
>
> This is fundamentally wrong. Most native GTK+ widget are "window"
> widgets and this includes GtkButton. Just look at gtk_button_realize
> in gtkbutton.c

No, GtkButton is GTK_NO_WINDOW, look at gtk_button_init. It uses a
GdkWindow, but it is an invisible, input-only one.

oweno

unread,
Nov 23, 2009, 3:13:08 PM11/23/09
to wx-dev

Aha, thanks for the information on the upcoming gtk changes, I'll try
it before I do anything else !

As far as I can tell I'm currently using gtk2.12 so I will report back
if I see any behaviour changes with a new version.
(it might take me between a few days and a week to try it, it's
suddenly got really busy round here !)

Taking what you've said on board and then looking more closely at the
output from the spy like tool I can now see what you mean about
multiple windows actually being the same Xwindow. I see that for each
of my custom buttons the Xid of the gtkscrolled window is the same,
but each contained gtkpizza has a different Xid. (not that I fully
understand that since gtkpizza derives from gtkscrolledwindow, I think
this reveals a gaping hole in my understanding of gtk)

The situation is slightly different when I look at the same screen
using wxButtons (which use GtkButton), here every GtkButton uses the
same Xid, but in addition every label on every button uses the same
Xid.

(and I think I'm almost certainly getting lost somewhere in my
understanding of the mapping between wxWindows, gtk and gdk ones and
Xwindows !)
(apologies if I'm sluggish to respond - messages off the list for some
reason aren't making it to my main email account)

many thanks
Owen

oweno

unread,
Nov 23, 2009, 3:18:53 PM11/23/09
to wx-dev

oh and I need to clarify something in my original post about freeze
and thaw,

it was the lack of implementation for them in the wx widgets class
(wxGtkWindow) that I was wondering about, but I think the comments
about them now being implemented in the gtk trunk explain it so I'll
try hooking them up and seeing what happens.

thanks
Owen


oweno

unread,
Nov 26, 2009, 7:51:29 AM11/26/09
to wx-dev

Hiya,

Right I've just about managed to test at least some of this,
(unfortunately some new version of something somewhere trashed my GDM
so had to rebuild the box from scratch, joys of development !)

At the same time as upgrading to gtk 2.18 I also (with hindsight
stupidly) also upgraded widgets version to the 2.9 truck.
I only say stupidly because I can't now seperate out the effects of
the two changes rather than some negative aspect of the upgrade.

I can safely say the behaviour is different, however I'm not entirely
sure how, I think I might have to build new GTK and widgets versions
for the ARM target machine to be sure (which is an entire world of
pain). The reason I say I'm not sure is that the gtkparasite tool I'm
using sometimes draws stuff really fast, and sometimes still the
previous slow way, which is different behaviour to that seen
previously.

I no longer have lots and lots of GtkScrolledWindows containing pizza
objects - instead I have just lots and lots of wxPizza objects. I
guess this is due to the previously referred to WX changes with
respect to scrollbars etc.

However each one of these pizza objects does still have a seperate
XID, so when comparing a wxWindow full of other child wxWindows to a
wxWindow full of wxButtons there is a big difference in the number of
x windows involved. (in the former case each child window has it's own
XID, in the latter case each XID is the same as the parent window)

I think I have encounted a bug in this configuration - I really need
to build a simple app to prove it though before logging a ticket, when
creating a toplevelwindow (in my case a wxFrame, if the default frame
style is used, all is good, however changing the frame style to
wxNO_BORDER (on it's own not combined with any other style) means you
get no window at all displayed. It's quite distressing at first until
you twig it's just down to the window style :-)
With the previous version combination this used to work just fine (I
guess whether or not it should have done is open to debate ?)

So in summary I think the new version combination does offer some
improvement in that there are fewer widgets involved at the gtk layer,
but I need to do more investigating to conclusively prove if it's
changed the redrawing behaviour by very much.

One good thing is that it meant I found a fix for my previous problem
when using a wxBitmapButton, so while it's completely selfish of me
(development timescales being upon me and I would like to go home one
weekend) I think I might revert to the previous versions and just use
a wxbitmapbutton for now. (I think the sizer behaviour has changed a
bit which we don't have time to sort right now either)

At some point I'll try and build a simple app to prove / disprove the
style problem, as well as probing the redrawing behaviour some more !

many thanks
Owen

Reply all
Reply to author
Forward
0 new messages