4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
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"
39 TextureID::TextureID(int window_id, int id, int w, int h, int components)
41 this->window_id = window_id;
45 this->components = components;
49 ShaderID::ShaderID(int window_id, unsigned int handle, char *source)
51 this->window_id = window_id;
52 this->handle = handle;
53 this->source = strdup(source);
62 PBufferID::PBufferID(int window_id, GLXPbuffer glx_pbuffer, GLXContext glx_context, int w, int h)
64 this->glx_pbuffer = glx_pbuffer;
65 this->glx_context = glx_context;
66 this->window_id = window_id;
75 BC_SynchGarbage::BC_SynchGarbage(BC_Synchronous *synchronous)
78 this->synchronous = synchronous;
79 more_garbage = new Condition(0, "BC_SynchGarbage::more_garbage", 0);
80 garbage_lock = new Mutex("BC_SyncGarbage::garbage_lock");
84 BC_SynchGarbage::~BC_SynchGarbage()
90 garbage.remove_all_objects();
95 void BC_SynchGarbage::send_garbage(BC_SynchronousCommand *command)
97 garbage_lock->lock("BC_SynchGarbage::send_garbage");
98 garbage.append(command);
99 garbage_lock->unlock();
100 more_garbage->unlock();
103 void BC_SynchGarbage::handle_garbage()
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();
111 switch(command->command) {
112 case BC_SynchronousCommand::QUIT:
116 case BC_SynchronousCommand::DELETE_WINDOW:
117 synchronous->delete_window_sync(command);
120 case BC_SynchronousCommand::DELETE_PIXMAP:
121 synchronous->delete_pixmap_sync(command);
124 case BC_SynchronousCommand::DELETE_DISPLAY:
125 synchronous->delete_display_sync(command);
130 garbage_lock->lock("BC_SynchGarbage::handle_garbage 1");
132 garbage_lock->unlock();
136 void BC_SynchGarbage::start()
142 void BC_SynchGarbage::stop()
150 void BC_SynchGarbage::quit()
152 BC_SynchronousCommand *command = new BC_SynchronousCommand();
153 command->command = BC_SynchronousCommand::QUIT;
154 send_garbage(command);
157 void BC_SynchGarbage::run()
160 more_garbage->lock("BC_SynchGarbage::run");
166 BC_SynchronousCommand::BC_SynchronousCommand()
168 command = BC_SynchronousCommand::NONE;
172 command_done = new Condition(0, "BC_SynchronousCommand::command_done", 0);
175 BC_SynchronousCommand::~BC_SynchronousCommand()
180 void BC_SynchronousCommand::copy_from(BC_SynchronousCommand *command)
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;
196 BC_Synchronous::BC_Synchronous()
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");
207 BC_WindowBase::get_resources()->set_synchronous(this);
210 BC_Synchronous::~BC_Synchronous()
216 commands.remove_all_objects();
224 void BC_Synchronous::sync_lock(const char *cp)
229 void BC_Synchronous::sync_lock(Display *display, const char *cp)
231 // get both display lock and sync_lock
232 XLockDisplay(display);
233 while( lock_sync->trylock(cp) ) {
234 XUnlockDisplay(display);
236 XLockDisplay(display);
240 void BC_Synchronous::sync_unlock()
246 BC_SynchronousCommand* BC_Synchronous::new_command()
248 return new BC_SynchronousCommand();
251 void BC_Synchronous::create_objects()
255 void BC_Synchronous::start()
257 sync_garbage->start();
261 void BC_Synchronous::quit()
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();
271 long BC_Synchronous::send_command(BC_SynchronousCommand *command)
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());
281 // Wait for completion
282 command2->command_done->lock("BC_Synchronous::send_command");
283 long result = command2->result;
288 void BC_Synchronous::run()
292 next_command->lock("BC_Synchronous::run");
293 command_lock->lock("BC_Synchronous::run");
294 BC_SynchronousCommand *command = 0;
296 command = commands.values[0];
297 commands.remove_number(0);
299 command_lock->unlock();
300 if( !command ) continue;
302 //printf("BC_Synchronous::run %d\n", command->command);
303 handle_command_base(command);
308 void BC_Synchronous::handle_command_base(BC_SynchronousCommand *command)
310 sync_lock("BC_Synchronous::handle_command_base");
311 switch(command->command) {
312 case BC_SynchronousCommand::QUIT:
317 handle_command(command);
320 command->command_done->unlock();
324 void BC_Synchronous::handle_command(BC_SynchronousCommand *command)
328 void BC_Synchronous::put_texture(int id, int w, int h, int components)
332 table_lock->lock("BC_Resources::put_texture");
333 // Search for duplicate
334 for(int i = 0; i < texture_ids.total; i++)
336 TextureID *ptr = texture_ids.values[i];
337 if(ptr->window_id == current_window->get_id() &&
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",
347 current_window->get_id(),
351 table_lock->unlock();
356 TextureID *new_id = new TextureID(current_window->get_id(),
361 texture_ids.append(new_id);
362 table_lock->unlock();
366 int BC_Synchronous::get_texture(int w, int h, int components)
368 table_lock->lock("BC_Resources::get_texture");
369 for(int i = 0; i < texture_ids.total; i++)
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())
377 int result = texture_ids.values[i]->id;
378 texture_ids.values[i]->in_use = 1;
379 table_lock->unlock();
383 table_lock->unlock();
387 void BC_Synchronous::release_texture(int window_id, int id)
389 table_lock->lock("BC_Resources::release_texture");
390 for(int i = 0; i < texture_ids.total; i++)
392 if(texture_ids.values[i]->id == id &&
393 texture_ids.values[i]->window_id == window_id)
395 texture_ids.values[i]->in_use = 0;
396 table_lock->unlock();
400 table_lock->unlock();
407 unsigned int BC_Synchronous::get_shader(char *source, int *got_it)
409 table_lock->lock("BC_Resources::get_shader");
410 for(int i = 0; i < shader_ids.total; i++)
412 if(shader_ids.values[i]->window_id == current_window->get_id() &&
413 !strcmp(shader_ids.values[i]->source, source))
415 unsigned int result = shader_ids.values[i]->handle;
416 table_lock->unlock();
421 table_lock->unlock();
426 void BC_Synchronous::put_shader(unsigned int handle,
429 table_lock->lock("BC_Resources::put_shader");
430 shader_ids.append(new ShaderID(current_window->get_id(), handle, source));
431 table_lock->unlock();
434 void BC_Synchronous::dump_shader(unsigned int handle)
437 table_lock->lock("BC_Resources::dump_shader");
438 for(int i = 0; i < shader_ids.total; i++)
440 if(shader_ids.values[i]->handle == handle)
442 printf("BC_Synchronous::dump_shader\n"
443 "%s", shader_ids.values[i]->source);
448 table_lock->unlock();
449 if(!got_it) printf("BC_Synchronous::dump_shader couldn't find %d\n", handle);
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)
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;
466 send_garbage(command);
467 command->command_done->lock("BC_Synchronous::delete_window");
471 void BC_Synchronous::delete_window_sync(BC_SynchronousCommand *command)
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");
482 // texture ID's are unique to different contexts
483 glXMakeContextCurrent(display, glx_win, glx_win, glx_context);
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",
492 texture_ids.remove_object_number(i);
497 for(int i = 0; i < shader_ids.total; i++)
499 if(shader_ids.values[i]->window_id == window_id)
501 glDeleteShader(shader_ids.values[i]->handle);
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);
510 for(int i = 0; i < pbuffer_ids.total; i++)
512 if(pbuffer_ids.values[i]->window_id == window_id)
514 glXDestroyPbuffer(display, pbuffer_ids.values[i]->glx_pbuffer);
515 glXDestroyContext(display, pbuffer_ids.values[i]->glx_context);
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);
525 table_lock->unlock();
527 XDestroyWindow(display, win);
529 glXDestroyContext(display, glx_context);
530 command->command_done->unlock();
531 XUnlockDisplay(display);
536 void BC_Synchronous::delete_display(BC_WindowBase *window)
539 BC_SynchronousCommand *command = new_command();
540 command->command = BC_SynchronousCommand::DELETE_DISPLAY;
541 command->display = window->get_display();
543 send_garbage(command);
547 void BC_Synchronous::delete_display_sync(BC_SynchronousCommand *command)
550 Display *display = command->display;
551 sync_lock(display, "BC_Synchronous::delete_display_sync");
552 XUnlockDisplay(display);
553 XCloseDisplay(display);
559 void BC_Synchronous::put_pbuffer(int w, int h,
560 GLXPbuffer glx_pbuffer, GLXContext glx_context)
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) {
574 PBufferID *ptr = new PBufferID(current_window->get_id(),
575 glx_pbuffer, glx_context, w, h);
576 pbuffer_ids.append(ptr);
578 table_lock->unlock();
581 GLXPbuffer BC_Synchronous::get_pbuffer(int w,
584 GLXContext *glx_context)
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;
595 table_lock->unlock();
599 table_lock->unlock();
603 void BC_Synchronous::release_pbuffer(int window_id, GLXPbuffer pbuffer)
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) {
612 table_lock->unlock();
615 void BC_Synchronous::delete_pixmap(BC_WindowBase *window,
616 GLXPixmap glx_pixmap, GLXContext glx_context)
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;
627 send_garbage(command);
631 void BC_Synchronous::delete_pixmap_sync(BC_SynchronousCommand *command)
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);
647 void BC_Synchronous::send_garbage(BC_SynchronousCommand *command)
649 sync_garbage->send_garbage(command);
652 BC_WindowBase* BC_Synchronous::get_window()
654 return current_window;