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