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