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