When single threaded, update the gui after rendering each frame.
[synfig.git] / synfig-studio / trunk / src / gtkmm / asyncrenderer.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file asyncrenderer.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "asyncrenderer.h"
33 #include "app.h"
34 #include <glibmm/thread.h>
35 #include <glibmm/dispatcher.h>
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 #ifdef HAVE_SYS_TYPES_H
42 #include <sys/types.h>
43 #endif
44
45 #ifdef HAVE_SYS_WAIT_H
46 #include <sys/wait.h>
47 #endif
48
49 #ifdef HAVE_SIGNAL_H
50 #include <signal.h>
51 #endif
52
53 #include <synfig/general.h>
54 #include <ETL/clock>
55
56 #endif
57
58 /* === U S I N G =========================================================== */
59
60 using namespace std;
61 using namespace etl;
62 using namespace synfig;
63 using namespace studio;
64
65 #define BOREDOM_TIMEOUT         50
66
67 #define REJOIN_ON_STOP  1
68
69 // The Glib::Dispatcher class is broken as of Glibmm 2.4.5.
70 // Defining this macro enables the workaround.
71 #define GLIB_DISPATCHER_BROKEN 1
72
73 /* === C L A S S E S ======================================================= */
74
75 class AsyncTarget_Tile : public synfig::Target_Tile
76 {
77 public:
78         etl::handle<synfig::Target_Tile> warm_target;
79
80         struct tile_t
81         {
82                 Surface surface;
83                 int x,y;
84                 tile_t(const Surface& surface,int x, int y):
85                         surface(surface),
86                         x(x),y(y)
87                 {
88                 }
89         };
90         std::list<tile_t> tile_queue;
91         Glib::Mutex mutex;
92
93 #ifndef GLIB_DISPATCHER_BROKEN
94         Glib::Dispatcher tile_ready_signal;
95 #endif
96         Glib::Cond cond_tile_queue_empty;
97         bool alive_flag;
98
99         sigc::connection ready_connection;
100
101 public:
102         AsyncTarget_Tile(etl::handle<synfig::Target_Tile> warm_target):
103                 warm_target(warm_target)
104         {
105                 set_avoid_time_sync(warm_target->get_avoid_time_sync());
106                 set_tile_w(warm_target->get_tile_w());
107                 set_tile_h(warm_target->get_tile_h());
108                 set_canvas(warm_target->get_canvas());
109                 set_quality(warm_target->get_quality());
110                 set_remove_alpha(warm_target->get_remove_alpha());
111                 set_threads(warm_target->get_threads());
112                 set_clipping(warm_target->get_clipping());
113                 set_rend_desc(&warm_target->rend_desc());
114                 alive_flag=true;
115 #ifndef GLIB_DISPATCHER_BROKEN
116                 ready_connection=tile_ready_signal.connect(sigc::mem_fun(*this,&AsyncTarget_Tile::tile_ready));
117 #endif
118         }
119
120         ~AsyncTarget_Tile()
121         {
122                 ready_connection.disconnect();
123         }
124         void set_dead()
125         {
126                 Glib::Mutex::Lock lock(mutex);
127                 alive_flag=false;
128         }
129
130         virtual int total_tiles()const
131         {
132                 return warm_target->total_tiles();
133         }
134
135         virtual int next_tile(int& x, int& y)
136         {
137                 if(!alive_flag)
138                         return 0;
139
140                 return warm_target->next_tile(x,y);
141         }
142
143         virtual int next_frame(Time& time)
144         {
145                 if(!alive_flag)
146                         return 0;
147                 return warm_target->next_frame(time);
148         }
149
150         virtual bool start_frame(synfig::ProgressCallback *cb=0)
151         {
152                 if(!alive_flag)
153                         return false;
154                 return warm_target->start_frame(cb);
155         }
156
157         virtual bool add_tile(const synfig::Surface &surface, int gx, int gy)
158         {
159                 assert(surface);
160                 if(!alive_flag)
161                         return false;
162                 Glib::Mutex::Lock lock(mutex);
163                 tile_queue.push_back(tile_t(surface,gx,gy));
164                 if(tile_queue.size()==1)
165                 {
166 #ifdef GLIB_DISPATCHER_BROKEN
167                 ready_connection=Glib::signal_timeout().connect(
168                         sigc::bind_return(
169                                 sigc::mem_fun(*this,&AsyncTarget_Tile::tile_ready),
170                                 false
171                         )
172                         ,0
173                 );
174 #else
175                 tile_ready_signal();
176 #endif
177                 }
178
179                 return alive_flag;
180         }
181
182         void tile_ready()
183         {
184                 Glib::Mutex::Lock lock(mutex);
185                 if(!alive_flag)
186                 {
187                         tile_queue.clear();
188                         cond_tile_queue_empty.signal();
189                         return;
190                 }
191                 while(!tile_queue.empty() && alive_flag)
192                 {
193                         tile_t& tile(tile_queue.front());
194
195                         alive_flag=warm_target->add_tile(tile.surface,tile.x,tile.y);
196
197                         tile_queue.pop_front();
198                 }
199                 cond_tile_queue_empty.signal();
200         }
201
202         virtual void end_frame()
203         {
204                 if (!single_threaded())
205                 {
206                         while(alive_flag)
207                         {
208                                 Glib::Mutex::Lock lock(mutex);
209                                 if(!tile_queue.empty() && alive_flag)
210                                 {
211                                         if(cond_tile_queue_empty.timed_wait(mutex,Glib::TimeVal(0,BOREDOM_TIMEOUT)))
212                                                 break;
213                                 }
214                                 else
215                                         break;
216                         }
217                 }
218                 Glib::Mutex::Lock lock(mutex);
219                 if(!alive_flag)
220                         return;
221                 return warm_target->end_frame();
222         }
223 };
224
225
226
227 class AsyncTarget_Scanline : public synfig::Target_Scanline
228 {
229 public:
230         etl::handle<synfig::Target_Scanline> warm_target;
231
232         int scanline_;
233         Surface surface;
234
235         Glib::Mutex mutex;
236
237 #ifndef GLIB_DISPATCHER_BROKEN
238         Glib::Dispatcher frame_ready_signal;
239 #endif
240         Glib::Cond cond_frame_queue_empty;
241         bool alive_flag;
242         bool ready_next;
243         sigc::connection ready_connection;
244
245
246 public:
247         AsyncTarget_Scanline(etl::handle<synfig::Target_Scanline> warm_target):
248                 warm_target(warm_target)
249         {
250                 set_avoid_time_sync(warm_target->get_avoid_time_sync());
251                 set_canvas(warm_target->get_canvas());
252                 set_quality(warm_target->get_quality());
253                 set_remove_alpha(warm_target->get_remove_alpha());
254                 set_threads(warm_target->get_threads());
255                 set_rend_desc(&warm_target->rend_desc());
256                 alive_flag=true;
257 #ifndef GLIB_DISPATCHER_BROKEN
258                 ready_connection=frame_ready_signal.connect(sigc::mem_fun(*this,&AsyncTarget_Scanline::frame_ready));
259 #endif
260                 surface.set_wh(warm_target->rend_desc().get_w(),warm_target->rend_desc().get_h());
261         }
262
263         ~AsyncTarget_Scanline()
264         {
265                 ready_connection.disconnect();
266         }
267
268         virtual int next_frame(Time& time)
269         {
270                 if(!alive_flag)
271                         return 0;
272                 return warm_target->next_frame(time);
273
274         }
275
276         void set_dead()
277         {
278                 Glib::Mutex::Lock lock(mutex);
279                 alive_flag=false;
280         }
281
282         virtual bool start_frame(synfig::ProgressCallback */*cb*/=0)
283         {
284                 return alive_flag;
285         }
286
287         virtual void end_frame()
288         {
289                 {
290                         Glib::Mutex::Lock lock(mutex);
291
292                         if(!alive_flag)
293                                 return;
294                         ready_next=false;
295
296 #ifdef GLIB_DISPATCHER_BROKEN
297                 ready_connection=Glib::signal_timeout().connect(
298                         sigc::bind_return(
299                                 sigc::mem_fun(*this,&AsyncTarget_Scanline::frame_ready),
300                                 false
301                         )
302                         ,0
303                 );
304 #else
305                         frame_ready_signal();
306 #endif
307                 }
308
309                 if (single_threaded())
310                         signal_progress()();
311                 else
312                         while(alive_flag && !ready_next)
313                         {
314                                 Glib::Mutex::Lock lock(mutex);
315                                 if(cond_frame_queue_empty.timed_wait(mutex,Glib::TimeVal(0,BOREDOM_TIMEOUT)))
316                                         break;
317                         }
318         }
319
320
321         virtual Color * start_scanline(int scanline)
322         {
323                 Glib::Mutex::Lock lock(mutex);
324
325                 return surface[scanline];
326         }
327
328         virtual bool end_scanline()
329         {
330                 return alive_flag;
331         }
332
333         void frame_ready()
334         {
335                 Glib::Mutex::Lock lock(mutex);
336                 if(alive_flag)
337                         alive_flag=warm_target->add_frame(&surface);
338                 if (!single_threaded()) cond_frame_queue_empty.signal();
339                 ready_next=true;
340         }
341 };
342
343 /* === G L O B A L S ======================================================= */
344
345 /* === P R O C E D U R E S ================================================= */
346
347 /* === M E T H O D S ======================================================= */
348
349 AsyncRenderer::AsyncRenderer(etl::handle<synfig::Target> target_,synfig::ProgressCallback *cb):
350         error(false),
351         success(false),
352         cb(cb),
353         updating(false)
354 {
355         render_thread=0;
356         if(etl::handle<synfig::Target_Tile>::cast_dynamic(target_))
357         {
358                 etl::handle<AsyncTarget_Tile> wrap_target(
359                         new AsyncTarget_Tile(etl::handle<synfig::Target_Tile>::cast_dynamic(target_))
360                 );
361
362                 signal_stop_.connect(sigc::mem_fun(*wrap_target,&AsyncTarget_Tile::set_dead));
363
364                 target=wrap_target;
365         }
366         else if(etl::handle<synfig::Target_Scanline>::cast_dynamic(target_))
367         {
368                 etl::handle<AsyncTarget_Scanline> wrap_target(
369                         new AsyncTarget_Scanline(
370                                 etl::handle<synfig::Target_Scanline>::cast_dynamic(target_)
371                         )
372                 );
373
374                 signal_stop_.connect(sigc::mem_fun(*wrap_target,&AsyncTarget_Scanline::set_dead));
375
376                 target=wrap_target;
377         }
378 }
379
380 AsyncRenderer::~AsyncRenderer()
381 {
382         stop();
383 }
384
385 void
386 AsyncRenderer::stop()
387 {
388         if(target)
389         {
390                 Glib::Mutex::Lock lock(mutex);
391                 done_connection.disconnect();
392
393                 if(render_thread)
394                 {
395                         signal_stop_();
396
397 #if REJOIN_ON_STOP
398                         if (!single_threaded()) render_thread->join();
399 #endif
400
401                         // Make sure all the dispatch crap is cleared out
402                         //Glib::MainContext::get_default()->iteration(false);
403
404                         if(success)
405                                 signal_success_();
406
407                         signal_finished_();
408
409                         target=0;
410                         render_thread=0;
411                 }
412         }
413 }
414
415 void
416 AsyncRenderer::pause()
417 {
418 }
419
420 void
421 AsyncRenderer::resume()
422 {
423 }
424
425 void
426 AsyncRenderer::start()
427 {
428         done_connection=Glib::signal_timeout().connect(
429                 sigc::bind_return(
430                         mem_fun(*this,&AsyncRenderer::start_),
431                         false
432                 )
433                 ,50
434         );
435 }
436
437 void
438 AsyncRenderer::rendering_progress()
439 {
440         updating = true;
441         while(studio::App::events_pending()) studio::App::iteration(false);
442         updating = false;
443 }
444
445 void
446 AsyncRenderer::start_()
447 {
448         error=false;success=false;
449         if(target)
450         {
451 #ifndef GLIB_DISPATCHER_BROKEN
452                 done_connection=signal_done_.connect(mem_fun(*this,&AsyncRenderer::stop));
453 #endif
454
455                 if (single_threaded())
456                 {
457                         synfig::info("%s:%d rendering in the same thread", __FILE__, __LINE__);
458                         target->signal_progress().connect(sigc::mem_fun(this,&AsyncRenderer::rendering_progress));
459                         render_thread = (Glib::Thread*)1;
460                         render_target();
461                 }
462                 else
463                 {
464                         render_thread=Glib::Thread::create(
465                                 sigc::mem_fun(*this,&AsyncRenderer::render_target),
466 #if REJOIN_ON_STOP
467                                 true
468 #else
469                                 false
470 #endif
471                                 );
472                         assert(render_thread);
473                 }
474         }
475         else
476         {
477                 stop();
478         }
479 }
480
481 void
482 AsyncRenderer::render_target()
483 {
484         etl::handle<Target> target(AsyncRenderer::target);
485
486         if(target && target->render())
487         {
488                 success=true;
489         }
490         else
491         {
492                 error=true;
493 #ifndef REJOIN_ON_STOP
494                 return;
495 #endif
496         }
497
498         if(mutex.trylock())
499         {
500 #ifdef GLIB_DISPATCHER_BROKEN
501                 done_connection=Glib::signal_timeout().connect(
502                         sigc::bind_return(
503                                 mem_fun(*this,&AsyncRenderer::stop),
504                                 false
505                         )
506                         ,0
507                 );
508 #else
509                 signal_done_.emit();
510 #endif
511                 mutex.unlock();
512         }
513 }