]> git.sesse.net Git - mlt/blob - src/modules/opengl/filter_glsl_manager.cpp
Set glsl_supported property to result of init_movit().
[mlt] / src / modules / opengl / filter_glsl_manager.cpp
1 /*
2  * filter_glsl_manager.cpp
3  * Copyright (C) 2011-2012 Christophe Thommeret <hftom@free.fr>
4  * Copyright (C) 2013 Dan Dennedy <dan@dennedy.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <stdlib.h>
22 #include <string>
23 #include "filter_glsl_manager.h"
24 #include <movit/init.h>
25 #include <movit/util.h>
26 #include <movit/effect_chain.h>
27 #include <movit/resource_pool.h>
28 #include "mlt_movit_input.h"
29 #include "mlt_flip_effect.h"
30 #include <mlt++/MltEvent.h>
31 #include <mlt++/MltProducer.h>
32
33 extern "C" {
34 #include <framework/mlt_factory.h>
35 }
36
37 #if defined(__DARWIN__)
38 #include <OpenGL/OpenGL.h>
39 #elif defined(WIN32)
40 #include <wingdi.h>
41 #else
42 #include <GL/glx.h>
43 #endif
44
45 using namespace movit;
46
47 void dec_ref_and_delete(GlslManager *p)
48 {
49         if (p->dec_ref() == 0) {
50                 delete p;
51         }
52 }
53
54 GlslManager::GlslManager()
55         : Mlt::Filter( mlt_filter_new() )
56         , resource_pool(new ResourcePool())
57         , pbo(0)
58         , initEvent(0)
59         , closeEvent(0)
60         , prev_sync(NULL)
61 {
62         mlt_filter filter = get_filter();
63         if ( filter ) {
64                 // Set the mlt_filter child in case we choose to override virtual functions.
65                 filter->child = this;
66                 add_ref(mlt_global_properties());
67
68                 mlt_events_register( get_properties(), "init glsl", NULL );
69                 mlt_events_register( get_properties(), "close glsl", NULL );
70                 initEvent = listen("init glsl", this, (mlt_listener) GlslManager::onInit);
71                 closeEvent = listen("close glsl", this, (mlt_listener) GlslManager::onClose);
72         }
73 }
74
75 GlslManager::~GlslManager()
76 {
77         mlt_log_debug(get_service(), "%s\n", __FUNCTION__);
78         cleanupContext();
79 // XXX If there is still a frame holding a reference to a texture after this
80 // destructor is called, then it will crash in release_texture().
81 //      while (texture_list.peek_back())
82 //              delete (glsl_texture) texture_list.pop_back();
83         delete initEvent;
84         delete closeEvent;
85         if (prev_sync != NULL) {
86                 glDeleteSync( prev_sync );
87         }
88         while (syncs_to_delete.count() > 0) {
89                 GLsync sync = (GLsync) syncs_to_delete.pop_front();
90                 glDeleteSync( sync );
91         }
92         delete resource_pool;
93 }
94
95 void GlslManager::add_ref(mlt_properties properties)
96 {
97         inc_ref();
98         mlt_properties_set_data(properties, "glslManager", this, 0,
99             (mlt_destructor) dec_ref_and_delete, NULL);
100 }
101
102 GlslManager* GlslManager::get_instance()
103 {
104         return (GlslManager*) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0);
105 }
106
107 glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format)
108 {
109         lock();
110         for (int i = 0; i < texture_list.count(); ++i) {
111                 glsl_texture tex = (glsl_texture) texture_list.peek(i);
112                 if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) {
113                         glBindTexture(GL_TEXTURE_2D, tex->texture);
114                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
115                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
116                         glBindTexture( GL_TEXTURE_2D, 0);
117                         tex->used = 1;
118                         unlock();
119                         return tex;
120                 }
121         }
122         unlock();
123
124         GLuint tex = 0;
125         glGenTextures(1, &tex);
126         if (!tex) {
127                 return NULL;
128         }
129
130         glsl_texture gtex = new glsl_texture_s;
131         if (!gtex) {
132                 glDeleteTextures(1, &tex);
133                 return NULL;
134         }
135
136         glBindTexture( GL_TEXTURE_2D, tex );
137         glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
138     glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
139     glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
140     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
141     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
142     glBindTexture( GL_TEXTURE_2D, 0 );
143
144         gtex->texture = tex;
145         gtex->width = width;
146         gtex->height = height;
147         gtex->internal_format = internal_format;
148         gtex->used = 1;
149         lock();
150         texture_list.push_back(gtex);
151         unlock();
152         return gtex;
153 }
154
155 void GlslManager::release_texture(glsl_texture texture)
156 {
157         texture->used = 0;
158 }
159
160 void GlslManager::delete_sync(GLsync sync)
161 {
162         // We do not know which thread we are called from, and we can only
163         // delete this if we are in one with a valid OpenGL context.
164         // Thus, store it for later deletion in render_frame_texture().
165         GlslManager* g = GlslManager::get_instance();
166         g->lock();
167         g->syncs_to_delete.push_back(sync);
168         g->unlock();
169 }
170
171 glsl_pbo GlslManager::get_pbo(int size)
172 {
173         lock();
174         if (!pbo) {
175                 GLuint pb = 0;
176                 glGenBuffers(1, &pb);
177                 if (!pb) {
178                         unlock();
179                         return NULL;
180                 }
181
182                 pbo = new glsl_pbo_s;
183                 if (!pbo) {
184                         glDeleteBuffers(1, &pb);
185                         unlock();
186                         return NULL;
187                 }
188                 pbo->pbo = pb;
189                 pbo->size = 0;
190         }
191         if (size > pbo->size) {
192                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo->pbo);
193                 glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, size, NULL, GL_STREAM_DRAW);
194                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
195                 pbo->size = size;
196         }
197         unlock();
198         return pbo;
199 }
200
201 void GlslManager::cleanupContext()
202 {
203         lock();
204         while (texture_list.peek_back()) {
205                 glsl_texture texture = (glsl_texture) texture_list.peek_back();
206                 glDeleteTextures(1, &texture->texture);
207                 delete texture;
208                 texture_list.pop_back();
209         }
210         if (pbo) {
211                 glDeleteBuffers(1, &pbo->pbo);
212                 delete pbo;
213                 pbo = 0;
214         }
215         unlock();
216 }
217
218 void GlslManager::onInit( mlt_properties owner, GlslManager* filter )
219 {
220         mlt_log_debug( filter->get_service(), "%s\n", __FUNCTION__ );
221 #ifdef WIN32
222         std::string path = std::string(mlt_environment("MLT_APPDIR")).append("\\share\\movit");
223 #elif defined(__DARWIN__) && defined(RELOCATABLE)
224         std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/share/movit");
225 #else
226         std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR);
227 #endif
228         bool success = init_movit( path, mlt_log_get_level() == MLT_LOG_DEBUG? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF );
229         filter->set( "glsl_supported", success );
230 }
231
232 void GlslManager::onClose( mlt_properties owner, GlslManager *filter )
233 {
234         filter->cleanupContext();
235 }
236
237 void GlslManager::onServiceChanged( mlt_properties owner, mlt_service aservice )
238 {
239         Mlt::Service service( aservice );
240         service.lock();
241         service.set( "movit chain", NULL, 0 );
242         service.unlock();
243 }
244
245 void GlslManager::onPropertyChanged( mlt_properties owner, mlt_service service, const char* property )
246 {
247         if ( property && std::string( property ) == "disable" )
248                 onServiceChanged( owner, service );
249 }
250
251 extern "C" {
252
253 mlt_filter filter_glsl_manager_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
254 {
255         GlslManager* g = GlslManager::get_instance();
256         if (g)
257                 g->inc_ref();
258         else
259                 g = new GlslManager();
260         return g->get_filter();
261 }
262
263 } // extern "C"
264
265 static void deleteChain( GlslChain* chain )
266 {
267         // The Input* is owned by the EffectChain, but the MltInput* is not.
268         // Thus, we have to delete it here.
269         for (std::map<mlt_producer, MltInput*>::iterator input_it = chain->inputs.begin();
270              input_it != chain->inputs.end();
271              ++input_it) {
272                 delete input_it->second;
273         }
274         delete chain->effect_chain;
275         delete chain;
276 }
277         
278 void* GlslManager::get_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, int *length )
279 {
280         const char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" );
281         char buf[256];
282         snprintf( buf, sizeof(buf), "%s_%s", key, unique_id );
283         return mlt_properties_get_data( MLT_FRAME_PROPERTIES(frame), buf, length );
284 }
285
286 int GlslManager::set_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
287 {
288         const char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" );
289         char buf[256];
290         snprintf( buf, sizeof(buf), "%s_%s", key, unique_id );
291         return mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), buf, value, length, destroy, serialise );
292 }
293
294 void GlslManager::set_chain( mlt_service service, GlslChain* chain )
295 {
296         mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "_movit chain", chain, 0, (mlt_destructor) deleteChain, NULL );
297 }
298
299 GlslChain* GlslManager::get_chain( mlt_service service )
300 {
301         return (GlslChain*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "_movit chain", NULL );
302 }
303         
304 Effect* GlslManager::get_effect( mlt_service service, mlt_frame frame )
305 {
306         return (Effect*) get_frame_specific_data( service, frame, "_movit effect", NULL );
307 }
308
309 Effect* GlslManager::set_effect( mlt_service service, mlt_frame frame, Effect* effect )
310 {
311         set_frame_specific_data( service, frame, "_movit effect", effect, 0, NULL, NULL );
312         return effect;
313 }
314
315 MltInput* GlslManager::get_input( mlt_producer producer, mlt_frame frame )
316 {
317         return (MltInput*) get_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input", NULL );
318 }
319
320 MltInput* GlslManager::set_input( mlt_producer producer, mlt_frame frame, MltInput* input )
321 {
322         set_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input", input, 0, NULL, NULL );
323         return input;
324 }
325
326 uint8_t* GlslManager::get_input_pixel_pointer( mlt_producer producer, mlt_frame frame )
327 {
328         return (uint8_t*) get_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", NULL );
329 }
330
331 uint8_t* GlslManager::set_input_pixel_pointer( mlt_producer producer, mlt_frame frame, uint8_t* image )
332 {
333         set_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", image, 0, NULL, NULL );
334         return image;
335 }
336
337 mlt_service GlslManager::get_effect_input( mlt_service service, mlt_frame frame )
338 {
339         return (mlt_service) get_frame_specific_data( service, frame, "_movit effect input", NULL );
340 }
341
342 void GlslManager::set_effect_input( mlt_service service, mlt_frame frame, mlt_service input_service )
343 {
344         set_frame_specific_data( service, frame, "_movit effect input", input_service, 0, NULL, NULL );
345 }
346
347 void GlslManager::get_effect_secondary_input( mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame)
348 {
349         *input_service = (mlt_service) get_frame_specific_data( service, frame, "_movit effect secondary input", NULL );
350         *input_frame = (mlt_frame) get_frame_specific_data( service, frame, "_movit effect secondary input frame", NULL );
351 }
352
353 void GlslManager::set_effect_secondary_input( mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame )
354 {
355         set_frame_specific_data( service, frame, "_movit effect secondary input", input_service, 0, NULL, NULL );
356         set_frame_specific_data( service, frame, "_movit effect secondary input frame", input_frame, 0, NULL, NULL );
357 }
358
359 int GlslManager::render_frame_texture(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image)
360 {
361         glsl_texture texture = get_texture( width, height, GL_RGBA8 );
362         if (!texture) {
363                 return 1;
364         }
365
366         GLuint fbo;
367         glGenFramebuffers( 1, &fbo );
368         check_error();
369         glBindFramebuffer( GL_FRAMEBUFFER, fbo );
370         check_error();
371         glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 );
372         check_error();
373         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
374         check_error();
375
376         lock();
377         while (syncs_to_delete.count() > 0) {
378                 GLsync sync = (GLsync) syncs_to_delete.pop_front();
379                 glDeleteSync( sync );
380         }
381         unlock();
382
383         // Make sure we never have more than one frame pending at any time.
384         // This ensures we do not swamp the GPU with so much work
385         // that we cannot actually display the frames we generate.
386         if (prev_sync != NULL) {
387                 glFlush();
388                 glClientWaitSync( prev_sync, 0, GL_TIMEOUT_IGNORED );
389                 glDeleteSync( prev_sync );
390         }
391         chain->render_to_fbo( fbo, width, height );
392         prev_sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
393         GLsync sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
394
395         check_error();
396         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
397         check_error();
398         glDeleteFramebuffers( 1, &fbo );
399         check_error();
400
401         *image = (uint8_t*) &texture->texture;
402         mlt_frame_set_image( frame, *image, 0, NULL );
403         mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0,
404                 (mlt_destructor) GlslManager::release_texture, NULL );
405         mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.fence", sync, 0,
406                 (mlt_destructor) GlslManager::delete_sync, NULL );
407
408         return 0;
409 }
410
411 int GlslManager::render_frame_rgba(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image)
412 {
413         glsl_texture texture = get_texture( width, height, GL_RGBA8 );
414         if (!texture) {
415                 return 1;
416         }
417
418         // Use a PBO to hold the data we read back with glReadPixels().
419         // (Intel/DRI goes into a slow path if we don't read to PBO.)
420         int img_size = width * height * 4;
421         glsl_pbo pbo = get_pbo( img_size );
422         if (!pbo) {
423                 release_texture(texture);
424                 return 1;
425         }
426
427         // Set the FBO
428         GLuint fbo;
429         glGenFramebuffers( 1, &fbo );
430         check_error();
431         glBindFramebuffer( GL_FRAMEBUFFER, fbo );
432         check_error();
433         glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 );
434         check_error();
435         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
436         check_error();
437
438         chain->render_to_fbo( fbo, width, height );
439
440         // Read FBO into PBO
441         glBindFramebuffer( GL_FRAMEBUFFER, fbo );
442         check_error();
443         glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo );
444         check_error();
445         glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ );
446         check_error();
447         glReadPixels( 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) );
448         check_error();
449
450         // Copy from PBO
451         uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY );
452         check_error();
453         *image = (uint8_t*) mlt_pool_alloc( img_size );
454         mlt_frame_set_image( frame, *image, img_size, mlt_pool_release );
455         memcpy( *image, buf, img_size );
456
457         // Convert BGRA to RGBA
458         register uint8_t *p = *image;
459         register int n = width * height + 1;
460         while ( --n ) {
461                 uint8_t b = p[0];
462                 *p = p[2]; p += 2;
463                 *p = b; p += 2;
464         }
465
466         // Release PBO and FBO
467         glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB );
468         check_error();
469         glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 );
470         check_error();
471         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
472         check_error();
473         glBindTexture( GL_TEXTURE_2D, 0 );
474         check_error();
475         mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0,
476                 (mlt_destructor) GlslManager::release_texture, NULL);
477         glDeleteFramebuffers( 1, &fbo );
478         check_error();
479
480         return 0;
481 }
482
483 void GlslManager::lock_service( mlt_frame frame )
484 {
485         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
486         producer.lock();
487 }
488
489 void GlslManager::unlock_service( mlt_frame frame )
490 {
491         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
492         producer.unlock();
493 }