]> git.sesse.net Git - mlt/blobdiff - src/modules/opengl/filter_glsl_manager.cpp
Replace glFinish with OpenGL fences.
[mlt] / src / modules / opengl / filter_glsl_manager.cpp
index cd479b74a492b5c0ca2aec39fc62fd95ec225710..68ef63dc4f56a013fe47fe8d0f96324202e92c4b 100644 (file)
@@ -20,7 +20,7 @@
 
 #include <stdlib.h>
 #include <string>
-#include "glsl_manager.h"
+#include "filter_glsl_manager.h"
 #include <movit/init.h>
 #include <movit/util.h>
 #include <movit/effect_chain.h>
@@ -33,6 +33,14 @@ extern "C" {
 #include <framework/mlt_factory.h>
 }
 
+#if defined(__DARWIN__)
+#include <OpenGL/OpenGL.h>
+#elif defined(WIN32)
+#include <wingdi.h>
+#else
+#include <GL/glx.h>
+#endif
+
 void deleteManager(GlslManager *p)
 {
        delete p;
@@ -43,6 +51,7 @@ GlslManager::GlslManager()
        , pbo(0)
        , initEvent(0)
        , closeEvent(0)
+       , prev_sync(NULL)
 {
        mlt_filter filter = get_filter();
        if ( filter ) {
@@ -62,8 +71,15 @@ GlslManager::~GlslManager()
 {
        mlt_log_debug(get_service(), "%s\n", __FUNCTION__);
        cleanupContext();
+// XXX If there is still a frame holding a reference to a texture after this
+// destructor is called, then it will crash in release_texture().
+//     while (texture_list.peek_back())
+//             delete (glsl_texture) texture_list.pop_back();
        delete initEvent;
        delete closeEvent;
+       if (prev_sync != NULL) {
+               glDeleteSync( prev_sync );
+       }
 }
 
 GlslManager* GlslManager::get_instance()
@@ -73,10 +89,18 @@ GlslManager* GlslManager::get_instance()
 
 glsl_fbo GlslManager::get_fbo(int width, int height)
 {
+#if defined(__DARWIN__)
+       CGLContextObj context = CGLGetCurrentContext();
+#elif defined(WIN32)
+       HGLRC context = wglGetCurrentContext();
+#else
+       GLXContext context = glXGetCurrentContext();
+#endif
+
        lock();
        for (int i = 0; i < fbo_list.count(); ++i) {
                glsl_fbo fbo = (glsl_fbo) fbo_list.peek(i);
-               if (!fbo->used && (fbo->width == width) && (fbo->height == height)) {
+               if (!fbo->used && (fbo->width == width) && (fbo->height == height) && (fbo->context == context)) {
                        fbo->used = 1;
                        unlock();
                        return fbo;
@@ -98,6 +122,7 @@ glsl_fbo GlslManager::get_fbo(int width, int height)
        fbo->width = width;
        fbo->height = height;
        fbo->used = 1;
+       fbo->context = context;
        lock();
        fbo_list.push_back(fbo);
        unlock();
@@ -124,18 +149,31 @@ glsl_texture GlslManager::get_texture(int width, int height, GLint internal_form
                        return tex;
                }
        }
+
+       // Recycle a glsl_texture with deleted glTexture.
+       glsl_texture gtex = 0;
+       for (int i = 0; i < texture_list.count(); ++i) {
+               glsl_texture tex = (glsl_texture) texture_list.peek(i);
+               if (!tex->used && !tex->width && !tex->height) {
+                       gtex = tex;
+                       break;
+               }
+       }
        unlock();
 
        GLuint tex = 0;
        glGenTextures(1, &tex);
-       if (!tex)
+       if (!tex) {
+               glDeleteTextures(1, &tex);
                return NULL;
+       }
 
-       glsl_texture gtex = new glsl_texture_s;
        if (!gtex) {
-               glDeleteTextures(1, &tex);
-               return NULL;
+               gtex = new glsl_texture_s;
+               if (!gtex)
+                       return NULL;
        }
+
        glBindTexture( GL_TEXTURE_2D, tex );
        glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, internal_format, GL_UNSIGNED_BYTE, NULL );
     glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
@@ -160,6 +198,11 @@ void GlslManager::release_texture(glsl_texture texture)
        texture->used = 0;
 }
 
+void GlslManager::delete_sync(GLsync sync)
+{
+       glDeleteSync(sync);
+}
+
 glsl_pbo GlslManager::get_pbo(int size)
 {
        lock();
@@ -198,10 +241,12 @@ void GlslManager::cleanupContext()
                glDeleteFramebuffers(1, &fbo->fbo);
                delete fbo;
        }
-       while (texture_list.peek_back()) {
-               glsl_texture texture = (glsl_texture) texture_list.pop_back();
+       for (int i = 0; i < texture_list.count(); ++i) {
+               glsl_texture texture = (glsl_texture) texture_list.peek(i);
                glDeleteTextures(1, &texture->texture);
-               delete texture;
+               texture->used = 0;
+               texture->width = 0;
+               texture->height = 0;
        }
        if (pbo) {
                glDeleteBuffers(1, &pbo->pbo);
@@ -313,30 +358,30 @@ void GlslManager::reset_finalized( mlt_service service )
        mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_movit finalized", 0 );
 }
 
-Effect* GlslManager::get_effect( mlt_filter filter, mlt_frame frame )
+Effect* GlslManager::get_effect( mlt_service service, mlt_frame frame )
 {
        Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
-       char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" );
+       char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" );
        return (Effect*) GlslManager::get_instance()->effect_list( producer ).get_data( unique_id );
 }
 
-Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect )
+Effect* GlslManager::add_effect( mlt_service service, mlt_frame frame, Effect* effect )
 {
        Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
        EffectChain* chain = (EffectChain*) producer.get_data( "movit chain" );
        chain->add_effect( effect );
-       char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" );
+       char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" );
        GlslManager::get_instance()->effect_list( producer ).set( unique_id, effect, 0 );
        return effect;
 }
 
-Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect, Effect* input_b )
+Effect* GlslManager::add_effect( mlt_service service, mlt_frame frame, Effect* effect, Effect* input_b )
 {
        Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
        EffectChain* chain = (EffectChain*) producer.get_data( "movit chain" );
        chain->add_effect( effect, chain->last_added_effect(),
                input_b? input_b : chain->last_added_effect() );
-       char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" );
+       char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" );
        GlslManager::get_instance()->effect_list( producer ).set( unique_id, effect, 0 );
        return effect;
 }
@@ -372,9 +417,18 @@ int GlslManager::render_frame_texture(mlt_service service, mlt_frame frame, int
        glBindFramebuffer( GL_FRAMEBUFFER, 0 );
        check_error();
 
+       // Make sure we never have more than one frame pending at any time.
+       // This ensures we do not swamp the GPU with so much work
+       // that we cannot actually display the frames we generate.
+       if (prev_sync != NULL) {
+               glFlush();
+               glClientWaitSync( prev_sync, 0, GL_TIMEOUT_IGNORED );
+               glDeleteSync( prev_sync );
+       }
        render_fbo( service, chain, fbo->fbo, width, height );
+       prev_sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
+       GLsync sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
 
-       glFinish();
        check_error();
        glBindFramebuffer( GL_FRAMEBUFFER, 0 );
        check_error();
@@ -384,6 +438,8 @@ int GlslManager::render_frame_texture(mlt_service service, mlt_frame frame, int
        mlt_frame_set_image( frame, *image, 0, NULL );
        mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0,
                (mlt_destructor) GlslManager::release_texture, NULL );
+       mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.fence", sync, 0,
+               (mlt_destructor) GlslManager::delete_sync, NULL );
 
        return 0;
 }
@@ -422,6 +478,8 @@ int GlslManager::render_frame_rgba(mlt_service service, mlt_frame frame, int wid
        render_fbo( service, chain, fbo->fbo, width, height );
 
        // Read FBO into PBO
+       glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo );
+       check_error();
        glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo );
        check_error();
        glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ );