]> git.sesse.net Git - mlt/blobdiff - src/modules/opengl/filter_glsl_manager.cpp
Rename glsl_manager.h to filter_glsl_manager.h, to be consistent with the .cpp file.
[mlt] / src / modules / opengl / filter_glsl_manager.cpp
index 362e0f9e517abdbd370e49101baf6bdd54d1577e..35c6074e89e05a270ff5d54be72e224159bb02c7 100644 (file)
@@ -20,8 +20,9 @@
 
 #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>
 #include "mlt_movit_input.h"
 #include "mlt_flip_effect.h"
@@ -32,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;
@@ -61,6 +70,10 @@ 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;
 }
@@ -72,13 +85,25 @@ 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;
                }
        }
+       unlock();
+
        GLuint fb = 0;
        glGenFramebuffers(1, &fb);
        if (!fb)
@@ -93,7 +118,10 @@ 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();
        return fbo;
 }
 
@@ -104,6 +132,7 @@ void GlslManager::release_fbo(glsl_fbo fbo)
 
 glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format)
 {
+       lock();
        for (int i = 0; i < texture_list.count(); ++i) {
                glsl_texture tex = (glsl_texture) texture_list.peek(i);
                if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) {
@@ -112,19 +141,35 @@ glsl_texture GlslManager::get_texture(int width, int height, GLint internal_form
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                        glBindTexture( GL_TEXTURE_2D, 0);
                        tex->used = 1;
+                       unlock();
                        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 );
@@ -138,7 +183,9 @@ glsl_texture GlslManager::get_texture(int width, int height, GLint internal_form
        gtex->height = height;
        gtex->internal_format = internal_format;
        gtex->used = 1;
+       lock();
        texture_list.push_back(gtex);
+       unlock();
        return gtex;
 }
 
@@ -149,15 +196,19 @@ void GlslManager::release_texture(glsl_texture texture)
 
 glsl_pbo GlslManager::get_pbo(int size)
 {
+       lock();
        if (!pbo) {
                GLuint pb = 0;
                glGenBuffers(1, &pb);
-               if (!pb)
+               if (!pb) {
+                       unlock();
                        return NULL;
+               }
 
                pbo = new glsl_pbo_s;
                if (!pbo) {
                        glDeleteBuffers(1, &pb);
+                       unlock();
                        return NULL;
                }
                pbo->pbo = pb;
@@ -169,26 +220,31 @@ glsl_pbo GlslManager::get_pbo(int size)
                glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
                pbo->size = size;
        }
+       unlock();
        return pbo;
 }
 
 void GlslManager::cleanupContext()
 {
+       lock();
        while (fbo_list.peek_back()) {
                glsl_fbo fbo = (glsl_fbo) fbo_list.pop_back();
                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);
                delete pbo;
                pbo = 0;
        }
+       unlock();
 }
 
 void GlslManager::onInit( mlt_properties owner, GlslManager* filter )
@@ -321,7 +377,7 @@ Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* eff
        return effect;
 }
 
-void GlslManager::render( mlt_service service, void* chain, GLuint fbo, int width, int height )
+void GlslManager::render_fbo( mlt_service service, void* chain, GLuint fbo, int width, int height )
 {
        EffectChain* effect_chain = (EffectChain*) chain;
        mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
@@ -333,6 +389,116 @@ void GlslManager::render( mlt_service service, void* chain, GLuint fbo, int widt
        effect_chain->render_to_fbo( fbo, width, height );
 }
 
+int GlslManager::render_frame_texture(mlt_service service, mlt_frame frame, int width, int height, uint8_t **image)
+{
+       EffectChain* chain = get_chain( service );
+       if (!chain) return 1;
+       glsl_fbo fbo = get_fbo( width, height );
+       if (!fbo) return 1;
+       glsl_texture texture = get_texture( width, height, GL_RGBA );
+       if (!texture) {
+               release_fbo( fbo );
+               return 1;
+       }
+
+       glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo );
+       check_error();
+       glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 );
+       check_error();
+       glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+       check_error();
+
+       render_fbo( service, chain, fbo->fbo, width, height );
+
+       glFinish();
+       check_error();
+       glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+       check_error();
+       release_fbo( fbo );
+
+       *image = (uint8_t*) &texture->texture;
+       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 );
+
+       return 0;
+}
+
+int GlslManager::render_frame_rgba(mlt_service service, mlt_frame frame, int width, int height, uint8_t **image)
+{
+       EffectChain* chain = get_chain( service );
+       if (!chain) return 1;
+       glsl_fbo fbo = get_fbo( width, height );
+       if (!fbo) return 1;
+       glsl_texture texture = get_texture( width, height, GL_RGBA );
+       if (!texture) {
+               release_fbo( fbo );
+               return 1;
+       }
+
+       // Use a PBO to hold the data we read back with glReadPixels().
+       // (Intel/DRI goes into a slow path if we don't read to PBO.)
+       int img_size = width * height * 4;
+       glsl_pbo pbo = get_pbo( img_size );
+       if (!pbo) {
+               release_fbo( fbo );
+               release_texture(texture);
+               return 1;
+       }
+
+       // Set the FBO
+       check_error();
+       glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo );
+       check_error();
+       glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 );
+       check_error();
+       glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+       check_error();
+
+       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 );
+       check_error();
+       glReadPixels( 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) );
+       check_error();
+
+       // Copy from PBO
+       uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY );
+       check_error();
+       *image = (uint8_t*) mlt_pool_alloc( img_size );
+       mlt_frame_set_image( frame, *image, img_size, mlt_pool_release );
+       memcpy( *image, buf, img_size );
+
+       // Convert BGRA to RGBA
+       register uint8_t *p = *image;
+       register int n = width * height + 1;
+       while ( --n ) {
+               uint8_t b = p[0];
+               *p = p[2]; p += 2;
+               *p = b; p += 2;
+       }
+
+       // Release PBO and FBO
+       glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB );
+       check_error();
+       glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 );
+       check_error();
+       glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+       check_error();
+       glBindTexture( GL_TEXTURE_2D, 0 );
+       check_error();
+       mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0,
+               (mlt_destructor) GlslManager::release_texture, NULL);
+       release_fbo( fbo );
+
+       return 0;
+}
+
 void GlslManager::lock_service( mlt_frame frame )
 {
        Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );