From e59125692262d20ab56dabf205368a63d83f7a0d Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 12 Oct 2013 16:28:10 -0700 Subject: [PATCH] add FBOs and render target loading --- libobs-opengl/gl-helpers.c | 2 +- libobs-opengl/gl-helpers.h | 8 +- libobs-opengl/gl-stagesurf.c | 4 +- libobs-opengl/gl-subsystem.c | 221 ++++++++++++++++++++++++++++++++++- libobs-opengl/gl-subsystem.h | 21 +++- libobs-opengl/gl-zstencil.c | 14 ++- 6 files changed, 262 insertions(+), 8 deletions(-) diff --git a/libobs-opengl/gl-helpers.c b/libobs-opengl/gl-helpers.c index 9607b121..cd6ba6eb 100644 --- a/libobs-opengl/gl-helpers.c +++ b/libobs-opengl/gl-helpers.c @@ -55,8 +55,8 @@ bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels, } bool gl_copy_texture(struct gs_device *device, - GLuint src, GLenum src_target, GLuint dst, GLenum dst_target, + GLuint src, GLenum src_target, uint32_t width, uint32_t height) { bool success = false; diff --git a/libobs-opengl/gl-helpers.h b/libobs-opengl/gl-helpers.h index 6f9ef092..7517e968 100644 --- a/libobs-opengl/gl-helpers.h +++ b/libobs-opengl/gl-helpers.h @@ -78,6 +78,12 @@ static inline bool gl_bind_renderbuffer(GLenum target, GLuint buffer) return gl_success("glBindRendebuffer"); } +static inline bool gl_bind_framebuffer(GLenum target, GLuint buffer) +{ + glBindFramebuffer(target, buffer); + return gl_success("glBindFramebuffer"); +} + static inline bool gl_tex_param_f(GLenum target, GLenum param, GLfloat val) { glTexParameterf(target, param, val); @@ -101,8 +107,8 @@ extern bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels, uint32_t width, uint32_t height, uint32_t size, void ***p_data); extern bool gl_copy_texture(struct gs_device *device, - GLuint src, GLenum src_target, GLuint dst, GLenum dst_target, + GLuint src, GLenum src_target, uint32_t width, uint32_t height); extern bool gl_create_buffer(GLenum target, GLuint *buffer, GLsizeiptr size, diff --git a/libobs-opengl/gl-stagesurf.c b/libobs-opengl/gl-stagesurf.c index 759fefa6..881e8947 100644 --- a/libobs-opengl/gl-stagesurf.c +++ b/libobs-opengl/gl-stagesurf.c @@ -126,8 +126,8 @@ void device_stage_texture(device_t device, stagesurf_t dst, texture_t src) if (!can_stage(dst, tex2d)) goto failed; - if (!gl_copy_texture(device, tex2d->base.texture, GL_TEXTURE_2D, - dst->texture, GL_TEXTURE_2D, + if (!gl_copy_texture(device, dst->texture, GL_TEXTURE_2D, + tex2d->base.texture, GL_TEXTURE_2D, dst->width, dst->height)) goto failed; diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index 4b2adbdc..5d434ee2 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -56,8 +56,14 @@ fail: void device_destroy(device_t device) { if (device) { + size_t i; + for (i = 0; i < device->fbos.num; i++) + fbo_info_destroy(device->fbos.array[i]); + if (device->pipeline) glDeleteProgramPipelines(1, &device->pipeline); + + da_free(device->fbos); gl_platform_destroy(device->plat); bfree(device); } @@ -348,18 +354,229 @@ zstencil_t device_getzstenciltarget(device_t device) return device->cur_zstencil_buffer; } -void device_setrendertarget(device_t device, texture_t tex, - zstencil_t zstencil) +static bool get_tex_dimensions(texture_t tex, uint32_t *width, uint32_t *height) +{ + if (tex->type == GS_TEXTURE_2D) { + struct gs_texture_2d *tex2d = (struct gs_texture_2d*)tex; + *width = tex2d->width; + *height = tex2d->height; + return true; + + } else if (tex->type == GS_TEXTURE_CUBE) { + struct gs_texture_cube *cube = (struct gs_texture_cube*)tex; + *width = cube->size; + *height = cube->size; + return true; + } + + blog(LOG_ERROR, "Texture must be 2D or cubemap"); + return false; +} + +/* + * This automatically manages FBOs so that render targets are always given + * an FBO that matches their width/height/format to maximize optimization + */ +static struct fbo_info *get_fbo(struct gs_device *device, texture_t tex) +{ + size_t i; + uint32_t width, height; + GLuint fbo; + struct fbo_info *ptr; + + if (!get_tex_dimensions(tex, &width, &height)) + return NULL; + + for (i = 0; i < device->fbos.num; i++) { + ptr = device->fbos.array[i]; + + if (ptr->width == width && ptr->height == height && + ptr->format == tex->format) + return ptr; + } + + glGenFramebuffers(1, &fbo); + if (!gl_success("glGenFramebuffers")) + return NULL; + + ptr = bmalloc(sizeof(struct fbo_info)); + ptr->fbo = fbo; + ptr->width = width; + ptr->height = height; + ptr->format = tex->format; + ptr->cur_render_target = NULL; + ptr->cur_render_side = 0; + ptr->cur_zstencil_buffer = NULL; + + da_push_back(device->fbos, &ptr); + return ptr; +} + +static bool set_current_fbo(device_t device, struct fbo_info *fbo) +{ + if (device->cur_fbo != fbo) { + GLuint fbo_obj = fbo ? fbo->fbo : 0; + if (!gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, fbo_obj)) + return false; + } + + device->cur_fbo = fbo; + return true; +} + +static bool attach_rendertarget(struct fbo_info *fbo, texture_t tex, int side) +{ + if (fbo->cur_render_target == tex) + return true; + + fbo->cur_render_target = tex; + + if (tex->type == GS_TEXTURE_2D) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + tex->texture, 0); + + } else if (tex->type == GS_TEXTURE_CUBE) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, + tex->texture, 0); + + } else { + return false; + } + + return gl_success("glFramebufferTexture2D"); +} + +static bool attach_zstencil(struct fbo_info *fbo, zstencil_t zs) +{ + GLuint zsbuffer = 0; + GLenum zs_attachment = GL_DEPTH_STENCIL_ATTACHMENT; + + if (fbo->cur_zstencil_buffer == zs) + return true; + + fbo->cur_zstencil_buffer = zs; + + if (zs) { + zsbuffer = zs->buffer; + zs_attachment = zs->attachment; + } + + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, + zs_attachment, GL_RENDERBUFFER, zsbuffer); + if (!gl_success("glFramebufferRenderbuffer")) + return false; + + return true; +} + +static bool set_target(device_t device, texture_t tex, int side, zstencil_t zs) { + struct fbo_info *fbo; + + if (device->cur_render_target == tex && + device->cur_zstencil_buffer == zs && + device->cur_render_side == side) + return true; + + device->cur_render_target = tex; + device->cur_render_side = side; + device->cur_zstencil_buffer = zs; + + if (!tex) + return set_current_fbo(device, NULL); + + fbo = get_fbo(device, tex); + if (!fbo) + return false; + + set_current_fbo(device, fbo); + + if (!attach_rendertarget(fbo, tex, side)) + return false; + if (!attach_zstencil(fbo, zs)) + return false; + + return true; +} + +void device_setrendertarget(device_t device, texture_t tex, zstencil_t zstencil) +{ + if (tex->type != GS_TEXTURE_2D) { + blog(LOG_ERROR, "Texture is not a 2D texture"); + goto fail; + } + + if (!tex->is_render_target) { + blog(LOG_ERROR, "Texture is not a render target"); + goto fail; + } + + if (!set_target(device, tex, 0, zstencil)) + goto fail; + + return; + +fail: + blog(LOG_ERROR, "device_setrendertarget (GL) failed"); } void device_setcuberendertarget(device_t device, texture_t cubetex, int side, zstencil_t zstencil) { + if (cubetex->type != GS_TEXTURE_CUBE) { + blog(LOG_ERROR, "Texture is not a cube texture"); + goto fail; + } + + if (!cubetex->is_render_target) { + blog(LOG_ERROR, "Texture is not a render target"); + goto fail; + } + + if (!set_target(device, cubetex, side, zstencil)) + goto fail; + + return; + +fail: + blog(LOG_ERROR, "device_setcuberendertarget (GL) failed"); } void device_copy_texture(device_t device, texture_t dst, texture_t src) { + struct gs_texture_2d *src2d = (struct gs_texture_2d*)src; + struct gs_texture_2d *dst2d = (struct gs_texture_2d*)dst; + + if (dst->format != src->format) { + blog(LOG_ERROR, "Source and destination texture formats do " + "not match"); + goto fail; + } + + if (dst->type != GS_TEXTURE_2D || src->type != GS_TEXTURE_2D) { + blog(LOG_ERROR, "Source and destination textures must be 2D " + "textures"); + goto fail; + } + + if (dst2d->width != src2d->width || dst2d->height != src2d->height) { + blog(LOG_ERROR, "Source and destination textures must have " + "the same dimensions"); + goto fail; + } + + if (!gl_copy_texture(device, dst->texture, dst->gl_target, + src->texture, src->gl_target, + src2d->width, src2d->height)) + goto fail; + + return; + +fail: + blog(LOG_ERROR, "device_copy_texture (GL) failed"); } void device_beginscene(device_t device) diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index 58e8c524..5c53db29 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/libobs-opengl/gl-subsystem.h @@ -326,6 +326,7 @@ struct gs_stage_surface { struct gs_zstencil_buffer { device_t device; GLuint buffer; + GLuint attachment; GLenum format; }; @@ -335,6 +336,23 @@ struct gs_swap_chain { struct gs_init_data info; }; +struct fbo_info { + GLuint fbo; + uint32_t width; + uint32_t height; + enum gs_color_format format; + + texture_t cur_render_target; + int cur_render_side; + zstencil_t cur_zstencil_buffer; +}; + +static inline void fbo_info_destroy(struct fbo_info *fbo) +{ + glDeleteFramebuffers(1, &fbo->fbo); + gl_success("glDeleteFramebuffers"); +} + struct gs_device { struct gl_platform *plat; GLuint pipeline; @@ -351,7 +369,8 @@ struct gs_device { shader_t cur_pixel_shader; swapchain_t cur_swap; - bool texture_changed[GS_MAX_TEXTURES]; + DARRAY(struct fbo_info*) fbos; + struct fbo_info *cur_fbo; }; extern struct gl_platform *gl_platform_create(device_t device, diff --git a/libobs-opengl/gl-zstencil.c b/libobs-opengl/gl-zstencil.c index e7f7fa82..abdfc244 100644 --- a/libobs-opengl/gl-zstencil.c +++ b/libobs-opengl/gl-zstencil.c @@ -35,6 +35,17 @@ static bool gl_init_zsbuffer(struct gs_zstencil_buffer *zs, uint32_t width, return true; } +static inline GLenum get_attachment(enum gs_zstencil_format format) +{ + switch (format) { + case GS_Z16: return GL_DEPTH_ATTACHMENT; + case GS_Z24_S8: return GL_DEPTH_STENCIL_ATTACHMENT; + case GS_Z32F: return GL_DEPTH_ATTACHMENT; + case GS_Z32F_S8X24: return GL_DEPTH_STENCIL_ATTACHMENT; + default: return 0; + } +} + zstencil_t device_create_zstencil(device_t device, uint32_t width, uint32_t height, enum gs_zstencil_format format) { @@ -42,7 +53,8 @@ zstencil_t device_create_zstencil(device_t device, uint32_t width, zs = bmalloc(sizeof(struct gs_zstencil_buffer)); memset(zs, 0, sizeof(struct gs_zstencil_buffer)); - zs->format = convert_zstencil_format(format); + zs->format = convert_zstencil_format(format); + zs->attachment = get_attachment(format); if (!gl_init_zsbuffer(zs, width, height)) { blog(LOG_ERROR, "device_create_zstencil (GL) failed"); -- GitLab