initial commit
[goodguy/history.git] / cinelerra-5.0 / guicast / bcsynchronous.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #define GL_GLEXT_PROTOTYPES
23 #include "bcresources.h"
24 #include "bcsignals.h"
25 #include "bcsynchronous.h"
26 #include "bcwindowbase.h"
27 #include "condition.h"
28 #include "mutex.h"
29
30
31 #ifdef HAVE_GL
32 #include <GL/gl.h>
33 #endif
34 #include <unistd.h>
35
36 #include <string.h>
37
38
39 TextureID::TextureID(int window_id, int id, int w, int h, int components)
40 {
41         this->window_id = window_id;
42         this->id = id;
43         this->w = w;
44         this->h = h;
45         this->components = components;
46         in_use = 1;
47 }
48
49 ShaderID::ShaderID(int window_id, unsigned int handle, char *source)
50 {
51         this->window_id = window_id;
52         this->handle = handle;
53         this->source = strdup(source);
54 }
55
56 ShaderID::~ShaderID()
57 {
58         free(source);
59 }
60
61 #ifdef HAVE_GL
62 PBufferID::PBufferID(int window_id, GLXPbuffer glx_pbuffer, GLXContext glx_context, int w, int h)
63 {
64         this->glx_pbuffer = glx_pbuffer;
65         this->glx_context = glx_context;
66         this->window_id = window_id;
67         this->w = w;
68         this->h = h;
69         in_use = 1;
70 }
71 #endif
72
73
74
75 BC_SynchGarbage::BC_SynchGarbage(BC_Synchronous *synchronous)
76  : Thread(1, 0, 0)
77 {
78         this->synchronous = synchronous;
79         more_garbage = new Condition(0, "BC_SynchGarbage::more_garbage", 0);
80         garbage_lock = new Mutex("BC_SyncGarbage::garbage_lock");
81         done = -1;
82 }
83
84 BC_SynchGarbage::~BC_SynchGarbage()
85 {
86         if( running() ) {
87                 quit();
88                 join();
89         }
90         garbage.remove_all_objects();
91         delete more_garbage;
92         delete garbage_lock;
93 }
94
95 void BC_SynchGarbage::send_garbage(BC_SynchronousCommand *command)
96 {
97         garbage_lock->lock("BC_SynchGarbage::send_garbage");
98         garbage.append(command);
99         garbage_lock->unlock();
100         more_garbage->unlock();
101 }
102
103 void BC_SynchGarbage::handle_garbage()
104 {
105         garbage_lock->lock("BC_SynchGarbage::handle_garbage 0");
106         while( !done && garbage.total ) {
107                 BC_SynchronousCommand *command = garbage.get(0);
108                 garbage.remove_number(0);
109                 garbage_lock->unlock();
110
111                 switch(command->command) {
112                 case BC_SynchronousCommand::QUIT:
113                         done = 1;
114                         break;
115
116                 case BC_SynchronousCommand::DELETE_WINDOW:
117                         synchronous->delete_window_sync(command);
118                         break;
119
120                 case BC_SynchronousCommand::DELETE_PIXMAP:
121                         synchronous->delete_pixmap_sync(command);
122                         break;
123
124                 case BC_SynchronousCommand::DELETE_DISPLAY:
125                         synchronous->delete_display_sync(command);
126                         break;
127                 }
128
129                 delete command;
130                 garbage_lock->lock("BC_SynchGarbage::handle_garbage 1");
131         }
132         garbage_lock->unlock();
133 }
134
135
136 void BC_SynchGarbage::start()
137 {
138         done = 0;
139         Thread::start();
140 }
141
142 void BC_SynchGarbage::stop()
143 {
144         if( running() ) {
145                 quit();
146                 join();
147         }
148 }
149
150 void BC_SynchGarbage::quit()
151 {
152         BC_SynchronousCommand *command = new BC_SynchronousCommand();
153         command->command = BC_SynchronousCommand::QUIT;
154         send_garbage(command);
155 }
156
157 void BC_SynchGarbage::run()
158 {
159         while( !done ) {
160                 more_garbage->lock("BC_SynchGarbage::run");
161                 handle_garbage();
162         }
163 }
164
165
166 BC_SynchronousCommand::BC_SynchronousCommand()
167 {
168         command = BC_SynchronousCommand::NONE;
169         frame = 0;
170         frame_return = 0;
171         result = 0;
172         command_done = new Condition(0, "BC_SynchronousCommand::command_done", 0);
173 }
174
175 BC_SynchronousCommand::~BC_SynchronousCommand()
176 {
177         delete command_done;
178 }
179
180 void BC_SynchronousCommand::copy_from(BC_SynchronousCommand *command)
181 {
182         this->command =         command->command;
183         this->colormodel =      command->colormodel;
184         this->window =          command->window;
185         this->frame =           command->frame;
186         this->window_id =       command->window_id;
187         this->frame_return =    command->frame_return;
188         this->id =              command->id;
189         this->w =               command->w;
190         this->h =               command->h;
191 }
192
193
194
195
196 BC_Synchronous::BC_Synchronous()
197  : Thread(1, 0, 0)
198 {
199         lock_sync = new Mutex("BC_Synchronous::lock_sync");
200         sync_garbage = new BC_SynchGarbage(this);
201         next_command = new Condition(0, "BC_Synchronous::next_command", 0);
202         command_lock = new Mutex("BC_Synchronous::command_lock");
203         table_lock = new Mutex("BC_Synchronous::table_lock");
204         done = 0;
205         is_running = 0;
206         current_window = 0;
207         BC_WindowBase::get_resources()->set_synchronous(this);
208 }
209
210 BC_Synchronous::~BC_Synchronous()
211 {
212         if( running() ) {
213                 quit();
214                 join();
215         }
216         commands.remove_all_objects();
217         delete sync_garbage;
218         delete lock_sync;
219         delete next_command;
220         delete command_lock;
221         delete table_lock;
222 }
223
224 void BC_Synchronous::sync_lock(const char *cp)
225 {
226         lock_sync->lock(cp);
227 }
228
229 void BC_Synchronous::sync_lock(Display *display, const char *cp)
230 {
231         // get both display lock and sync_lock
232         XLockDisplay(display);
233         while( lock_sync->trylock(cp) ) {
234                 XUnlockDisplay(display);
235                 usleep(100000);
236                 XLockDisplay(display);
237         }
238 }
239
240 void BC_Synchronous::sync_unlock()
241 {
242         lock_sync->unlock();
243 }
244
245 void sync_unlock();
246 BC_SynchronousCommand* BC_Synchronous::new_command()
247 {
248         return new BC_SynchronousCommand();
249 }
250
251 void BC_Synchronous::create_objects()
252 {
253 }
254
255 void BC_Synchronous::start()
256 {
257         sync_garbage->start();
258         run();
259 }
260
261 void BC_Synchronous::quit()
262 {
263         BC_SynchronousCommand *command = new_command();
264         command->command = BC_SynchronousCommand::QUIT;
265         command_lock->lock("BC_Synchronous::quit");
266         commands.append(command);
267         command_lock->unlock();
268         next_command->unlock();
269 }
270
271 long BC_Synchronous::send_command(BC_SynchronousCommand *command)
272 {
273         BC_SynchronousCommand *command2 = new_command();
274         command2->copy_from(command);
275         command_lock->lock("BC_Synchronous::send_command");
276         commands.append(command2);
277         command_lock->unlock();
278         next_command->unlock();
279 //printf("BC_Synchronous::send_command 1 %d\n", next_command->get_value());
280
281 // Wait for completion
282         command2->command_done->lock("BC_Synchronous::send_command");
283         long result = command2->result;
284         delete command2;
285         return result;
286 }
287
288 void BC_Synchronous::run()
289 {
290         is_running = 1;
291         while(!done) {
292                 next_command->lock("BC_Synchronous::run");
293                 command_lock->lock("BC_Synchronous::run");
294                 BC_SynchronousCommand *command = 0;
295                 if(commands.total) {
296                         command = commands.values[0];
297                         commands.remove_number(0);
298                 }
299                 command_lock->unlock();
300                 if( !command ) continue;
301
302 //printf("BC_Synchronous::run %d\n", command->command);
303                 handle_command_base(command);
304         }
305         is_running = 0;
306 }
307
308 void BC_Synchronous::handle_command_base(BC_SynchronousCommand *command)
309 {
310         sync_lock("BC_Synchronous::handle_command_base");
311         switch(command->command) {
312         case BC_SynchronousCommand::QUIT:
313                 done = 1;
314                 break;
315
316         default:
317                 handle_command(command);
318                 break;
319         }
320         command->command_done->unlock();
321         sync_unlock();
322 }
323
324 void BC_Synchronous::handle_command(BC_SynchronousCommand *command)
325 {
326 }
327
328 void BC_Synchronous::put_texture(int id, int w, int h, int components)
329 {
330         if(id >= 0)
331         {
332                 table_lock->lock("BC_Resources::put_texture");
333 // Search for duplicate
334                 for(int i = 0; i < texture_ids.total; i++)
335                 {
336                         TextureID *ptr = texture_ids.values[i];
337                         if(ptr->window_id == current_window->get_id() &&
338                                 ptr->id == id)
339                         {
340                                 printf("BC_Synchronous::push_texture: texture exists\n"
341                                         "exists: window=%d id=%d w=%d h=%d\n"
342                                         "new:    window=%d id=%d w=%d h=%d\n",
343                                         ptr->window_id,
344                                         ptr->id,
345                                         ptr->w,
346                                         ptr->h,
347                                         current_window->get_id(),
348                                         id,
349                                         w,
350                                         h);
351                                 table_lock->unlock();
352                                 return;
353                         }
354                 }
355
356                 TextureID *new_id = new TextureID(current_window->get_id(),
357                         id,
358                         w,
359                         h,
360                         components);
361                 texture_ids.append(new_id);
362                 table_lock->unlock();
363         }
364 }
365
366 int BC_Synchronous::get_texture(int w, int h, int components)
367 {
368         table_lock->lock("BC_Resources::get_texture");
369         for(int i = 0; i < texture_ids.total; i++)
370         {
371                 if(texture_ids.values[i]->w == w &&
372                         texture_ids.values[i]->h == h &&
373                         texture_ids.values[i]->components == components &&
374                         !texture_ids.values[i]->in_use &&
375                         texture_ids.values[i]->window_id == current_window->get_id())
376                 {
377                         int result = texture_ids.values[i]->id;
378                         texture_ids.values[i]->in_use = 1;
379                         table_lock->unlock();
380                         return result;
381                 }
382         }
383         table_lock->unlock();
384         return -1;
385 }
386
387 void BC_Synchronous::release_texture(int window_id, int id)
388 {
389         table_lock->lock("BC_Resources::release_texture");
390         for(int i = 0; i < texture_ids.total; i++)
391         {
392                 if(texture_ids.values[i]->id == id &&
393                         texture_ids.values[i]->window_id == window_id)
394                 {
395                         texture_ids.values[i]->in_use = 0;
396                         table_lock->unlock();
397                         return;
398                 }
399         }
400         table_lock->unlock();
401 }
402
403
404
405
406
407 unsigned int BC_Synchronous::get_shader(char *source, int *got_it)
408 {
409         table_lock->lock("BC_Resources::get_shader");
410         for(int i = 0; i < shader_ids.total; i++)
411         {
412                 if(shader_ids.values[i]->window_id == current_window->get_id() &&
413                         !strcmp(shader_ids.values[i]->source, source))
414                 {
415                         unsigned int result = shader_ids.values[i]->handle;
416                         table_lock->unlock();
417                         *got_it = 1;
418                         return result;
419                 }
420         }
421         table_lock->unlock();
422         *got_it = 0;
423         return 0;
424 }
425
426 void BC_Synchronous::put_shader(unsigned int handle,
427         char *source)
428 {
429         table_lock->lock("BC_Resources::put_shader");
430         shader_ids.append(new ShaderID(current_window->get_id(), handle, source));
431         table_lock->unlock();
432 }
433
434 void BC_Synchronous::dump_shader(unsigned int handle)
435 {
436         int got_it = 0;
437         table_lock->lock("BC_Resources::dump_shader");
438         for(int i = 0; i < shader_ids.total; i++)
439         {
440                 if(shader_ids.values[i]->handle == handle)
441                 {
442                         printf("BC_Synchronous::dump_shader\n"
443                                 "%s", shader_ids.values[i]->source);
444                         got_it = 1;
445                         break;
446                 }
447         }
448         table_lock->unlock();
449         if(!got_it) printf("BC_Synchronous::dump_shader couldn't find %d\n", handle);
450 }
451
452 // has to run in sync_garbage thread, because mwindow playback_3d
453 // thread ends before all windows are deleted.  runs as synchronous
454 // command since resources must be freed immediately
455 void BC_Synchronous::delete_window(BC_WindowBase *window)
456 {
457 #ifdef HAVE_GL
458         BC_SynchronousCommand *command = new_command();
459         command->command = BC_SynchronousCommand::DELETE_WINDOW;
460         command->window_id = window->get_id();
461         command->display = window->get_display();
462         command->win = window->win;
463         command->glx_win = window->glx_win;
464         command->glx_context = window->glx_win_context;
465
466         send_garbage(command);
467         command->command_done->lock("BC_Synchronous::delete_window");
468 #endif
469 }
470
471 void BC_Synchronous::delete_window_sync(BC_SynchronousCommand *command)
472 {
473 #ifdef HAVE_GL
474         int window_id = command->window_id;
475         Display *display = command->display;
476         Window win = command->win;
477         GLXWindow glx_win = command->glx_win;
478         GLXContext glx_context = command->glx_context;
479         sync_lock(display, "BC_Synchronous::delete_window_sync");
480 //int debug = 0;
481
482 // texture ID's are unique to different contexts
483         glXMakeContextCurrent(display, glx_win, glx_win, glx_context);
484
485         table_lock->lock("BC_Resources::release_textures");
486         for(int i = 0; i < texture_ids.total; i++) {
487                 if(texture_ids.values[i]->window_id == window_id) {
488                         GLuint id = texture_ids.values[i]->id;
489                         glDeleteTextures(1, &id);
490 //if(debug) printf("BC_Synchronous::delete_window_sync texture_id=%d window_id=%d\n",
491 // id, window_id);
492                         texture_ids.remove_object_number(i);
493                         i--;
494                 }
495         }
496
497         for(int i = 0; i < shader_ids.total; i++)
498         {
499                 if(shader_ids.values[i]->window_id == window_id)
500                 {
501                         glDeleteShader(shader_ids.values[i]->handle);
502 //if(debug)
503 //printf("BC_Synchronous::delete_window_sync shader_id=%d window_id=%d\n",
504 //shader_ids.values[i]->handle, window_id);
505                         shader_ids.remove_object_number(i);
506                         i--;
507                 }
508         }
509
510         for(int i = 0; i < pbuffer_ids.total; i++)
511         {
512                 if(pbuffer_ids.values[i]->window_id == window_id)
513                 {
514                         glXDestroyPbuffer(display, pbuffer_ids.values[i]->glx_pbuffer);
515                         glXDestroyContext(display, pbuffer_ids.values[i]->glx_context);
516 //if(debug)
517 //printf("BC_Synchronous::delete_window_sync pbuffer_id=%p window_id=%d\n",
518 //  (void*)pbuffer_ids.values[i]->pbuffer, window_id);
519                         pbuffer_ids.remove_object_number(i);
520                         i--;
521                 }
522         }
523
524
525         table_lock->unlock();
526
527         XDestroyWindow(display, win);
528         if( glx_context )
529                 glXDestroyContext(display, glx_context);
530         command->command_done->unlock();
531         XUnlockDisplay(display);
532         sync_unlock();
533 #endif
534 }
535
536 void BC_Synchronous::delete_display(BC_WindowBase *window)
537 {
538 #ifdef HAVE_GL
539         BC_SynchronousCommand *command = new_command();
540         command->command = BC_SynchronousCommand::DELETE_DISPLAY;
541         command->display = window->get_display();
542
543         send_garbage(command);
544 #endif
545 }
546
547 void BC_Synchronous::delete_display_sync(BC_SynchronousCommand *command)
548 {
549 #ifdef HAVE_GL
550         Display *display = command->display;
551         sync_lock(display, "BC_Synchronous::delete_display_sync");
552         XUnlockDisplay(display);
553         XCloseDisplay(display);
554         sync_unlock();
555 #endif
556 }
557
558 #ifdef HAVE_GL
559 void BC_Synchronous::put_pbuffer(int w, int h,
560                 GLXPbuffer glx_pbuffer, GLXContext glx_context)
561 {
562         int exists = 0;
563         table_lock->lock("BC_Resources::release_textures");
564         for(int i = 0; i < pbuffer_ids.total; i++) {
565                 PBufferID *ptr = pbuffer_ids.values[i];
566                 if(ptr->w == w && ptr->h == h && ptr->glx_pbuffer == glx_pbuffer) {
567                         exists = 1;
568                         break;
569                 }
570         }
571
572
573         if(!exists) {
574                 PBufferID *ptr = new PBufferID(current_window->get_id(),
575                         glx_pbuffer, glx_context, w, h);
576                 pbuffer_ids.append(ptr);
577         }
578         table_lock->unlock();
579 }
580
581 GLXPbuffer BC_Synchronous::get_pbuffer(int w,
582         int h,
583         int *window_id,
584         GLXContext *glx_context)
585 {
586         table_lock->lock("BC_Resources::release_textures");
587         for(int i = 0; i < pbuffer_ids.total; i++) {
588                 PBufferID *ptr = pbuffer_ids.values[i];
589                 if(ptr->w == w && ptr->h == h && !ptr->in_use &&
590                         ptr->window_id == current_window->get_id() ) {
591                         GLXPbuffer result = ptr->glx_pbuffer;
592                         *glx_context = ptr->glx_context;
593                         *window_id = ptr->window_id;
594                         ptr->in_use = 1;
595                         table_lock->unlock();
596                         return result;
597                 }
598         }
599         table_lock->unlock();
600         return 0;
601 }
602
603 void BC_Synchronous::release_pbuffer(int window_id, GLXPbuffer pbuffer)
604 {
605         table_lock->lock("BC_Resources::release_textures");
606         for(int i = 0; i < pbuffer_ids.total; i++) {
607                 PBufferID *ptr = pbuffer_ids.values[i];
608                 if(ptr->window_id == window_id) {
609                         ptr->in_use = 0;
610                 }
611         }
612         table_lock->unlock();
613 }
614
615 void BC_Synchronous::delete_pixmap(BC_WindowBase *window,
616         GLXPixmap glx_pixmap, GLXContext glx_context)
617 {
618         BC_SynchronousCommand *command = new_command();
619         command->command = BC_SynchronousCommand::DELETE_PIXMAP;
620         command->window_id = window->get_id();
621         command->display = window->get_display();
622         command->win = window->win;
623         command->glx_win = window->glx_win;
624         command->glx_pixmap = glx_pixmap;
625         command->glx_context = glx_context;
626
627         send_garbage(command);
628 }
629 #endif
630
631 void BC_Synchronous::delete_pixmap_sync(BC_SynchronousCommand *command)
632 {
633 #ifdef HAVE_GL
634         Display *display = command->display;
635         GLXWindow glx_win = command->glx_win;
636         sync_lock(display, "BC_Synchronous::delete_pixmap_sync");
637         glXMakeContextCurrent(display, glx_win, glx_win, command->glx_context);
638         glXDestroyContext(display, command->glx_context);
639         glXDestroyGLXPixmap(display, command->glx_pixmap);
640         XUnlockDisplay(display);
641         sync_unlock();
642 #endif
643 }
644
645
646
647 void BC_Synchronous::send_garbage(BC_SynchronousCommand *command)
648 {
649         sync_garbage->send_garbage(command);
650 }
651
652 BC_WindowBase* BC_Synchronous::get_window()
653 {
654         return current_window;
655 }
656
657
658
659
660
661
662