2

I am creating a GTK+ 3 application which draws an animation using Cairo in a GtkDrawingArea widget. I get visual glitches, such as the ones observed in the images below. These appear only for a single frame, there may be none, or one, or more than one per frame. I am asking for help in identifying the possible issue. Here are the details of my code:

In my main method before starting the gtk_main() loop I hook up a timeout.

g_timeout_add(50, queue_draw, gtk_builder_get_object(builder, "window")); 

"window" is the id of my GtkWindow. The queue_draw function is as follows:

gboolean queue_draw(gpointer user_data)
{
  gtk_widget_queue_draw(GTK_WIDGET(user_data));
  return TRUE;
}

I would think that I could pass the GtkDrawingArea object to this function rather than my whole GtkWindow, but the animation disappears in that case. I am also interested comments on this behavior but it is not my main question.

The draw signal of my GtkDrawingArea is hooked to a function gboolean drawing_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data). Inside this method I draw my 3D bar chart by a lazy painter's algorithm, each bar being made of three parallelograms, and the bars being drawn in z order.

It is not a problem with my computer failing to keep up with the frame rendering, somehow corrupting a frame buffer. I set the timeout to 1000ms to capture the images below.

I have not made any calls to gtk_widget_set_double_buffered().

I was not able to observe the issue on Windows Subsystem for Linux (WSL) using XMing as the X server, which makes me think it might be a library issue, or some poorly defined behavior.

The first image is of the visual glitch during my program in normal operation. In the second, I modified the code and fixed the height of the bars to a gentle gradient. This gives a much better view of the issue, but it still very puzzling.

image of glitch image of glitch

development library package details:

$ dpkg --list | egrep 'lib(cairo|gtk).*-dev'
ii  libcairo2-dev:amd64                                1.15.10-2ubuntu0.1                           amd64        Development files for the Cairo 2D graphics library
ii  libgtk-3-dev:amd64                                 3.22.30-1ubuntu3                             amd64        development files for the GTK+ library

library metainformation details:

$ pkg-config --modversion gtk+-3.0 glib-2.0 gdk-pixbuf-2.0 cairo
3.22.30
2.58.1
2.36.11
1.15.10

x11 details:

$ xdpyinfo | head -n 5
name of display:    :0
version number:    11.0
vendor string:    The X.Org Foundation
vendor release number:    12001000
X.Org version: 1.20.1

Linux details (actually Zorin OS 15 not Ubuntu 18.04):

$ uname -a
Linux <hostname> 4.18.0-25-generic #26~18.04.1-Ubuntu SMP Thu Jun 27 07:28:31 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

edit: Here is another very interesting screenshot of the issue.

image of glitch

Void Star
  • 2,401
  • 4
  • 32
  • 57

1 Answers1

3

Fist, calling gtk_widget_queue_draw on the main window makes no sense as only the GtkDrawingArea need to be updated at a high frequency. Redrawing controls you didn't interact with adds overhead for no value.

Next, I see nothing in your UI that wants to have the drawing area redrawn constantly. You should redraw in reaction to events: a parameter value that has changed in your left panel, or the user clicking in the drawing area to change the viewpoint (if that's something you support). You may event fire up a timeout in reaction to control changes and reinitialize that when another one is changed, so that it lets half a second to the user to change all the settings they want and then display the final result instead of the intermediate changes. This can be useful when you have controls with a value that can change fast like the GtkSpinButtons you use.

From your testing, if calling the drawing code every second instead of every 50ms gives you that result, then the problem is most probably in your drawing code, not on the way GTK+ draws it. To make sure that's the case, you can disable the timeout source that redraws, and add a button that when clicked triggers a single redraw. This way the whole frequency thing is out of the equation, and you should still have those rendering bugs.

The next step is to show us the code in the draw signal handler, as the bug probably lays there. If you want to debug it, you probably can split the drawing so that you save the cairo surface you're drawing to in a file after each histogram bar is painted. Then seeing the changes image by image with an image editor, you will see on which image the problem happens. With some logging, you will see which values trigger the problem.

liberforce
  • 11,189
  • 37
  • 48
  • I agree, redrawing the whole GtkWindow makes no sense. If I queue a redraw for the GtkDrawingArea, however, my graphics simply disappear. I understand drawing is usually event driven, but keep in mind this is an animation. I am queuing the draw regularly in order to animate my graphics. Suggestions are welcome. With the height of the bars hard-coded, the code makes the exact same sequence of Cairo calls every time, and yet these glitches appear randomly for only a single frame sometimes. – Void Star Jul 04 '19 at 16:05
  • 1
    The graphics shouldn't disappear when redrawing a GtkDrawingArea. It should just schedule a call to your callback handling the `draw` signal from the event loop, which would draw your scene. If your hardcoded values trigger the bug, then as I said, save the intermediate images to a file and make sure that's the drawing pipeline that is at fault, not the display one. – liberforce Jul 08 '19 at 13:06
  • Haven't gotten to that experiment yet but I tried setting gtk_widget_set_double_buffered FALSE on my drawing area, now the behavior is very strange. If I queue the draw on the window, the normal glitchy behavior occurs. If I queue the draw on the drawing area, the glitchy behavior occurs but there are two plots overlaid on each other and offset horizontally a bit. I suspect that the glitch could be due to the draw handler being called twice concurrently on separate threads. But why would that be? Why is it offset somehow? None of this makes much sense. – Void Star Jul 09 '19 at 03:38
  • Additionally the offset plot in the background disappears as I resize the window and appears again once I stop resizing, but the glitches continue regardless. Perhaps these are two separate issues. – Void Star Jul 09 '19 at 03:42
  • Are you using threads? GTK uses an event loop, so unless you're creating your own threads, you can't have the drawing interrupted. – liberforce Jul 09 '19 at 13:29
  • I am not, and what you're saying makes sense. Last night I tried my program (unmodified) on another computer running Ubuntu 18.04. There were no glitches. But, when I modified the code to queue the draw on the drawing area rather than the window, it disappeared as per usual. These are definitely separate issues. I wonder if I just have an OS bug, but if so, why does it not affect other applications? – Void Star Jul 09 '19 at 21:20
  • I'm sorry but without any code, there's little we can do here... Please post an [MCVE](https://stackoverflow.com/help/minimal-reproducible-example) for more advice. – liberforce Jul 11 '19 at 08:53
  • I agree I haven't had time yet. – Void Star Jul 11 '19 at 16:57
  • You also might share a link to your work if it's using a Free license and is on a public repository. An MCVE is better, but at least that would give food for thought. – liberforce Jul 12 '19 at 08:18