Put the mutex code back as it was originally. We can still use it when single threaded.
[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                         while(alive_flag && !ready_next)
311                         {
312                                 Glib::Mutex::Lock lock(mutex);
313                                 if(cond_frame_queue_empty.timed_wait(mutex,Glib::TimeVal(0,BOREDOM_TIMEOUT)))
314                                         break;
315                         }
316         }
317
318
319         virtual Color * start_scanline(int scanline)
320         {
321                 Glib::Mutex::Lock lock(mutex);
322
323                 return surface[scanline];
324         }
325
326         virtual bool end_scanline()
327         {
328                 return alive_flag;
329         }
330
331         void frame_ready()
332         {
333                 Glib::Mutex::Lock lock(mutex);
334                 if(alive_flag)
335                         alive_flag=warm_target->add_frame(&surface);
336                 if (!single_threaded()) cond_frame_queue_empty.signal();
337                 ready_next=true;
338         }
339 };
340
341 /* === G L O B A L S ======================================================= */
342
343 /* === P R O C E D U R E S ================================================= */
344
345 /* === M E T H O D S ======================================================= */
346
347 AsyncRenderer::AsyncRenderer(etl::handle<synfig::Target> target_,synfig::ProgressCallback *cb):
348         error(false),
349         success(false),
350         cb(cb),
351         updating(false)
352 {
353         render_thread=0;
354         if(etl::handle<synfig::Target_Tile>::cast_dynamic(target_))
355         {
356                 etl::handle<AsyncTarget_Tile> wrap_target(
357                         new AsyncTarget_Tile(etl::handle<synfig::Target_Tile>::cast_dynamic(target_))
358                 );
359
360                 signal_stop_.connect(sigc::mem_fun(*wrap_target,&AsyncTarget_Tile::set_dead));
361
362                 target=wrap_target;
363         }
364         else if(etl::handle<synfig::Target_Scanline>::cast_dynamic(target_))
365         {
366                 etl::handle<AsyncTarget_Scanline> wrap_target(
367                         new AsyncTarget_Scanline(
368                                 etl::handle<synfig::Target_Scanline>::cast_dynamic(target_)
369                         )
370                 );
371
372                 signal_stop_.connect(sigc::mem_fun(*wrap_target,&AsyncTarget_Scanline::set_dead));
373
374                 target=wrap_target;
375         }
376 }
377
378 AsyncRenderer::~AsyncRenderer()
379 {
380         stop();
381 }
382
383 void
384 AsyncRenderer::stop()
385 {
386         if(target)
387         {
388                 Glib::Mutex::Lock lock(mutex);
389                 done_connection.disconnect();
390
391                 if(render_thread)
392                 {
393                         signal_stop_();
394
395 #if REJOIN_ON_STOP
396                         if (!single_threaded()) render_thread->join();
397 #endif
398
399                         // Make sure all the dispatch crap is cleared out
400                         //Glib::MainContext::get_default()->iteration(false);
401
402                         if(success)
403                                 signal_success_();
404
405                         signal_finished_();
406
407                         target=0;
408                         render_thread=0;
409                 }
410         }
411 }
412
413 void
414 AsyncRenderer::pause()
415 {
416 }
417
418 void
419 AsyncRenderer::resume()
420 {
421 }
422
423 void
424 AsyncRenderer::start()
425 {
426         done_connection=Glib::signal_timeout().connect(
427                 sigc::bind_return(
428                         mem_fun(*this,&AsyncRenderer::start_),
429                         false
430                 )
431                 ,50
432         );
433 }
434
435 void
436 AsyncRenderer::rendering_progress()
437 {
438         updating = true;
439         while(studio::App::events_pending()) studio::App::iteration(false);
440         updating = false;
441 }
442
443 void
444 AsyncRenderer::start_()
445 {
446         error=false;success=false;
447         if(target)
448         {
449 #ifndef GLIB_DISPATCHER_BROKEN
450                 done_connection=signal_done_.connect(mem_fun(*this,&AsyncRenderer::stop));
451 #endif
452
453                 if (single_threaded())
454                 {
455                         synfig::info("%s:%d rendering in the same thread", __FILE__, __LINE__);
456                         target->signal_progress().connect(sigc::mem_fun(this,&AsyncRenderer::rendering_progress));
457                         render_thread = (Glib::Thread*)1;
458                         render_target();
459                 }
460                 else
461                 {
462                         render_thread=Glib::Thread::create(
463                                 sigc::mem_fun(*this,&AsyncRenderer::render_target),
464 #if REJOIN_ON_STOP
465                                 true
466 #else
467                                 false
468 #endif
469                                 );
470                         assert(render_thread);
471                 }
472         }
473         else
474         {
475                 stop();
476         }
477 }
478
479 void
480 AsyncRenderer::render_target()
481 {
482         etl::handle<Target> target(AsyncRenderer::target);
483
484         if(target && target->render())
485         {
486                 success=true;
487         }
488         else
489         {
490                 error=true;
491 #ifndef REJOIN_ON_STOP
492                 return;
493 #endif
494         }
495
496         if(mutex.trylock())
497         {
498 #ifdef GLIB_DISPATCHER_BROKEN
499                 done_connection=Glib::signal_timeout().connect(
500                         sigc::bind_return(
501                                 mem_fun(*this,&AsyncRenderer::stop),
502                                 false
503                         )
504                         ,0
505                 );
506 #else
507                 signal_done_.emit();
508 #endif
509                 mutex.unlock();
510         }
511 }