From 0600c39dcb539c281fd5b46aa0197ebac09bcf5e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 2 Sep 2006 21:37:47 +0000 Subject: [PATCH] r854: Merge 2.1: Introduce a framework to use OpenGL for hardware accelerated rendering. Traditional rendering still works as usual. The OpenGL parts are bracketed by HAVE_GL and are not compiled. A configure option to enable OpenGL code follows later. This revision should match GIT::09beda0819560b02520b316b8e62045fe100026f. --- cinelerra/Makefile.am | 2 + cinelerra/attachmentpoint.C | 7 +- cinelerra/attachmentpoint.h | 1 + cinelerra/canvas.C | 103 +-- cinelerra/canvas.h | 26 +- cinelerra/cwindowgui.C | 57 +- cinelerra/filejpeg.C | 3 + cinelerra/filemov.C | 19 + cinelerra/filempeg.C | 3 + cinelerra/mwindow.C | 39 +- cinelerra/mwindow.h | 13 +- cinelerra/mwindowedit.C | 10 + cinelerra/playback3d.C | 1489 +++++++++++++++++++++++++++++++++++++++++ cinelerra/playback3d.h | 304 +++++++++ cinelerra/playback3d.inc | 12 + cinelerra/pluginclient.C | 5 + cinelerra/pluginclient.h | 4 + cinelerra/pluginserver.C | 60 +- cinelerra/pluginserver.h | 23 +- cinelerra/pluginvclient.C | 16 +- cinelerra/pluginvclient.h | 12 +- cinelerra/preferencesthread.C | 13 +- cinelerra/quit.C | 6 +- cinelerra/resizetrackthread.C | 11 + cinelerra/savefile.C | 5 + cinelerra/setformat.C | 12 + cinelerra/vattachmentpoint.C | 35 +- cinelerra/vattachmentpoint.h | 3 +- cinelerra/vdeviceprefs.C | 7 + cinelerra/vdevicex11.C | 330 +++++++-- cinelerra/vdevicex11.h | 82 ++- cinelerra/videodevice.C | 4 + cinelerra/videodevice.inc | 2 + cinelerra/virtualvconsole.C | 37 +- cinelerra/virtualvconsole.h | 5 + cinelerra/virtualvnode.C | 97 ++- cinelerra/virtualvnode.h | 16 +- cinelerra/vmodule.C | 126 +++- cinelerra/vmodule.h | 7 +- cinelerra/vwindowgui.C | 35 +- guicast/Makefile.am | 27 +- guicast/bcpbuffer.C | 172 +++++ guicast/bcpbuffer.h | 53 ++ guicast/bcpbuffer.inc | 12 + guicast/bcresources.C | 31 + guicast/bcresources.h | 11 +- guicast/bcsynchronous.C | 567 ++++++++++++++++ guicast/bcsynchronous.h | 263 ++++++++ guicast/bcsynchronous.inc | 12 + guicast/bctexture.C | 227 +++++++ guicast/bctexture.h | 64 ++ guicast/bctexture.inc | 8 + guicast/bcwindow3d.C | 79 +++ guicast/bcwindowbase.C | 153 ++--- guicast/bcwindowbase.h | 39 +- guicast/condition.C | 29 +- guicast/condition.h | 4 +- guicast/guicast.h | 4 + guicast/vframe.C | 69 +- guicast/vframe.h | 134 +++- guicast/vframe3d.C | 515 ++++++++++++++ 61 files changed, 5166 insertions(+), 348 deletions(-) create mode 100644 cinelerra/playback3d.C create mode 100644 cinelerra/playback3d.h create mode 100644 cinelerra/playback3d.inc create mode 100644 guicast/bcpbuffer.C create mode 100644 guicast/bcpbuffer.h create mode 100644 guicast/bcpbuffer.inc create mode 100644 guicast/bcsynchronous.C create mode 100644 guicast/bcsynchronous.h create mode 100644 guicast/bcsynchronous.inc create mode 100644 guicast/bctexture.C create mode 100644 guicast/bctexture.h create mode 100644 guicast/bctexture.inc create mode 100644 guicast/bcwindow3d.C create mode 100644 guicast/vframe3d.C diff --git a/cinelerra/Makefile.am b/cinelerra/Makefile.am index fda10670..c23c03a8 100644 --- a/cinelerra/Makefile.am +++ b/cinelerra/Makefile.am @@ -176,6 +176,7 @@ cinelerra_SOURCES = aattachmentpoint.C \ picture.C \ pipe.C \ playabletracks.C \ + playback3d.C \ playbackconfig.C \ playbackengine.C \ playbackprefs.C \ @@ -480,6 +481,7 @@ noinst_HEADERS = aattachmentpoint.h \ picture.h \ pipe.h \ playabletracks.h \ + playback3d.h \ playbackconfig.h \ playbackengine.h \ playbackprefs.h \ diff --git a/cinelerra/attachmentpoint.C b/cinelerra/attachmentpoint.C index d67842ba..ba4c2e0b 100644 --- a/cinelerra/attachmentpoint.C +++ b/cinelerra/attachmentpoint.C @@ -201,7 +201,12 @@ void AttachmentPoint::render_gui(void *data, int size) renderengine->mwindow->render_plugin_gui(data, size, plugin); } - +int AttachmentPoint::gui_open() +{ + if(renderengine && renderengine->mwindow) + return renderengine->mwindow->plugin_gui_open(plugin); + return 0; +} diff --git a/cinelerra/attachmentpoint.h b/cinelerra/attachmentpoint.h index 8d33fd2c..e2aef448 100644 --- a/cinelerra/attachmentpoint.h +++ b/cinelerra/attachmentpoint.h @@ -52,6 +52,7 @@ public: // Called by plugin server to render GUI with data. void render_gui(void *data); void render_gui(void *data, int size); + int gui_open(); virtual int get_buffer_size() { return 0; }; // For unshared plugins, virtual plugins to send configuration events to and diff --git a/cinelerra/canvas.C b/cinelerra/canvas.C index 8bd01c04..03f4e23d 100644 --- a/cinelerra/canvas.C +++ b/cinelerra/canvas.C @@ -5,7 +5,9 @@ #include "edlsession.h" #include "keys.h" #include "language.h" +#include "mainsession.h" #include "mutex.h" +#include "mwindow.h" #include "vframe.h" @@ -287,14 +289,14 @@ void Canvas::output_to_canvas(EDL *edl, int single_channel, float &x, float &y) void Canvas::get_transfers(EDL *edl, - int &in_x, - int &in_y, - int &in_w, - int &in_h, - int &out_x, - int &out_y, - int &out_w, - int &out_h, + float &output_x1, + float &output_y1, + float &output_x2, + float &output_y2, + float &canvas_x1, + float &canvas_y1, + float &canvas_x2, + float &canvas_y2, int canvas_w, int canvas_h) { @@ -356,14 +358,14 @@ void Canvas::get_transfers(EDL *edl, // printf("Canvas::get_transfers 2 %.0f %.0f %.0f %.0f -> %.0f %.0f %.0f %.0f\n", // in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2); - in_x = (int)in_x1; - in_y = (int)in_y1; - in_w = (int)(in_x2 - in_x1); - in_h = (int)(in_y2 - in_y1); - out_x = (int)out_x1; - out_y = (int)out_y1; - out_w = (int)(out_x2 - out_x1); - out_h = (int)(out_y2 - out_y1); + output_x1 = in_x1; + output_y1 = in_y1; + output_x2 = in_x2; + output_y2 = in_y2; + canvas_x1 = out_x1; + canvas_y1 = out_y1; + canvas_x2 = out_x2; + canvas_y2 = out_y2; // Center on canvas // if(!scrollbars_exist()) @@ -374,47 +376,66 @@ void Canvas::get_transfers(EDL *edl, } else +// The output frame is normalized to the canvas { - out_x = 0; - out_y = 0; - out_w = canvas_w; - out_h = canvas_h; +// Default canvas coords fill the entire canvas + canvas_x1 = 0; + canvas_y1 = 0; + canvas_x2 = canvas_w; + canvas_y2 = canvas_h; if(edl) { - if((float)out_w / out_h > edl->get_aspect_ratio()) +// Use EDL aspect ratio to shrink one of the canvas dimensions + float out_w = canvas_x2 - canvas_x1; + float out_h = canvas_y2 - canvas_y1; + if(out_w / out_h > edl->get_aspect_ratio()) { out_w = (int)(out_h * edl->get_aspect_ratio() + 0.5); - out_x = canvas_w / 2 - out_w / 2; + canvas_x1 = canvas_w / 2 - out_w / 2; } else { out_h = (int)(out_w / edl->get_aspect_ratio() + 0.5); - out_y = canvas_h / 2 - out_h / 2; + canvas_y1 = canvas_h / 2 - out_h / 2; } - - in_x = 0; - in_y = 0; - in_w = get_output_w(edl); - in_h = get_output_h(edl); + canvas_x2 = canvas_x1 + out_w; + canvas_y2 = canvas_y1 + out_h; + +// Get output frame coords from EDL + output_x1 = 0; + output_y1 = 0; + output_x2 = get_output_w(edl); + output_y2 = get_output_h(edl); } else +// No EDL to get aspect ratio or output frame coords from { - in_x = 0; - in_y = 0; - in_w = this->output_w; - in_h = this->output_h; + output_x1 = 0; + output_y1 = 0; + output_x2 = this->output_w; + output_y2 = this->output_h; } } - in_x = MAX(0, in_x); - in_y = MAX(0, in_y); - in_w = MAX(0, in_w); - in_h = MAX(0, in_h); - out_x = MAX(0, out_x); - out_y = MAX(0, out_y); - out_w = MAX(0, out_w); - out_h = MAX(0, out_h); +// Clamp to minimum value + output_x1 = MAX(0, output_x1); + output_y1 = MAX(0, output_y1); + output_x2 = MAX(output_x1, output_x2); + output_y2 = MAX(output_y1, output_y2); + canvas_x1 = MAX(0, canvas_x1); + canvas_y1 = MAX(0, canvas_y1); + canvas_x2 = MAX(canvas_x1, canvas_x2); + canvas_y2 = MAX(canvas_y1, canvas_y2); +// printf("Canvas::get_transfers 2 %f,%f %f,%f -> %f,%f %f,%f\n", +// output_x1, +// output_y1, +// output_x2, +// output_y2, +// canvas_x1, +// canvas_y1, +// canvas_x2, +// canvas_y2); } int Canvas::scrollbars_exist() @@ -643,7 +664,7 @@ int Canvas::button_press_event() if(get_fullscreen()) fullscreen_menu->activate_menu(); else - canvas_menu->activate_menu(); + canvas_menu->activate_menu(); result = 1; } diff --git a/cinelerra/canvas.h b/cinelerra/canvas.h index b6ebce38..9bb4eaec 100644 --- a/cinelerra/canvas.h +++ b/cinelerra/canvas.h @@ -92,15 +92,20 @@ public: // Provide canvas dimensions since a BC_Bitmap containing obsolete dimensions -// is often the output being transferred to - void get_transfers(EDL *edl, int &in_x, - int &in_y, - int &in_w, - int &in_h, - int &out_x, - int &out_y, - int &out_w, - int &out_h, +// is often the output being transferred to. +// This gets the input coordinates on the device output_frame +// and the corresponding output coordinates on the canvas. +// Must be floating point to support OpenGL. + void get_transfers(EDL *edl, + float &output_x1, + float &output_y1, + float &output_x2, + float &output_y2, + float &canvas_x1, + float &canvas_y1, + float &canvas_x2, + float &canvas_y2, +// passing -1 causes automatic size detection int canvas_w = -1, int canvas_h = -1); void reposition_window(EDL *edl, int x, int y, int w, int h); @@ -173,7 +178,8 @@ public: int use_vwindow; // Used in record monitor int output_w, output_h; -// Store frame in native format after playback for refreshes +// Last frame played is stored here in driver format for +// refreshes. VFrame *refresh_frame; // Results from last get_scrollbars int w_needed; diff --git a/cinelerra/cwindowgui.C b/cinelerra/cwindowgui.C index ca602ffb..bc2c0668 100644 --- a/cinelerra/cwindowgui.C +++ b/cinelerra/cwindowgui.C @@ -851,36 +851,45 @@ void CWindowCanvas::draw_refresh() if(refresh_frame) { - int in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h; + float in_x1, in_y1, in_x2, in_y2; + float out_x1, out_y1, out_x2, out_y2; get_transfers(mwindow->edl, - in_x, - in_y, - in_w, - in_h, - out_x, - out_y, - out_w, - out_h); + in_x1, + in_y1, + in_x2, + in_y2, + out_x1, + out_y1, + out_x2, + out_y2); + + get_canvas()->clear_box(0, + 0, + get_canvas()->get_w(), + get_canvas()->get_h()); -// printf("CWindowCanvas::draw_refresh %d %d %d %d -> %d %d %d %d\n", -// in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h); +// printf("CWindowCanvas::draw_refresh %f %f %f %f -> %f %f %f %f\n", +// in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2); - if(out_w > 0 && out_h > 0 && in_w > 0 && in_h > 0) + if(out_x2 > out_x1 && + out_y2 > out_y1 && + in_x2 > in_x1 && + in_y2 > in_y1) + { +// Can't use OpenGL here because it is called asynchronously of the +// playback operation. get_canvas()->draw_vframe(refresh_frame, - out_x, - out_y, - out_w, - out_h, - in_x, - in_y, - in_w, - in_h, + (int)out_x1, + (int)out_y1, + (int)(out_x2 - out_x1), + (int)(out_y2 - out_y1), + (int)in_x1, + (int)in_y1, + (int)(in_x2 - in_x1), + (int)(in_y2 - in_y1), 0); - } - else - { - get_canvas()->clear_box(0, 0, get_canvas()->get_w(), get_canvas()->get_h()); + } } draw_overlays(); diff --git a/cinelerra/filejpeg.C b/cinelerra/filejpeg.C index 5281b0cc..9dc49226 100644 --- a/cinelerra/filejpeg.C +++ b/cinelerra/filejpeg.C @@ -108,6 +108,9 @@ int FileJPEG::get_best_colormodel(Asset *asset, int driver) case PLAYBACK_FIREWIRE: return BC_YUV420P; break; + case PLAYBACK_X11_GL: + return BC_YUV888; + break; case PLAYBACK_LML: case PLAYBACK_BUZ: return BC_YUV422P; diff --git a/cinelerra/filemov.C b/cinelerra/filemov.C index 455ba4f2..b3b1c6cb 100644 --- a/cinelerra/filemov.C +++ b/cinelerra/filemov.C @@ -459,6 +459,25 @@ int FileMOV::get_best_colormodel(Asset *asset, int driver) if(match4(asset->vcodec, QUICKTIME_HV64)) return BC_YUV420P; if(match4(asset->vcodec, QUICKTIME_DIV3)) return BC_YUV420P; break; + case PLAYBACK_X11_GL: + if(match4(asset->vcodec, QUICKTIME_YUV420) || + match4(asset->vcodec, QUICKTIME_YUV422) || + match4(asset->vcodec, QUICKTIME_2VUY) || + match4(asset->vcodec, QUICKTIME_JPEG) || + match4(asset->vcodec, QUICKTIME_MJPA) || + match4(asset->vcodec, QUICKTIME_DV) || + match4(asset->vcodec, QUICKTIME_DVCP) || + match4(asset->vcodec, QUICKTIME_DVSD) || + match4(asset->vcodec, QUICKTIME_HV60) || + match4(asset->vcodec, QUICKTIME_DIVX) || + match4(asset->vcodec, QUICKTIME_DVSD) || + match4(asset->vcodec, QUICKTIME_MP4V) || + match4(asset->vcodec, QUICKTIME_H263) || + match4(asset->vcodec, QUICKTIME_H264) || + match4(asset->vcodec, QUICKTIME_HV64) || + match4(asset->vcodec, QUICKTIME_DIV3) || + match4(asset->vcodec, QUICKTIME_DVSD)) return BC_YUV888; + break; case PLAYBACK_DV1394: case PLAYBACK_FIREWIRE: if(match4(asset->vcodec, QUICKTIME_DV) || diff --git a/cinelerra/filempeg.C b/cinelerra/filempeg.C index b34c5c56..2af7c724 100644 --- a/cinelerra/filempeg.C +++ b/cinelerra/filempeg.C @@ -656,6 +656,9 @@ int FileMPEG::get_best_colormodel(Asset *asset, int driver) if(asset->vmpeg_cmodel == MPEG_YUV420) return BC_YUV420P; if(asset->vmpeg_cmodel == MPEG_YUV422) return BC_YUV422P; break; + case PLAYBACK_X11_GL: + return BC_YUV888; + break; case PLAYBACK_LML: case PLAYBACK_BUZ: return BC_YUV422P; diff --git a/cinelerra/mwindow.C b/cinelerra/mwindow.C index 65a31be2..db666545 100644 --- a/cinelerra/mwindow.C +++ b/cinelerra/mwindow.C @@ -46,6 +46,7 @@ #include "mwindow.h" #include "new.h" #include "patchbay.h" +#include "playback3d.h" #include "playbackengine.h" #include "plugin.h" #include "pluginserver.h" @@ -122,6 +123,7 @@ int atexit(void (*function)(void)) MWindow::MWindow() + : Thread(1, 0, 0) { plugin_gui_lock = new Mutex("MWindow::plugin_gui_lock"); brender_lock = new Mutex("MWindow::brender_lock"); @@ -590,6 +592,12 @@ void MWindow::init_theme() theme->check_used(); } +void MWindow::init_3d() +{ + playback_3d = new Playback3D(this); + playback_3d->create_objects(); +} + void MWindow::init_edl() { edl = new EDL; @@ -1218,6 +1226,8 @@ void MWindow::create_objects(int want_gui, edl = 0; + + init_3d(); show_splash(); // For some reason, init_signals must come after show_splash or the signals won't @@ -1320,11 +1330,18 @@ void MWindow::hide_splash() void MWindow::start() { +ENABLE_BUFFER vwindow->start(); awindow->start(); cwindow->start(); lwindow->start(); gwindow->start(); + Thread::start(); + playback_3d->start(); +} + +void MWindow::run() +{ gui->run_window(); } @@ -1522,8 +1539,12 @@ void MWindow::show_plugin(Plugin *plugin) void MWindow::hide_plugin(Plugin *plugin, int lock) { - if(lock) plugin_gui_lock->lock("MWindow::hide_plugin"); plugin->show = 0; + gui->lock_window("MWindow::hide_plugin"); + gui->update(0, 1, 0, 0, 0, 0, 0); + gui->unlock_window(); + + if(lock) plugin_gui_lock->lock("MWindow::hide_plugin"); for(int i = 0; i < plugin_guis->total; i++) { if(plugin_guis->values[i]->plugin == plugin) @@ -1558,6 +1579,22 @@ void MWindow::update_plugin_guis() plugin_gui_lock->unlock(); } +int MWindow::plugin_gui_open(Plugin *plugin) +{ + int result = 0; + plugin_gui_lock->lock("MWindow::plugin_gui_open"); + for(int i = 0; i < plugin_guis->total; i++) + { + if(plugin_guis->values[i]->plugin->identical_location(plugin)) + { + result = 1; + break; + } + } + plugin_gui_lock->unlock(); + return result; +} + void MWindow::render_plugin_gui(void *data, Plugin *plugin) { plugin_gui_lock->lock("MWindow::render_plugin_gui"); diff --git a/cinelerra/mwindow.h b/cinelerra/mwindow.h index 699d8312..af51882a 100644 --- a/cinelerra/mwindow.h +++ b/cinelerra/mwindow.h @@ -32,6 +32,7 @@ #include "mwindowgui.inc" #include "new.inc" #include "patchbay.inc" +#include "playback3d.inc" #include "playbackengine.inc" #include "plugin.inc" #include "pluginserver.inc" @@ -44,6 +45,7 @@ #include "sighandler.inc" #include "splashgui.inc" #include "theme.inc" +#include "thread.h" #include "threadloader.inc" #include "timebar.inc" #include "timebomb.h" @@ -63,7 +65,7 @@ // All entry points for commands except for window locking should be here. // This allows scriptability. -class MWindow +class MWindow : public Thread { public: MWindow(); @@ -76,6 +78,7 @@ public: void show_splash(); void hide_splash(); void start(); + void run(); int run_script(FileXML *script); int new_project(); @@ -210,6 +213,11 @@ public: void render_plugin_gui(void *data, Plugin *plugin); void render_plugin_gui(void *data, int size, Plugin *plugin); +// Called from PluginVClient::process_buffer +// Returns 1 if a GUI for the plugin is open so OpenGL routines can determine if +// they can run. + int plugin_gui_open(Plugin *plugin); + // ============================= editing commands ======================== @@ -370,8 +378,10 @@ public: int set_loop_boundaries(); // toggle loop playback and set boundaries for loop playback + Playback3D *playback_3d; SplashGUI *splash_window; +// Main undo stack MainUndo *undo; BC_Hash *defaults; Assets *assets; @@ -482,6 +492,7 @@ public: void init_menus(); void init_indexes(); void init_gui(); + void init_3d(); void init_playbackcursor(); void delete_plugins(); // diff --git a/cinelerra/mwindowedit.C b/cinelerra/mwindowedit.C index 7aeef820..d498a817 100644 --- a/cinelerra/mwindowedit.C +++ b/cinelerra/mwindowedit.C @@ -134,6 +134,16 @@ void MWindow::asset_to_size() edl->session->output_w = w; edl->session->output_h = h; + if(((edl->session->output_w % 4) || + (edl->session->output_h % 4)) && + edl->session->playback_config->vconfig->driver == PLAYBACK_X11_GL) + { + MainError::show_error( + _("This project's dimensions are not multiples of 4 so\n" + "it can't be rendered by OpenGL.")); + } + + // Get aspect ratio if(defaults->get("AUTOASPECT", 0)) { diff --git a/cinelerra/playback3d.C b/cinelerra/playback3d.C new file mode 100644 index 00000000..2b5d47e8 --- /dev/null +++ b/cinelerra/playback3d.C @@ -0,0 +1,1489 @@ +#define GL_GLEXT_PROTOTYPES + +#include "bcsignals.h" +#include "bcwindowbase.h" +#include "canvas.h" +#include "clip.h" +#include "condition.h" +#include "maskautos.h" +#include "maskauto.h" +#include "mutex.h" +#include "overlayframe.inc" +#include "playback3d.h" +#include "pluginclient.h" +#include "pluginvclient.h" +#include "transportque.inc" +#include "vframe.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#ifdef HAVE_GL +#include +#include +#include +#endif + +#include +#include + + +// Shaders +// These should be passed to VFrame::make_shader to construct shaders. +// Can't hard code sampler2D + +static char *yuv_to_rgb_frag = + "uniform sampler2D tex;\n" + "void main()\n" + "{\n" + " vec3 yuv = vec3(texture2D(tex, gl_TexCoord[0].st));\n" + " yuv -= vec3(0, 0.5, 0.5);\n" + " const mat3 yuv_to_rgb_matrix = mat3(\n" + " 1, 1, 1, \n" + " 0, -0.34414, 1.77200, \n" + " 1.40200, -0.71414, 0);\n" + " gl_FragColor = vec4(yuv_to_rgb_matrix * yuv, 1);\n" + "}\n"; + +static char *yuva_to_rgba_frag = + "uniform sampler2D tex;\n" + "void main()\n" + "{\n" + " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n" + " yuva.rgb -= vec3(0, 0.5, 0.5);\n" + " const mat3 yuv_to_rgb_matrix = mat3(\n" + " 1, 1, 1, \n" + " 0, -0.34414, 1.77200, \n" + " 1.40200, -0.71414, 0);\n" + " gl_FragColor = vec4(yuv_to_rgb_matrix * yuva.rgb, yuva.a);\n" + "}\n"; + +static char *blend_add_frag = + "uniform sampler2D tex2;\n" + "uniform vec2 tex2_dimensions;\n" + "void main()\n" + "{\n" + " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" + " vec3 opacity = vec3(gl_FragColor.a, gl_FragColor.a, gl_FragColor.a);\n" + " vec3 transparency = vec3(1.0, 1.0, 1.0) - opacity;\n" + " gl_FragColor.rgb += canvas.rgb;\n" + " gl_FragColor.rgb *= opacity;\n" + " gl_FragColor.rgb += canvas.rgb * transparency;\n" + " gl_FragColor.a = max(gl_FragColor.a, canvas.a);\n" + "}\n"; + +static char *blend_max_frag = + "uniform sampler2D tex2;\n" + "uniform vec2 tex2_dimensions;\n" + "void main()\n" + "{\n" + " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" + " vec3 opacity = vec3(gl_FragColor.a, gl_FragColor.a, gl_FragColor.a);\n" + " vec3 transparency = vec3(1.0, 1.0, 1.0) - opacity;\n" + " gl_FragColor.r = max(canvas.r, gl_FragColor.r);\n" + " gl_FragColor.g = max(canvas.g, gl_FragColor.g);\n" + " gl_FragColor.b = max(canvas.b, gl_FragColor.b);\n" + " gl_FragColor.rgb *= opacity;\n" + " gl_FragColor.rgb += canvas.rgb * transparency;\n" + " gl_FragColor.a = max(gl_FragColor.a, canvas.a);\n" + "}\n"; + +static char *blend_subtract_frag = + "uniform sampler2D tex2;\n" + "uniform vec2 tex2_dimensions;\n" + "void main()\n" + "{\n" + " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" + " vec3 opacity = vec3(gl_FragColor.a, gl_FragColor.a, gl_FragColor.a);\n" + " vec3 transparency = vec3(1.0, 1.0, 1.0) - opacity;\n" + " gl_FragColor.rgb = canvas.rgb - gl_FragColor.rgb;\n" + " gl_FragColor.rgb *= opacity;\n" + " gl_FragColor.rgb += canvas.rgb * transparency;\n" + " gl_FragColor.a = max(gl_FragColor.a, canvas.a);\n" + "}\n"; + +static char *blend_multiply_frag = + "uniform sampler2D tex2;\n" + "uniform vec2 tex2_dimensions;\n" + "void main()\n" + "{\n" + " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" + " vec3 opacity = vec3(gl_FragColor.a, gl_FragColor.a, gl_FragColor.a);\n" + " vec3 transparency = vec3(1.0, 1.0, 1.0) - opacity;\n" + " gl_FragColor.rgb *= canvas.rgb;\n" + " gl_FragColor.rgb *= opacity;\n" + " gl_FragColor.rgb += canvas.rgb * transparency;\n" + " gl_FragColor.a = max(gl_FragColor.a, canvas.a);\n" + "}\n"; + +static char *blend_divide_frag = + "uniform sampler2D tex2;\n" + "uniform vec2 tex2_dimensions;\n" + "void main()\n" + "{\n" + " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" + " vec3 opacity = vec3(gl_FragColor.a, gl_FragColor.a, gl_FragColor.a);\n" + " vec3 transparency = vec3(1.0, 1.0, 1.0) - opacity;\n" + " vec3 result = canvas.rgb / gl_FragColor.rgb;\n" + " if(!gl_FragColor.r) result.r = 1.0;\n" + " if(!gl_FragColor.g) result.g = 1.0;\n" + " if(!gl_FragColor.b) result.b = 1.0;\n" + " result *= opacity;\n" + " result += canvas.rgb * transparency;\n" + " gl_FragColor = vec4(result, max(gl_FragColor.a, canvas.a));\n" + "}\n"; + +static char *multiply_alpha_frag = + "void main()\n" + "{\n" + " gl_FragColor.rgb *= vec3(gl_FragColor.a, gl_FragColor.a, gl_FragColor.a);\n" + "}\n"; + +static char *read_texture_frag = + "uniform sampler2D tex;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" + "}\n"; + +static char *multiply_mask4_frag = + "uniform sampler2D tex;\n" + "uniform sampler2D tex1;\n" + "uniform float scale;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" + " gl_FragColor.a *= texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n" + "}\n"; + +static char *multiply_mask3_frag = + "uniform sampler2D tex;\n" + "uniform sampler2D tex1;\n" + "uniform float scale;\n" + "uniform bool is_yuv;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" + " float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n" + " gl_FragColor.rgb *= vec3(a, a, a);\n" + "}\n"; + +static char *multiply_yuvmask3_frag = + "uniform sampler2D tex;\n" + "uniform sampler2D tex1;\n" + "uniform float scale;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" + " float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n" + " gl_FragColor.gb -= vec2(0.5, 0.5);\n" + " gl_FragColor.rgb *= vec3(a, a, a);\n" + " gl_FragColor.gb += vec2(0.5, 0.5);\n" + "}\n"; + +static char *fade_rgba_frag = + "uniform sampler2D tex;\n" + "uniform float alpha;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" + " gl_FragColor.a *= alpha;\n" + "}\n"; + +static char *fade_yuv_frag = + "uniform sampler2D tex;\n" + "uniform float alpha;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" + " gl_FragColor.r *= alpha;\n" + " gl_FragColor.gb -= vec2(0.5, 0.5);\n" + " gl_FragColor.g *= alpha;\n" + " gl_FragColor.b *= alpha;\n" + " gl_FragColor.gb += vec2(0.5, 0.5);\n" + "}\n"; + + + + + + + + +Playback3DCommand::Playback3DCommand() + : BC_SynchronousCommand() +{ + canvas = 0; +} + +void Playback3DCommand::copy_from(BC_SynchronousCommand *command) +{ + Playback3DCommand *ptr = (Playback3DCommand*)command; + this->canvas = ptr->canvas; + this->is_cleared = ptr->is_cleared; + + this->in_x1 = ptr->in_x1; + this->in_y1 = ptr->in_y1; + this->in_x2 = ptr->in_x2; + this->in_y2 = ptr->in_y2; + this->out_x1 = ptr->out_x1; + this->out_y1 = ptr->out_y1; + this->out_x2 = ptr->out_x2; + this->out_y2 = ptr->out_y2; + this->alpha = ptr->alpha; + this->mode = ptr->mode; + this->interpolation_type = ptr->interpolation_type; + + this->input = ptr->input; + this->start_position_project = ptr->start_position_project; + this->keyframe_set = ptr->keyframe_set; + this->keyframe = ptr->keyframe; + this->default_auto = ptr->default_auto; + this->plugin_client = ptr->plugin_client; + this->want_texture = ptr->want_texture; + + BC_SynchronousCommand::copy_from(command); +} + + + + +Playback3D::Playback3D(MWindow *mwindow) + : BC_Synchronous() +{ + this->mwindow = mwindow; + temp_texture = 0; +} + +Playback3D::~Playback3D() +{ +} + + + + +BC_SynchronousCommand* Playback3D::new_command() +{ + return new Playback3DCommand; +} + + + +void Playback3D::handle_command(BC_SynchronousCommand *command) +{ +//printf("Playback3D::handle_command 1 %d\n", command->command); + switch(command->command) + { + case Playback3DCommand::WRITE_BUFFER: + write_buffer_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::CLEAR_OUTPUT: + clear_output_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::CLEAR_INPUT: + clear_input_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::DO_CAMERA: + do_camera_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::OVERLAY: + overlay_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::DO_FADE: + do_fade_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::DO_MASK: + do_mask_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::PLUGIN: + run_plugin_sync((Playback3DCommand*)command); + break; + + case Playback3DCommand::COPY_FROM: + copy_from_sync((Playback3DCommand*)command); + break; + +// case Playback3DCommand::DRAW_REFRESH: +// draw_refresh_sync((Playback3DCommand*)command); +// break; + } +//printf("Playback3D::handle_command 10\n"); +} + + + + +void Playback3D::copy_from(Canvas *canvas, + VFrame *dst, + VFrame *src, + int want_texture) +{ + Playback3DCommand command; + command.command = Playback3DCommand::COPY_FROM; + command.canvas = canvas; + command.frame = dst; + command.input = src; + command.want_texture = want_texture; + send_command(&command); +} + +void Playback3D::copy_from_sync(Playback3DCommand *command) +{ +#ifdef HAVE_GL + command->canvas->lock_canvas("Playback3D::draw_refresh_sync"); + BC_WindowBase *window = command->canvas->get_canvas(); + if(window) + { + window->lock_window("Playback3D:draw_refresh_sync"); + window->enable_opengl(); + + if(command->input->get_opengl_state() == VFrame::SCREEN && + command->input->get_w() == command->frame->get_w() && + command->input->get_h() == command->frame->get_h()) + { +// printf("Playback3D::copy_from_sync 1 %d %d %d %d %d\n", +// command->input->get_w(), +// command->input->get_h(), +// command->frame->get_w(), +// command->frame->get_h(), +// command->frame->get_color_model()); + int w = command->input->get_w(); + int h = command->input->get_h(); +// With NVidia at least, + if(command->input->get_w() % 4) + { + printf("Playback3D::copy_from_sync: w=%d not supported because it is not divisible by 4.\n", w); + } + else +// Copy to texture + if(command->want_texture) + { +//printf("Playback3D::copy_from_sync 1 dst=%p src=%p\n", command->frame, command->input); +// Screen_to_texture requires the source pbuffer enabled. + command->input->enable_opengl(); + command->frame->screen_to_texture(); + command->frame->set_opengl_state(VFrame::TEXTURE); + } + else +// Copy to RAM + { + command->input->enable_opengl(); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glReadPixels(0, + 0, + w, + command->input->get_h(), + GL_RGB, + GL_UNSIGNED_BYTE, + command->frame->get_rows()[0]); + command->frame->flip_vert(); + command->frame->set_opengl_state(VFrame::RAM); + } + } + else + { + printf("Playback3D::copy_from_sync: invalid formats opengl_state=%d %dx%d -> %dx%d\n", + command->input->get_opengl_state(), + command->input->get_w(), + command->input->get_h(), + command->frame->get_w(), + command->frame->get_h()); + } + + window->unlock_window(); + } + command->canvas->unlock_canvas(); +#endif +} + + + + +// void Playback3D::draw_refresh(Canvas *canvas, +// VFrame *frame, +// float in_x1, +// float in_y1, +// float in_x2, +// float in_y2, +// float out_x1, +// float out_y1, +// float out_x2, +// float out_y2) +// { +// Playback3DCommand command; +// command.command = Playback3DCommand::DRAW_REFRESH; +// command.canvas = canvas; +// command.frame = frame; +// command.in_x1 = in_x1; +// command.in_y1 = in_y1; +// command.in_x2 = in_x2; +// command.in_y2 = in_y2; +// command.out_x1 = out_x1; +// command.out_y1 = out_y1; +// command.out_x2 = out_x2; +// command.out_y2 = out_y2; +// send_command(&command); +// } +// +// void Playback3D::draw_refresh_sync(Playback3DCommand *command) +// { +// command->canvas->lock_canvas("Playback3D::draw_refresh_sync"); +// BC_WindowBase *window = command->canvas->get_canvas(); +// if(window) +// { +// window->lock_window("Playback3D:draw_refresh_sync"); +// window->enable_opengl(); +// +// // Read output pbuffer back to RAM in project colormodel +// // RGB 8bit is fastest for OpenGL to read back. +// command->frame->reallocate(0, +// 0, +// 0, +// 0, +// command->frame->get_w(), +// command->frame->get_h(), +// BC_RGB888, +// -1); +// command->frame->to_ram(); +// +// window->clear_box(0, +// 0, +// window->get_w(), +// window->get_h()); +// window->draw_vframe(command->frame, +// (int)command->out_x1, +// (int)command->out_y1, +// (int)(command->out_x2 - command->out_x1), +// (int)(command->out_y2 - command->out_y1), +// (int)command->in_x1, +// (int)command->in_y1, +// (int)(command->in_x2 - command->in_x1), +// (int)(command->in_y2 - command->in_y1), +// 0); +// +// window->unlock_window(); +// } +// command->canvas->unlock_canvas(); +// } + + + + + +void Playback3D::write_buffer(Canvas *canvas, + VFrame *frame, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + int is_cleared) +{ + Playback3DCommand command; + command.command = Playback3DCommand::WRITE_BUFFER; + command.canvas = canvas; + command.frame = frame; + command.in_x1 = in_x1; + command.in_y1 = in_y1; + command.in_x2 = in_x2; + command.in_y2 = in_y2; + command.out_x1 = out_x1; + command.out_y1 = out_y1; + command.out_x2 = out_x2; + command.out_y2 = out_y2; + command.is_cleared = is_cleared; + send_command(&command); +} + + +void Playback3D::write_buffer_sync(Playback3DCommand *command) +{ + command->canvas->lock_canvas("Playback3D::write_buffer_sync"); + if(command->canvas->get_canvas()) + { + BC_WindowBase *window = command->canvas->get_canvas(); + window->lock_window("Playback3D::write_buffer_sync"); +// Update hidden cursor + window->update_video_cursor(); +// Make sure OpenGL is enabled first. + window->enable_opengl(); + + +//printf("Playback3D::write_buffer_sync 1 %d\n", window->get_id()); + switch(command->frame->get_opengl_state()) + { +// Upload texture and composite to screen + case VFrame::RAM: + command->frame->to_texture(); + draw_output(command); + break; +// Composite texture to screen and swap buffer + case VFrame::TEXTURE: + draw_output(command); + break; + case VFrame::SCREEN: +// swap buffers only + window->flip_opengl(); + break; + default: + printf("Playback3D::write_buffer_sync unknown state\n"); + break; + } + window->unlock_window(); + } + + command->canvas->unlock_canvas(); +} + + + +void Playback3D::draw_output(Playback3DCommand *command) +{ +#ifdef HAVE_GL + int texture_id = command->frame->get_texture_id(); + BC_WindowBase *window = command->canvas->get_canvas(); + +// printf("Playback3D::draw_output 1 texture_id=%d window=%p\n", +// texture_id, +// command->canvas->get_canvas()); + + + + +// If virtual console is being used, everything in this function has +// already been done except the page flip. + if(texture_id >= 0) + { + canvas_w = window->get_w(); + canvas_h = window->get_h(); + VFrame::init_screen(canvas_w, canvas_h); + + if(!command->is_cleared) + { +// If we get here, the virtual console was not used. + init_frame(command); + } + +// Texture +// Undo any previous shader settings + command->frame->bind_texture(0); + + + + +// Convert colormodel + unsigned int frag_shader = 0; + switch(command->frame->get_color_model()) + { + case BC_YUV888: + frag_shader = VFrame::make_shader(0, + yuv_to_rgb_frag, + 0); + break; + + case BC_YUVA8888: + frag_shader = VFrame::make_shader(0, + yuva_to_rgba_frag, + 0); + break; + } + + + if(frag_shader > 0) + { + glUseProgram(frag_shader); + int variable = glGetUniformLocation(frag_shader, "tex"); +// Set texture unit of the texture + glUniform1i(variable, 0); + } + + if(cmodel_components(command->frame->get_color_model()) == 4) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + command->frame->draw_texture(command->in_x1, + command->in_y1, + command->in_x2, + command->in_y2, + command->out_x1, + command->out_y1, + command->out_x2, + command->out_y2, + 1); + + +// printf("Playback3D::draw_output 2 %f,%f %f,%f -> %f,%f %f,%f\n", +// command->in_x1, +// command->in_y1, +// command->in_x2, +// command->in_y2, +// command->out_x1, +// command->out_y1, +// command->out_x2, +// command->out_y2); + + glUseProgram(0); + + command->canvas->get_canvas()->flip_opengl(); + + } +#endif +} + + +void Playback3D::init_frame(Playback3DCommand *command) +{ +#ifdef HAVE_GL + canvas_w = command->canvas->get_canvas()->get_w(); + canvas_h = command->canvas->get_canvas()->get_h(); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#endif +} + + +void Playback3D::clear_output(Canvas *canvas, VFrame *output) +{ + Playback3DCommand command; + command.command = Playback3DCommand::CLEAR_OUTPUT; + command.canvas = canvas; + command.frame = output; + send_command(&command); +} + +void Playback3D::clear_output_sync(Playback3DCommand *command) +{ + command->canvas->lock_canvas("Playback3D::clear_output_sync"); + if(command->canvas->get_canvas()) + { + command->canvas->get_canvas()->lock_window("Playback3D::clear_output_sync"); +// If we get here, the virtual console is being used. + command->canvas->get_canvas()->enable_opengl(); + +// Using pbuffer for refresh frame. + if(command->frame) + { + command->frame->enable_opengl(); + } + + + init_frame(command); + command->canvas->get_canvas()->unlock_window(); + } + command->canvas->unlock_canvas(); +} + + +void Playback3D::clear_input(Canvas *canvas, VFrame *frame) +{ + Playback3DCommand command; + command.command = Playback3DCommand::CLEAR_INPUT; + command.canvas = canvas; + command.frame = frame; + send_command(&command); +} + +void Playback3D::clear_input_sync(Playback3DCommand *command) +{ + command->canvas->lock_canvas("Playback3D::clear_output_sync"); + if(command->canvas->get_canvas()) + { + command->canvas->get_canvas()->lock_window("Playback3D::clear_output_sync"); + command->canvas->get_canvas()->enable_opengl(); + command->frame->enable_opengl(); + command->frame->clear_pbuffer(); + command->frame->set_opengl_state(VFrame::SCREEN); + command->canvas->get_canvas()->unlock_window(); + } + command->canvas->unlock_canvas(); +} + +void Playback3D::do_camera(Canvas *canvas, + VFrame *output, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2) +{ + Playback3DCommand command; + command.command = Playback3DCommand::DO_CAMERA; + command.canvas = canvas; + command.input = input; + command.frame = output; + command.in_x1 = in_x1; + command.in_y1 = in_y1; + command.in_x2 = in_x2; + command.in_y2 = in_y2; + command.out_x1 = out_x1; + command.out_y1 = out_y1; + command.out_x2 = out_x2; + command.out_y2 = out_y2; + send_command(&command); +} + +void Playback3D::do_camera_sync(Playback3DCommand *command) +{ + command->canvas->lock_canvas("Playback3D::do_camera_sync"); + if(command->canvas->get_canvas()) + { + command->canvas->get_canvas()->lock_window("Playback3D::clear_output_sync"); + command->canvas->get_canvas()->enable_opengl(); + + command->input->to_texture(); + command->frame->enable_opengl(); + command->frame->init_screen(); + command->frame->clear_pbuffer(); + + command->input->bind_texture(0); +// Must call draw_texture in input frame to get the texture coordinates right. + +// printf("Playback3D::do_camera_sync 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n", +// command->in_x1, +// command->in_y2, +// command->in_x2, +// command->in_y1, +// command->out_x1, +// (float)command->input->get_h() - command->out_y1, +// command->out_x2, +// (float)command->input->get_h() - command->out_y2); + command->input->draw_texture( + command->in_x1, + command->in_y2, + command->in_x2, + command->in_y1, + command->out_x1, + (float)command->frame->get_h() - command->out_y1, + command->out_x2, + (float)command->frame->get_h() - command->out_y2); + + + command->frame->set_opengl_state(VFrame::SCREEN); + command->canvas->get_canvas()->unlock_window(); + } + command->canvas->unlock_canvas(); +} + +void Playback3D::overlay(Canvas *canvas, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + float alpha, // 0 - 1 + int mode, + int interpolation_type, + VFrame *output) +{ + Playback3DCommand command; + command.command = Playback3DCommand::OVERLAY; + command.canvas = canvas; + command.frame = output; + command.input = input; + command.in_x1 = in_x1; + command.in_y1 = in_y1; + command.in_x2 = in_x2; + command.in_y2 = in_y2; + command.out_x1 = out_x1; + command.out_y1 = out_y1; + command.out_x2 = out_x2; + command.out_y2 = out_y2; + command.alpha = alpha; + command.mode = mode; + command.interpolation_type = interpolation_type; + send_command(&command); +} + +void Playback3D::overlay_sync(Playback3DCommand *command) +{ +#ifdef HAVE_GL + command->canvas->lock_canvas("Playback3D::overlay_sync"); + if(command->canvas->get_canvas()) + { + BC_WindowBase *window = command->canvas->get_canvas(); + window->lock_window("Playback3D::overlay_sync"); +// Make sure OpenGL is enabled first. + window->enable_opengl(); + + window->update_video_cursor(); + + +// Render to PBuffer + if(command->frame) + { + command->frame->enable_opengl(); + command->frame->set_opengl_state(VFrame::SCREEN); + canvas_w = command->frame->get_w(); + canvas_h = command->frame->get_h(); + } + else + { + canvas_w = window->get_w(); + canvas_h = window->get_h(); + } + + glColor4f(1, 1, 1, 1); + +//printf("Playback3D::overlay_sync 1 %d\n", command->input->get_opengl_state()); + switch(command->input->get_opengl_state()) + { +// Upload texture and composite to screen + case VFrame::RAM: + command->input->to_texture(); + break; +// Just composite texture to screen + case VFrame::TEXTURE: + break; +// read from PBuffer to texture, then composite texture to screen + case VFrame::SCREEN: + command->input->enable_opengl(); + command->input->screen_to_texture(); + if(command->frame) + command->frame->enable_opengl(); + else + window->enable_opengl(); + break; + default: + printf("Playback3D::overlay_sync unknown state\n"); + break; + } + + + char *shader_stack[3] = { 0, 0, 0 }; + int total_shaders = 0; + + VFrame::init_screen(canvas_w, canvas_h); + +// Enable texture + command->input->bind_texture(0); + + +// Convert colormodel. + switch(command->input->get_color_model()) + { + case BC_YUV888: + shader_stack[total_shaders++] = yuv_to_rgb_frag; + break; + case BC_YUVA8888: + shader_stack[total_shaders++] = yuva_to_rgba_frag; + break; + } + +// Change blend operation + switch(command->mode) + { + case TRANSFER_NORMAL: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + + case TRANSFER_REPLACE: +// This requires overlaying an alpha multiplied image on a black screen. + glDisable(GL_BLEND); + if(command->input->get_texture_components() == 4) + { + if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; + shader_stack[total_shaders++] = multiply_alpha_frag; + } + break; + +// To do these operations, we need to copy the input buffer to a texture +// and blend 2 textures in another shader + case TRANSFER_ADDITION: + enable_overlay_texture(command); + if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; + shader_stack[total_shaders++] = blend_add_frag; + break; + case TRANSFER_SUBTRACT: + enable_overlay_texture(command); + if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; + shader_stack[total_shaders++] = blend_subtract_frag; + break; + case TRANSFER_MULTIPLY: + enable_overlay_texture(command); + if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; + shader_stack[total_shaders++] = blend_multiply_frag; + break; + case TRANSFER_MAX: + enable_overlay_texture(command); + if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; + shader_stack[total_shaders++] = blend_max_frag; + break; + case TRANSFER_DIVIDE: + enable_overlay_texture(command); + if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; + shader_stack[total_shaders++] = blend_divide_frag; + break; + } + + unsigned int frag_shader = 0; + if(shader_stack[0]) + { + frag_shader = VFrame::make_shader(0, + shader_stack[0], + shader_stack[1], + 0); + + glUseProgram(frag_shader); + + +// Set texture unit of the texture + glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0); +// Set texture unit of the temp texture + glUniform1i(glGetUniformLocation(frag_shader, "tex2"), 1); +// Set dimensions of the temp texture + if(temp_texture) + glUniform2f(glGetUniformLocation(frag_shader, "tex2_dimensions"), + (float)temp_texture->get_texture_w(), + (float)temp_texture->get_texture_h()); + } + else + glUseProgram(0); + + + + + + +// printf("Playback3D::overlay_sync %f %f %f %f %f %f %f %f\n", +// command->in_x1, +// command->in_y1, +// command->in_x2, +// command->in_y2, +// command->out_x1, +// command->out_y1, +// command->out_x2, +// command->out_y2); + + + + + command->input->draw_texture(command->in_x1, + command->in_y1, + command->in_x2, + command->in_y2, + command->out_x1, + command->out_y1, + command->out_x2, + command->out_y2, + 1); + + + glUseProgram(0); + + +// Delete temp texture + if(temp_texture) + { + delete temp_texture; + temp_texture = 0; + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + } + glActiveTexture(GL_TEXTURE0); + glDisable(GL_TEXTURE_2D); + + + + window->unlock_window(); + } + command->canvas->unlock_canvas(); +#endif +} + + +void Playback3D::enable_overlay_texture(Playback3DCommand *command) +{ +#ifdef HAVE_GL + glDisable(GL_BLEND); + + glActiveTexture(GL_TEXTURE1); + BC_Texture::new_texture(&temp_texture, + canvas_w, + canvas_h, + command->input->get_color_model()); + temp_texture->bind(1); + +// Read canvas into texture + glReadBuffer(GL_BACK); + glCopyTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + 0, + 0, + canvas_w, + canvas_h); +#endif +} + + +void Playback3D::do_mask(Canvas *canvas, + VFrame *output, + int64_t start_position_project, + MaskAutos *keyframe_set, + MaskAuto *keyframe, + MaskAuto *default_auto) +{ + Playback3DCommand command; + command.command = Playback3DCommand::DO_MASK; + command.canvas = canvas; + command.frame = output; + command.start_position_project = start_position_project; + command.keyframe_set = keyframe_set; + command.keyframe = keyframe; + command.default_auto = default_auto; + + send_command(&command); +} + + + +#ifdef HAVE_GL +static void combine_callback(GLdouble coords[3], + GLdouble *vertex_data[4], + GLfloat weight[4], + GLdouble **dataOut) +{ + GLdouble *vertex; + + vertex = (GLdouble *) malloc(6 * sizeof(GLdouble)); + vertex[0] = coords[0]; + vertex[1] = coords[1]; + vertex[2] = coords[2]; + + for (int i = 3; i < 6; i++) + { + vertex[i] = weight[0] * vertex_data[0][i] + + weight[1] * vertex_data[1][i] + + weight[2] * vertex_data[2][i] + + weight[3] * vertex_data[3][i]; + } + + *dataOut = vertex; +} +#endif + + +void Playback3D::do_mask_sync(Playback3DCommand *command) +{ +#ifdef HAVE_GL + command->canvas->lock_canvas("Playback3D::do_mask_sync"); + if(command->canvas->get_canvas()) + { + BC_WindowBase *window = command->canvas->get_canvas(); + window->lock_window("Playback3D::do_mask_sync"); + window->enable_opengl(); + + switch(command->frame->get_opengl_state()) + { + case VFrame::RAM: +// Time to upload to the texture + command->frame->to_texture(); + break; + + case VFrame::SCREEN: +// Read back from PBuffer +// Bind context to pbuffer + command->frame->enable_opengl(); + command->frame->screen_to_texture(); + break; + } + + + +// Create PBuffer and draw the mask on it + command->frame->enable_opengl(); + +// Initialize coordinate system + int w = command->frame->get_w(); + int h = command->frame->get_h(); + command->frame->init_screen(); + +// Clear screen + glDisable(GL_TEXTURE_2D); + if(command->default_auto->mode == MASK_MULTIPLY_ALPHA) + { + glClearColor(0.0, 0.0, 0.0, 0.0); + glColor4f((float)command->keyframe->value / 100, + (float)command->keyframe->value / 100, + (float)command->keyframe->value / 100, + 1.0); + } + else + { + glClearColor(1.0, 1.0, 1.0, 1.0); + glColor4f((float)1.0 - (float)command->keyframe->value / 100, + (float)1.0 - (float)command->keyframe->value / 100, + (float)1.0 - (float)command->keyframe->value / 100, + 1.0); + } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + +// Draw mask with scaling to simulate feathering + GLUtesselator *tesselator = gluNewTess(); + gluTessProperty(tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); + gluTessCallback(tesselator, GLU_TESS_VERTEX, (GLvoid (*) ( )) &glVertex3dv); + gluTessCallback(tesselator, GLU_TESS_BEGIN, (GLvoid (*) ( )) &glBegin); + gluTessCallback(tesselator, GLU_TESS_END, (GLvoid (*) ( )) &glEnd); + gluTessCallback(tesselator, GLU_TESS_COMBINE, (GLvoid (*) ( ))&combine_callback); + + +// Draw every submask as a new polygon + int total_submasks = command->keyframe_set->total_submasks( + command->start_position_project, + PLAY_FORWARD); + float scale = command->keyframe->feather + 1; + int display_list = glGenLists(1); + glNewList(display_list, GL_COMPILE); + for(int k = 0; k < total_submasks; k++) + { + gluTessBeginPolygon(tesselator, NULL); + gluTessBeginContour(tesselator); + ArrayList *points = new ArrayList; + command->keyframe_set->get_points(points, + k, + command->start_position_project, + PLAY_FORWARD); + + int first_point = 0; +// Need to tabulate every vertex in persistent memory because +// gluTessVertex doesn't copy them. + ArrayList coords; + for(int i = 0; i < points->total; i++) + { + MaskPoint *point1 = points->values[i]; + MaskPoint *point2 = (i >= points->total - 1) ? + points->values[0] : + points->values[i + 1]; + +#ifndef SQR +#define SQR(x) ((x) * (x)) +#endif + +// This is very slow. + float x, y; + int segments = (int)(sqrt(SQR(point1->x - point2->x) + SQR(point1->y - point2->y))); + if(point1->control_x2 == 0 && + point1->control_y2 == 0 && + point2->control_x1 == 0 && + point2->control_y1 == 0) + segments = 1; + + float x0 = point1->x; + float y0 = point1->y; + float x1 = point1->x + point1->control_x2; + float y1 = point1->y + point1->control_y2; + float x2 = point2->x + point2->control_x1; + float y2 = point2->y + point2->control_y1; + float x3 = point2->x; + float y3 = point2->y; + + for(int j = 0; j <= segments; j++) + { + float t = (float)j / segments; + float tpow2 = t * t; + float tpow3 = t * t * t; + float invt = 1 - t; + float invtpow2 = invt * invt; + float invtpow3 = invt * invt * invt; + + x = ( invtpow3 * x0 + + 3 * t * invtpow2 * x1 + + 3 * tpow2 * invt * x2 + + tpow3 * x3); + y = ( invtpow3 * y0 + + 3 * t * invtpow2 * y1 + + 3 * tpow2 * invt * y2 + + tpow3 * y3); + + + if(j > 0 || first_point) + { + GLdouble *coord = new GLdouble[3]; + coord[0] = x / scale; + coord[1] = -h + y / scale; + coord[2] = 0; + coords.append(coord); + first_point = 0; + } + } + } + +// Now that we know the total vertices, send them to GLU + for(int i = 0; i < coords.total; i++) + gluTessVertex(tesselator, coords.values[i], coords.values[i]); + + gluTessEndContour(tesselator); + gluTessEndPolygon(tesselator); + points->remove_all_objects(); + delete points; + coords.remove_all_objects(); + } + glEndList(); + glCallList(display_list); + glDeleteLists(display_list, 1); + + glColor4f(1, 1, 1, 1); + + +// Read mask into temporary texture. +// For feathering, just read the part of the screen after the downscaling. + + + float w_scaled = w / scale; + float h_scaled = h / scale; +// Don't vary the texture size according to scaling because that +// would waste memory. +// This enables and binds the temporary texture. + glActiveTexture(GL_TEXTURE1); + BC_Texture::new_texture(&temp_texture, + w, + h, + command->frame->get_color_model()); + temp_texture->bind(1); + glReadBuffer(GL_BACK); + +// Need to add extra size to fill in the bottom right + glCopyTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + 0, + 0, + (int)MIN(w_scaled + 2, w), + (int)MIN(h_scaled + 2, h)); + + command->frame->bind_texture(0); + + +// For feathered masks, use a shader to multiply. +// For unfeathered masks, we could use a stencil buffer +// for further optimization but we also need a YUV algorithm. + unsigned int frag_shader = 0; + switch(temp_texture->get_texture_components()) + { + case 3: + if(command->frame->get_color_model() == BC_YUV888) + frag_shader = VFrame::make_shader(0, + multiply_yuvmask3_frag, + 0); + else + frag_shader = VFrame::make_shader(0, + multiply_mask3_frag, + 0); + break; + case 4: + frag_shader = VFrame::make_shader(0, + multiply_mask4_frag, + 0); + break; + } + + if(frag_shader) + { + int variable; + glUseProgram(frag_shader); + if((variable = glGetUniformLocation(frag_shader, "tex")) >= 0) + glUniform1i(variable, 0); + if((variable = glGetUniformLocation(frag_shader, "tex1")) >= 0) + glUniform1i(variable, 1); + if((variable = glGetUniformLocation(frag_shader, "scale")) >= 0) + glUniform1f(variable, scale); + } + + + +// Write texture to PBuffer with multiply and scaling for feather. + + + command->frame->draw_texture(0, 0, w, h, 0, 0, w, h); + command->frame->set_opengl_state(VFrame::SCREEN); + + +// Disable temp texture + glUseProgram(0); + + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + delete temp_texture; + temp_texture = 0; + + glActiveTexture(GL_TEXTURE0); + glDisable(GL_TEXTURE_2D); + +// Default drawable + window->enable_opengl(); + window->unlock_window(); + } + command->canvas->unlock_canvas(); +#endif +} + + + + + + + + + + + + +void Playback3D::do_fade(Canvas *canvas, VFrame *frame, float fade) +{ + Playback3DCommand command; + command.command = Playback3DCommand::DO_FADE; + command.canvas = canvas; + command.frame = frame; + command.alpha = fade; + send_command(&command); +} + +void Playback3D::do_fade_sync(Playback3DCommand *command) +{ +#ifdef HAVE_GL + command->canvas->lock_canvas("Playback3D::do_mask_sync"); + if(command->canvas->get_canvas()) + { + BC_WindowBase *window = command->canvas->get_canvas(); + window->lock_window("Playback3D::do_fade_sync"); + window->enable_opengl(); + + switch(command->frame->get_opengl_state()) + { + case VFrame::RAM: + command->frame->to_texture(); + break; + + case VFrame::SCREEN: +// Read back from PBuffer +// Bind context to pbuffer + command->frame->enable_opengl(); + command->frame->screen_to_texture(); + break; + } + + + command->frame->enable_opengl(); + command->frame->init_screen(); + command->frame->bind_texture(0); + +// glClearColor(0.0, 0.0, 0.0, 0.0); +// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDisable(GL_BLEND); + unsigned int frag_shader = 0; + switch(command->frame->get_color_model()) + { +// For the alpha colormodels, the native function seems to multiply the +// components by the alpha instead of just the alpha. + case BC_RGBA8888: + case BC_RGBA_FLOAT: + case BC_YUVA8888: + frag_shader = VFrame::make_shader(0, + fade_rgba_frag, + 0); + break; + + case BC_RGB888: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ZERO); + glColor4f(command->alpha, command->alpha, command->alpha, 1); + break; + + + case BC_YUV888: + frag_shader = VFrame::make_shader(0, + fade_yuv_frag, + 0); + break; + } + + + if(frag_shader) + { + glUseProgram(frag_shader); + int variable; + if((variable = glGetUniformLocation(frag_shader, "tex")) >= 0) + glUniform1i(variable, 0); + if((variable = glGetUniformLocation(frag_shader, "alpha")) >= 0) + glUniform1f(variable, command->alpha); + } + + command->frame->draw_texture(); + command->frame->set_opengl_state(VFrame::SCREEN); + + if(frag_shader) + { + glUseProgram(0); + } + + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + + window->unlock_window(); + } + command->canvas->unlock_canvas(); +#endif +} + + + + + + + + + + + +int Playback3D::run_plugin(Canvas *canvas, PluginClient *client) +{ + Playback3DCommand command; + command.command = Playback3DCommand::PLUGIN; + command.canvas = canvas; + command.plugin_client = client; + return send_command(&command); +} + +void Playback3D::run_plugin_sync(Playback3DCommand *command) +{ + command->canvas->lock_canvas("Playback3D::run_plugin_sync"); + if(command->canvas->get_canvas()) + { + BC_WindowBase *window = command->canvas->get_canvas(); + window->lock_window("Playback3D::run_plugin_sync"); + window->enable_opengl(); + + command->result = ((PluginVClient*)command->plugin_client)->handle_opengl(); + + window->unlock_window(); + } + command->canvas->unlock_canvas(); +} + + diff --git a/cinelerra/playback3d.h b/cinelerra/playback3d.h new file mode 100644 index 00000000..03090fb3 --- /dev/null +++ b/cinelerra/playback3d.h @@ -0,0 +1,304 @@ +#ifndef PLAYBACK3D_H +#define PLAYBACK3D_H + +#include "arraylist.h" +#include "bcpixmap.inc" +#include "bcsynchronous.h" +#include "bcwindowbase.inc" +#include "canvas.inc" +#include "condition.inc" +#include "maskauto.inc" +#include "maskautos.inc" +#include "mutex.inc" +#include "mwindow.inc" +#include "pluginclient.inc" +#include "thread.h" +#include "vframe.inc" + + + + + +// Macros for useful fragment shaders +#define YUV_TO_RGB_FRAG(PIXEL) \ + PIXEL ".gb -= vec2(0.5, 0.5);\n" \ + PIXEL ".rgb = mat3(\n" \ + " 1, 1, 1, \n" \ + " 0, -0.34414, 1.77200, \n" \ + " 1.40200, -0.71414, 0) * " PIXEL ".rgb;\n" + +#define RGB_TO_YUV_FRAG(PIXEL) \ + PIXEL ".rgb = mat3(\n" \ + " 0.29900, -0.16874, 0.50000, \n" \ + " 0.58700, -0.33126, -0.41869, \n" \ + " 0.11400, 0.50000, -0.08131) * " PIXEL ".rgb;\n" \ + PIXEL ".gb += vec2(0.5, 0.5);\n" + +#define RGB_TO_HSV_FRAG(PIXEL) \ + "{\n" \ + "float r, g, b;\n" \ + "float h, s, v;\n" \ + "float min, max, delta;\n" \ + "float f, p, q, t;\n" \ + "r = " PIXEL ".r;\n" \ + "g = " PIXEL ".g;\n" \ + "b = " PIXEL ".b;\n" \ + "min = ((r < g) ? r : g) < b ? ((r < g) ? r : g) : b;\n" \ + "max = ((r > g) ? r : g) > b ? ((r > g) ? r : g) : b;\n" \ + "v = max;\n" \ + "delta = max - min;\n" \ + "if(max != 0.0 && delta != 0.0)\n" \ + "{\n" \ + " s = delta / max;\n" \ + " if(r == max)\n" \ + " h = (g - b) / delta;\n" \ + " else \n" \ + " if(g == max)\n" \ + " h = 2.0 + (b - r) / delta;\n" \ + " else\n" \ + " h = 4.0 + (r - g) / delta;\n" \ + "\n" \ + " h *= 60.0;\n" \ + " if(h < 0.0)\n" \ + " h += 360.0;\n" \ + "}\n" \ + "else\n" \ + "{\n" \ + " s = 0.0;\n" \ + " h = -1.0;\n" \ + "}\n" \ + "" PIXEL ".r = h;\n" \ + "" PIXEL ".g = s;\n" \ + "" PIXEL ".b = v;\n" \ + "}\n" + +#define HSV_TO_RGB_FRAG(PIXEL) \ + "{\n" \ + "int i;\n" \ + "float r, g, b;\n" \ + "float h, s, v;\n" \ + "float min, max, delta;\n" \ + "float f, p, q, t;\n" \ + "h = " PIXEL ".r;\n" \ + "s = " PIXEL ".g;\n" \ + "v = " PIXEL ".b;\n" \ + "if(s == 0.0) \n" \ + "{\n" \ + " r = g = b = v;\n" \ + "}\n" \ + "else\n" \ + "{\n" \ + " h /= 60.0;\n" \ + " i = int(h);\n" \ + " f = h - float(i);\n" \ + " p = v * (1.0 - s);\n" \ + " q = v * (1.0 - s * f);\n" \ + " t = v * (1.0 - s * (1.0 - f));\n" \ + "\n" \ + " if(i == 0)\n" \ + " {\n" \ + " r = v;\n" \ + " g = t;\n" \ + " b = p;\n" \ + " }\n" \ + " else\n" \ + " if(i == 1)\n" \ + " {\n" \ + " r = q;\n" \ + " g = v;\n" \ + " b = p;\n" \ + " }\n" \ + " else\n" \ + " if(i == 2)\n" \ + " {\n" \ + " r = p;\n" \ + " g = v;\n" \ + " b = t;\n" \ + " }\n" \ + " else\n" \ + " if(i == 3)\n" \ + " {\n" \ + " r = p;\n" \ + " g = q;\n" \ + " b = v;\n" \ + " }\n" \ + " else\n" \ + " if(i == 4)\n" \ + " {\n" \ + " r = t;\n" \ + " g = p;\n" \ + " b = v;\n" \ + " }\n" \ + " else\n" \ + " if(i == 5)\n" \ + " {\n" \ + " r = v;\n" \ + " g = p;\n" \ + " b = q;\n" \ + " }\n" \ + "}\n" \ + "" PIXEL ".r = r;\n" \ + "" PIXEL ".g = g;\n" \ + "" PIXEL ".b = b;\n" \ + "}\n" + + + +class Playback3DCommand : public BC_SynchronousCommand +{ +public: + Playback3DCommand(); + void copy_from(BC_SynchronousCommand *command); + +// Extra commands + enum + { +// 5 + WRITE_BUFFER = LAST_COMMAND, + CLEAR_OUTPUT, + OVERLAY, + DO_FADE, + DO_MASK, + PLUGIN, + CLEAR_INPUT, + DO_CAMERA, + COPY_FROM + }; + + Canvas *canvas; + int is_cleared; + +// Parameters for overlay command + float in_x1; + float in_y1; + float in_x2; + float in_y2; + float out_x1; + float out_y1; + float out_x2; + float out_y2; +// 0 - 1 + float alpha; + int mode; + int interpolation_type; + VFrame *input; + int want_texture; + + int64_t start_position_project; + MaskAutos *keyframe_set; + MaskAuto *keyframe; + MaskAuto *default_auto; + PluginClient *plugin_client; +}; + + +class Playback3D : public BC_Synchronous +{ +public: + Playback3D(MWindow *mwindow); + ~Playback3D(); + + BC_SynchronousCommand* new_command(); + void handle_command(BC_SynchronousCommand *command); + +// Called by VDeviceX11::write_buffer during video playback + void write_buffer(Canvas *canvas, + VFrame *frame, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + int is_cleared); + +// Reads from pbuffer to either RAM or texture and updates the dst state +// want_texture - causes read into texture if 1 + void copy_from(Canvas *canvas, + VFrame *dst, + VFrame *src, + int want_texture = 0); + +// Clear framebuffer before composing virtual console +// output - passed when rendering refresh frame. If 0, the canvas is cleared. + void clear_output(Canvas *canvas, VFrame *output); + + void do_fade(Canvas *canvas, VFrame *fade, float fade); + + void do_mask(Canvas *canvas, + VFrame *output, + int64_t start_position_project, + MaskAutos *keyframe_set, + MaskAuto *keyframe, + MaskAuto *default_auto); + + +// Overlay a virtual node on the framebuffer + void overlay(Canvas *canvas, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + float alpha, // 0 - 1 + int mode, + int interpolation_type, +// supplied if rendering single frame to PBuffer. + VFrame *output = 0); + + + int run_plugin(Canvas *canvas, PluginClient *client); + + void clear_input(Canvas *canvas, VFrame *frame); + void do_camera(Canvas *canvas, + VFrame *output, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2); + +private: +// Called by write_buffer and clear_frame to initialize OpenGL flags + void init_frame(Playback3DCommand *command); + void write_buffer_sync(Playback3DCommand *command); + void draw_output(Playback3DCommand *command); + void clear_output_sync(Playback3DCommand *command); + void clear_input_sync(Playback3DCommand *command); + void overlay_sync(Playback3DCommand *command); +// Read frame buffer back into texture for overlay operation + void enable_overlay_texture(Playback3DCommand *command); + void do_fade_sync(Playback3DCommand *command); + void do_mask_sync(Playback3DCommand *command); + void run_plugin_sync(Playback3DCommand *command); + void do_camera_sync(Playback3DCommand *command); +// void draw_refresh_sync(Playback3DCommand *command); + void copy_from_sync(Playback3DCommand *command); + +// Print errors from shader compilation + void print_error(unsigned int object, int is_program); + +// This quits the program when it's 1. + MWindow *mwindow; +// Temporaries for render to texture + BC_Texture *temp_texture; +// This is set by clear_output and used in compositing directly +// to the output framebuffer. + int canvas_w; + int canvas_h; +}; + + + + +#endif diff --git a/cinelerra/playback3d.inc b/cinelerra/playback3d.inc new file mode 100644 index 00000000..070f477d --- /dev/null +++ b/cinelerra/playback3d.inc @@ -0,0 +1,12 @@ +#ifndef PLAYBACK3D_INC +#define PLAYBACK3D_INC + + + + +class Playback3D; + + + + +#endif diff --git a/cinelerra/pluginclient.C b/cinelerra/pluginclient.C index 1da73ad8..aa5ae9f4 100644 --- a/cinelerra/pluginclient.C +++ b/cinelerra/pluginclient.C @@ -291,6 +291,11 @@ int64_t PluginClient::edl_to_local(int64_t position) return position; } +int PluginClient::get_use_opengl() +{ + return server->get_use_opengl(); +} + int PluginClient::get_total_buffers() { return total_in_buffers; diff --git a/cinelerra/pluginclient.h b/cinelerra/pluginclient.h index acbc3038..fdce20e0 100644 --- a/cinelerra/pluginclient.h +++ b/cinelerra/pluginclient.h @@ -338,6 +338,10 @@ public: // Get the direction of the most recent process_buffer int get_direction(); +// Plugin must call this before performing OpenGL operations. +// Returns 1 if the user supports opengl buffers. + int get_use_opengl(); + // Get total tracks to process int get_total_buffers(); diff --git a/cinelerra/pluginserver.C b/cinelerra/pluginserver.C index e5d9fa51..9d477573 100644 --- a/cinelerra/pluginserver.C +++ b/cinelerra/pluginserver.C @@ -27,7 +27,9 @@ #include "mainsession.h" #include "trackcanvas.h" #include "transportque.h" +#include "vdevicex11.h" #include "vframe.h" +#include "videodevice.h" #include "virtualanode.h" #include "virtualvnode.h" #include "vmodule.h" @@ -126,6 +128,8 @@ int PluginServer::reset_parameters() transition = 0; new_plugin = 0; client = 0; + use_opengl = 0; + vdevice = 0; is_lad = 0; lad_descriptor_function = 0; @@ -341,6 +345,7 @@ int PluginServer::init_realtime(int realtime_sched, // set for realtime priority // initialize plugin // Call start_realtime + this->total_in_buffers = this->total_out_buffers = total_in_buffers; client->plugin_init_realtime(realtime_sched, total_in_buffers, buffer_size); @@ -360,8 +365,18 @@ void PluginServer::process_transition(VFrame *input, vclient->source_position = current_position; vclient->source_start = 0; vclient->total_len = total_len; + + vclient->input = new VFrame*[1]; + vclient->output = new VFrame*[1]; + + vclient->input[0] = input; + vclient->output[0] = output; + vclient->process_realtime(input, output); vclient->age_temp(); + delete [] vclient->input; + delete [] vclient->output; + use_opengl = 0; } void PluginServer::process_transition(double *input, @@ -394,6 +409,13 @@ void PluginServer::process_buffer(VFrame **frame, vclient->source_position = current_position; vclient->total_len = total_len; vclient->frame_rate = frame_rate; + vclient->input = new VFrame*[total_in_buffers]; + vclient->output = new VFrame*[total_in_buffers]; + for(int i = 0; i < total_in_buffers; i++) + { + vclient->input[i] = frame[i]; + vclient->output[i] = frame[i]; + } vclient->source_start = (int64_t)(plugin ? plugin->startproject * frame_rate / @@ -411,8 +433,14 @@ void PluginServer::process_buffer(VFrame **frame, vclient->process_buffer(frame[0], current_position, frame_rate); } + for(int i = 0; i < total_in_buffers; i++) + frame[i]->push_prev_effect(title); + + delete [] vclient->input; + delete [] vclient->output; vclient->age_temp(); + use_opengl = 0; } void PluginServer::process_buffer(double **buffer, @@ -607,7 +635,8 @@ int PluginServer::read_samples(double *buffer, int PluginServer::read_frame(VFrame *buffer, int channel, int64_t start_position, - double frame_rate) + double frame_rate, + int use_opengl) { // Data source depends on whether we're part of a virtual console or a // plugin array. @@ -629,7 +658,8 @@ int PluginServer::read_frame(VFrame *buffer, { result = ((VirtualVNode*)nodes->values[channel])->read_data(buffer, start_position, - frame_rate); + frame_rate, + use_opengl); } else if(modules->total > channel) @@ -639,7 +669,8 @@ int PluginServer::read_frame(VFrame *buffer, PLAY_FORWARD, frame_rate, 0, - 0); + 0, + use_opengl); } else { @@ -778,6 +809,29 @@ int PluginServer::set_string(char *string) return 0; } +int PluginServer::gui_open() +{ + if(attachmentpoint) return attachmentpoint->gui_open(); + return 0; +} + +void PluginServer::set_use_opengl(int value, VideoDevice *vdevice) +{ + this->use_opengl = value; + this->vdevice = vdevice; +} + +int PluginServer::get_use_opengl() +{ + return use_opengl; +} + + +void PluginServer::run_opengl(PluginClient *plugin_client) +{ + if(vdevice) + ((VDeviceX11*)vdevice->get_output_base())->run_plugin(plugin_client); +} // ============================= queries diff --git a/cinelerra/pluginserver.h b/cinelerra/pluginserver.h index 756759cf..ee7a11de 100644 --- a/cinelerra/pluginserver.h +++ b/cinelerra/pluginserver.h @@ -25,6 +25,7 @@ #include "thread.h" #include "track.inc" #include "vframe.inc" +#include "videodevice.inc" #include "virtualnode.inc" #include @@ -107,7 +108,19 @@ public: void update_gui(); void update_title(); void client_side_close(); - +// Set to 1 before every process call if the user supports OpenGL buffers. +// Also provides the driver location. + void set_use_opengl(int value, VideoDevice *vdevice); +// Plugin must call this before performing OpenGL operations. + int get_use_opengl(); + +// Called from plugin client +// Returns 1 if a GUI is open so OpenGL routines can determine if +// they can run. + int gui_open(); + +// Called by plugin client to request synchronous routine. + void run_opengl(PluginClient *plugin_client); // set the string that appears on the plugin title int set_string(char *string); @@ -116,7 +129,6 @@ public: int total_in_buffers, int buffer_size); // process the data in the buffers -// Really process_realtime replaced by pull method but still needed for transitions // input - the current edit's data // output - the previous edit's data and the destination of the transition output // current_position - Position from start of the transition and @@ -206,7 +218,9 @@ public: int read_frame(VFrame *buffer, int channel, int64_t start_position, - double frame_rate); + double frame_rate, +// Set to 1 if the reader can use OpenGL objects. + int use_opengl = 0); int read_samples(double *buffer, int channel, int64_t sample_rate, @@ -338,6 +352,9 @@ private: int is_lad; LADSPA_Descriptor_Function lad_descriptor_function; const LADSPA_Descriptor *lad_descriptor; + int use_opengl; +// Driver for opengl calls. + VideoDevice *vdevice; }; diff --git a/cinelerra/pluginvclient.C b/cinelerra/pluginvclient.C index 5d032264..a6cdf28b 100644 --- a/cinelerra/pluginvclient.C +++ b/cinelerra/pluginvclient.C @@ -230,6 +230,16 @@ int PluginVClient::plugin_process_loop(VFrame **buffers, int64_t &write_length) return result; } +int PluginVClient::handle_opengl() +{ + return 0; +} + + + + + + int PluginVClient::read_frame(VFrame *buffer, int channel, @@ -251,12 +261,14 @@ int PluginVClient::read_frame(VFrame *buffer, int PluginVClient::read_frame(VFrame *buffer, int channel, int64_t start_position, - double frame_rate) + double frame_rate, + int use_opengl) { return server->read_frame(buffer, channel, start_position, - frame_rate); + frame_rate, + use_opengl); } diff --git a/cinelerra/pluginvclient.h b/cinelerra/pluginvclient.h index 2b75b608..1ffb4b39 100644 --- a/cinelerra/pluginvclient.h +++ b/cinelerra/pluginvclient.h @@ -87,7 +87,13 @@ public: int read_frame(VFrame *buffer, int channel, int64_t start_position, - double frame_rate); + double frame_rate, + int use_opengl = 0); + +// Called by Playback3D to run opengl commands synchronously. +// Overridden by the user with the commands to run synchronously. + virtual int handle_opengl(); + // Called by user to allocate the temporary for the current process_buffer. // It may be deleted after the process_buffer to conserve memory. @@ -116,6 +122,10 @@ public: VFrame ***input_ptr_render; VFrame ***output_ptr_render; +// ======================== Realtime buffer pointers =========================== +// These are provided by the plugin server for the opengl handler. + VFrame **input; + VFrame **output; // Frame rate of EDL diff --git a/cinelerra/preferencesthread.C b/cinelerra/preferencesthread.C index 079e885d..b6d6765b 100644 --- a/cinelerra/preferencesthread.C +++ b/cinelerra/preferencesthread.C @@ -16,6 +16,7 @@ #include "language.h" #include "levelwindow.h" #include "levelwindowgui.h" +#include "mainerror.h" #include "meterpanel.h" #include "mutex.h" #include "mwindow.h" @@ -37,7 +38,7 @@ #define WIDTH 750 -#define HEIGHT 700 +#define HEIGHT 730 PreferencesMenuitem::PreferencesMenuitem(MWindow *mwindow) @@ -182,6 +183,16 @@ int PreferencesThread::apply_settings() mwindow->preferences->copy_from(preferences); mwindow->init_brender(); + if(((mwindow->edl->session->output_w % 4) || + (mwindow->edl->session->output_h % 4)) && + mwindow->edl->session->playback_config->vconfig->driver == PLAYBACK_X11_GL) + { + MainError::show_error( + _("This project's dimensions are not multiples of 4 so\n" + "it can't be rendered by OpenGL.")); + } + + if(redraw_meters) { mwindow->cwindow->gui->lock_window("PreferencesThread::apply_settings"); diff --git a/cinelerra/quit.C b/cinelerra/quit.C index e022ebe5..c8490821 100644 --- a/cinelerra/quit.C +++ b/cinelerra/quit.C @@ -8,6 +8,7 @@ #include "mainmenu.h" #include "mwindow.h" #include "mwindowgui.h" +#include "playback3d.h" #include "quit.h" #include "record.h" #include "render.h" @@ -16,7 +17,6 @@ #include "videowindow.h" #include "videowindowgui.h" -#include Quit::Quit(MWindow *mwindow) : BC_MenuItem(_("Quit"), "q", 'q'), Thread() @@ -44,6 +44,8 @@ int Quit::handle_event() mwindow->gui->unlock_window(); mwindow->interrupt_indexes(); mwindow->gui->set_done(0); +// BC_WindowBase::get_resources()->synchronous->quit(); + mwindow->playback_3d->quit(); mwindow->gui->lock_window(); } return 0; @@ -96,6 +98,8 @@ void Quit::run() mwindow->interrupt_indexes(); // Last command in program mwindow->gui->set_done(0); +// BC_WindowBase::get_resources()->synchronous->quit(); + mwindow->playback_3d->quit(); } break; diff --git a/cinelerra/resizetrackthread.C b/cinelerra/resizetrackthread.C index 219301eb..80837b58 100644 --- a/cinelerra/resizetrackthread.C +++ b/cinelerra/resizetrackthread.C @@ -1,5 +1,7 @@ #include "edl.h" +#include "edlsession.h" #include "language.h" +#include "mainerror.h" #include "mainundo.h" #include "mwindow.h" #include "mwindowgui.h" @@ -65,6 +67,15 @@ void ResizeTrackThread::run() mwindow->resize_track(track, w, h); } } + + if(((w % 4) || + (h % 4)) && + mwindow->edl->session->playback_config->vconfig->driver == PLAYBACK_X11_GL) + { + MainError::show_error( + _("This track's dimensions are not multiples of 4 so\n" + "it can't be rendered by OpenGL.")); + } } diff --git a/cinelerra/savefile.C b/cinelerra/savefile.C index 3f20b0bb..d2a47b31 100644 --- a/cinelerra/savefile.C +++ b/cinelerra/savefile.C @@ -10,6 +10,7 @@ #include "mainmenu.h" #include "mwindow.h" #include "mwindowgui.h" +#include "playback3d.h" #include "savefile.h" #include "mainsession.h" @@ -94,7 +95,9 @@ int Save::handle_event() mwindow->gui->show_message(string); } mwindow->session->changes_made = 0; +// Last command in program if(saveas->quit_now) mwindow->gui->set_done(0); + if(saveas->quit_now) mwindow->playback_3d->quit(); } return 1; } @@ -199,7 +202,9 @@ void SaveAs::run() mwindow->session->changes_made = 0; mmenu->add_load(filename); +// Last command in program if(quit_now) mwindow->gui->set_done(0); + if(quit_now) mwindow->playback_3d->quit(); return; } diff --git a/cinelerra/setformat.C b/cinelerra/setformat.C index e2718dc9..2f06c8b6 100644 --- a/cinelerra/setformat.C +++ b/cinelerra/setformat.C @@ -9,6 +9,7 @@ #include "language.h" #include "levelwindow.h" #include "levelwindowgui.h" +#include "mainerror.h" #include "mainundo.h" #include "mutex.h" #include "mwindow.h" @@ -151,6 +152,17 @@ void SetFormatThread::apply_changes() mwindow->lwindow->gui->flush(); mwindow->lwindow->gui->unlock_window(); +// Warn user + if(((mwindow->edl->session->output_w % 4) || + (mwindow->edl->session->output_h % 4)) && + mwindow->edl->session->playback_config->vconfig->driver == PLAYBACK_X11_GL) + { + MainError::show_error( + _("This project's dimensions are not multiples of 4 so\n" + "it can't be rendered by OpenGL.")); + } + + // Flash frame mwindow->sync_parameters(CHANGE_ALL); } diff --git a/cinelerra/vattachmentpoint.C b/cinelerra/vattachmentpoint.C index c06b7821..b97fef4c 100644 --- a/cinelerra/vattachmentpoint.C +++ b/cinelerra/vattachmentpoint.C @@ -8,6 +8,8 @@ #include "renderengine.h" #include "transportque.h" #include "vattachmentpoint.h" +#include "vdevicex11.h" +#include "videodevice.h" #include "vframe.h" VAttachmentPoint::VAttachmentPoint(RenderEngine *renderengine, Plugin *plugin) @@ -67,15 +69,17 @@ void VAttachmentPoint::render(VFrame *output, int buffer_number, int64_t start_position, double frame_rate, - int debug_render) + int debug_render, + int use_opengl) { if(!this) printf("VAttachmentPoint::render NULL\n"); if(!plugin_server || !plugin->on) return; if(debug_render) - printf(" VAttachmentPoint::render %s %d\n", + printf(" VAttachmentPoint::render \"%s\" multi=%d opengl=%d\n", plugin_server->title, - plugin_server->multichannel); + plugin_server->multichannel, + use_opengl); if(plugin_server->multichannel) { @@ -84,7 +88,22 @@ void VAttachmentPoint::render(VFrame *output, this->start_position == start_position && EQUIV(this->frame_rate, frame_rate)) { - output->copy_from(buffer_vector[buffer_number]); +// Need to copy PBuffer if OpenGL, regardless of use_opengl + if(buffer_vector[buffer_number]->get_opengl_state() == VFrame::RAM) + { + output->copy_from(buffer_vector[buffer_number]); + output->set_opengl_state(VFrame::RAM); + } + else + if(renderengine && renderengine->video) + { +// Need to copy PBuffer to texture +// printf("VAttachmentPoint::render temp=%p output=%p\n", +// buffer_vector[buffer_number], +// output); + VDeviceX11 *x11_device = (VDeviceX11*)renderengine->video->get_output_base(); + x11_device->copy_frame(output, buffer_vector[buffer_number]); + } return; } @@ -92,7 +111,7 @@ void VAttachmentPoint::render(VFrame *output, this->start_position = start_position; this->frame_rate = frame_rate; -// Allocate buffer vector +// Allocate buffer vector for subsequent render calls new_buffer_vector(output->get_w(), output->get_h(), output->get_color_model()); @@ -109,6 +128,9 @@ void VAttachmentPoint::render(VFrame *output, // Process plugin //printf("VAttachmentPoint::render 1 %d\n", buffer_number); + if(renderengine) + plugin_servers.values[0]->set_use_opengl(use_opengl, + renderengine->video); plugin_servers.values[0]->process_buffer(output_temp, start_position, frame_rate, @@ -126,6 +148,9 @@ void VAttachmentPoint::render(VFrame *output, { VFrame *output_temp[1]; output_temp[0] = output; + if(renderengine) + plugin_servers.values[buffer_number]->set_use_opengl(use_opengl, + renderengine->video); plugin_servers.values[buffer_number]->process_buffer(output_temp, start_position, frame_rate, diff --git a/cinelerra/vattachmentpoint.h b/cinelerra/vattachmentpoint.h index bbe90437..11ce208a 100644 --- a/cinelerra/vattachmentpoint.h +++ b/cinelerra/vattachmentpoint.h @@ -17,7 +17,8 @@ public: int buffer_number, int64_t start_position, double frame_rate, - int debug_render); + int debug_render, + int use_opengl = 0); void dispatch_plugin_server(int buffer_number, int64_t current_position, int64_t fragment_size); diff --git a/cinelerra/vdeviceprefs.C b/cinelerra/vdeviceprefs.C index c4cb792b..8a1c027e 100644 --- a/cinelerra/vdeviceprefs.C +++ b/cinelerra/vdeviceprefs.C @@ -115,6 +115,7 @@ int VDevicePrefs::initialize() break; case PLAYBACK_X11: case PLAYBACK_X11_XV: + case PLAYBACK_X11_GL: create_x11_objs(); break; case PLAYBACK_DV1394: @@ -438,6 +439,9 @@ char* VDriverMenu::driver_to_string(int driver) case PLAYBACK_X11_XV: sprintf(string, PLAYBACK_X11_XV_TITLE); break; + case PLAYBACK_X11_GL: + sprintf(string, PLAYBACK_X11_GL_TITLE); + break; case PLAYBACK_LML: sprintf(string, PLAYBACK_LML_TITLE); break; @@ -481,6 +485,9 @@ int VDriverMenu::create_objects() { add_item(new VDriverItem(this, PLAYBACK_X11_TITLE, PLAYBACK_X11)); add_item(new VDriverItem(this, PLAYBACK_X11_XV_TITLE, PLAYBACK_X11_XV)); +#ifdef HAVE_GL + add_item(new VDriverItem(this, PLAYBACK_X11_GL_TITLE, PLAYBACK_X11_GL)); +#endif add_item(new VDriverItem(this, PLAYBACK_BUZ_TITLE, PLAYBACK_BUZ)); #ifdef HAVE_FIREWIRE add_item(new VDriverItem(this, PLAYBACK_FIREWIRE_TITLE, PLAYBACK_FIREWIRE)); diff --git a/cinelerra/vdevicex11.C b/cinelerra/vdevicex11.C index 40960b0f..b7cc80c5 100644 --- a/cinelerra/vdevicex11.C +++ b/cinelerra/vdevicex11.C @@ -3,7 +3,10 @@ #include "bcsignals.h" #include "canvas.h" #include "colormodels.h" +#include "edl.h" +#include "edlsession.h" #include "mwindow.h" +#include "playback3d.h" #include "playbackconfig.h" #include "preferences.h" #include "recordconfig.h" @@ -35,17 +38,17 @@ int VDeviceX11::reset_parameters() bitmap = 0; bitmap_w = 0; bitmap_h = 0; - output = 0; - in_x = 0; - in_y = 0; - in_w = 0; - in_h = 0; - out_x = 0; - out_y = 0; - out_w = 0; - out_h = 0; + output_x1 = 0; + output_y1 = 0; + output_x2 = 0; + output_y2 = 0; + canvas_x1 = 0; + canvas_y1 = 0; + canvas_x2 = 0; + canvas_y2 = 0; capture_bitmap = 0; color_model_selected = 0; + is_cleared = 0; return 0; } @@ -72,6 +75,7 @@ int VDeviceX11::open_output() output->start_single(); output->get_canvas()->unlock_window(); +// Enable opengl in the first routine that needs it, to reduce the complexity. output->unlock_canvas(); } @@ -170,7 +174,6 @@ int VDeviceX11::close_all() if(output) { - output->get_canvas()->unlock_window(); output->unlock_canvas(); @@ -197,6 +200,21 @@ int VDeviceX11::get_best_colormodel(Asset *asset) int VDeviceX11::get_best_colormodel(int colormodel) { int result = -1; + + if(device->out_config->driver == PLAYBACK_X11_GL) + { + if(colormodel == BC_RGB888 || + colormodel == BC_RGBA8888 || + colormodel == BC_YUV888 || + colormodel == BC_YUVA8888 || + colormodel == BC_RGB_FLOAT || + colormodel == BC_RGBA_FLOAT) + { + return colormodel; + } + return BC_RGB888; + } + if(!device->single_frame) { switch(colormodel) @@ -209,18 +227,22 @@ int VDeviceX11::get_best_colormodel(int colormodel) } } +// 2 more colormodels are supported by OpenGL + if(device->out_config->driver == PLAYBACK_X11_GL) + { + if(colormodel == BC_RGB_FLOAT || + colormodel == BC_RGBA_FLOAT) + result = colormodel; + } + if(result < 0) { switch(colormodel) { case BC_RGB888: case BC_RGBA8888: - case BC_RGB161616: - case BC_RGBA16161616: case BC_YUV888: case BC_YUVA8888: - case BC_YUV161616: - case BC_YUVA16161616: result = colormodel; break; @@ -454,14 +476,15 @@ int VDeviceX11::write_buffer(VFrame *output_channels, EDL *edl) output->get_transfers(edl, - in_x, - in_y, - in_w, - in_h, - out_x, - out_y, - out_w, - out_h, + output_x1, + output_y1, + output_x2, + output_y2, + canvas_x1, + canvas_y1, + canvas_x2, + canvas_y2, +// Canvas may be a different size than the temporary bitmap for pure software (bitmap_type == BITMAP_TEMP && !bitmap->hardware_scaling()) ? bitmap->get_w() : -1, (bitmap_type == BITMAP_TEMP && !bitmap->hardware_scaling()) ? bitmap->get_h() : -1); @@ -519,14 +542,14 @@ int VDeviceX11::write_buffer(VFrame *output_channels, EDL *edl) output_channels->get_y(), output_channels->get_u(), output_channels->get_v(), - in_x, - in_y, - in_w, - in_h, + (int)output_x1, + (int)output_y1, + (int)(output_x2 - output_x1), + (int)(output_y2 - output_y1), 0, 0, - out_w, - out_h, + (int)(canvas_x2 - canvas_x1), + (int)(canvas_y2 - canvas_y1), output_channels->get_color_model(), bitmap->get_color_model(), 0, @@ -555,32 +578,57 @@ int VDeviceX11::write_buffer(VFrame *output_channels, EDL *edl) // Cause X server to display it + if(device->out_config->driver == PLAYBACK_X11_GL) + { +// Output is drawn in close_all if no video. + if(output->get_canvas()->get_video_on()) + { +// Draw output frame directly. Not used for compositing. + output->get_canvas()->unlock_window(); + output->unlock_canvas(); + output->mwindow->playback_3d->write_buffer(output, + output_frame, + output_x1, + output_y1, + output_x2, + output_y2, + canvas_x1, + canvas_y1, + canvas_x2, + canvas_y2, + is_cleared); + is_cleared = 0; + output->lock_canvas("VDeviceX11::write_buffer 2"); + output->get_canvas()->lock_window("VDeviceX11::write_buffer 2"); + } + } + else if(bitmap->hardware_scaling()) { output->get_canvas()->draw_bitmap(bitmap, !device->single_frame, - out_x, - out_y, - out_w, - out_h, - in_x, - in_y, - in_w, - in_h, + (int)canvas_x1, + (int)canvas_y1, + (int)(canvas_x2 - canvas_x1), + (int)(canvas_y2 - canvas_y1), + (int)output_x1, + (int)output_y1, + (int)(output_x2 - output_x1), + (int)(output_y2 - output_y1), 0); } else { output->get_canvas()->draw_bitmap(bitmap, !device->single_frame, - out_x, - out_y, - out_w, - out_h, + (int)canvas_x1, + (int)canvas_y1, + (int)(canvas_x2 - canvas_x1), + (int)(canvas_y2 - canvas_y1), 0, 0, - out_w, - out_h, + (int)(canvas_x2 - canvas_x1), + (int)(canvas_y2 - canvas_y1), 0); } @@ -591,3 +639,201 @@ int VDeviceX11::write_buffer(VFrame *output_channels, EDL *edl) } +void VDeviceX11::clear_output() +{ + is_cleared = 1; + + output->mwindow->playback_3d->clear_output(output, + output->get_canvas()->get_video_on() ? 0 : output_frame); + +} + + +void VDeviceX11::clear_input(VFrame *frame) +{ + this->output->mwindow->playback_3d->clear_input(this->output, frame); +} + +void VDeviceX11::do_camera(VFrame *output, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2) +{ + this->output->mwindow->playback_3d->do_camera(this->output, + output, + input, + in_x1, + in_y1, + in_x2, + in_y2, + out_x1, + out_y1, + out_x2, + out_y2); +} + + +void VDeviceX11::do_fade(VFrame *output_temp, float fade) +{ + this->output->mwindow->playback_3d->do_fade(this->output, output_temp, fade); +} + + +void VDeviceX11::overlay(VFrame *output_frame, + VFrame *input, +// This is the transfer from track to output frame + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + float alpha, // 0 - 1 + int mode, + EDL *edl) +{ + int interpolation_type = edl->session->interpolation_type; + +// printf("VDeviceX11::overlay 1:\n" +// "in_x1=%f in_y1=%f in_x2=%f in_y2=%f\n" +// "out_x1=%f out_y1=%f out_x2=%f out_y2=%f\n", +// in_x1, +// in_y1, +// in_x2, +// in_y2, +// out_x1, +// out_y1, +// out_x2, +// out_y2); +// Convert node coords to canvas coords in here + output->lock_canvas("VDeviceX11::overlay"); + output->get_canvas()->lock_window("VDeviceX11::overlay"); + +// This is the transfer from output frame to canvas + output->get_transfers(edl, + output_x1, + output_y1, + output_x2, + output_y2, + canvas_x1, + canvas_y1, + canvas_x2, + canvas_y2, + -1, + -1); + + output->get_canvas()->unlock_window(); + output->unlock_canvas(); + + +// If single frame playback, use full sized PBuffer as output. + if(device->single_frame) + { + output->mwindow->playback_3d->overlay(output, + input, + in_x1, + in_y1, + in_x2, + in_y2, + out_x1, + out_y1, + out_x2, + out_y2, + alpha, // 0 - 1 + mode, + interpolation_type, + output_frame); +// printf("VDeviceX11::overlay 1 %p %d %d %d\n", +// output_frame, +// output_frame->get_w(), +// output_frame->get_h(), +// output_frame->get_opengl_state()); + } + else + { + +// Get transfer from track to canvas + float track_xscale = (out_x2 - out_x1) / (in_x2 - in_x1); + float track_yscale = (out_y2 - out_y1) / (in_y2 - in_y1); + float canvas_xscale = (float)(canvas_x2 - canvas_x1) / (output_x2 - output_x1); + float canvas_yscale = (float)(canvas_y2 - canvas_y1) / (output_y2 - output_y1); + + +// Get coordinates of canvas relative to track frame + float track_x1 = (float)(output_x1 - out_x1) / track_xscale + in_x1; + float track_y1 = (float)(output_y1 - out_y1) / track_yscale + in_y1; + float track_x2 = (float)(output_x2 - out_x2) / track_xscale + in_x2; + float track_y2 = (float)(output_y2 - out_y2) / track_yscale + in_y2; + +// Clamp canvas coords to track boundary + if(track_x1 < 0) + { + float difference = -track_x1; + track_x1 += difference; + canvas_x1 += difference * track_xscale * canvas_xscale; + } + if(track_y1 < 0) + { + float difference = -track_y1; + track_y1 += difference; + canvas_y1 += difference * track_yscale * canvas_yscale; + } + + if(track_x2 > input->get_w()) + { + float difference = track_x2 - input->get_w(); + track_x2 -= difference; + canvas_x2 -= difference * track_xscale * canvas_xscale; + } + if(track_y2 > input->get_h()) + { + float difference = track_y2 - input->get_h(); + track_y2 -= difference; + canvas_y2 -= difference * track_yscale * canvas_yscale; + } + + + + + +// Overlay directly from track buffer to canvas, skipping output buffer + if(track_x2 > track_x1 && + track_y2 > track_y1 && + canvas_x2 > canvas_x1 && + canvas_y2 > canvas_y1) + { + output->mwindow->playback_3d->overlay(output, + input, + track_x1, + track_y1, + track_x2, + track_y2, + canvas_x1, + canvas_y1, + canvas_x2, + canvas_y2, + alpha, // 0 - 1 + mode, + interpolation_type); + } + } +} + +void VDeviceX11::run_plugin(PluginClient *client) +{ + output->mwindow->playback_3d->run_plugin(output, client); +} + +void VDeviceX11::copy_frame(VFrame *dst, VFrame *src) +{ + output->mwindow->playback_3d->copy_from(output, dst, src, 1); +} + diff --git a/cinelerra/vdevicex11.h b/cinelerra/vdevicex11.h index e2cd85b5..d3b844b3 100644 --- a/cinelerra/vdevicex11.h +++ b/cinelerra/vdevicex11.h @@ -4,6 +4,7 @@ #include "canvas.inc" #include "edl.inc" #include "guicast.h" +#include "pluginclient.inc" #include "thread.h" #include "vdevicebase.h" @@ -32,26 +33,93 @@ public: // After loading the bitmap with a picture, write it int write_buffer(VFrame *result, EDL *edl); -private: // Closest colormodel the hardware can do for playback int get_best_colormodel(int colormodel); // Get best colormodel for recording int get_best_colormodel(Asset *asset); - BC_Bitmap *bitmap; // Bitmap to be written to device - VFrame *output_frame; // Wrapper for bitmap or intermediate frame - int bitmap_type; // Type of output_frame - int bitmap_w, bitmap_h; // dimensions of buffers written to window + +//=========================== compositing stages =============================== +// For compositing with OpenGL, must clear the frame buffer +// before overlaying tracks. + void clear_output(); + +// Called by VModule::import_frame + void do_camera(VFrame *output, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2); + +// Called by VModule::import_frame for cases with no media. + void clear_input(VFrame *frame); + + void do_fade(VFrame *output_temp, float fade); + + +// The idea is to composite directly in the frame buffer if OpenGL. +// OpenGL can do all the blending using the frame buffer. +// Unfortunately if the output is lower resolution than the frame buffer, the +// rendered output is the resolution of the frame buffer, not the output. +// Also, the frame buffer has to be copied back to textures for nonstandard +// blending equations and blended at the framebuffer resolution. +// If the frame buffer is higher resolution than the +// output frame, like a 2560x1600 display, it could cause unnecessary slowness. +// Finally, there's the problem of updating the refresh frame. +// It requires recompositing the previous frame in software every time playback was +// stops, a complicated operation. + void overlay(VFrame *output_frame, + VFrame *input, + float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + float alpha, // 0 - 1 + int mode, + EDL *edl); + +// For plugins, lock the canvas, enable opengl, and run a function in the +// plugin client in the synchronous thread. The user must override the +// pluginclient function. + void run_plugin(PluginClient *client); + +// For multichannel plugins, copy from the temporary pbuffer to +// the plugin output texture. +// Set the output OpenGL state to TEXTURE. + void copy_frame(VFrame *dst, VFrame *src); + +// Bitmap to be written to device + BC_Bitmap *bitmap; +// Wrapper for bitmap or intermediate buffer for user to write to + VFrame *output_frame; +// Type of output_frame + int bitmap_type; +// dimensions of buffers written to window + int bitmap_w, bitmap_h; ArrayList render_strategies; // Canvas for output Canvas *output; int color_model; int color_model_selected; -// Transfers for last frame rendered. +// Transfer coordinates from the output frame to the canvas +// for last frame rendered. // These stick the last frame to the display. - int in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h; +// Must be floats to support OpenGL + float output_x1, output_y1, output_x2, output_y2; + float canvas_x1, canvas_y1, canvas_x2, canvas_y2; // Screen capture BC_Capture *capture_bitmap; +// Set when OpenGL rendering has cleared the frame buffer before write_buffer + int is_cleared; }; #endif diff --git a/cinelerra/videodevice.C b/cinelerra/videodevice.C index 70333269..a1026f86 100644 --- a/cinelerra/videodevice.C +++ b/cinelerra/videodevice.C @@ -311,6 +311,9 @@ char* VideoDevice::drivertostr(int driver) case PLAYBACK_X11_XV: return PLAYBACK_X11_XV_TITLE; break; + case PLAYBACK_X11_GL: + return PLAYBACK_X11_GL_TITLE; + break; case PLAYBACK_BUZ: return PLAYBACK_BUZ_TITLE; break; @@ -598,6 +601,7 @@ int VideoDevice::open_output(VideoOutConfig *config, break; case PLAYBACK_X11: case PLAYBACK_X11_XV: + case PLAYBACK_X11_GL: output_base = new VDeviceX11(this, output); break; diff --git a/cinelerra/videodevice.inc b/cinelerra/videodevice.inc index d00a95f6..713d1977 100644 --- a/cinelerra/videodevice.inc +++ b/cinelerra/videodevice.inc @@ -26,6 +26,7 @@ class VideoDevice; #define PLAYBACK_X11 10 #define PLAYBACK_X11_XV 14 +#define PLAYBACK_X11_GL 17 #define PLAYBACK_BUZ 11 #define PLAYBACK_LML 12 #define PLAYBACK_FIREWIRE 13 @@ -34,6 +35,7 @@ class VideoDevice; #define PLAYBACK_X11_TITLE "X11" #define PLAYBACK_X11_XV_TITLE "X11-XV" +#define PLAYBACK_X11_GL_TITLE "X11-OpenGL" #define PLAYBACK_BUZ_TITLE "Buz" #define PLAYBACK_LML_TITLE "LML 33" #ifdef HAVE_FIREWIRE diff --git a/cinelerra/virtualvconsole.C b/cinelerra/virtualvconsole.C index c0556e5f..3addf192 100644 --- a/cinelerra/virtualvconsole.C +++ b/cinelerra/virtualvconsole.C @@ -9,6 +9,7 @@ #include "renderengine.h" #include "tracks.h" #include "transportque.h" +#include "vdevicex11.h" #include "vframe.h" #include "videodevice.h" #include "virtualvconsole.h" @@ -32,6 +33,10 @@ VirtualVConsole::~VirtualVConsole() } } +VDeviceBase* VirtualVConsole::get_vdriver() +{ + return renderengine->video->get_output_base(); +} void VirtualVConsole::get_playable_tracks() { @@ -62,12 +67,30 @@ int VirtualVConsole::process_buffer(int64_t input_position) +// The use of single frame is determined in RenderEngine::arm_command + use_opengl = (renderengine->video && + renderengine->video->out_config->driver == PLAYBACK_X11_GL); +// printf("VirtualVConsole::process_buffer %p %d %d\n", +// renderengine->video, +// renderengine->video->out_config->driver, +// use_opengl); if(debug_tree) printf("VirtualVConsole::process_buffer begin exit_nodes=%d\n", exit_nodes.total); + if(use_opengl) + { +// clear hardware framebuffer + + ((VDeviceX11*)get_vdriver())->clear_output(); + +// que OpenGL driver that everything is overlaid in the framebuffer + vrender->video_out->set_opengl_state(VFrame::SCREEN); + + } + else { // clear device buffer vrender->video_out->clear_frame(); @@ -107,6 +130,7 @@ Timer timer; if(!output_temp) { +// Texture is created on demand output_temp = new VFrame(0, track->track_w, track->track_h, @@ -114,10 +138,19 @@ Timer timer; -1); } -//printf("VirtualVConsole::process_buffer %p\n", output_temp->get_rows()); +// Reset OpenGL state + if(use_opengl) + output_temp->set_opengl_state(VFrame::RAM); + + +// Assume openGL is used for the final stage and let console +// disable. + output_temp->clear_stacks(); result |= node->render(output_temp, input_position + track->nudge, - renderengine->edl->session->frame_rate); + renderengine->edl->session->frame_rate, + use_opengl); + } //printf("VirtualVConsole::process_buffer timer=%lld\n", timer.get_difference()); diff --git a/cinelerra/virtualvconsole.h b/cinelerra/virtualvconsole.h index 9a9a7013..b043921b 100644 --- a/cinelerra/virtualvconsole.h +++ b/cinelerra/virtualvconsole.h @@ -4,6 +4,7 @@ #include "guicast.h" #include "maxbuffers.h" #include "vframe.inc" +#include "videodevice.inc" #include "virtualconsole.h" #include "vrender.inc" #include "vtrack.inc" @@ -25,6 +26,8 @@ public: Module *module, int track_number); + VDeviceBase* get_vdriver(); + // Composite a frame // start_position - start of buffer in project if forward. end of buffer if reverse int process_buffer(int64_t input_position); @@ -34,6 +37,8 @@ public: VFrame *output_temp; VRender *vrender; +// Calculated at the start of every process_buffer + int use_opengl; }; diff --git a/cinelerra/virtualvnode.C b/cinelerra/virtualvnode.C index fa000cf3..bbc8eb60 100644 --- a/cinelerra/virtualvnode.C +++ b/cinelerra/virtualvnode.C @@ -22,8 +22,9 @@ #include "transition.h" #include "transportque.h" #include "vattachmentpoint.h" -#include "vedit.h" +#include "vdevicex11.h" #include "vframe.h" +#include "videodevice.h" #include "virtualvconsole.h" #include "virtualvnode.h" #include "vmodule.h" @@ -80,54 +81,63 @@ VirtualNode* VirtualVNode::create_plugin(Plugin *real_plugin) int VirtualVNode::read_data(VFrame *output_temp, int64_t start_position, - double frame_rate) + double frame_rate, + int use_opengl) { VirtualNode *previous_plugin = 0; + int result = 0; if(!output_temp) printf("VirtualVNode::read_data output_temp=%p\n", output_temp); if(vconsole->debug_tree) - printf(" VirtualVNode::read_data position=%lld rate=%f title=%s\n", + printf(" VirtualVNode::read_data position=%lld rate=%f title=%s opengl=%d\n", start_position, frame_rate, - track->title); + track->title, + use_opengl); // This is a plugin on parent module with a preceeding effect. // Get data from preceeding effect on parent module. if(parent_node && (previous_plugin = parent_node->get_previous_plugin(this))) { - return ((VirtualVNode*)previous_plugin)->render(output_temp, + result = ((VirtualVNode*)previous_plugin)->render(output_temp, start_position, - frame_rate); + frame_rate, + use_opengl); } else -// First plugin on parent module. +// The current node is the first plugin on parent module. +// The parent module has an edit to read from or the current node +// has no source to read from. // Read data from parent module if(parent_node) { - return ((VirtualVNode*)parent_node)->read_data(output_temp, + result = ((VirtualVNode*)parent_node)->read_data(output_temp, start_position, - frame_rate); + frame_rate, + use_opengl); } else { // This is the first node in the tree - return ((VModule*)real_module)->render(output_temp, + result = ((VModule*)real_module)->render(output_temp, start_position, renderengine->command->get_direction(), frame_rate, 0, - vconsole->debug_tree); + vconsole->debug_tree, + use_opengl); } - return 0; + return result; } int VirtualVNode::render(VFrame *output_temp, int64_t start_position, - double frame_rate) + double frame_rate, + int use_opengl) { VRender *vrender = ((VirtualVConsole*)vconsole)->vrender; if(real_module) @@ -135,21 +145,24 @@ int VirtualVNode::render(VFrame *output_temp, render_as_module(vrender->video_out, output_temp, start_position, - frame_rate); + frame_rate, + use_opengl); } else if(real_plugin) { render_as_plugin(output_temp, start_position, - frame_rate); + frame_rate, + use_opengl); } return 0; } void VirtualVNode::render_as_plugin(VFrame *output_temp, int64_t start_position, - double frame_rate) + double frame_rate, + int use_opengl) { if(!attachment || !real_plugin || @@ -157,21 +170,25 @@ void VirtualVNode::render_as_plugin(VFrame *output_temp, if(vconsole->debug_tree) - printf(" VirtualVNode::render_as_plugin title=%s\n", track->title); + printf(" VirtualVNode::render_as_plugin title=%s use_opengl=%d\n", + track->title, + use_opengl); ((VAttachmentPoint*)attachment)->render( output_temp, plugin_buffer_number, start_position, frame_rate, - vconsole->debug_tree); + vconsole->debug_tree, + use_opengl); } int VirtualVNode::render_as_module(VFrame *video_out, VFrame *output_temp, int64_t start_position, - double frame_rate) + double frame_rate, + int use_opengl) { int direction = renderengine->command->get_direction(); @@ -183,8 +200,11 @@ int VirtualVNode::render_as_module(VFrame *video_out, if(direction == PLAY_REVERSE) start_position_project--; if(vconsole->debug_tree) - printf(" VirtualVNode::render_as_module title=%s\n", - track->title); + printf(" VirtualVNode::render_as_module title=%s use_opengl=%d video_out=%p output_temp=%p\n", + track->title, + use_opengl, + video_out, + output_temp); output_temp->push_next_effect("VirtualVNode::render_as_module"); @@ -195,14 +215,16 @@ int VirtualVNode::render_as_module(VFrame *video_out, VirtualVNode *node = (VirtualVNode*)subnodes.values[subnodes.total - 1]; node->render(output_temp, start_position, - frame_rate); + frame_rate, + use_opengl); } else // Read data from previous entity { read_data(output_temp, start_position, - frame_rate); + frame_rate, + use_opengl); } output_temp->pop_next_effect(); @@ -290,11 +312,16 @@ int VirtualVNode::render_fade(VFrame *output, // Can't use overlay here because overlayer blends the frame with itself. -// The fade engine can compensate for lack of alpha channels by reducing the -// color components. +// The fade engine can compensate for lack of alpha channels by multiplying the +// color components by alpha. if(!EQUIV(intercept / 100, 1)) { - fader->do_fade(output, output, intercept / 100); + if(((VirtualVConsole*)vconsole)->use_opengl) + ((VDeviceX11*)((VirtualVConsole*)vconsole)->get_vdriver())->do_fade( + output, + intercept / 100); + else + fader->do_fade(output, output, intercept / 100); } return 0; @@ -364,6 +391,24 @@ int VirtualVNode::render_projector(VFrame *input, vconsole->current_exit_node == vconsole->total_exit_nodes - 1) mode = TRANSFER_REPLACE; + if(((VirtualVConsole*)vconsole)->use_opengl) + { + ((VDeviceX11*)((VirtualVConsole*)vconsole)->get_vdriver())->overlay( + output, + input, + in_x1, + in_y1, + in_x2, + in_y2, + out_x1, + out_y1, + out_x2, + out_y2, + 1, + mode, + renderengine->edl); + } + else { vrender->overlayer->overlay(output, input, diff --git a/cinelerra/virtualvnode.h b/cinelerra/virtualvnode.h index f81455fe..461e0bf2 100644 --- a/cinelerra/virtualvnode.h +++ b/cinelerra/virtualvnode.h @@ -32,23 +32,28 @@ public: // Called by VirtualVConsole::process_buffer to process exit nodes. // start_position - end of frame if reverse. start of frame if forward. // frame_rate - rate start_position is relative to +// use_opengl - if opengl is available for this step int render(VFrame *output_temp, int64_t start_position, - double frame_rate); + double frame_rate, + int use_opengl); // Read data from what comes before this node. int read_data(VFrame *output_temp, int64_t start_position, - double frame_rate); + double frame_rate, + int use_opengl); private: int render_as_module(VFrame *video_out, VFrame *output_temp, int64_t start_position, - double frame_rate); + double frame_rate, + int use_opengl); void render_as_plugin(VFrame *output_temp, int64_t start_position, - double frame_rate); + double frame_rate, + int use_opengl); int render_projector(VFrame *input, VFrame *output, @@ -61,6 +66,9 @@ private: Autos *autos, int direction); + void render_mask(VFrame *output_temp, + int64_t start_position_project, + int use_opengl); FadeEngine *fader; }; diff --git a/cinelerra/vmodule.C b/cinelerra/vmodule.C index 07ae9be7..98540537 100644 --- a/cinelerra/vmodule.C +++ b/cinelerra/vmodule.C @@ -20,8 +20,10 @@ #include "transportque.h" #include "units.h" #include "vattachmentpoint.h" +#include "vdevicex11.h" #include "vedit.h" #include "vframe.h" +#include "videodevice.h" #include "vmodule.h" #include "vrender.h" #include "vplugin.h" @@ -79,7 +81,8 @@ int VModule::import_frame(VFrame *output, VEdit *current_edit, int64_t input_position, double frame_rate, - int direction) + int direction, + int use_opengl) { int64_t corrected_position; int64_t corrected_position_project; @@ -108,6 +111,17 @@ int VModule::import_frame(VFrame *output, input_position_project--; } + VDeviceX11 *x11_device = 0; + if(use_opengl) + { + if(renderengine && renderengine->video) + { + x11_device = (VDeviceX11*)renderengine->video->get_output_base(); + output->set_opengl_state(VFrame::RAM); + } + } + + // Load frame into output if(current_edit && current_edit->asset) @@ -233,6 +247,7 @@ int VModule::import_frame(VFrame *output, + (*input)->copy_stacks(output); // file -> temp // Cache for single frame only @@ -241,11 +256,18 @@ int VModule::import_frame(VFrame *output, result = source->read_frame((*input)); // if(renderengine && renderengine->command->single_frame()) source->set_cache_frames(0); + (*input)->set_opengl_state(VFrame::RAM); //printf("VModule::import_frame 1 %lld %f\n", input_position, frame_rate); +// Find an overlayer object to perform the camera transformation OverlayFrame *overlayer = 0; +// OpenGL playback uses hardware + if(use_opengl) + { + } + else // Realtime playback if(commonrender) { @@ -278,28 +300,50 @@ int VModule::import_frame(VFrame *output, // for(int j = 0; j < output->get_w() * 3 * 5; j++) // output->get_rows()[0][j] = 255; - output->clear_frame(); + if(use_opengl) + { + x11_device->do_camera(output, + (*input), + in_x1, + in_y1, + in_x1 + in_w1, + in_y1 + in_h1, + out_x1, + out_y1, + out_x1 + out_w1, + out_y1 + out_h1); + } + else + { + output->clear_frame(); // get_cache()->check_in(current_edit->asset); // return; - int mode = TRANSFER_REPLACE; - - overlayer->overlay(output, - (*input), - in_x1, - in_y1, - in_x1 + in_w1, - in_y1 + in_h1, - out_x1, - out_y1, - out_x1 + out_w1, - out_y1 + out_h1, - 1, - mode, - get_edl()->session->interpolation_type); +// TRANSFER_REPLACE is the fastest transfer mode but it has the disadvantage +// of producing green borders in floating point translation of YUV + int mode = TRANSFER_REPLACE; + if(get_edl()->session->interpolation_type != NEAREST_NEIGHBOR && + cmodel_is_yuv(output->get_color_model())) + mode = TRANSFER_NORMAL; + + overlayer->overlay(output, + (*input), + in_x1, + in_y1, + in_x1 + in_w1, + in_y1 + in_h1, + out_x1, + out_y1, + out_x1 + out_w1, + out_y1 + out_h1, + 1, + mode, + get_edl()->session->interpolation_type); + } result = 1; + output->copy_stacks((*input)); } else // file -> output @@ -310,20 +354,35 @@ int VModule::import_frame(VFrame *output, result = source->read_frame(output); // if(renderengine && renderengine->command->single_frame()) source->set_cache_frames(0); + output->set_opengl_state(VFrame::RAM); } get_cache()->check_in(current_edit->asset); } else { - output->clear_frame(); + if(use_opengl) + { + x11_device->clear_input(output); + } + else + { + output->clear_frame(); + } result = 1; } } else // Silence { - output->clear_frame(); + if(use_opengl) + { + x11_device->clear_input(output); + } + else + { + output->clear_frame(); + } } @@ -337,7 +396,8 @@ int VModule::render(VFrame *output, int direction, double frame_rate, int use_nudge, - int debug_render) + int debug_render, + int use_opengl) { int result = 0; double edl_rate = get_edl()->session->frame_rate; @@ -351,12 +411,6 @@ int VModule::render(VFrame *output, frame_rate + 0.5); - if(debug_render) - printf(" VModule::render %d %lld %s\n", - use_nudge, - start_position_project, - track->title); - update_transition(start_position_project, direction); @@ -365,6 +419,15 @@ int VModule::render(VFrame *output, 0); VEdit* previous_edit = 0; + if(debug_render) + printf(" VModule::render %d %lld %s transition=%p opengl=%d current_edit=%p output=%p\n", + use_nudge, + start_position_project, + track->title, + transition, + use_opengl, + current_edit, + output); if(!current_edit) { @@ -416,7 +479,8 @@ int VModule::render(VFrame *output, current_edit, start_position, frame_rate, - direction); + direction, + use_opengl); // Load transition buffer @@ -426,9 +490,12 @@ int VModule::render(VFrame *output, previous_edit, start_position, frame_rate, - direction); + direction, + use_opengl); // Execute plugin with transition_input and output here + if(renderengine) + transition_server->set_use_opengl(use_opengl, renderengine->video); transition_server->process_transition((*transition_input), output, (direction == PLAY_FORWARD) ? @@ -443,7 +510,8 @@ int VModule::render(VFrame *output, current_edit, start_position, frame_rate, - direction); + direction, + use_opengl); } int64_t mask_position; diff --git a/cinelerra/vmodule.h b/cinelerra/vmodule.h index dc0f76cd..5c2ed700 100644 --- a/cinelerra/vmodule.h +++ b/cinelerra/vmodule.h @@ -40,17 +40,20 @@ public: int get_buffer_size(); CICache* get_cache(); +// Read frame from file and perform camera transformation int import_frame(VFrame *output, VEdit *current_edit, int64_t input_position, double frame_rate, - int direction); + int direction, + int use_opengl); int render(VFrame *output, int64_t start_position, int direction, double frame_rate, int use_nudge, - int debug_render); + int debug_render, + int use_opengl = 0); // synchronization with tracks FloatAutos* get_fade_automation(); // get the fade automation for this module diff --git a/cinelerra/vwindowgui.C b/cinelerra/vwindowgui.C index ad6d8d0f..99fa38cc 100644 --- a/cinelerra/vwindowgui.C +++ b/cinelerra/vwindowgui.C @@ -823,25 +823,26 @@ void VWindowCanvas::draw_refresh() if(!get_canvas()->get_video_on()) get_canvas()->clear_box(0, 0, get_canvas()->get_w(), get_canvas()->get_h()); if(!get_canvas()->get_video_on() && refresh_frame && edl) { - int in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h; + float in_x1, in_y1, in_x2, in_y2; + float out_x1, out_y1, out_x2, out_y2; get_transfers(edl, - in_x, - in_y, - in_w, - in_h, - out_x, - out_y, - out_w, - out_h); + in_x1, + in_y1, + in_x2, + in_y2, + out_x1, + out_y1, + out_x2, + out_y2); get_canvas()->draw_vframe(refresh_frame, - out_x, - out_y, - out_w, - out_h, - in_x, - in_y, - in_w, - in_h, + (int)out_x1, + (int)out_y1, + (int)(out_x2 - out_x1), + (int)(out_y2 - out_y1), + (int)in_x1, + (int)in_y1, + (int)(in_x2 - in_x1), + (int)(in_y2 - in_y1), 0); } diff --git a/guicast/Makefile.am b/guicast/Makefile.am index 7f5636d1..eff87047 100644 --- a/guicast/Makefile.am +++ b/guicast/Makefile.am @@ -17,6 +17,7 @@ libguicast_la_SOURCES = \ bchash.C \ bcipc.C \ bclistbox.C \ + bclistboxitem.C \ bcmenu.C \ bcmenubar.C \ bcmenuitem.C \ @@ -24,6 +25,7 @@ libguicast_la_SOURCES = \ bcmeter.C \ bcnewfolder.C \ bcpan.C \ + bcpbuffer.C \ bcpixmap.C \ bcpixmapsw.C \ bcpopup.C \ @@ -39,16 +41,21 @@ libguicast_la_SOURCES = \ bcsignals.C \ bcslider.C \ bcsubwindow.C \ + bcsynchronous.C \ bctextbox.C \ + bctexture.C \ + bctheme.C \ bctimer.C \ bctitle.C \ bctoggle.C \ bctumble.C \ bcwidgetgrid.C \ bcwindow.C \ + bcwindow3d.C \ bcwindowbase.C \ bcwindowdraw.C \ bcwindowevents.C \ + condition.C \ debug.h \ error.h \ errorbox.C \ @@ -60,7 +67,10 @@ libguicast_la_SOURCES = \ stringfile.C \ thread.C \ units.C \ - vframe.C workarounds.C bclistboxitem.C bctheme.C condition.C + vframe.C \ + vframe3d.C \ + workarounds.C + noinst_HEADERS = \ arraylist.h \ bcbar.h \ @@ -103,6 +113,8 @@ noinst_HEADERS = \ bcnewfolder.h \ bcnewfolder.inc \ bcpan.h bcpan.inc \ + bcpbuffer.h \ + bcpbuffer.inc \ bcpixmap.h \ bcpixmap.inc \ bcpixmapsw.h \ @@ -125,7 +137,18 @@ noinst_HEADERS = \ bcresources.inc \ bcscrollbar.h \ bcscrollbar.inc \ - bcsignals.h bcsignals.inc bcslider.h bcslider.inc bcsubwindow.h bcsubwindow.inc bctextbox.h bctextbox.inc \ + bcsignals.h \ + bcsignals.inc \ + bcslider.h \ + bcslider.inc \ + bcsubwindow.h \ + bcsubwindow.inc \ + bcsynchronous.h \ + bcsynchronous.inc \ + bctextbox.h \ + bctextbox.inc \ + bctextbox.h \ + bctextbox.inc \ bctheme.h \ bctheme.inc \ bctimer.h \ diff --git a/guicast/bcpbuffer.C b/guicast/bcpbuffer.C new file mode 100644 index 00000000..28c955b1 --- /dev/null +++ b/guicast/bcpbuffer.C @@ -0,0 +1,172 @@ +#include "bcpbuffer.h" +#include "bcresources.h" +#include "bcsignals.h" +#include "bcsynchronous.h" +#include "bcwindowbase.h" + + + + +BC_PBuffer::BC_PBuffer(int w, int h) +{ + reset(); + this->w = w; + this->h = h; + + + new_pbuffer(w, h); +} + +BC_PBuffer::~BC_PBuffer() +{ +#ifdef HAVE_GL + BC_WindowBase::get_synchronous()->release_pbuffer(window_id, pbuffer); +#endif +} + + +void BC_PBuffer::reset() +{ +#ifdef HAVE_GL + pbuffer = 0; + window_id = -1; +#endif +} + +#ifdef HAVE_GL + +GLXPbuffer BC_PBuffer::get_pbuffer() +{ + return pbuffer; +} +#endif + + +void BC_PBuffer::new_pbuffer(int w, int h) +{ +#ifdef HAVE_GL + + if(!pbuffer) + { + BC_WindowBase *current_window = BC_WindowBase::get_synchronous()->current_window; +// Try previously created PBuffers + pbuffer = BC_WindowBase::get_synchronous()->get_pbuffer(w, + h, + &window_id, + &gl_context); + + if(pbuffer) + { + return; + } + + + + + +// You're supposed to try different configurations of decreasing overhead +// until one works. +// In reality, only a very specific configuration works at all. +#define TOTAL_CONFIGS 1 + static int framebuffer_attributes[] = + { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, False, + GLX_DEPTH_SIZE, 1, + GLX_ACCUM_RED_SIZE, 1, + GLX_ACCUM_GREEN_SIZE, 1, + GLX_ACCUM_BLUE_SIZE, 1, + GLX_ACCUM_ALPHA_SIZE, 1, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + None + }; + + static int pbuffer_attributes[] = + { + GLX_PBUFFER_WIDTH, 0, + GLX_PBUFFER_HEIGHT, 0, + GLX_LARGEST_PBUFFER, False, + GLX_PRESERVED_CONTENTS, True, + None + }; + + pbuffer_attributes[1] = w; + pbuffer_attributes[3] = h; + if(w % 4) pbuffer_attributes[1] += 4 - (w % 4); + if(h % 4) pbuffer_attributes[3] += 4 - (h % 4); + + GLXFBConfig *config_result; + XVisualInfo *visinfo; + int config_result_count = 0; + config_result = glXChooseFBConfig(current_window->get_display(), + current_window->get_screen(), + framebuffer_attributes, + &config_result_count); +// printf("BC_PBuffer::new_pbuffer 1 config_result=%p config_result_count=%d\n", +// config_result, +// config_result_count); + + if(!config_result || !config_result_count) + { + printf("BC_PBuffer::new_pbuffer: glXChooseFBConfig failed\n"); + return; + } + + +static int current_config = 0; + + BC_Resources::error = 0; + pbuffer = glXCreatePbuffer(current_window->get_display(), + config_result[current_config], + pbuffer_attributes); + visinfo = glXGetVisualFromFBConfig(current_window->get_display(), + config_result[current_config]); +// printf("BC_PBuffer::new_pbuffer 2 visual=%d current_config=%d error=%d pbuffer=%p\n", +// visinfo->visual, +// current_config, +// BC_Resources::error, +// pbuffer); +///current_config++; + +// Got it + if(!BC_Resources::error && pbuffer && visinfo) + { + window_id = current_window->get_id(); + gl_context = glXCreateContext(current_window->get_display(), + visinfo, + current_window->gl_win_context, + 1); + BC_WindowBase::get_synchronous()->put_pbuffer(w, + h, + pbuffer, + gl_context); +// printf("BC_PBuffer::new_pbuffer gl_context=%p window_id=%d\n", +// gl_context, +// current_window->get_id()); + } + + if(config_result) XFree(config_result); + if(visinfo) XFree(visinfo); + } + + + if(!pbuffer) printf("BC_PBuffer::new_pbuffer: failed\n"); +#endif +} + + +void BC_PBuffer::enable_opengl() +{ +#ifdef HAVE_GL + BC_WindowBase *current_window = BC_WindowBase::get_synchronous()->current_window; + int result = glXMakeCurrent(current_window->get_display(), + pbuffer, + gl_context); + BC_WindowBase::get_synchronous()->is_pbuffer = 1; +#endif +} + diff --git a/guicast/bcpbuffer.h b/guicast/bcpbuffer.h new file mode 100644 index 00000000..adbb6f9e --- /dev/null +++ b/guicast/bcpbuffer.h @@ -0,0 +1,53 @@ +#ifndef BCPBUFFER_H +#define BCPBUFFER_H + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#ifdef HAVE_GL +#include +#endif + +#include "vframe.inc" + + +// This is created by the user to create custom PBuffers and by VFrame. +// Uses the window currently bound by BC_WindowBase::enable_opengl to +// create pbuffer. +// Must be called synchronously. +class BC_PBuffer +{ +public: + BC_PBuffer(int w, int h); + ~BC_PBuffer(); + + friend class VFrame; + + void reset(); +// Must be called after BC_WindowBase::enable_opengl to make the PBuffer +// the current drawing surface. Call BC_WindowBase::enable_opengl +// after this to switch back to the window. + void enable_opengl(); +#ifdef HAVE_GL + GLXPbuffer get_pbuffer(); +#endif + +private: +// Called by constructor + void new_pbuffer(int w, int h); + +#ifdef HAVE_GL + GLXPbuffer pbuffer; + GLXContext gl_context; +#endif + int w; + int h; + int window_id; +}; + + + + + +#endif diff --git a/guicast/bcpbuffer.inc b/guicast/bcpbuffer.inc new file mode 100644 index 00000000..aab62861 --- /dev/null +++ b/guicast/bcpbuffer.inc @@ -0,0 +1,12 @@ +#ifndef BC_PBUFFER_INC +#define BC_PBUFFER_INC + + + + +class BC_PBuffer; + + + + +#endif diff --git a/guicast/bcresources.C b/guicast/bcresources.C index bca3b280..ea75c208 100644 --- a/guicast/bcresources.C +++ b/guicast/bcresources.C @@ -3,6 +3,7 @@ #include "bclistbox.inc" #include "bcresources.h" #include "bcsignals.h" +#include "bcsynchronous.h" #include "bcwindowbase.h" #include "colors.h" #include "colormodels.h" @@ -81,8 +82,10 @@ int BC_Resources::x_error_handler(Display *display, XErrorEvent *event) BC_Resources::BC_Resources() { + synchronous = 0; display_info = new BC_DisplayInfo("", 0); id_lock = new Mutex("BC_Resources::id_lock"); + id = 0; for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++) filebox_history[i][0] = 0; @@ -547,6 +550,25 @@ int BC_Resources::init_shm(BC_WindowBase *window) return 0; } + + + +BC_Synchronous* BC_Resources::get_synchronous() +{ + return synchronous; +} + +void BC_Resources::set_synchronous(BC_Synchronous *synchronous) +{ + this->synchronous = synchronous; +} + + + + + + + int BC_Resources::get_top_border() { return display_info->get_top_border(); @@ -578,3 +600,12 @@ int BC_Resources::get_bg_light1() { return bg_light1; } int BC_Resources::get_bg_light2() { return bg_light2; } + +int BC_Resources::get_id() +{ + id_lock->lock("BC_Resources::get_id"); + int result = id++; + id_lock->unlock(); + return result; +} + diff --git a/guicast/bcresources.h b/guicast/bcresources.h index c5e39f18..a09b85ce 100644 --- a/guicast/bcresources.h +++ b/guicast/bcresources.h @@ -12,6 +12,7 @@ #include "bcfilebox.h" #include "bcresources.inc" #include "bcsignals.inc" +#include "bcsynchronous.inc" #include "bcwindowbase.inc" #include "vframe.inc" @@ -33,6 +34,8 @@ public: int initialize_display(BC_WindowBase *window); +// Get unique ID + int get_id(); int get_bg_color(); // window backgrounds int get_bg_shadow1(); // border for windows int get_bg_shadow2(); @@ -43,7 +46,10 @@ public: int get_left_border(); int get_right_border(); int get_bottom_border(); - +// Get synchronous thread for OpenGL + BC_Synchronous* get_synchronous(); +// Called by user after synchronous thread is created. + void set_synchronous(BC_Synchronous *synchronous); // Pointer to signal handler class to run after ipc static BC_Signals *signal_handler; @@ -306,6 +312,9 @@ private: Mutex *id_lock; + BC_Synchronous *synchronous; + + int id; }; diff --git a/guicast/bcsynchronous.C b/guicast/bcsynchronous.C new file mode 100644 index 00000000..b2a8dae9 --- /dev/null +++ b/guicast/bcsynchronous.C @@ -0,0 +1,567 @@ +#define GL_GLEXT_PROTOTYPES +#include "bcresources.h" +#include "bcsignals.h" +#include "bcsynchronous.h" +#include "bcwindowbase.h" +#include "condition.h" +#include "mutex.h" + + +#ifdef HAVE_GL +#include +#endif +#include + +#include + + +TextureID::TextureID(int window_id, int id, int w, int h, int components) +{ + this->window_id = window_id; + this->id = id; + this->w = w; + this->h = h; + this->components = components; + in_use = 1; +} + +ShaderID::ShaderID(int window_id, unsigned int handle, char *source) +{ + this->window_id = window_id; + this->handle = handle; + this->source = strdup(source); +} + +ShaderID::~ShaderID() +{ + free(source); +} + +#ifdef HAVE_GL +PBufferID::PBufferID(int window_id, + GLXPbuffer pbuffer, + GLXContext gl_context, + int w, + int h) +{ + this->pbuffer = pbuffer; + this->gl_context = gl_context; + this->window_id = window_id; + this->w = w; + this->h = h; + in_use = 1; +} +#endif + + + +BC_SynchronousCommand::BC_SynchronousCommand() +{ + command = BC_SynchronousCommand::NONE; + frame = 0; + frame_return = 0; + result = 0; + command_done = new Condition(0, "BC_SynchronousCommand::command_done", 0); +} + +BC_SynchronousCommand::~BC_SynchronousCommand() +{ + delete command_done; +} + +void BC_SynchronousCommand::copy_from(BC_SynchronousCommand *command) +{ + this->command = command->command; + this->colormodel = command->colormodel; + this->window = command->window; + this->frame = command->frame; + this->window_id = command->window_id; + + this->frame_return = command->frame_return; + + this->id = command->id; + this->w = command->w; + this->h = command->h; +} + + + + +BC_Synchronous::BC_Synchronous() + : Thread(1, 0, 0) +{ + next_command = new Condition(0, "BC_Synchronous::next_command", 0); + command_lock = new Mutex("BC_Synchronous::command_lock"); + table_lock = new Mutex("BC_Synchronous::table_lock"); + done = 0; + is_running = 0; + current_window = 0; + BC_WindowBase::get_resources()->set_synchronous(this); +} + +BC_Synchronous::~BC_Synchronous() +{ + commands.remove_all_objects(); +} + +BC_SynchronousCommand* BC_Synchronous::new_command() +{ + return new BC_SynchronousCommand; +} + +void BC_Synchronous::create_objects() +{ +} + +void BC_Synchronous::start() +{ + run(); +} + +void BC_Synchronous::quit() +{ + command_lock->lock("BC_Synchronous::quit"); + BC_SynchronousCommand *command = new_command(); + commands.append(command); + command->command = BC_SynchronousCommand::QUIT; + command_lock->unlock(); + + next_command->unlock(); +} + +int BC_Synchronous::send_command(BC_SynchronousCommand *command) +{ + command_lock->lock("BC_Synchronous::send_command"); + BC_SynchronousCommand *command2 = new_command(); + commands.append(command2); + command2->copy_from(command); + command_lock->unlock(); + + next_command->unlock(); +//printf("BC_Synchronous::send_command 1 %d\n", next_command->get_value()); + +// Wait for completion + command2->command_done->lock("BC_Synchronous::send_command"); + int result = command2->result; + delete command2; + return result; +} + +void BC_Synchronous::run() +{ + is_running = 1; + while(!done) + { + next_command->lock("BC_Synchronous::run"); + + + command_lock->lock("BC_Synchronous::run"); + BC_SynchronousCommand *command = 0; + if(commands.total) + { + command = commands.values[0]; + commands.remove_number(0); + } +// Prevent executing the same command twice if spurious unlock. + command_lock->unlock(); +//printf("BC_Synchronous::run %d\n", command->command); + + handle_command_base(command); +// delete command; + } + is_running = 0; +} + +void BC_Synchronous::handle_command_base(BC_SynchronousCommand *command) +{ + + if(command) + { +//printf("BC_Synchronous::handle_command_base 1 %d\n", command->command); + switch(command->command) + { + case BC_SynchronousCommand::QUIT: + done = 1; + break; + + default: + handle_command(command); + break; + } + } + + handle_garbage(); + + if(command) + { + command->command_done->unlock(); + } +} + +void BC_Synchronous::handle_command(BC_SynchronousCommand *command) +{ +} + +void BC_Synchronous::handle_garbage() +{ + while(1) + { + table_lock->lock("BC_Synchronous::handle_garbage"); + if(!garbage.total) + { + table_lock->unlock(); + return; + } + + BC_SynchronousCommand *command = garbage.values[0]; + garbage.remove_number(0); + table_lock->unlock(); + + switch(command->command) + { + case BC_SynchronousCommand::DELETE_WINDOW: + delete_window_sync(command); + break; + + case BC_SynchronousCommand::DELETE_PIXMAP: + delete_pixmap_sync(command); + break; + } + + delete command; + } +} + +void BC_Synchronous::put_texture(int id, int w, int h, int components) +{ + if(id >= 0) + { + table_lock->lock("BC_Resources::put_texture"); +// Search for duplicate + for(int i = 0; i < texture_ids.total; i++) + { + TextureID *ptr = texture_ids.values[i]; + if(ptr->window_id == current_window->get_id() && + ptr->id == id) + { + printf("BC_Synchronous::push_texture: texture exists\n" + "exists: window=%d id=%d w=%d h=%d\n" + "new: window=%d id=%d w=%d h=%d\n", + ptr->window_id, + ptr->id, + ptr->w, + ptr->h, + current_window->get_id(), + id, + w, + h); + table_lock->unlock(); + return; + } + } + + TextureID *new_id = new TextureID(current_window->get_id(), + id, + w, + h, + components); + texture_ids.append(new_id); + table_lock->unlock(); + } +} + +int BC_Synchronous::get_texture(int w, int h, int components) +{ + table_lock->lock("BC_Resources::get_texture"); + for(int i = 0; i < texture_ids.total; i++) + { + if(texture_ids.values[i]->w == w && + texture_ids.values[i]->h == h && + texture_ids.values[i]->components == components && + !texture_ids.values[i]->in_use && + texture_ids.values[i]->window_id == current_window->get_id()) + { + int result = texture_ids.values[i]->id; + texture_ids.values[i]->in_use = 1; + table_lock->unlock(); + return result; + } + } + table_lock->unlock(); + return -1; +} + +void BC_Synchronous::release_texture(int window_id, int id) +{ + table_lock->lock("BC_Resources::release_texture"); + for(int i = 0; i < texture_ids.total; i++) + { + if(texture_ids.values[i]->id == id && + texture_ids.values[i]->window_id == window_id) + { + texture_ids.values[i]->in_use = 0; + table_lock->unlock(); + return; + } + } + table_lock->unlock(); +} + + + + + +unsigned int BC_Synchronous::get_shader(char *source, int *got_it) +{ + table_lock->lock("BC_Resources::get_shader"); + for(int i = 0; i < shader_ids.total; i++) + { + if(shader_ids.values[i]->window_id == current_window->get_id() && + !strcmp(shader_ids.values[i]->source, source)) + { + unsigned int result = shader_ids.values[i]->handle; + table_lock->unlock(); + *got_it = 1; + return result; + } + } + table_lock->unlock(); + *got_it = 0; + return 0; +} + +void BC_Synchronous::put_shader(unsigned int handle, + char *source) +{ + table_lock->lock("BC_Resources::put_shader"); + shader_ids.append(new ShaderID(current_window->get_id(), handle, source)); + table_lock->unlock(); +} + +void BC_Synchronous::dump_shader(unsigned int handle) +{ + int got_it = 0; + table_lock->lock("BC_Resources::dump_shader"); + for(int i = 0; i < shader_ids.total; i++) + { + if(shader_ids.values[i]->handle == handle) + { + printf("BC_Synchronous::dump_shader\n" + "%s", shader_ids.values[i]->source); + got_it = 1; + break; + } + } + table_lock->unlock(); + if(!got_it) printf("BC_Synchronous::dump_shader couldn't find %d\n", handle); +} + + +void BC_Synchronous::delete_window(BC_WindowBase *window) +{ +#ifdef HAVE_GL + BC_SynchronousCommand *command = new_command(); + command->command = BC_SynchronousCommand::DELETE_WINDOW; + command->window_id = window->get_id(); + command->display = window->get_display(); + command->win = window->win; + command->gl_context = window->gl_win_context; + + send_garbage(command); +#endif +} + +void BC_Synchronous::delete_window_sync(BC_SynchronousCommand *command) +{ +#ifdef HAVE_GL + int window_id = command->window_id; + Display *display = command->display; + Window win = command->win; + GLXContext gl_context = command->gl_context; +int debug = 0; + +// texture ID's are unique to different contexts + glXMakeCurrent(display, + win, + gl_context); + + table_lock->lock("BC_Resources::release_textures"); + for(int i = 0; i < texture_ids.total; i++) + { + if(texture_ids.values[i]->window_id == window_id) + { + GLuint id = texture_ids.values[i]->id; + glDeleteTextures(1, &id); +if(debug) +printf("BC_Synchronous::delete_window_sync texture_id=%d window_id=%d\n", +id, +window_id); + texture_ids.remove_object_number(i); + i--; + } + } + + for(int i = 0; i < shader_ids.total; i++) + { + if(shader_ids.values[i]->window_id == window_id) + { + glDeleteShader(shader_ids.values[i]->handle); +if(debug) +printf("BC_Synchronous::delete_window_sync shader_id=%d window_id=%d\n", +shader_ids.values[i]->handle, +window_id); + shader_ids.remove_object_number(i); + i--; + } + } + + for(int i = 0; i < pbuffer_ids.total; i++) + { + if(pbuffer_ids.values[i]->window_id == window_id) + { + glXDestroyPbuffer(display, pbuffer_ids.values[i]->pbuffer); + glXDestroyContext(display, pbuffer_ids.values[i]->gl_context); +if(debug) +printf("BC_Synchronous::delete_window_sync pbuffer_id=%p window_id=%d\n", +pbuffer_ids.values[i]->pbuffer, +window_id); + pbuffer_ids.remove_object_number(i); + i--; + } + } + + + table_lock->unlock(); + + XDestroyWindow(display, win); + if(gl_context) glXDestroyContext(display, gl_context); +#endif +} + + + +#ifdef HAVE_GL +void BC_Synchronous::put_pbuffer(int w, + int h, + GLXPbuffer pbuffer, + GLXContext gl_context) +{ + int exists = 0; + table_lock->lock("BC_Resources::release_textures"); + for(int i = 0; i < pbuffer_ids.total; i++) + { + PBufferID *ptr = pbuffer_ids.values[i]; + if(ptr->w == w && + ptr->h == h && + ptr->pbuffer == pbuffer) + { +// Exists + exists = 1; + break; + } + } + + + if(!exists) + { + PBufferID *ptr = new PBufferID(current_window->get_id(), + pbuffer, + gl_context, + w, + h); + pbuffer_ids.append(ptr); + } + table_lock->unlock(); +} + +GLXPbuffer BC_Synchronous::get_pbuffer(int w, + int h, + int *window_id, + GLXContext *gl_context) +{ + table_lock->lock("BC_Resources::release_textures"); + for(int i = 0; i < pbuffer_ids.total; i++) + { + PBufferID *ptr = pbuffer_ids.values[i]; + if(ptr->w == w && + ptr->h == h && + ptr->window_id == current_window->get_id() && + !ptr->in_use) + { + GLXPbuffer result = ptr->pbuffer; + *gl_context = ptr->gl_context; + *window_id = ptr->window_id; + ptr->in_use = 1; + table_lock->unlock(); + return result; + } + } + table_lock->unlock(); + return 0; +} + +void BC_Synchronous::release_pbuffer(int window_id, GLXPbuffer pbuffer) +{ + table_lock->lock("BC_Resources::release_textures"); + for(int i = 0; i < pbuffer_ids.total; i++) + { + PBufferID *ptr = pbuffer_ids.values[i]; + if(ptr->window_id == window_id) + { + ptr->in_use = 0; + } + } + table_lock->unlock(); +} + +void BC_Synchronous::delete_pixmap(BC_WindowBase *window, + GLXPixmap pixmap, + GLXContext context) +{ + BC_SynchronousCommand *command = new_command(); + command->command = BC_SynchronousCommand::DELETE_PIXMAP; + command->window_id = window->get_id(); + command->display = window->get_display(); + command->win = window->win; + command->gl_pixmap = pixmap; + command->gl_context = context; + + send_garbage(command); +} +#endif + +void BC_Synchronous::delete_pixmap_sync(BC_SynchronousCommand *command) +{ +#ifdef HAVE_GL + Display *display = command->display; + Window win = command->win; + glXMakeCurrent(display, + win, + command->gl_context); + glXDestroyContext(display, command->gl_context); + glXDestroyGLXPixmap(display, command->gl_pixmap); +#endif +} + + + +void BC_Synchronous::send_garbage(BC_SynchronousCommand *command) +{ + table_lock->lock("BC_Synchronous::delete_window"); + garbage.append(command); + table_lock->unlock(); + + next_command->unlock(); +} + +BC_WindowBase* BC_Synchronous::get_window() +{ + return current_window; +} + + + + + + + diff --git a/guicast/bcsynchronous.h b/guicast/bcsynchronous.h new file mode 100644 index 00000000..2e14c747 --- /dev/null +++ b/guicast/bcsynchronous.h @@ -0,0 +1,263 @@ +#ifndef BCSYNCHRONOUS_H +#define BCSYNCHRONOUS_H + +#include "arraylist.h" +#include "bcpixmap.inc" +#include "bctexture.inc" +#include "bcwindowbase.inc" +#include "condition.inc" +#include "mutex.inc" +#include "thread.h" +#include "vframe.inc" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + + +#ifdef HAVE_GL +#include +#include +#endif + +#include + +// This takes requests and runs all OpenGL calls in the main thread. +// Past experience showed OpenGL couldn't be run from multiple threads +// reliably even if MakeCurrent was used and only 1 thread at a time did +// anything. + +// Also manages texture memory. Textures are not deleted until the gl_context +// is deleted, so they have to be reused as much as possible. +// Must be run as the main loop of the application. Other threads must +// call into it to dispatch commands. + +// In addition to synchronous operations, it handles global OpenGL variables. + +// Users should create a subclass of the command and thread components to +// add specific commands. + + + +class TextureID +{ +public: + TextureID(int window_id, int id, int w, int h, int components); + int window_id; + int id; + int w; + int h; + int components; + BC_WindowBase *window; + int in_use; +}; + +class ShaderID +{ +public: + ShaderID(int window_id, unsigned int handle, char *source); + ~ShaderID(); + +// Should really use an MD5 to compare sources but this is easiest. + char *source; + int window_id; + unsigned int handle; +}; + +class PBufferID +{ +public: + PBufferID() {}; +#ifdef HAVE_GL + PBufferID(int window_id, + GLXPbuffer pbuffer, + GLXContext gl_context, + int w, + int h); + GLXPbuffer pbuffer; + GLXContext gl_context; + int window_id; + int w; + int h; + int in_use; +#endif +}; + +class BC_SynchronousCommand +{ +public: + BC_SynchronousCommand(); + ~BC_SynchronousCommand(); + + virtual void copy_from(BC_SynchronousCommand *command); + + Condition *command_done; + int result; + int command; + +// Commands + enum + { + NONE, + QUIT, +// Used by garbage collector + DELETE_WINDOW, + DELETE_PIXMAP, +// subclasses create new commands starting with this enumeration + LAST_COMMAND + }; + + int colormodel; + BC_WindowBase *window; + VFrame *frame; + + VFrame *frame_return; + + int id; + int w; + int h; + +// For garbage collection + int window_id; + Display *display; + Window win; +#ifdef HAVE_GL + GLXContext gl_context; + GLXPixmap gl_pixmap; +#endif +}; + + +class BC_Synchronous : public Thread +{ +public: + BC_Synchronous(); + virtual ~BC_Synchronous(); + + friend class BC_WindowBase; + friend class VFrame; + friend class BC_PBuffer; + friend class BC_Pixmap; + friend class BC_Texture; + +// Called by another thread +// Quits the loop + void quit(); +// Must be called after constructor to create inherited objects. + void create_objects(); + void start(); + void run(); + + virtual BC_SynchronousCommand* new_command(); +// Handle extra commands not part of the base class. +// Contains a switch statement starting with LAST_COMMAND + virtual void handle_command(BC_SynchronousCommand *command); + + +// OpenGL texture removal doesn't work. Need to store the ID's of all the deleted +// textures in this stack and reuse them. Also since OpenGL needs synchronous +// commands, be sure this is always called synchronously. +// Called when a texture is created to associate it with the current window. +// Must be called inside synchronous loop. + void put_texture(int id, int w, int h, int components); +// Search for existing texture matching the parameters and not in use +// and return it. If -1 is returned, a new texture must be created. +// Must be called inside synchronous loop. +// If someone proves OpenGL can delete texture memory, this function can be +// forced to always return -1. + int get_texture(int w, int h, int components); +// Release a texture for use by the get_texture call. +// Can be called outside synchronous loop. + void release_texture(int window_id, int id); + +// Get the shader by window_id and source comparison if it exists. +// Not run in OpenGL thread because it has its own lock. +// Sets *got_it to 1 on success. + unsigned int get_shader(char *source, int *got_it); +// Add a new shader program by title if it doesn't exist. +// Doesn't check if it already exists. + void put_shader(unsigned int handle, char *source); + void dump_shader(unsigned int handle); + + +#ifdef HAVE_GL +// Push a pbuffer when it's created. +// Must be called inside synchronous loop. + void put_pbuffer(int w, + int h, + GLXPbuffer pbuffer, + GLXContext gl_context); +// Get the PBuffer by window_id and dimensions if it exists. +// Must be called inside synchronous loop. + GLXPbuffer get_pbuffer(int w, + int h, + int *window_id, + GLXContext *gl_context); +// Release a pbuffer for use by get_pbuffer. + void release_pbuffer(int window_id, GLXPbuffer pbuffer); + +// Schedule GL pixmap for deletion by the garbage collector. +// Pixmaps don't wait until until the window is deleted but they must be +// deleted before the window is deleted to have the display connection. + void delete_pixmap(BC_WindowBase *window, + GLXPixmap pixmap, + GLXContext context); +#endif + + + +// Called by ~BC_WindowBase to delete OpenGL objects related to the window. +// This function returns immediately instead of waiting for the synchronous +// part to finish. + void delete_window(BC_WindowBase *window); + + + int send_command(BC_SynchronousCommand *command); + void send_garbage(BC_SynchronousCommand *command); + + +// Get the window currently bound to the context. + BC_WindowBase* get_window(); + +private: + void handle_command_base(BC_SynchronousCommand *command); + +// Execute commands which can't be executed until the caller returns. + void handle_garbage(); + +// Release OpenGL objects related to the window id. +// Called from the garbage collector only + void delete_window_sync(BC_SynchronousCommand *command); + void delete_pixmap_sync(BC_SynchronousCommand *command); + + Condition *next_command; + Mutex *command_lock; + +// Must be locked in order of current_window->lock_window, table_lock +// or just table_lock. + Mutex *table_lock; + +// This quits the program when it's 1. + int done; +// Command stack + ArrayList commands; + int is_running; +// The window the opengl context is currently bound to. +// Set by BC_WindowBase::enable_opengl. + BC_WindowBase *current_window; + + ArrayList shader_ids; + ArrayList texture_ids; + ArrayList pbuffer_ids; +// Commands which can't be executed until the caller returns. + ArrayList garbage; + +// When the context is bound to a pbuffer, this +// signals glCopyTexSubImage2D to use the front buffer. + int is_pbuffer; +}; + + + + +#endif diff --git a/guicast/bcsynchronous.inc b/guicast/bcsynchronous.inc new file mode 100644 index 00000000..99e4ae20 --- /dev/null +++ b/guicast/bcsynchronous.inc @@ -0,0 +1,12 @@ +#ifndef BCSYNCHRONOUS_INC +#define BCSYNCHRONOUS_INC + + + + +class BC_Synchronous; + + + + +#endif diff --git a/guicast/bctexture.C b/guicast/bctexture.C new file mode 100644 index 00000000..36767cb9 --- /dev/null +++ b/guicast/bctexture.C @@ -0,0 +1,227 @@ +#define GL_GLEXT_PROTOTYPES + +#include "bcsynchronous.h" +#include "bctexture.h" +#include "bcwindowbase.h" +#include "colormodels.h" + + +BC_Texture::BC_Texture(int w, int h, int colormodel) +{ + this->w = w; + this->h = h; + this->colormodel = colormodel; + texture_id = -1; + texture_id = -1; + texture_w = 0; + texture_h = 0; + texture_components = 0; + window_id = -1; + create_texture(w, h, colormodel); +} + + +BC_Texture::~BC_Texture() +{ + clear_objects(); +} + +void BC_Texture::clear_objects() +{ + if(get_texture_id() >= 0) + { +// printf("VFrame::clear_objects %p window_id=%d texture_id=%d w=%d h=%d\n", +// this, window_id, texture_id, texture_w, texture_h); + BC_WindowBase::get_synchronous()->release_texture( + window_id, + texture_id); + texture_id = -1; + } +} + +void BC_Texture::new_texture(BC_Texture **texture, + int w, + int h, + int colormodel) +{ + if(!(*texture)) + { + (*texture) = new BC_Texture(w, h, colormodel); + } + else + { + (*texture)->create_texture(w, h, colormodel); + } +} + +void BC_Texture::create_texture(int w, int h, int colormodel) +{ +#ifdef HAVE_GL + +// Get max texture size from the server. +// Maximum size was 4096 on the earliest cards that could do video. + int max_texture_size = 0; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + +// Calculate dimensions of texture + int new_w = calculate_texture_size(w, &max_texture_size); + int new_h = calculate_texture_size(h, &max_texture_size); + int new_components = cmodel_components(colormodel); + + + if(new_w < w || new_h < h) + { + printf("BC_Texture::create_texture frame size %dx%d bigger than maximum texture %dx%d.\n", + w, + h, + max_texture_size, + max_texture_size); + } + +// Delete existing texture + if(texture_id >= 0 && + (new_h != texture_h || + new_w != texture_w || + new_components != texture_components || + BC_WindowBase::get_synchronous()->current_window->get_id() != window_id)) + { +// printf("BC_Texture::create_texture released window_id=%d texture_id=%d\n", +// BC_WindowBase::get_synchronous()->current_window->get_id(), +// texture_id); + BC_WindowBase::get_synchronous()->release_texture( + window_id, + texture_id); + texture_id = -1; + window_id = -1; + } + + + texture_w = new_w; + texture_h = new_h; + texture_components = new_components; + +// Get matching texture + if(texture_id < 0) + { + texture_id = BC_WindowBase::get_synchronous()->get_texture( + texture_w, + texture_h, + texture_components); +// A new VFrame has no window_id, so it must read it from the matching texture. + if(texture_id >= 0) + window_id = BC_WindowBase::get_synchronous()->current_window->get_id(); + } + + +// No matching texture exists. +// Create new texture with the proper dimensions + if(texture_id < 0) + { + glGenTextures(1, (GLuint*)&texture_id); + glBindTexture(GL_TEXTURE_2D, (GLuint)texture_id); + glEnable(GL_TEXTURE_2D); + if(texture_components == 4) + glTexImage2D(GL_TEXTURE_2D, + 0, + 4, + texture_w, + texture_h, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + 0); + else + glTexImage2D(GL_TEXTURE_2D, + 0, + 3, + texture_w, + texture_h, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + 0); + + window_id = BC_WindowBase::get_synchronous()->current_window->get_id(); + BC_WindowBase::get_synchronous()->put_texture(texture_id, + texture_w, + texture_h, + texture_components); +// printf("VFrame::new_texture created texture_id=%d window_id=%d\n", +// *texture_id, +// *window_id); + } + else + { + glBindTexture(GL_TEXTURE_2D, (GLuint)texture_id); + glEnable(GL_TEXTURE_2D); + } +#endif +} + +int BC_Texture::calculate_texture_size(int w, int *max) +{ + int i; + for(i = 2; (max && i <= *max) || (!max && i < w); i *= 2) + { + if(i >= w) + { + return i; + break; + } + } + if(max && i > *max) return 16; + return i; +} + +int BC_Texture::get_texture_id() +{ + return texture_id; +} + +int BC_Texture::get_texture_w() +{ + return texture_w; +} + +int BC_Texture::get_texture_h() +{ + return texture_h; +} + +int BC_Texture::get_texture_components() +{ + return texture_components; +} + +int BC_Texture::get_window_id() +{ + return window_id; +} + + +void BC_Texture::bind(int texture_unit) +{ +#ifdef HAVE_GL +// Bind the texture + if(texture_id >= 0) + { + if(texture_unit >= 0) glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_2D, texture_id); + glEnable(GL_TEXTURE_2D); + if(texture_unit >= 0) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + +// GL_REPEAT in this case causes the upper left corners of the masks +// to blur. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + +// Get the texture to alpha blend + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + } +#endif +} + diff --git a/guicast/bctexture.h b/guicast/bctexture.h new file mode 100644 index 00000000..7b60b2bf --- /dev/null +++ b/guicast/bctexture.h @@ -0,0 +1,64 @@ +#ifndef BCTEXTURE_H +#define BCTEXTURE_H + + +#include "vframe.inc" + + +// Container for texture objects + +class BC_Texture +{ +public: + BC_Texture(int w, int h, int colormodel); + ~BC_Texture(); + + friend class VFrame; + +// Create a new texture if *texture if 0 +// or update the existing texture if *texture is +// nonzero. The created texture object is stored in *texture. +// The texture parameters are stored in the texture manager. +// The user must delete *texture when finished with it. +// The texture is bound to the current texture unit and enabled. +// Must be called from a synchronous opengl thread after enable_opengl. + static void new_texture(BC_Texture **texture, + int w, + int h, + int colormodel); + +// Bind the frame's texture to GL_TEXTURE_2D and enable it. +// If a texture_unit is supplied, the texture unit is made active +// and the commands are run in the right sequence to +// initialize it to our preferred specifications. +// The texture unit initialization requires the texture to be bound. + void bind(int texture_unit = -1); + +// Calculate the power of 2 size for allocating textures + static int calculate_texture_size(int w, int *max = 0); + int get_texture_id(); + int get_texture_w(); + int get_texture_h(); + int get_texture_components(); + int get_window_id(); + +private: + void clear_objects(); + +// creates a new texture or updates an existing texture to work with the +// current window. + void create_texture(int w, int h, int colormodel); + + + int window_id; + int texture_id; + int texture_w; + int texture_h; + int texture_components; + int colormodel; + int w; + int h; +}; + + +#endif diff --git a/guicast/bctexture.inc b/guicast/bctexture.inc new file mode 100644 index 00000000..f5afbf55 --- /dev/null +++ b/guicast/bctexture.inc @@ -0,0 +1,8 @@ +#ifndef BCTEXTURE_INC +#define BCTEXTURE_INC + + +class BC_Texture; + + +#endif diff --git a/guicast/bcwindow3d.C b/guicast/bcwindow3d.C new file mode 100644 index 00000000..73a2ee91 --- /dev/null +++ b/guicast/bcwindow3d.C @@ -0,0 +1,79 @@ +#define GL_GLEXT_PROTOTYPES +#include "bcpixmap.h" +#include "bcresources.h" +#include "bcsignals.h" +#include "bcsynchronous.h" +#include "bcwindowbase.h" + +// OpenGL functions in BC_WindowBase + +void BC_WindowBase::enable_opengl() +{ +#ifdef HAVE_GL + XVisualInfo viproto; + XVisualInfo *visinfo; + int nvi; + + top_level->sync_display(); + + get_synchronous()->is_pbuffer = 0; + if(!gl_win_context) + { + viproto.screen = top_level->screen; + visinfo = XGetVisualInfo(top_level->display, + VisualScreenMask, + &viproto, + &nvi); + + gl_win_context = glXCreateContext(top_level->display, + visinfo, + 0, + 1); + } + + +// if(video_is_on()) +// { +// Make the front buffer's context current. Pixmaps don't work. + get_synchronous()->current_window = this; + glXMakeCurrent(top_level->display, + win, + gl_win_context); +// } +// else +// { +// get_synchronous()->current_window = this; +// pixmap->enable_opengl(); +// } + +#endif +} + +void BC_WindowBase::disable_opengl() +{ +#ifdef HAVE_GL +// unsigned long valuemask = CWEventMask; +// XSetWindowAttributes attributes; +// attributes.event_mask = DEFAULT_EVENT_MASKS; +// XChangeWindowAttributes(top_level->display, win, valuemask, &attributes); +#endif +} + +void BC_WindowBase::flip_opengl() +{ +#ifdef HAVE_GL + glXSwapBuffers(top_level->display, win); + glFlush(); +#endif +} + + + + + + + + + + + diff --git a/guicast/bcwindowbase.C b/guicast/bcwindowbase.C index d02bdcb6..50eb7923 100644 --- a/guicast/bcwindowbase.C +++ b/guicast/bcwindowbase.C @@ -9,6 +9,7 @@ #include "bcresources.h" #include "bcsignals.h" #include "bcsubwindow.h" +#include "bcsynchronous.h" #include "bctimer.h" #include "bcwidgetgrid.h" #include "bcwindowbase.h" @@ -50,7 +51,6 @@ BC_ResizeCall::BC_ResizeCall(int w, int h) -Mutex BC_WindowBase::opengl_lock; BC_Resources BC_WindowBase::resources; @@ -84,16 +84,15 @@ BC_WindowBase::~BC_WindowBase() // Delete the subwindows is_deleting = 1; - if(subwindows) - { - while (subwindows->total) - { - // NOTE: since the value is itself a subwindow, the - // recursion removes the item from subwindows - delete subwindows->values[0]; - } - delete subwindows; - } + if(subwindows) + { + while(subwindows->total) + { +// Subwindow removes its own pointer + delete subwindows->values[0]; + } + delete subwindows; + } if(widgetgrids) { @@ -106,9 +105,13 @@ BC_WindowBase::~BC_WindowBase() } - delete pixmap; + delete pixmap; - XDestroyWindow(top_level->display, win); +// Destroyed in synchronous thread if gl context exists. +#ifdef HAVE_GL + if(!gl_win_context || !get_resources()->get_synchronous()) +#endif + XDestroyWindow(top_level->display, win); if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap; if(icon_pixmap) delete icon_pixmap; @@ -130,9 +133,12 @@ BC_WindowBase::~BC_WindowBase() XftFontClose (display, (XftFont*)smallfont_xft); #endif flush(); -// Can't close display if another thread is waiting for events - XCloseDisplay(display); -// XCloseDisplay(event_display); +// Can't close display if another thread is waiting for events. +// Synchronous thread must delete display if gl_context exists. +#ifdef HAVE_GL + if(!gl_win_context || !get_resources()->get_synchronous()) +#endif + XCloseDisplay(display); clipboard->stop_clipboard(); delete clipboard; } @@ -141,6 +147,18 @@ BC_WindowBase::~BC_WindowBase() flush(); } +// Must be last reference to display. +// This only works if it's a MAIN_WINDOW since the display deletion for +// a subwindow is not determined by the subwindow. +#ifdef HAVE_GL + if(gl_win_context && get_resources()->get_synchronous()) + { + printf("BC_WindowBase::~BC_WindowBase window deleted but opengl deletion is not\n" + "implemented for BC_Pixmap.\n"); + get_resources()->get_synchronous()->delete_window(this); + } +#endif + resize_history.remove_all_objects(); common_events.remove_all_objects(); delete event_lock; @@ -216,6 +234,9 @@ int BC_WindowBase::initialize() event_condition = new Condition(0, "BC_WindowBase::event_condition"); cursor_timer = new Timer; event_thread = 0; +#ifdef HAVE_GL + gl_win_context = 0; +#endif return 0; } @@ -254,6 +275,8 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, int vm; #endif + id = get_resources()->get_id(); + if(parent_window) top_level = parent_window->top_level; #ifdef HAVE_LIBXXF86VM @@ -443,7 +466,7 @@ int BC_WindowBase::create_window(BC_WindowBase *parent_window, if(top_level->is_hourglass) attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR); else - attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR); + attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR); attr.override_redirect = True; attr.save_under = True; @@ -574,6 +597,16 @@ Display* BC_WindowBase::init_display(char *display_name) return display; } +Display* BC_WindowBase::get_display() +{ + return top_level->display; +} + +int BC_WindowBase::get_screen() +{ + return top_level->screen; +} + int BC_WindowBase::run_window() { done = 0; @@ -1731,15 +1764,15 @@ int BC_WindowBase::init_fonts() { if((largefont = XLoadQueryFont(display, _(resources.large_font))) == NULL) if((largefont = XLoadQueryFont(display, _(resources.large_font2))) == NULL) - largefont = XLoadQueryFont(display, "fixed"); + largefont = XLoadQueryFont(display, "fixed"); if((mediumfont = XLoadQueryFont(display, _(resources.medium_font))) == NULL) if((mediumfont = XLoadQueryFont(display, _(resources.medium_font2))) == NULL) - mediumfont = XLoadQueryFont(display, "fixed"); + mediumfont = XLoadQueryFont(display, "fixed"); if((smallfont = XLoadQueryFont(display, _(resources.small_font))) == NULL) if((smallfont = XLoadQueryFont(display, _(resources.small_font2))) == NULL) - smallfont = XLoadQueryFont(display, "fixed"); + smallfont = XLoadQueryFont(display, "fixed"); #ifdef HAVE_XFT if(get_resources()->use_xft) @@ -2296,7 +2329,7 @@ int BC_WindowBase::get_text_ascent(int font) (FcChar8*)"O", 1, &extents); - return extents.y; + return extents.y + 2; } else #endif @@ -2764,6 +2797,11 @@ BC_Resources* BC_WindowBase::get_resources() return &BC_WindowBase::resources; } +BC_Synchronous* BC_WindowBase::get_synchronous() +{ + return BC_WindowBase::resources.get_synchronous(); +} + int BC_WindowBase::get_bg_color() { return bg_color; @@ -3469,88 +3507,22 @@ void BC_WindowBase::restore_vm() #endif -#ifdef HAVE_GL -extern "C" -{ - GLXContext glXCreateContext(Display *dpy, - XVisualInfo *vis, - GLXContext shareList, - int direct); - - int glXMakeCurrent(Display *dpy, - Drawable drawable, - GLXContext ctx); - void glXSwapBuffers(Display *dpy, - Drawable drawable); -}; -void BC_WindowBase::enable_opengl() -{ - lock_window("BC_WindowBase::enable_opengl"); - opengl_lock.lock(); - XVisualInfo viproto; - XVisualInfo *visinfo; - int nvi; - viproto.screen = top_level->screen; - visinfo = XGetVisualInfo(top_level->display, - VisualScreenMask, - &viproto, - &nvi); - gl_context = glXCreateContext(top_level->display, - visinfo, - 0, - 1); - glXMakeCurrent(top_level->display, - win, - gl_context); -// Need expose events for 3D - unsigned long valuemask = CWEventMask; - XSetWindowAttributes attributes; - attributes.event_mask = DEFAULT_EVENT_MASKS | - ExposureMask; - XChangeWindowAttributes(top_level->display, win, valuemask, &attributes); - opengl_lock.unlock(); - unlock_window(); -} -void BC_WindowBase::disable_opengl() -{ - unsigned long valuemask = CWEventMask; - XSetWindowAttributes attributes; - attributes.event_mask = DEFAULT_EVENT_MASKS; - XChangeWindowAttributes(top_level->display, win, valuemask, &attributes); -} -void BC_WindowBase::lock_opengl() -{ - lock_window("BC_WindowBase::lock_opengl"); - opengl_lock.lock(); - glXMakeCurrent(top_level->display, - win, - gl_context); -} -void BC_WindowBase::unlock_opengl() -{ - opengl_lock.unlock(); - unlock_window(); -} -void BC_WindowBase::flip_opengl() -{ - glXSwapBuffers(top_level->display, win); -} -#endif + int BC_WindowBase::get_event_count() { @@ -3587,7 +3559,10 @@ void BC_WindowBase::put_event(XEvent *event) event_condition->unlock(); } - +int BC_WindowBase::get_id() +{ + return id; +} diff --git a/guicast/bcwindowbase.h b/guicast/bcwindowbase.h index 77640732..eeecf27b 100644 --- a/guicast/bcwindowbase.h +++ b/guicast/bcwindowbase.h @@ -29,6 +29,7 @@ #include "bcmenubar.inc" #include "bcmeter.inc" #include "bcpan.inc" +#include "bcpbuffer.inc" #include "bcpixmap.inc" #include "bcpopup.inc" #include "bcpopupmenu.inc" @@ -40,6 +41,7 @@ #include "bcscrollbar.inc" #include "bcslider.inc" #include "bcsubwindow.inc" +#include "bcsynchronous.inc" #include "bctextbox.inc" #include "bctimer.inc" #include "bctitle.inc" @@ -71,7 +73,13 @@ #include #endif #ifdef HAVE_GL -typedef void* GLXContext; +#include +#endif + + + +#ifdef HAVE_GL +//typedef void* GLXContext; #endif class BC_ResizeCall @@ -105,6 +113,7 @@ public: friend class BC_MenuPopup; friend class BC_Meter; friend class BC_Pan; + friend class BC_PBuffer; friend class BC_Pixmap; friend class BC_PixmapSW; friend class BC_Popup; @@ -116,6 +125,7 @@ public: friend class BC_ScrollBar; friend class BC_Slider; friend class BC_SubWindow; + friend class BC_Synchronous; friend class BC_TextBox; friend class BC_Title; friend class BC_Toggle; @@ -160,10 +170,19 @@ public: // Shouldn't deference a pointer to delete a window if a parent is // currently being deleted. This returns 1 if any parent is being deleted. int get_deleting(); + + + +//============================= OpenGL functions =============================== +// OpenGL functions must be called from inside a BC_Synchronous command. +// Create openGL context and bind it to the current window. +// If it's called inside start_video/stop_video, the context is bound to the window. +// If it's called outside start_video/stop_video, the context is bound to the pixmap. +// Must be called at the beginning of any opengl routine to make sure +// the context is current. +// No locking is performed. void enable_opengl(); void disable_opengl(); - void lock_opengl(); - void unlock_opengl(); void flip_opengl(); int flash(int x, int y, int w, int h, int flush = 1); @@ -182,6 +201,9 @@ public: static BC_Resources* get_resources(); +// User must create synchronous object first + static BC_Synchronous* get_synchronous(); + // Dimensions virtual int get_w(); virtual int get_h(); @@ -393,6 +415,7 @@ public: char* get_title(); void start_video(); void stop_video(); + int get_id(); void set_done(int return_value); // Get a bitmap to draw on the window with BC_Bitmap* new_bitmap(int w, int h, int color_model = -1); @@ -496,6 +519,9 @@ private: int group_it); static Display* init_display(char *display_name); +// Get display from top level + Display* get_display(); + int get_screen(); virtual int initialize(); int get_atoms(); void init_cursors(); @@ -698,9 +724,10 @@ private: Display *event_display; Window win; #ifdef HAVE_GL - GLXContext gl_context; +// The first context to be created and the one whose texture id +// space is shared with the other contexts. + GLXContext gl_win_context; #endif - static Mutex opengl_lock; int window_lock; GC gc; // Depth given by the X Server @@ -765,6 +792,8 @@ private: int is_deleting; // Hide cursor when video is enabled Timer *cursor_timer; +// unique ID of window. + int id; }; diff --git a/guicast/condition.C b/guicast/condition.C index 7254292e..d3c8b3f0 100644 --- a/guicast/condition.C +++ b/guicast/condition.C @@ -7,8 +7,9 @@ #include #include -Condition::Condition(int init_value, char *title) +Condition::Condition(int init_value, char *title, int is_binary) { + this->is_binary = is_binary; this->title = title; pthread_mutex_init(&mutex, 0); pthread_cond_init(&cond, NULL); @@ -41,19 +42,26 @@ void Condition::lock(char *location) pthread_mutex_lock(&mutex); while(value <= 0) pthread_cond_wait(&cond, &mutex); #ifndef NO_GUICAST - SET_LOCK2 + UNSET_LOCK2 #endif - value--; + if(is_binary) + value = 0; + else + value--; pthread_mutex_unlock(&mutex); } void Condition::unlock() { -#ifndef NO_GUICAST - UNSET_LOCK(this); -#endif +// The lock trace is created and removed by the acquirer +//#ifndef NO_GUICAST +// UNSET_LOCK(this); +//#endif pthread_mutex_lock(&mutex); - value++; + if(is_binary) + value = 1; + else + value++; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } @@ -89,9 +97,12 @@ int Condition::timed_lock(int microseconds, char *location) { //printf("Condition::timed_lock 2 %s %s\n", title, location); #ifndef NO_GUICAST - SET_LOCK2 + UNSET_LOCK2 #endif - value--; + if(is_binary) + value = 0; + else + value--; result = 0; } pthread_mutex_unlock(&mutex); diff --git a/guicast/condition.h b/guicast/condition.h index a62793b2..6b692f21 100644 --- a/guicast/condition.h +++ b/guicast/condition.h @@ -6,7 +6,7 @@ class Condition { public: - Condition(int init_value = 0, char *title = 0); + Condition(int init_value = 0, char *title = 0, int is_binary = 0); ~Condition(); @@ -18,6 +18,7 @@ public: void unlock(); // Block for requested duration if value <= 0. // value is decreased whether or not the condition is unlocked in time +// Returns 1 if timeout or 0 if success. int timed_lock(int microseconds, char *location = 0); int get_value(); @@ -25,6 +26,7 @@ public: pthread_mutex_t mutex; int value; int init_value; + int is_binary; char *title; }; diff --git a/guicast/guicast.h b/guicast/guicast.h index eb00577a..5a888d74 100644 --- a/guicast/guicast.h +++ b/guicast/guicast.h @@ -1,6 +1,8 @@ #ifndef GUICAST_H #define GUICAST_H +#define GL_GLEXT_PROTOTYPES + #include "bcbar.h" #include "bcbitmap.h" #include "bcbutton.h" @@ -23,7 +25,9 @@ #include "bcscrollbar.h" #include "bcslider.h" #include "bcsubwindow.h" +#include "bcsynchronous.h" #include "bctextbox.h" +#include "bctexture.h" #include "bctheme.h" #include "bctitle.h" #include "bctoggle.h" diff --git a/guicast/vframe.C b/guicast/vframe.C index ebf735f3..1fce60f5 100644 --- a/guicast/vframe.C +++ b/guicast/vframe.C @@ -4,6 +4,7 @@ #include //#include "bccounter.h" +#include "bcpbuffer.h" #include "bcsignals.h" #include "clip.h" #include "colormodels.h" @@ -36,16 +37,14 @@ public: VFrame::VFrame(unsigned char *png_data) { -//printf("VFrame::VFrame 1\n"); - reset_parameters(); -//printf("VFrame::VFrame 1\n"); + reset_parameters(1); read_png(png_data); //printf("VFrame::VFrame 2\n"); } VFrame::VFrame(VFrame &frame) { - reset_parameters(); + reset_parameters(1); allocate_data(0, 0, 0, 0, frame.w, frame.h, frame.color_model, frame.bytes_per_line); memcpy(data, frame.data, bytes_per_line * h); } @@ -56,7 +55,7 @@ VFrame::VFrame(unsigned char *data, int color_model, long bytes_per_line) { - reset_parameters(); + reset_parameters(1); allocate_data(data, 0, 0, 0, w, h, color_model, bytes_per_line); } @@ -69,7 +68,7 @@ VFrame::VFrame(unsigned char *data, int color_model, long bytes_per_line) { - reset_parameters(); + reset_parameters(1); allocate_data(data, y_offset, u_offset, @@ -82,7 +81,7 @@ VFrame::VFrame(unsigned char *data, VFrame::VFrame() { - reset_parameters(); + reset_parameters(1); this->color_model = BC_COMPRESSED; } @@ -98,7 +97,7 @@ VFrame::VFrame() VFrame::~VFrame() { - clear_objects(); + clear_objects(1); // Delete effect stack prev_effects.remove_all_objects(); next_effects.remove_all_objects(); @@ -131,7 +130,7 @@ int VFrame::params_match(int w, int h, int color_model) } -int VFrame::reset_parameters() +int VFrame::reset_parameters(int do_opengl) { field2_offset = -1; shared = 0; @@ -151,13 +150,27 @@ int VFrame::reset_parameters() sequence_number = -1; is_keyframe = 0; + if(do_opengl) + { +// By default, anything is going to be done in RAM + opengl_state = VFrame::RAM; + pbuffer = 0; + } + prev_effects.set_array_delete(); next_effects.set_array_delete(); return 0; } -int VFrame::clear_objects() +int VFrame::clear_objects(int do_opengl) { +// Remove texture + if(do_opengl) + { + delete pbuffer; + pbuffer = 0; + } + // Delete data if(!shared) { @@ -345,7 +358,7 @@ void VFrame::set_compressed_memory(unsigned char *data, int data_size, int data_allocated) { - clear_objects(); + clear_objects(0); shared = 1; this->data = data; this->compressed_allocated = data_allocated; @@ -363,8 +376,8 @@ int VFrame::reallocate(unsigned char *data, int color_model, long bytes_per_line) { - clear_objects(); - reset_parameters(); + clear_objects(0); + reset_parameters(0); allocate_data(data, y_offset, u_offset, @@ -578,7 +591,7 @@ void VFrame::rotate90() } // Swap frames - clear_objects(); + clear_objects(0); data = new_data; rows = new_rows; bytes_per_line = new_bytes_per_line; @@ -609,7 +622,7 @@ void VFrame::rotate270() } // Swap frames - clear_objects(); + clear_objects(0); data = new_data; rows = new_rows; bytes_per_line = new_bytes_per_line; @@ -836,5 +849,31 @@ void VFrame::pop_next_effect() next_effects.remove_object(next_effects.last()); } +void VFrame::clear_stacks() +{ + next_effects.remove_all_objects(); + prev_effects.remove_all_objects(); +} + + +void VFrame::copy_stacks(VFrame *src) +{ + clear_stacks(); + + for(int i = 0; i < src->next_effects.total; i++) + { + char *ptr; + next_effects.append(ptr = new char[strlen(src->next_effects.values[i]) + 1]); + strcpy(ptr, src->next_effects.values[i]); + } + for(int i = 0; i < src->prev_effects.total; i++) + { + char *ptr; + prev_effects.append(ptr = new char[strlen(src->prev_effects.values[i]) + 1]); + strcpy(ptr, src->prev_effects.values[i]); + } +} + + diff --git a/guicast/vframe.h b/guicast/vframe.h index 14db0d5d..1344faef 100644 --- a/guicast/vframe.h +++ b/guicast/vframe.h @@ -2,6 +2,8 @@ #define VFRAME_H #include "arraylist.h" +#include "bcpbuffer.inc" +#include "bctexture.inc" #include "colormodels.h" #include "vframe.inc" @@ -131,6 +133,104 @@ public: int out_x1, int out_y1); +// If the opengl state is RAM, transfer image from RAM to the texture +// referenced by this frame. +// If the opengl state is TEXTURE, do nothing. +// If the opengl state is SCREEN, switch the current drawable to the pbuffer and +// transfer the image to the texture with screen_to_texture. +// The opengl state is changed to TEXTURE. +// If no textures exist, textures are created. +// If the textures already exist, they are reused. +// Textures are resized to match the current dimensions. +// Must be called from a synchronous opengl thread after enable_opengl. + void to_texture(); + + +// Transfer contents of current pbuffer to texture, +// creating a new texture if necessary. +// Coordinates are the coordinates in the drawable to copy. + void screen_to_texture(int x = -1, + int y = -1, + int w = -1, + int h = -1); + +// Transfer contents of texture to the current drawable. +// Just calls the vertex functions but doesn't initialize. +// The coordinates are relative to the VFrame size and flipped to make +// the texture upright. +// The default coordinates are the size of the VFrame. +// flip_y flips the texture in the vertical direction and only used when +// writing to the final surface. + void draw_texture(float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + int flip_y = 0); +// Draw the texture using the frame's size as the input and output coordinates. + void draw_texture(int flip_y = 0); + + + +// ================================ OpenGL functions =========================== +// Location of working image if OpenGL playback + int get_opengl_state(); + void set_opengl_state(int value); +// OpenGL states + enum + { +// Undefined + UNKNOWN, +// OpenGL image is in RAM + RAM, +// OpenGL image is in texture + TEXTURE, +// OpenGL image is composited in PBuffer or back buffer + SCREEN + }; + +// Texture ID + int get_texture_id(); + int get_texture_w(); + int get_texture_h(); + int get_texture_components(); + + +// Binds the opengl context to this frame's PBuffer + void enable_opengl(); + +// Clears the pbuffer with the right values depending on YUV + void clear_pbuffer(); + + +// Bind the frame's texture to GL_TEXTURE_2D and enable it. +// If a texture_unit is supplied, the texture unit is made active +// and the commands are run in the right sequence to +// initialize it to our preferred specifications. + void bind_texture(int texture_unit = -1); + + + +// Create a frustum with 0,0 in the upper left and w,-h in the bottom right. +// Set preferred opengl settings. + static void init_screen(int w, int h); +// Calls init_screen with the current frame's dimensions. + void init_screen(); + +// Compiles and links the shaders into a program. +// Adds the program with put_shader. +// Returns the program handle. +// Requires a null terminated argument list of shaders to link together. +// At least one shader argument must have a main() function. make_shader +// replaces all the main() functions with unique functions and calls them in +// sequence, so multiple independant shaders can be linked. +// x is a placeholder for va_arg and should be 0. + static unsigned int make_shader(int x, ...); + static void dump_shader(int shader_id); + // Because OpenGL is faster if multiple effects are combined, we need // to provide ways for effects to aggregate. // The prev_effect is the object providing the data to read_frame. @@ -143,9 +243,30 @@ public: void push_next_effect(char *name); void pop_next_effect(); + +// Copy stacks and params from another frame +// Replaces the stacks with the src stacks but only updates the params. + void copy_stacks(VFrame *src); + +// This clears the stacks and the param table + void clear_stacks(); + + private: - int clear_objects(); - int reset_parameters(); + +// Create a PBuffer matching this frame's dimensions and to be +// referenced by this frame. Does nothing if the pbuffer already exists. +// If the frame is resized, the PBuffer is deleted. +// Called by enable_opengl. +// This allows PBuffers, textures, and bitmaps to travel through the entire +// rendering chain without requiring the user to manage a lot of objects. +// Must be called from a synchronous opengl thread after enable_opengl. + void create_pbuffer(); + + + + int clear_objects(int do_opengl); + int reset_parameters(int do_opengl); void create_row_pointers(); int allocate_data(unsigned char *data, long y_offset, @@ -187,7 +308,16 @@ private: long image_size; // For writing discontinuous frames in background rendering long sequence_number; + +// OpenGL support int is_keyframe; +// State of the current texture + BC_Texture *texture; +// State of the current PBuffer + BC_PBuffer *pbuffer; + +// Location of working image if OpenGL playback + int opengl_state; ArrayList prev_effects; ArrayList next_effects; diff --git a/guicast/vframe3d.C b/guicast/vframe3d.C new file mode 100644 index 00000000..f9bbb7f3 --- /dev/null +++ b/guicast/vframe3d.C @@ -0,0 +1,515 @@ +#define GL_GLEXT_PROTOTYPES + +#include "bcpbuffer.h" +#include "bcresources.h" +#include "bcsignals.h" +#include "bcsynchronous.h" +#include "bctexture.h" +#include "bcwindowbase.h" +#include "vframe.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#ifdef HAVE_GL +#include +#include +#endif + +#include +#include +#include +#include + +int VFrame::get_opengl_state() +{ + return opengl_state; +} + +void VFrame::set_opengl_state(int value) +{ + opengl_state = value; +} + + +int VFrame::get_texture_id() +{ + return texture ? texture->texture_id : -1; +} + +int VFrame::get_texture_w() +{ + return texture ? texture->texture_w : 0; +} + +int VFrame::get_texture_h() +{ + return texture ? texture->texture_h : 0; +} + + + + + + + + + + + + +void VFrame::to_texture() +{ +#ifdef HAVE_GL + +// Must be here so user can create textures without copying data by setting +// opengl_state to TEXTURE. + BC_Texture::new_texture(&texture, + get_w(), + get_h(), + get_color_model()); + +// Determine what to do based on state + switch(opengl_state) + { + case VFrame::TEXTURE: + return; + + case VFrame::SCREEN: + if((get_w() % 4) || (get_h() % 4)) + { + printf("VFrame::to_texture w=%d h=%d\n", get_w(), get_h()); + return; + } + if(pbuffer) + { + enable_opengl(); + screen_to_texture(); + } + opengl_state = VFrame::TEXTURE; + return; + } + +//printf("VFrame::to_texture %d\n", texture_id); + + switch(color_model) + { + case BC_RGB888: + case BC_YUV888: + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + get_w(), + get_h(), + GL_RGB, + GL_UNSIGNED_BYTE, + get_rows()[0]); + break; + + case BC_RGBA8888: + case BC_YUVA8888: + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + get_w(), + get_h(), + GL_RGBA, + GL_UNSIGNED_BYTE, + get_rows()[0]); + break; + + case BC_RGB_FLOAT: + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + get_w(), + get_h(), + GL_RGB, + GL_FLOAT, + get_rows()[0]); + break; + + case BC_RGBA_FLOAT: + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + get_w(), + get_h(), + GL_RGBA, + GL_FLOAT, + get_rows()[0]); + break; + + default: + fprintf(stderr, + "VFrame::to_texture: unsupported color model %d.\n", + color_model); + break; + } + + opengl_state = VFrame::TEXTURE; +#endif +} + + +void VFrame::create_pbuffer() +{ +SET_TRACE + if(pbuffer && + pbuffer->window_id != BC_WindowBase::get_synchronous()->current_window->get_id()) + { +SET_TRACE + delete pbuffer; +SET_TRACE + pbuffer = 0; + } + + if((get_w() % 4) || (get_h() % 4)) + { + printf("VFrame::create_pbuffer w=%d h=%d\n", get_w(), get_h()); + return; + } + +SET_TRACE + if(!pbuffer) + { + pbuffer = new BC_PBuffer(get_w(), get_h()); + } +SET_TRACE +} + +void VFrame::enable_opengl() +{ + create_pbuffer(); + if(pbuffer) + { + pbuffer->enable_opengl(); + } +} + + + +void VFrame::screen_to_texture(int x, int y, int w, int h) +{ +#ifdef HAVE_GL +// Create texture + BC_Texture::new_texture(&texture, + get_w(), + get_h(), + get_color_model()); + + if(pbuffer) + { + glEnable(GL_TEXTURE_2D); + +// Read canvas into texture +// According to the man page, it must be GL_BACK for the onscreen buffer +// and GL_FRONT for a single buffered PBuffer. In reality it must be +// GL_BACK for a single buffered PBuffer if the PBuffer has alpha and using +// GL_FRONT captures the onscreen front buffer. +// glReadBuffer(BC_WindowBase::get_synchronous()->is_pbuffer ? +// GL_FRONT : GL_BACK); + glReadBuffer(GL_BACK); + glCopyTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + x >= 0 ? x : 0, + y >= 0 ? y : 0, + w >= 0 ? w : get_w(), + h >= 0 ? h : get_h()); + } +#endif +} + +void VFrame::draw_texture(float in_x1, + float in_y1, + float in_x2, + float in_y2, + float out_x1, + float out_y1, + float out_x2, + float out_y2, + int flip_y) +{ +#ifdef HAVE_GL + glBegin(GL_QUADS); + glNormal3f(0, 0, 1.0); + + glTexCoord2f(in_x1 / get_texture_w(), in_y1 / get_texture_h()); + glVertex3f(out_x1, flip_y ? -out_y1 : -out_y2, 0); + + glTexCoord2f(in_x2 / get_texture_w(), in_y1 / get_texture_h()); + glVertex3f(out_x2, flip_y ? -out_y1 : -out_y2, 0); + + glTexCoord2f(in_x2 / get_texture_w(), in_y2 / get_texture_h()); + glVertex3f(out_x2, flip_y ? -out_y2 : -out_y1, 0); + + glTexCoord2f(in_x1 / get_texture_w(), in_y2 / get_texture_h()); + glVertex3f(out_x1, flip_y ? -out_y2 : -out_y1, 0); + + + glEnd(); + +#endif +} + +void VFrame::draw_texture(int flip_y) +{ + draw_texture(0, + 0, + get_w(), + get_h(), + 0, + 0, + get_w(), + get_h(), + flip_y); +} + + +void VFrame::bind_texture(int texture_unit) +{ +// Bind the texture + if(texture) + { + texture->bind(texture_unit); + } +} + + + + + + +void VFrame::init_screen(int w, int h) +{ +#ifdef HAVE_GL + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + float near = 1; + float far = 100; + float frustum_ratio = near / ((near + far) / 2); + float near_h = (float)h * + frustum_ratio; + float near_w = (float)w * + frustum_ratio; + glFrustum(-near_w / 2, + near_w / 2, + -near_h / 2, + near_h / 2, + near, + far); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +// Shift down and right so 0,0 is the top left corner + glTranslatef(-w / 2, h / 2, 0.0); + glTranslatef(0.0, 0.0, -(far + near) / 2); + + glDisable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); +// Default for direct copy playback + glDisable(GL_BLEND); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_CULL_FACE); + glEnable(GL_NORMALIZE); + glAlphaFunc(GL_GREATER, 0); + glDisable(GL_LIGHTING); + + const GLfloat one[] = { 1, 1, 1, 1 }; + const GLfloat zero[] = { 0, 0, 0, 0 }; + const GLfloat light_position[] = { 0, 0, -1, 0 }; + const GLfloat light_direction[] = { 0, 0, 1, 0 }; + +// glEnable(GL_LIGHT0); +// glLightfv(GL_LIGHT0, GL_AMBIENT, zero); +// glLightfv(GL_LIGHT0, GL_DIFFUSE, one); +// glLightfv(GL_LIGHT0, GL_SPECULAR, one); +// glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180); +// glLightfv(GL_LIGHT0, GL_POSITION, light_position); +// glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_direction); +// glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); +// glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, zero); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0); +#endif +} + +void VFrame::init_screen() +{ + init_screen(get_w(), get_h()); +} + +static int print_error(char *source, unsigned int object, int is_program) +{ +#ifdef HAVE_GL + char string[BCTEXTLEN]; + int len = 0; + if(is_program) + glGetProgramInfoLog(object, BCTEXTLEN, &len, string); + else + glGetShaderInfoLog(object, BCTEXTLEN, &len, string); + if(len > 0) printf("Playback3D::print_error:\n%s\n%s\n", source, string); + if(len > 0) return 1; + return 0; +#endif +} + + + + + +unsigned int VFrame::make_shader(int x, ...) +{ + unsigned int result = 0; +#ifdef HAVE_GL +// Construct single source file out of arguments + char *complete_program = 0; + int complete_size = 0; + int current_shader = 0; + + va_list list; + va_start(list, x); + + while(1) + { + char *text = va_arg(list, char*); + if(!text) break; + +SET_TRACE +// Replace one occurrance in each source of main() with a unique id. + char main_replacement[BCTEXTLEN]; +SET_TRACE + sprintf(main_replacement, "main%03d()", current_shader); +//printf("VFrame::make_shader %s %s\n", text, main_replacement); +SET_TRACE + char *source_replacement = new char[strlen(text) + strlen(main_replacement) + 1]; +SET_TRACE + char *ptr = strstr(text, "main()"); +SET_TRACE + + if(ptr) + { + memcpy(source_replacement, text, ptr - text); + source_replacement[ptr - text] = 0; + strcat(source_replacement, main_replacement); + ptr += strlen("main()"); + strcat(source_replacement, ptr); + current_shader++; + } + else + { + memcpy(source_replacement, text, strlen(text)); + source_replacement[strlen(text)] = 0; + } +SET_TRACE + + if(!complete_program) + { + complete_size = strlen(source_replacement) + 1; + complete_program = (char*)malloc(complete_size); + strcpy(complete_program, source_replacement); + } + else + { + complete_size += strlen(source_replacement); + complete_program = (char*)realloc(complete_program, complete_size); + strcat(complete_program, source_replacement); + } + + delete [] source_replacement; +SET_TRACE + } + +// Add main() function which calls all the unique main replacements in order + char main_function[BCTEXTLEN]; + sprintf(main_function, + "\n" + "void main()\n" + "{\n"); + + for(int i = 0; i < current_shader; i++) + { + char main_replacement[BCTEXTLEN]; + sprintf(main_replacement, "\tmain%03d();\n", i); + strcat(main_function, main_replacement); + } + + strcat(main_function, "}\n"); + if(!complete_program) + { + complete_size = strlen(main_function) + 1; + complete_program = (char*)malloc(complete_size); + strcpy(complete_program, main_function); + } + else + { + complete_size += strlen(main_function); + complete_program = (char*)realloc(complete_program, complete_size); + strcat(complete_program, main_function); + } + + + + + + int got_it = 0; + result = BC_WindowBase::get_synchronous()->get_shader(complete_program, + &got_it); + + if(!got_it) + { + result = glCreateProgram(); + + unsigned int shader; + shader = glCreateShader(GL_FRAGMENT_SHADER); + const GLchar *text_ptr = complete_program; + glShaderSource(shader, 1, &text_ptr, NULL); + glCompileShader(shader); + int error = print_error(complete_program, shader, 0); + glAttachShader(result, shader); + glDeleteShader(shader); + + glLinkProgram(result); + if(!error) error = print_error(complete_program, result, 1); + + +// printf("BC_WindowBase::make_shader: shader=%d window_id=%d\n", +// result, +// BC_WindowBase::get_synchronous()->current_window->get_id()); + BC_WindowBase::get_synchronous()->put_shader(result, complete_program); + } + +//printf("VFrame::make_shader\n%s\n", complete_program); + delete [] complete_program; + +#endif + return result; +} + +void VFrame::dump_shader(int shader_id) +{ + BC_WindowBase::get_synchronous()->dump_shader(shader_id); +} + + +void VFrame::clear_pbuffer() +{ +#ifdef HAVE_GL + if(cmodel_is_yuv(get_color_model())) + glClearColor(0.0, 0.5, 0.5, 0.0); + else + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#endif +} + -- 2.11.4.GIT