diff --git a/libobs-opengl/gl-cocoa.m b/libobs-opengl/gl-cocoa.m index 70d667fbc..f3fc911a3 100644 --- a/libobs-opengl/gl-cocoa.m +++ b/libobs-opengl/gl-cocoa.m @@ -25,13 +25,16 @@ struct gl_windowinfo { NSView *view; + NSOpenGLContext *context; + gs_texture_t *texture; + GLuint fbo; }; struct gl_platform { NSOpenGLContext *context; }; -static NSOpenGLContext *gl_context_create(void) +static NSOpenGLContext *gl_context_create(NSOpenGLContext *share) { unsigned attrib_count = 0; @@ -62,7 +65,8 @@ static NSOpenGLContext *gl_context_create(void) } NSOpenGLContext *context; - context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil]; + context = [[NSOpenGLContext alloc] initWithFormat:pf + shareContext:share]; [pf release]; if (!context) { blog(LOG_ERROR, "Failed to create context"); @@ -76,28 +80,30 @@ static NSOpenGLContext *gl_context_create(void) struct gl_platform *gl_platform_create(gs_device_t *device, uint32_t adapter) { - struct gl_platform *plat = bzalloc(sizeof(struct gl_platform)); - GLint interval = 0; - - plat->context = gl_context_create(); - if (!plat->context) - goto fail; - - [plat->context makeCurrentContext]; - [plat->context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; - - if (!gladLoadGL()) - goto fail; - - return plat; - -fail: - blog(LOG_ERROR, "gl_platform_create failed"); - gl_platform_destroy(plat); - UNUSED_PARAMETER(device); UNUSED_PARAMETER(adapter); - return NULL; + + NSOpenGLContext *context = gl_context_create(nil); + if (!context) { + blog(LOG_ERROR, "gl_context_create failed"); + return NULL; + } + + [context makeCurrentContext]; + GLint interval = 0; + [context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; + const bool success = gladLoadGL() != 0; + [NSOpenGLContext clearCurrentContext]; + + if (!success) { + blog(LOG_ERROR, "gladLoadGL failed"); + [context release]; + return NULL; + } + + struct gl_platform *plat = bzalloc(sizeof(struct gl_platform)); + plat->context = context; + return plat; } void gl_platform_destroy(struct gl_platform *platform) @@ -113,14 +119,72 @@ void gl_platform_destroy(struct gl_platform *platform) bool gl_platform_init_swapchain(struct gs_swap_chain *swap) { - UNUSED_PARAMETER(swap); + NSOpenGLContext *parent = swap->device->plat->context; + NSOpenGLContext *context = gl_context_create(parent); + bool success = context != nil; + if (success) { + CGLContextObj parent_obj = [parent CGLContextObj]; + CGLLockContext(parent_obj); - return true; + [parent makeCurrentContext]; + struct gs_init_data *init_data = &swap->info; + swap->wi->texture = device_texture_create( + swap->device, init_data->cx, init_data->cy, + init_data->format, 1, NULL, GS_RENDER_TARGET); + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + CGLContextObj context_obj = [context CGLContextObj]; + CGLLockContext(context_obj); + + [context makeCurrentContext]; + [context setView:swap->wi->view]; + GLint interval = 0; + [context setValues:&interval + forParameter:NSOpenGLCPSwapInterval]; + gl_gen_framebuffers(1, &swap->wi->fbo); + gl_bind_framebuffer(GL_FRAMEBUFFER, swap->wi->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + swap->wi->texture->texture, 0); + gl_success("glFrameBufferTexture2D"); + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + CGLUnlockContext(context_obj); + + CGLUnlockContext(parent_obj); + + swap->wi->context = context; + } + + return success; } void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap) { - UNUSED_PARAMETER(swap); + NSOpenGLContext *parent = swap->device->plat->context; + CGLContextObj parent_obj = [parent CGLContextObj]; + CGLLockContext(parent_obj); + + NSOpenGLContext *context = swap->wi->context; + CGLContextObj context_obj = [context CGLContextObj]; + CGLLockContext(context_obj); + + [context makeCurrentContext]; + gl_delete_framebuffers(1, &swap->wi->fbo); + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + CGLUnlockContext(context_obj); + + [parent makeCurrentContext]; + gs_texture_destroy(swap->wi->texture); + glFlush(); + [NSOpenGLContext clearCurrentContext]; + swap->wi->context = nil; + + CGLUnlockContext(parent_obj); } struct gl_windowinfo *gl_windowinfo_create(const struct gs_init_data *info) @@ -150,19 +214,62 @@ void gl_windowinfo_destroy(struct gl_windowinfo *wi) void gl_update(gs_device_t *device) { - [device->plat->context update]; + gs_swapchain_t *swap = device->cur_swap; + NSOpenGLContext *parent = device->plat->context; + NSOpenGLContext *context = swap->wi->context; + dispatch_async(dispatch_get_main_queue(), ^() { + CGLContextObj parent_obj = [parent CGLContextObj]; + CGLLockContext(parent_obj); + + CGLContextObj context_obj = [context CGLContextObj]; + CGLLockContext(context_obj); + + [context makeCurrentContext]; + [context update]; + struct gs_init_data *info = &swap->info; + gs_texture_t *previous = swap->wi->texture; + swap->wi->texture = device_texture_create(device, info->cx, + info->cy, + info->format, 1, NULL, + GS_RENDER_TARGET); + gl_bind_framebuffer(GL_FRAMEBUFFER, swap->wi->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + swap->wi->texture->texture, 0); + gl_success("glFrameBufferTexture2D"); + gs_texture_destroy(previous); + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + CGLUnlockContext(context_obj); + + CGLUnlockContext(parent_obj); + }); +} + +void gl_clear_context(gs_device_t *device) +{ + UNUSED_PARAMETER(device); + [NSOpenGLContext clearCurrentContext]; } void device_enter_context(gs_device_t *device) { + CGLLockContext([device->plat->context CGLContextObj]); + [device->plat->context makeCurrentContext]; } void device_leave_context(gs_device_t *device) { - UNUSED_PARAMETER(device); - + glFlush(); [NSOpenGLContext clearCurrentContext]; + device->cur_render_target = NULL; + device->cur_zstencil_buffer = NULL; + device->cur_swap = NULL; + device->cur_fbo = NULL; + + CGLUnlockContext([device->plat->context CGLContextObj]); } void *device_get_device_obj(gs_device_t *device) @@ -177,15 +284,35 @@ void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swap) device->cur_swap = swap; if (swap) { - [device->plat->context setView:swap->wi->view]; - } else { - [device->plat->context clearDrawable]; + device_set_render_target(device, swap->wi->texture, NULL); } } void device_present(gs_device_t *device) { - [device->plat->context flushBuffer]; + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + CGLUnlockContext([device->plat->context CGLContextObj]); + + CGLLockContext([device->cur_swap->wi->context CGLContextObj]); + + [device->cur_swap->wi->context makeCurrentContext]; + gl_bind_framebuffer(GL_READ_FRAMEBUFFER, device->cur_swap->wi->fbo); + gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, 0); + const uint32_t width = device->cur_swap->info.cx; + const uint32_t height = device->cur_swap->info.cy; + glBlitFramebuffer(0, 0, width, height, 0, height, width, 0, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + [device->cur_swap->wi->context flushBuffer]; + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + CGLUnlockContext([device->cur_swap->wi->context CGLContextObj]); + + CGLLockContext([device->plat->context CGLContextObj]); + + [device->plat->context makeCurrentContext]; } void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width, diff --git a/libobs-opengl/gl-helpers.h b/libobs-opengl/gl-helpers.h index 10a1c9f08..8daad18a3 100644 --- a/libobs-opengl/gl-helpers.h +++ b/libobs-opengl/gl-helpers.h @@ -149,12 +149,24 @@ static inline bool gl_bind_renderbuffer(GLenum target, GLuint buffer) return gl_success("glBindRendebuffer"); } +static inline bool gl_gen_framebuffers(GLsizei num_arrays, GLuint *arrays) +{ + glGenFramebuffers(num_arrays, arrays); + return gl_success("glGenFramebuffers"); +} + static inline bool gl_bind_framebuffer(GLenum target, GLuint buffer) { glBindFramebuffer(target, buffer); return gl_success("glBindFramebuffer"); } +static inline void gl_delete_framebuffers(GLsizei num_arrays, GLuint *arrays) +{ + glDeleteFramebuffers(num_arrays, arrays); + gl_success("glDeleteFramebuffers"); +} + static inline bool gl_tex_param_f(GLenum target, GLenum param, GLfloat val) { glTexParameterf(target, param, val); diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index 57c25ded6..5af89c4a1 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -245,7 +245,7 @@ int device_create(gs_device_t **p_device, uint32_t adapter) gl_enable(GL_CULL_FACE); gl_gen_vertex_arrays(1, &device->empty_vao); - device_leave_context(device); + gl_clear_context(device); device->cur_swap = NULL; #ifdef _WIN32 diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index a5293a4d2..f471f21ed 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/libobs-opengl/gl-subsystem.h @@ -627,6 +627,7 @@ extern struct fbo_info *get_fbo(gs_texture_t *tex, uint32_t width, uint32_t height); extern void gl_update(gs_device_t *device); +extern void gl_clear_context(gs_device_t *device); extern struct gl_platform *gl_platform_create(gs_device_t *device, uint32_t adapter); diff --git a/libobs-opengl/gl-windows.c b/libobs-opengl/gl-windows.c index 734006503..c180b1150 100644 --- a/libobs-opengl/gl-windows.c +++ b/libobs-opengl/gl-windows.c @@ -415,6 +415,12 @@ void gl_update(gs_device_t *device) UNUSED_PARAMETER(device); } +void gl_clear_context(gs_device_t *device) +{ + UNUSED_PARAMETER(device); + wglMakeCurrent(NULL, NULL); +} + static void init_dummy_swap_info(struct gs_init_data *info) { info->format = GS_RGBA; @@ -541,8 +547,8 @@ void device_enter_context(gs_device_t *device) void device_leave_context(gs_device_t *device) { - wglMakeCurrent(NULL, NULL); UNUSED_PARAMETER(device); + wglMakeCurrent(NULL, NULL); } void *device_get_device_obj(gs_device_t *device) diff --git a/libobs-opengl/gl-x11.c b/libobs-opengl/gl-x11.c index 4ea1ca59c..d04115b04 100644 --- a/libobs-opengl/gl-x11.c +++ b/libobs-opengl/gl-x11.c @@ -230,7 +230,6 @@ gl_windowinfo_create(const struct gs_init_data *info) extern void gl_windowinfo_destroy(struct gl_windowinfo *info) { - UNUSED_PARAMETER(info); bfree(info); } @@ -485,6 +484,15 @@ extern void gl_getclientsize(const struct gs_swap_chain *swap, uint32_t *width, free(geometry); } +extern void gl_clear_context(gs_device_t *device) +{ + Display *display = device->plat->display; + + if (!glXMakeContextCurrent(display, None, None, NULL)) { + blog(LOG_ERROR, "Failed to reset current context."); + } +} + extern void gl_update(gs_device_t *device) { Display *display = device->plat->display;