Commit b2e01872 authored by Yann Diorcet's avatar Yann Diorcet
Browse files

Fix multiple issue on opengl stuff

Add Triple texture buffer: avoid interface lag when concurrent access to textures
parent d63fdef9
......@@ -41,61 +41,36 @@
#include "opengles_display.h"
@interface IOSDisplay : UIView {
@private
UIView* imageView;
@public
struct opengles_display* display_helper;
@private
NSRecursiveLock* lock;
EAGLContext* context;
GLuint defaultFrameBuffer, colorRenderBuffer;
struct opengles_display* helper;
id displayLink;
BOOL animating;
int deviceRotation;
CGRect prevBounds;
}
- (void)drawView:(id)sender;
- (BOOL)loadShaders;
- (void)initIOSDisplay;
@property (nonatomic, retain) UIView* parentView;
@property (assign) int deviceRotation;
@property (assign) int displayRotation;
@end
@implementation IOSDisplay
@synthesize parentView;
- (id)init {
self = [super init];
if (self) {
[self initIOSDisplay];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self initIOSDisplay];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initIOSDisplay];
}
return self;
}
@synthesize deviceRotation;
@synthesize displayRotation;
- (void)initIOSDisplay {
self->deviceRotation = 0;
self->helper = ogl_display_new();
self->lock = [[NSRecursiveLock alloc] init];
self->display_helper = ogl_display_new();
self->prevBounds = CGRectMake(0, 0, 0, 0);
// Init view
[self setOpaque:YES];
[self setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
......@@ -103,8 +78,10 @@
// Init layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
[eaglLayer setOpaque:YES];
[eaglLayer setDrawableProperties: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
nil]];
// Init OpenGL context
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context || ![EAGLContext setCurrentContext:context]) {
......@@ -114,28 +91,53 @@
glGenFramebuffers(1, &defaultFrameBuffer);
glGenRenderbuffers(1, &colorRenderBuffer);
glGenRenderbuffers(1, &colorRenderBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, defaultFrameBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderBuffer);
ogl_display_init(display_helper, prevBounds.size.width, prevBounds.size.height);
// release GL context for this thread
[EAGLContext setCurrentContext:nil];
}
- (void)drawView:(id)sender {
- (id)init {
self = [super init];
if (self) {
[self initIOSDisplay];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self initIOSDisplay];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initIOSDisplay];
}
return self;
}
- (void)drawView {
/* no opengl es call made when in background */
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
return;
@synchronized(self) {
if([lock tryLock]) {
if (![EAGLContext setCurrentContext:context]) {
ms_error("Failed to bind GL context");
return;
}
if (!CGRectEqualToRect(prevBounds, [self bounds])) {
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
CAEAGLLayer* layer = (CAEAGLLayer*)self.layer;
if (prevBounds.size.width != 0 || prevBounds.size.height != 0) {
......@@ -146,24 +148,23 @@
prevBounds = [self bounds];
// allocate storage
if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]) {
ms_error("Error in renderbufferStorage (layer %p frame size: %f x %f)", layer, layer.frame.size.width, layer.frame.size.height);
} else {
if ([context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]) {
ms_message("GL renderbuffer allocation size (layer %p frame size: %f x %f)", layer, layer.frame.size.width, layer.frame.size.height);
ogl_display_init(helper, prevBounds.size.width, prevBounds.size.height);
glClearColor(0, 0, 0, 1);
ogl_display_set_size(display_helper, prevBounds.size.width, prevBounds.size.height);
glClear(GL_COLOR_BUFFER_BIT);
} else {
ms_error("Error in renderbufferStorage (layer %p frame size: %f x %f)", layer, layer.frame.size.width, layer.frame.size.height);
}
}
}
if (!animating) {
glClear(GL_COLOR_BUFFER_BIT);
} else {
ogl_display_render(helper, 0);
ogl_display_render(display_helper, 0);
}
[context presentRenderbuffer:GL_RENDERBUFFER];
[lock unlock];
}
}
......@@ -179,7 +180,7 @@
[displayLink invalidate];
displayLink = nil;
[self drawView:0];
[self drawView];
// remove from parent
[self removeFromSuperview];
......@@ -199,7 +200,7 @@
[parentView addSubview:self];
// schedule rendering
displayLink = [self.window.screen displayLinkWithTarget:self selector:@selector(drawView:)];
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView)];
[displayLink setFrameInterval:1];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
......@@ -209,30 +210,41 @@
return [CAEAGLLayer class];
}
static void iosdisplay_init(MSFilter *f) {
f->data = [[IOSDisplay alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
}
- (void)dealloc {
[EAGLContext setCurrentContext:context];
glFinish();
ogl_display_uninit(helper, TRUE);
ogl_display_free(helper);
helper = NULL;
ogl_display_uninit(display_helper, TRUE);
ogl_display_free(display_helper);
display_helper = NULL;
glDeleteFramebuffers(GL_FRAMEBUFFER, &defaultFrameBuffer);
glDeleteRenderbuffers(GL_RENDERBUFFER, &colorRenderBuffer);
[EAGLContext setCurrentContext:0];
[context release];
[lock release];
self.parentView = nil;
[super dealloc];
}
@end
static void iosdisplay_init(MSFilter *f) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
f->data = [[IOSDisplay alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[loopPool drain];
}
static void iosdisplay_process(MSFilter *f) {
IOSDisplay* thiz = (IOSDisplay*)f->data;
mblk_t *m = ms_queue_peek_last(f->inputs[0]);
if (thiz != nil && m != nil) {
ogl_display_set_yuv_to_display(thiz->helper, m);
ogl_display_set_yuv_to_display(thiz->display_helper, m);
}
ms_queue_flush(f->inputs[0]);
......@@ -243,28 +255,24 @@ static void iosdisplay_process(MSFilter *f) {
static void iosdisplay_unit(MSFilter *f) {
IOSDisplay* thiz = (IOSDisplay*)f->data;
if(thiz != nil) {
[thiz performSelectorOnMainThread:@selector(setParentView:) withObject:nil waitUntilDone:NO];
[thiz release];
f->data = NULL;
}
[thiz release];
}
static int iosdisplay_set_native_window(MSFilter *f, void *arg) {
IOSDisplay *thiz = (IOSDisplay*)f->data;
UIView* parentView = *(UIView**)arg;
if (thiz != nil) {
if (thiz != nil) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
// set current parent view
if (parentView) {
[thiz performSelectorOnMainThread:@selector(setParentView:) withObject:parentView waitUntilDone:NO];
}
[thiz performSelectorOnMainThread:@selector(setParentView:) withObject:parentView waitUntilDone:NO];
[loopPool drain];
}
return 0;
}
static int iosdisplay_get_native_window(MSFilter *f, void *arg) {
IOSDisplay* thiz=(IOSDisplay*)f->data;
arg = &thiz->parentView;
*(UIView**)arg = thiz.parentView;
return 0;
}
......@@ -272,7 +280,7 @@ static int iosdisplay_set_device_orientation(MSFilter* f, void* arg) {
IOSDisplay* thiz=(IOSDisplay*)f->data;
if (!thiz)
return 0;
//thiz->deviceRotation = 0;//*((int*)arg);
thiz.deviceRotation = *((int*)arg);
return 0;
}
......@@ -280,13 +288,13 @@ static int iosdisplay_set_device_orientation_display(MSFilter* f, void* arg) {
IOSDisplay* thiz=(IOSDisplay*)f->data;
if (!thiz)
return 0;
thiz->deviceRotation = *((int*)arg);
thiz.displayRotation = *((int*)arg);
return 0;
}
static int iosdisplay_set_zoom(MSFilter* f, void* arg) {
IOSDisplay* thiz=(IOSDisplay*)f->data;
ogl_display_zoom(thiz->helper, arg);
ogl_display_zoom(thiz->display_helper, arg);
}
static MSFilterMethod iosdisplay_methods[] = {
......@@ -298,8 +306,6 @@ static MSFilterMethod iosdisplay_methods[] = {
{ 0, NULL }
};
@end
MSFilterDesc ms_iosdisplay_desc = {
.id=MS_IOS_DISPLAY_ID, /* from Allfilters.h*/
.name="IOSDisplay",
......
......@@ -12,23 +12,22 @@
@interface CAMsGLLayer : CAOpenGLLayer {
@public
struct opengles_display* display_helper;
@private
CGLPixelFormatObj cglPixelFormat;
CGLContextObj cglContext;
NSRecursiveLock* lock;
CGRect prevBounds;
}
- (void)resizeToWindow:(NSWindow *)window;
@property (assign) CGRect prevBounds;
@property (assign) CGSize sourceSize;
@property (readonly) NSRecursiveLock* lock;
@end
@implementation CAMsGLLayer
@synthesize prevBounds;
@synthesize sourceSize;
@synthesize lock;
- (id)init {
self = [super init];
......@@ -58,6 +57,15 @@
cglContext = [super copyCGLContextForPixelFormat:cglPixelFormat];
assert(cglContext);
CGLContextObj savedContext = CGLGetCurrentContext();
CGLSetCurrentContext(cglContext);
CGLLockContext(cglContext);
ogl_display_init(display_helper, prevBounds.size.width, prevBounds.size.height);
CGLUnlockContext(cglContext);
CGLSetCurrentContext(savedContext);
}
return self;
}
......@@ -109,10 +117,9 @@
if (!NSEqualRects(prevBounds, [self bounds])) {
prevBounds = [self bounds];
ogl_display_init(display_helper, prevBounds.size.width, prevBounds.size.height);
ogl_display_set_size(display_helper, prevBounds.size.width, prevBounds.size.height);
}
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
ogl_display_render(display_helper, 0);
......@@ -152,7 +159,8 @@
@property (nonatomic, retain) CALayer* layer;
@property (nonatomic, retain) CAMsGLLayer* glLayer;
- (void)createWindow;
- (void)createWindowIfNeeded;
- (void)resetContainers;
@end
......@@ -240,28 +248,30 @@
}
}
- (void)createWindow {
NSWindow *awindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) styleMask:(NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask) backing:NSBackingStoreBuffered defer:NO];
[awindow setBackgroundColor: [NSColor blueColor]];
[awindow makeKeyAndOrderFront:NSApp];
[awindow setTitle: @"Video"];
[awindow setMovable:YES];
[awindow setMovableByWindowBackground:YES];
[awindow setReleasedWhenClosed:NO];
CGFloat xPos = NSWidth([[awindow screen] frame])/2 - NSWidth([awindow frame])/2;
CGFloat yPos = NSHeight([[awindow screen] frame])/2 - NSHeight([awindow frame])/2;
[awindow setFrame:NSMakeRect(xPos, yPos, NSWidth([awindow frame]), NSHeight([awindow frame])) display:YES];
// Init view
NSView *innerView = [[NSView alloc] initWithFrame:[window frame]];
[innerView setWantsLayer:YES];
[innerView.layer setAutoresizingMask: kCALayerWidthSizable | kCALayerHeightSizable];
[innerView.layer setNeedsDisplayOnBoundsChange: YES];
[awindow setContentView: innerView];
[innerView release];
self.window = awindow;
self.closeWindow = TRUE;
- (void)createWindowIfNeeded {
if(window == nil && layer == nil && view == nil) {
NSWindow *awindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) styleMask:(NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask) backing:NSBackingStoreBuffered defer:NO];
[awindow setBackgroundColor: [NSColor blueColor]];
[awindow makeKeyAndOrderFront:NSApp];
[awindow setTitle: @"Video"];
[awindow setMovable:YES];
[awindow setMovableByWindowBackground:YES];
[awindow setReleasedWhenClosed:NO];
CGFloat xPos = NSWidth([[awindow screen] frame])/2 - NSWidth([awindow frame])/2;
CGFloat yPos = NSHeight([[awindow screen] frame])/2 - NSHeight([awindow frame])/2;
[awindow setFrame:NSMakeRect(xPos, yPos, NSWidth([awindow frame]), NSHeight([awindow frame])) display:YES];
// Init view
NSView *innerView = [[NSView alloc] initWithFrame:[window frame]];
[innerView setWantsLayer:YES];
[innerView.layer setAutoresizingMask: kCALayerWidthSizable | kCALayerHeightSizable];
[innerView.layer setNeedsDisplayOnBoundsChange: YES];
[awindow setContentView: innerView];
[innerView release];
self.window = awindow;
self.closeWindow = TRUE;
}
}
- (void)dealloc {
......@@ -274,16 +284,17 @@
@end
static void osx_gl_init(MSFilter* f) {
f->data = [[OSXDisplay alloc] init];
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
f->data = [[OSXDisplay alloc] init];
[loopPool drain];
}
static void osx_gl_preprocess(MSFilter* f) {
OSXDisplay* thiz = (OSXDisplay*) f->data;
// Init window
if(thiz.window == nil && thiz.layer == nil && thiz.view == nil) {
[thiz performSelectorOnMainThread:@selector(createWindow) withObject:nil waitUntilDone:FALSE];
}
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
[thiz performSelectorOnMainThread:@selector(createWindowIfNeeded) withObject:nil waitUntilDone:FALSE];
[loopPool drain];
}
static void osx_gl_process(MSFilter* f) {
......@@ -323,9 +334,7 @@ static void osx_gl_process(MSFilter* f) {
}
ms_queue_flush(f->inputs[1]);
}
// From Apple's doc: "An autorelease pool should always be drained in the same context (such as the invocation of a method or function, or the body of a loop) in which it was created." So we cannot create on autorelease pool in init and drain it in uninit.
[loopPool drain];
}
......@@ -357,6 +366,8 @@ static int osx_gl_get_native_window_id(MSFilter* f, void* arg) {
static int osx_gl_set_native_window_id(MSFilter* f, void* arg) {
OSXDisplay* thiz = (OSXDisplay*) f->data;
NSObject *obj = *((NSObject **)arg);
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
if(obj != nil) {
if([obj isKindOfClass:[NSWindow class]]) {
[thiz performSelectorOnMainThread:@selector(setWindow:) withObject:(NSWindow*)obj waitUntilDone:NO];
......@@ -368,7 +379,10 @@ static int osx_gl_set_native_window_id(MSFilter* f, void* arg) {
[thiz performSelectorOnMainThread:@selector(setLayer:) withObject:(CALayer*)obj waitUntilDone:NO];
return 0;
}
} else {
[thiz performSelectorOnMainThread:@selector(resetContainers) withObject:nil waitUntilDone:NO];
}
[loopPool drain];
return -1;
}
......
......@@ -35,7 +35,7 @@ static unsigned int align_on_power_of_2(unsigned int value);
static bool_t update_textures_with_yuv(struct opengles_display* gldisp, enum ImageType type);
#define CHECK_GL_ERROR
//#define CHECK_GL_ERROR
#ifdef CHECK_GL_ERROR
#define GL_OPERATION(x) \
......@@ -67,18 +67,22 @@ enum {
V
};
#define TEXTURE_BUFFER_SIZE 3
struct opengles_display {
/* input: yuv image to display */
ms_mutex_t yuv_mutex;
mblk_t *yuv[2];
bool_t new_yuv_image[2];
bool_t new_yuv_image[TEXTURE_BUFFER_SIZE][2];
/* GL resources */
bool_t glResourcesInitialized;
GLuint program, textures[2][3];
GLuint program, textures[TEXTURE_BUFFER_SIZE][2][3];
GLint uniforms[NUM_UNIFORMS];
MSVideoSize allocatedTexturesSize[2];
int texture_index;
/* GL view size */
GLint backingWidth;
GLint backingHeight;
......@@ -103,6 +107,7 @@ struct opengles_display* ogl_display_new() {
memset(result, 0, sizeof(struct opengles_display));
result->zoom_factor = 1;
result->zoom_cx = result->zoom_cy = 0;
result->texture_index = 0;
ms_mutex_init(&result->yuv_mutex, NULL);
ms_message("%s : %p\n", __FUNCTION__, result);
......@@ -128,8 +133,17 @@ void ogl_display_free(struct opengles_display* gldisp) {
free(gldisp);
}
void ogl_display_set_size(struct opengles_display* gldisp, int width, int height) {
gldisp->backingWidth = width;
gldisp->backingHeight = height;
GL_OPERATION(glViewport(0, 0, gldisp->backingWidth, gldisp->backingHeight));
check_GL_errors("ogl_display_set_size");
}
void ogl_display_init(struct opengles_display* gldisp, int width, int height) {
int i;
int i,j;
static bool_t version_displayed = FALSE;
if (!gldisp) {
ms_error("%s called with null struct opengles_display", __FUNCTION__);
......@@ -139,19 +153,19 @@ void ogl_display_init(struct opengles_display* gldisp, int width, int height) {
ms_message("init opengles_display (%d x %d, gl initialized:%d)", width, height, gldisp->glResourcesInitialized);
GL_OPERATION(glDisable(GL_DEPTH_TEST))
GL_OPERATION(glClearColor(0, 0, 0, 1))
gldisp->backingWidth = width;
gldisp->backingHeight = height;
GL_OPERATION(glViewport(0, 0, gldisp->backingWidth, gldisp->backingHeight));
ogl_display_set_size(gldisp, width, height);
if (gldisp->glResourcesInitialized)
return;
// init textures
for(i=0; i<2; i++) {
GL_OPERATION(glGenTextures(3, gldisp->textures[i]))
gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
for(j=0; j<TEXTURE_BUFFER_SIZE; j++) {
// init textures
for(i=0; i<2; i++) {
GL_OPERATION(glGenTextures(3, gldisp->textures[j][i]))
gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
}
}
if (!version_displayed) {
......@@ -164,15 +178,16 @@ void ogl_display_init(struct opengles_display* gldisp, int width, int height) {
ms_message("OpenGL GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
}
load_shaders(&gldisp->program, gldisp->uniforms);
check_GL_errors("load_shaders");
GL_OPERATION(glUseProgram(gldisp->program))
gldisp->glResourcesInitialized = TRUE;
check_GL_errors("ogl_display_init");
}
void ogl_display_uninit(struct opengles_display* gldisp, bool_t freeGLresources) {
int i;
int i,j;
if (!gldisp) {
ms_error("%s called with null struct opengles_display", __FUNCTION__);
......@@ -187,15 +202,19 @@ void ogl_display_uninit(struct opengles_display* gldisp, bool_t freeGLresources)
}
if (gldisp->glResourcesInitialized && freeGLresources) {
// destroy gl resources
for(i=0; i<2; i++) {
GL_OPERATION(glDeleteTextures(3, gldisp->textures[i]));
gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
for(j=0; j<TEXTURE_BUFFER_SIZE; j++) {
// destroy gl resources
for(i=0; i<2; i++) {
GL_OPERATION(glDeleteTextures(3, gldisp->textures[j][i]));
gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
}
}
GL_OPERATION(glDeleteProgram(gldisp->program));
}
gldisp->glResourcesInitialized = FALSE;
check_GL_errors("ogl_display_uninit");
}
static void ogl_display_set_yuv(struct opengles_display* gldisp, mblk_t *yuv, enum ImageType type) {
......@@ -207,7 +226,11 @@ static void ogl_display_set_yuv(struct opengles_display* gldisp, mblk_t *yuv, en
if (gldisp->yuv[type])
freemsg(gldisp->yuv[type]);
gldisp->yuv[type] = dupmsg(yuv);
gldisp->new_yuv_image[type] = TRUE;
int j;
for(j = 0; j < TEXTURE_BUFFER_SIZE; ++j) {
gldisp->new_yuv_image[j][type] = TRUE;
}
ms_mutex_unlock(&gldisp->yuv_mutex);
}