Recent

Author Topic: Drawing to GTK2 offscreen bitmap canvas from thread  (Read 7265 times)

Chronos

  • Full Member
  • ***
  • Posts: 212
    • PascalClassLibrary
Drawing to GTK2 offscreen bitmap canvas from thread
« on: February 18, 2016, 12:05:57 pm »
Hi all,
It is possible to draw safely to offscreen bitmap on Linux with GTK2 widgetset? I used this method with Win32 API and it works on Windows well. Concept is to prepare image in background task so main task is not blocked for several milliseconds and is more responsive. But running same application on linux leads immediately to exception related to gtk2.

Example code would be:
Code: Pascal  [Select][+][-]
  1.   TDrawThread = class(TThread)
  2.     TempBitmap: TBitmap;
  3.     procedure Execute; override;
  4.   end;
  5.  
  6. ...
  7.  
  8. procedure TDrawThread.Execute;
  9. begin
  10.   TempBitmap := TBitmap.Create;
  11.   TempBitmap.SetSize(1000, 600);
  12.   while True do begin
  13.     TempBitmap.Canvas.FillRect(0, 0, TempBitmap.Width, TempBitmap.Height);
  14.     Sleep(1);
  15.   end;
  16. end;
  17.  

Here is stacktrace:
Quote
#0 __GI_raise(6, 6) at ../sysdeps/unix/sysv/linux/raise.c:55
#1 __GI_abort at abort.c:89
#2 __assert_fail_base(0x7ffff7f40029 '%s%s%s:%u: %s%sP'#197#153'edpoklad '#226#128#158'%s'#226#128#156' nespln'#196#155'n.'#10'%n', 0x7ffff772c95d '!xcb_xlib_unknown_req_in_deq', 0x7ffff772c95d '!xcb_xlib_unknown_req_in_deq', 0x7ffff772c92b '../../src/xcb_io.c', 0x7ffff772c92b '../../src/xcb_io.c', 179, 179, 0x7ffff772ce00 'dequeue_pending_request', 0x7ffff772ce00 'dequeue_pending_request') at assert.c:92
#3 __GI___assert_fail(0x7ffff772c95d '!xcb_xlib_unknown_req_in_deq', 0x7ffff772c92b '../../src/xcb_io.c', 179, 0x7ffff772ce00 'dequeue_pending_request') at assert.c:101
#4 ?? at :0
#5 _XReply at :0
#6 XSync at :0
#7 ?? at :0
#8 ?? at :0
#9 RAWIMAGE_CREATEBITMAPS(0x7ffff7f7f8d0, {DESCRIPTION = {FORMAT = RICFRGBA, WIDTH = 1000, HEIGHT = 600, DEPTH = 24, BITORDER = RIBOBITSINORDER, BYTEORDER = RIBOLSBFIRST, LINEORDER = RILOTOPTOBOTTOM, LINEEND = RILEDWORDBOUNDARY, BITSPERPIXEL = 32, REDPREC = 8, REDSHIFT = 16, GREENPREC = 8, GREENSHIFT = 8, BLUEPREC = 8, BLUESHIFT = 0, ALPHAPREC = 0, ALPHASHIFT = 0, MASKBITSPERPIXEL = 1, MASKSHIFT = 0, MASKLINEEND = RILEBYTEBOUNDARY, MASKBITORDER = RIBOBITSINORDER, PALETTECOLORCOUNT = 0, PALETTEBITSPERINDEX = 0, PALETTESHIFT = 0, PALETTELINEEND = RILETIGHT, PALETTEBITORDER = RIBOBITSINORDER, PALETTEBYTEORDER = RIBOLSBFIRST}, DATA = 0x7fffef93b050 '', DATASIZE = 2400000, MASK = 0x7fffef8fb050 '', MASKSIZE = 75000, PALETTE = 0x0, PALETTESIZE = 0}, 140737215869120, 0, false) at gtk2/gtk2lclintf.inc:405
#10 RAWIMAGE_CREATEBITMAPS({DESCRIPTION = {FORMAT = RICFRGBA, WIDTH = 1000, HEIGHT = 600, DEPTH = 24, BITORDER = RIBOBITSINORDER, BYTEORDER = RIBOLSBFIRST, LINEORDER = RILOTOPTOBOTTOM, LINEEND = RILEDWORDBOUNDARY, BITSPERPIXEL = 32, REDPREC = 8, REDSHIFT = 16, GREENPREC = 8, GREENSHIFT = 8, BLUEPREC = 8, BLUESHIFT = 0, ALPHAPREC = 0, ALPHASHIFT = 0, MASKBITSPERPIXEL = 1, MASKSHIFT = 0, MASKLINEEND = RILEBYTEBOUNDARY, MASKBITORDER = RIBOBITSINORDER, PALETTECOLORCOUNT = 0, PALETTEBITSPERINDEX = 0, PALETTESHIFT = 0, PALETTELINEEND = RILETIGHT, PALETTEBITORDER = RIBOBITSINORDER, PALETTEBYTEORDER = RIBOLSBFIRST}, DATA = 0x7fffef93b050 '', DATASIZE = 2400000, MASK = 0x7fffef8fb050 '', MASKSIZE = 75000, PALETTE = 0x0, PALETTESIZE = 0}, 140737215869120, 0, false) at include/lclintf.inc:152
#11 CREATECOMPATIBLEBITMAPS({DESCRIPTION = {FORMAT = RICFRGBA, WIDTH = 1000, HEIGHT = 600, DEPTH = 24, BITORDER = RIBOBITSINORDER, BYTEORDER = RIBOLSBFIRST, LINEORDER = RILOTOPTOBOTTOM, LINEEND = RILEDWORDBOUNDARY, BITSPERPIXEL = 32, REDPREC = 8, REDSHIFT = 16, GREENPREC = 8, GREENSHIFT = 8, BLUEPREC = 8, BLUESHIFT = 0, ALPHAPREC = 0, ALPHASHIFT = 0, MASKBITSPERPIXEL = 1, MASKSHIFT = 0, MASKLINEEND = RILEBYTEBOUNDARY, MASKBITORDER = RIBOBITSINORDER, PALETTECOLORCOUNT = 0, PALETTEBITSPERINDEX = 0, PALETTESHIFT = 0, PALETTELINEEND = RILETIGHT, PALETTEBITORDER = RIBOBITSINORDER, PALETTEBYTEORDER = RIBOLSBFIRST}, DATA = 0x7fffef93b050 '', DATASIZE = 2400000, MASK = 0x7fffef8fb050 '', MASKSIZE = 75000, PALETTE = 0x0, PALETTESIZE = 0}, 140737215869120, 0, false) at graphics.pp:2334
#12 BITMAPHANDLENEEDED(0x7fffefc23040) at include/rasterimage.inc:188
#13 CREATEHANDLE(0x7fffefbb3040) at include/bitmapcanvas.inc:43
#14 REQUIREDSTATE(0x7fffefbb3040, 1) at include/canvas.inc:1698
#15 DOMOVETO(0x7fffefbb3040, 10, 10) at include/canvas.inc:566
#16 FPCANVAS_TFPCUSTOMCANVAS_$__MOVETO$LONGINT$LONGINT at :0
#17 ?? at :0
#18 DOLINE(0x7fffefbb3040, 10, 10, 200, 200) at include/canvas.inc:582
#19 FPCANVAS_TFPCUSTOMCANVAS_$__LINE$LONGINT$LONGINT$LONGINT$LONGINT at :0
#20 ?? at :0
#21 ?? at :0
#22 ?? at :0
#23 ?? at :0
#24 _$FORMS$_Ld110 at :0
#25 EXECUTE(0x7ffff7f791c0) at unit1.pas:53
#26 CLASSES_THREADFUNC$POINTER$$INT64 at :0
#27 CTHREADS_THREADMAIN$POINTER$$POINTER at :0
#28 ?? at :0
#29 ?? at :0
#30 ?? at :0
#31 ?? at :0
#32 ?? at :0

circular

  • Hero Member
  • *****
  • Posts: 3440
    • Personal webpage
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #1 on: February 18, 2016, 06:52:11 pm »
Hmm that seem deep in the system.

What about using CanvasBGRA property instead of Canvas?
Conscience is the debugger of the mind

Chronos

  • Full Member
  • ***
  • Posts: 212
    • PascalClassLibrary
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #2 on: February 18, 2016, 08:47:53 pm »
I can use alternative graphics library but this will be more as workaround as I would rather use native solution.
Another workaround could be to compile with qt widgetset instead of gtk2. But main question is if drawing from non-main thread is even supported. I found interesting related article http://blogs.operationaldynamics.com/andrew/software/gnome-desktop/gtk-thread-awareness
So perhaps from LCL perspective locking is either not implemented or need to be done specially from non main thread. I can just compare this to windows API and I can say it is not good that it is not in same way. So either I need to do more investigation to do it properly or I need to add conditional compilation something like:
{$ifdef windows}prepare offscreen bitmap from worker thread{$endif}
{$ifdef unix}prepare offscreen bitmap from main thread{$endif}

taazz

  • Hero Member
  • *****
  • Posts: 5365
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #3 on: February 18, 2016, 09:13:58 pm »
I can use alternative graphics library but this will be more as workaround as I would rather use native solution.
Another workaround could be to compile with qt widgetset instead of gtk2. But main question is if drawing from non-main thread is even supported. I found interesting related article http://blogs.operationaldynamics.com/andrew/software/gnome-desktop/gtk-thread-awareness
So perhaps from LCL perspective locking is either not implemented or need to be done specially from non main thread. I can just compare this to windows API and I can say it is not good that it is not in same way. So either I need to do more investigation to do it properly or I need to add conditional compilation something like:
{$ifdef windows}prepare offscreen bitmap from worker thread{$endif}
{$ifdef unix}prepare offscreen bitmap from main thread{$endif}
May I make a silly question? Can you please point me to the GTK part on your back trace? I've been looking at it for a couple of minutes now trying to see where is the GTK involved in this and to be honest I do not see it. There is an XLib failure that is visible but no GTK call that I can find.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Chronos

  • Full Member
  • ***
  • Posts: 212
    • PascalClassLibrary
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #4 on: February 19, 2016, 07:38:20 am »
I don't know where exactly problem is. I just know that with Win32 API it works but with linux and GTK2 widgetset doesn't. If problem is really in some Xlib library or Xlib is ok and just gtk library is doing bad thing without proper locking need to be verified. My original question was more about if it is expected behavior or it is bug. And where I can found more information. Maybe I am just first who tried this way with GTK2 widgetset so it is more about "boldly go where no man gone before..."

GetMem

  • Hero Member
  • *****
  • Posts: 3757
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #5 on: February 19, 2016, 09:28:18 am »
Try this, it should work both under linux and windows:

Chronos

  • Full Member
  • ***
  • Posts: 212
    • PascalClassLibrary
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #6 on: February 19, 2016, 12:45:11 pm »
Thanks for suggestion but what I really need is drawing to bitmap in thread. This drawing consists of multiple drawing steps and lets say it can take around 20 ms depending on screen resolution, CPU power and various other factors. In main thread there is TTimer with interval 20 ms which signalize redrawing "on demand" to this secondary thread and also draw content of temporary bitmap to timage or tpaintbox. Access to TempBitmap is controlled by critical section. As result main thread is not blocked during long TempBitmap preparation and can handle message queue.

So critical part for this concept is to be able to draw to canvas concurrently from thread. Which means from inside Execute method, not constructor.

circular

  • Hero Member
  • *****
  • Posts: 3440
    • Personal webpage
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #7 on: February 19, 2016, 02:28:35 pm »
If it is only the CreateBitmap that does not work, maybe you could create the bitmap in the main thread, so that it is completely initialized, and afterwards do further drawing in a thread.
Conscience is the debugger of the mind

felipemdc

  • Administrator
  • Hero Member
  • *
  • Posts: 3541
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #8 on: February 19, 2016, 03:34:57 pm »
You could also try the CustomDrawn widgetset if your app doesn't use much more than TBitmap it would probably work better and probably will work in the thread as well.

Gtk is not thread-safe, that's the problem.

Chronos

  • Full Member
  • ***
  • Posts: 212
    • PascalClassLibrary
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #9 on: February 19, 2016, 04:25:09 pm »
Main problem is not bitmap creation but many operations on off screen bitmap.

First I would like to find out what is current status and if it could be achieved in same way as with winapi. I consider using customdraw as "last resort" solution. My application is pretty complex with modular system and many threads.

I found this code in gtk2 implementation:
Code: Pascal  [Select][+][-]
  1.  {$IFDEF EnabledGtkThreading}
  2.     {$IFNDEF Win32}
  3.       GtkThread := g_thread_self();
  4.       if GtkThread <> nil then
  5.       begin
  6.         if GtkThread^.data = nil then
  7.           GtkThread^.data := @Self
  8.         else
  9.           FIsLibraryInstance := True;
  10.       end;
  11.       if GetThreadManager(TM{%H-}) and Assigned(TM.InitManager) and g_thread_supported then
  12.       begin
  13.         g_thread_init(nil);
  14.         {$IFDEF USE_GTK_MAIN_OLD_ITERATION}
  15.         gdk_threads_init;
  16.         gdk_threads_enter;
  17.         {$ENDIF}
  18.         fMultiThreadingEnabled := True;
  19.       end;
  20.     {$ELSE}
  21.       g_thread_init(nil);
  22.     {$ENDIF}
  23.   {$ENDIF}              

Combination of gdk_threads_enter and gdk_threads_leave is probably official solution but is not used by default in LCL gtk2 widgetset implementation. There is MultiThreadingEnabled property which is in fact not used elsewhere. I tried to guard canvas operations with gdk_threads_enter and gdk_threads_leave and drawing nearly worked but random exceptions occurred. So it was not useful solution. And ifdef gtk2 would be needed for that.

I attached updated demo app with gtk locking which is not working reliably.

Chronos

  • Full Member
  • ***
  • Posts: 212
    • PascalClassLibrary
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #10 on: February 19, 2016, 04:26:07 pm »
Another similar interesting article about multi-threaded GTK http://blog.borovsak.si/2009/06/multi-threaded-gtk-applications.html

GetMem

  • Hero Member
  • *****
  • Posts: 3757
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #11 on: February 19, 2016, 05:28:30 pm »
@Chronos
What about creating another dedicated process and use some inter-process communication(memory mapped files, sockets, shared memory, etc...) ? Just an idea...

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #12 on: February 20, 2016, 02:22:52 pm »
Would BeginUpdate, EndUpdate pairs help? Doesn't it sound like the system was flooded with too many requests? Application.ProcessMessages might also do something.

kris

  • Jr. Member
  • **
  • Posts: 59
Re: Drawing to GTK2 offscreen bitmap canvas from thread
« Reply #13 on: September 27, 2016, 09:12:56 am »
I am struggling with exactly the same problem now, have anyone found out a solution yet?

 

TinyPortal © 2005-2018