3c0e601df008fe5cbe5cbc78d1448426ce6315a0
[goodguy/history.git] / cinelerra-5.1 / 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_SynchronousCommand::BC_SynchronousCommand()
76 {
77         command = BC_SynchronousCommand::NONE;
78         frame = 0;
79         frame_return = 0;
80         result = 0;
81         command_done = new Condition(0, "BC_SynchronousCommand::command_done", 0);
82 }
83
84 BC_SynchronousCommand::~BC_SynchronousCommand()
85 {
86         delete command_done;
87 }
88
89 void BC_SynchronousCommand::copy_from(BC_SynchronousCommand *command)
90 {
91         this->command =         command->command;
92         this->colormodel =      command->colormodel;
93         this->window =          command->window;
94         this->frame =           command->frame;
95         this->window_id =       command->window_id;
96         this->frame_return =    command->frame_return;
97         this->id =              command->id;
98         this->w =               command->w;
99         this->h =               command->h;
100 }
101
102
103
104
105 BC_Synchronous::BC_Synchronous()
106  : Thread(1, 0, 0)
107 {
108         lock_sync = new Mutex("BC_Synchronous::lock_sync");
109         next_command = new Condition(0, "BC_Synchronous::next_command", 1);
110         command_lock = new Mutex("BC_Synchronous::command_lock");
111         table_lock = new Mutex("BC_Synchronous::table_lock");
112         done = 0;
113         is_started = 0;
114         current_window = 0;
115         BC_WindowBase::get_resources()->set_synchronous(this);
116 }
117
118 BC_Synchronous::~BC_Synchronous()
119 {
120         if( running() ) {
121                 quit();
122         }
123         join();
124         commands.remove_all_objects();
125         delete lock_sync;
126         delete next_command;
127         delete command_lock;
128         delete table_lock;
129 }
130
131 void BC_Synchronous::sync_lock(const char *cp)
132 {
133         lock_sync->lock(cp);
134 }
135
136 void BC_Synchronous::sync_unlock()
137 {
138         lock_sync->unlock();
139 }
140
141 BC_SynchronousCommand* BC_Synchronous::new_command()
142 {
143         return new BC_SynchronousCommand();
144 }
145
146 void BC_Synchronous::create_objects()
147 {
148 }
149
150 void BC_Synchronous::start()
151 {
152         is_started = 1;
153         //run();
154         Thread::start();
155 }
156
157 void BC_Synchronous::quit()
158 {
159         if( !is_started ) return;
160         is_started = 0;
161         BC_SynchronousCommand *command = BC_Synchronous::new_command();
162         command->command = BC_SynchronousCommand::QUIT;
163         send_garbage(command);
164         command->command_done->lock("BC_Synchronous::quit");
165         delete command;
166 }
167
168 long BC_Synchronous::send_command(BC_SynchronousCommand *command)
169 {
170         BC_SynchronousCommand *command2 = new_command();
171         command2->copy_from(command);
172         command_lock->lock("BC_Synchronous::send_command");
173         commands.append(command2);
174         command_lock->unlock();
175         next_command->unlock();
176 //printf("BC_Synchronous::send_command 1 %d\n", next_command->get_value());
177
178 // Wait for completion
179         command2->command_done->lock("BC_Synchronous::send_command");
180         long result = command2->result;
181         delete command2;
182         return result;
183 }
184
185 void BC_Synchronous::run()
186 {
187         sync_lock("BC_Synchronous::run 0");
188         while(!done) {
189                 command_lock->lock("BC_Synchronous::run");
190                 BC_SynchronousCommand *command = 0;
191                 if(commands.total) {
192                         command = commands.values[0];
193                         commands.remove_number(0);
194                 }
195                 command_lock->unlock();
196                 if( !command ) {
197                         sync_unlock();
198                         next_command->lock("BC_Synchronous::run");
199                         sync_lock("BC_Synchronous::run 1");
200                         continue;
201                 }
202 //printf("BC_Synchronous::run %d\n", command->command);
203                 handle_command_base(command);
204         }
205         sync_unlock();
206 }
207
208 void BC_Synchronous::handle_command_base(BC_SynchronousCommand *command)
209 {
210         switch(command->command) {
211         case BC_SynchronousCommand::QUIT:
212                 done = 1;
213                 command->command_done->unlock();
214                 return;
215
216         case BC_SynchronousCommand::DELETE_WINDOW:
217                 delete_window_sync(command);
218                 command->command_done->unlock();
219                 return;
220
221         case BC_SynchronousCommand::DELETE_PIXMAP:
222                 delete_pixmap_sync(command);
223                 break;
224
225         case BC_SynchronousCommand::DELETE_DISPLAY:
226                 delete_display_sync(command);
227                 break;
228
229         default:
230                 handle_command(command);
231                 command->command_done->unlock();
232                 return;
233         }
234         delete command;
235 }
236
237 void BC_Synchronous::handle_command(BC_SynchronousCommand *command)
238 {
239 }
240
241 void BC_Synchronous::put_texture(int id, int w, int h, int components)
242 {
243         if(id >= 0)
244         {
245                 table_lock->lock("BC_Resources::put_texture");
246 // Search for duplicate
247                 for(int i = 0; i < texture_ids.total; i++)
248                 {
249                         TextureID *ptr = texture_ids.values[i];
250                         if(ptr->window_id == current_window->get_id() &&
251                                 ptr->id == id)
252                         {
253                                 printf("BC_Synchronous::push_texture: texture exists\n"
254                                         "exists: window=%d id=%d w=%d h=%d\n"
255                                         "new:    window=%d id=%d w=%d h=%d\n",
256                                         ptr->window_id,
257                                         ptr->id,
258                                         ptr->w,
259                                         ptr->h,
260                                         current_window->get_id(),
261                                         id,
262                                         w,
263                                         h);
264                                 table_lock->unlock();
265                                 return;
266                         }
267                 }
268
269                 TextureID *new_id = new TextureID(current_window->get_id(),
270                         id,
271                         w,
272                         h,
273                         components);
274                 texture_ids.append(new_id);
275                 table_lock->unlock();
276         }
277 }
278
279 int BC_Synchronous::get_texture(int w, int h, int components)
280 {
281         table_lock->lock("BC_Resources::get_texture");
282         for(int i = 0; i < texture_ids.total; i++)
283         {
284                 if(texture_ids.values[i]->w == w &&
285                         texture_ids.values[i]->h == h &&
286                         texture_ids.values[i]->components == components &&
287                         !texture_ids.values[i]->in_use &&
288                         texture_ids.values[i]->window_id == current_window->get_id())
289                 {
290                         int result = texture_ids.values[i]->id;
291                         texture_ids.values[i]->in_use = 1;
292                         table_lock->unlock();
293                         return result;
294                 }
295         }
296         table_lock->unlock();
297         return -1;
298 }
299
300 void BC_Synchronous::release_texture(int window_id, int id)
301 {
302         table_lock->lock("BC_Resources::release_texture");
303         for(int i = 0; i < texture_ids.total; i++)
304         {
305                 if(texture_ids.values[i]->id == id &&
306                         texture_ids.values[i]->window_id == window_id)
307                 {
308                         texture_ids.values[i]->in_use = 0;
309                         table_lock->unlock();
310                         return;
311                 }
312         }
313         table_lock->unlock();
314 }
315
316
317
318
319
320 unsigned int BC_Synchronous::get_shader(char *source, int *got_it)
321 {
322         table_lock->lock("BC_Resources::get_shader");
323         for(int i = 0; i < shader_ids.total; i++)
324         {
325                 if(shader_ids.values[i]->window_id == current_window->get_id() &&
326                         !strcmp(shader_ids.values[i]->source, source))
327                 {
328                         unsigned int result = shader_ids.values[i]->handle;
329                         table_lock->unlock();
330                         *got_it = 1;
331                         return result;
332                 }
333         }
334         table_lock->unlock();
335         *got_it = 0;
336         return 0;
337 }
338
339 void BC_Synchronous::put_shader(unsigned int handle,
340         char *source)
341 {
342         table_lock->lock("BC_Resources::put_shader");
343         shader_ids.append(new ShaderID(current_window->get_id(), handle, source));
344         table_lock->unlock();
345 }
346
347 void BC_Synchronous::dump_shader(unsigned int handle)
348 {
349         int got_it = 0;
350         table_lock->lock("BC_Resources::dump_shader");
351         for(int i = 0; i < shader_ids.total; i++)
352         {
353                 if(shader_ids.values[i]->handle == handle)
354                 {
355                         printf("BC_Synchronous::dump_shader\n"
356                                 "%s", shader_ids.values[i]->source);
357                         got_it = 1;
358                         break;
359                 }
360         }
361         table_lock->unlock();
362         if(!got_it) printf("BC_Synchronous::dump_shader couldn't find %d\n", handle);
363 }
364
365 void BC_Synchronous::delete_window(BC_WindowBase *window)
366 {
367 #ifdef HAVE_GL
368         BC_SynchronousCommand *command = BC_Synchronous::new_command();
369         command->command = BC_SynchronousCommand::DELETE_WINDOW;
370         command->window_id = window->get_id();
371         command->display = window->get_display();
372         command->win = window->win;
373         command->glx_win = window->glx_win;
374         command->glx_context = window->glx_win_context;
375
376         send_garbage(command);
377         command->command_done->lock("BC_Synchronous::quit");
378         delete command;
379 #endif
380 }
381
382 void BC_Synchronous::delete_window_sync(BC_SynchronousCommand *command)
383 {
384 #ifdef HAVE_GL
385         int window_id = command->window_id;
386         Display *display = command->display;
387 //      Window win = command->win;
388         GLXWindow glx_win = command->glx_win;
389         GLXContext glx_context = command->glx_context;
390         XLockDisplay(display);
391 //int debug = 0;
392
393 // texture ID's are unique to different contexts
394         glXMakeContextCurrent(display, glx_win, glx_win, glx_context);
395
396         table_lock->lock("BC_Resources::release_textures");
397         for(int i = 0; i < texture_ids.total; i++) {
398                 if(texture_ids.values[i]->window_id == window_id) {
399                         GLuint id = texture_ids.values[i]->id;
400                         glDeleteTextures(1, &id);
401 //if(debug) printf("BC_Synchronous::delete_window_sync texture_id=%d window_id=%d\n",
402 // id, window_id);
403                         texture_ids.remove_object_number(i);
404                         i--;
405                 }
406         }
407
408         for(int i = 0; i < shader_ids.total; i++)
409         {
410                 if(shader_ids.values[i]->window_id == window_id)
411                 {
412                         glDeleteShader(shader_ids.values[i]->handle);
413 //if(debug)
414 //printf("BC_Synchronous::delete_window_sync shader_id=%d window_id=%d\n",
415 //shader_ids.values[i]->handle, window_id);
416                         shader_ids.remove_object_number(i);
417                         i--;
418                 }
419         }
420
421         for(int i = 0; i < pbuffer_ids.total; i++)
422         {
423                 if(pbuffer_ids.values[i]->window_id == window_id)
424                 {
425                         glXDestroyPbuffer(display, pbuffer_ids.values[i]->glx_pbuffer);
426                         glXDestroyContext(display, pbuffer_ids.values[i]->glx_context);
427 //if(debug)
428 //printf("BC_Synchronous::delete_window_sync pbuffer_id=%p window_id=%d\n",
429 //  (void*)pbuffer_ids.values[i]->pbuffer, window_id);
430                         pbuffer_ids.remove_object_number(i);
431                         i--;
432                 }
433         }
434
435
436         table_lock->unlock();
437
438         glXMakeContextCurrent(display, None, None, 0);
439         if( glx_context )
440                 glXDestroyContext(display, glx_context);
441 // causes xerror BadWindow (invalid Window parameter)
442 //      XDestroyWindow(display, glx_win);
443 // win destroyed in bcwindowbase
444         XUnlockDisplay(display);
445 #endif
446 }
447
448 void BC_Synchronous::delete_display(BC_WindowBase *window)
449 {
450 #ifdef HAVE_GL
451         BC_SynchronousCommand *command = BC_Synchronous::new_command();
452         command->command = BC_SynchronousCommand::DELETE_DISPLAY;
453         command->display = window->get_display();
454
455         send_garbage(command);
456 #endif
457 }
458
459 void BC_Synchronous::delete_display_sync(BC_SynchronousCommand *command)
460 {
461 #ifdef HAVE_GL
462         Display *display = command->display;
463         XLockDisplay(display);
464         XUnlockDisplay(display);
465         glXMakeContextCurrent(display, None, None, 0);
466         XCloseDisplay(display);
467 #endif
468 }
469
470 #ifdef HAVE_GL
471 void BC_Synchronous::put_pbuffer(int w, int h,
472                 GLXPbuffer glx_pbuffer, GLXContext glx_context)
473 {
474         int exists = 0;
475         table_lock->lock("BC_Resources::release_textures");
476         for(int i = 0; i < pbuffer_ids.total; i++) {
477                 PBufferID *ptr = pbuffer_ids.values[i];
478                 if( ptr->w == w && ptr->h == h && ptr->glx_pbuffer == glx_pbuffer ) {
479                         exists = 1;
480                         break;
481                 }
482         }
483
484         if(!exists) {
485                 PBufferID *ptr = new PBufferID(current_window->get_id(),
486                         glx_pbuffer, glx_context, w, h);
487                 pbuffer_ids.append(ptr);
488         }
489         table_lock->unlock();
490 }
491
492 GLXPbuffer BC_Synchronous::get_pbuffer(int w, int h, GLXContext *glx_context)
493 {
494         table_lock->lock("BC_Resources::release_textures");
495         for(int i = 0; i < pbuffer_ids.total; i++) {
496                 PBufferID *ptr = pbuffer_ids.values[i];
497                 if(ptr->w == w && ptr->h == h && !ptr->in_use &&
498                         ptr->window_id == current_window->get_id() ) {
499                         GLXPbuffer result = ptr->glx_pbuffer;
500                         *glx_context = ptr->glx_context;
501                         ptr->in_use = 1;
502                         table_lock->unlock();
503                         return result;
504                 }
505         }
506         table_lock->unlock();
507         return 0;
508 }
509
510 void BC_Synchronous::release_pbuffer(int window_id, GLXPbuffer pbuffer)
511 {
512         table_lock->lock("BC_Resources::release_textures");
513         for(int i = 0; i < pbuffer_ids.total; i++) {
514                 PBufferID *ptr = pbuffer_ids.values[i];
515                 if( ptr->window_id == window_id ) {
516                         ptr->in_use = 0;
517                 }
518         }
519         table_lock->unlock();
520 }
521
522 void BC_Synchronous::delete_pixmap(BC_WindowBase *window,
523         GLXPixmap glx_pixmap, GLXContext glx_context)
524 {
525         BC_SynchronousCommand *command = BC_Synchronous::new_command();
526         command->command = BC_SynchronousCommand::DELETE_PIXMAP;
527         command->window_id = window->get_id();
528         command->display = window->get_display();
529         command->win = window->win;
530         command->glx_win = window->glx_win;
531         command->glx_pixmap = glx_pixmap;
532         command->glx_context = glx_context;
533
534         send_garbage(command);
535 }
536 #endif
537
538 void BC_Synchronous::delete_pixmap_sync(BC_SynchronousCommand *command)
539 {
540 #ifdef HAVE_GL
541         Display *display = command->display;
542         GLXWindow glx_win = command->glx_win;
543         XLockDisplay(display);
544         glXMakeContextCurrent(display, glx_win, glx_win, command->glx_context);
545         glXDestroyContext(display, command->glx_context);
546         glXDestroyGLXPixmap(display, command->glx_pixmap);
547         XUnlockDisplay(display);
548 #endif
549 }
550
551
552
553 void BC_Synchronous::send_garbage(BC_SynchronousCommand *command)
554 {
555         command_lock->lock("BC_Synchronous::send_garbage");
556         commands.append(command);
557         command_lock->unlock();
558         next_command->unlock();
559 }
560
561 BC_WindowBase* BC_Synchronous::get_window()
562 {
563         return current_window;
564 }
565
566
567
568
569
570
571