diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:38:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:38:23 +0000 |
commit | ff6e3c025658a5fa1affd094f220b623e7e1b24b (patch) | |
tree | 9faab72d69c92d24e349d184f5869b9796f17e0c /src/opengl/swapchain.c | |
parent | Initial commit. (diff) | |
download | libplacebo-ff6e3c025658a5fa1affd094f220b623e7e1b24b.tar.xz libplacebo-ff6e3c025658a5fa1affd094f220b623e7e1b24b.zip |
Adding upstream version 6.338.2.upstream/6.338.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/opengl/swapchain.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/opengl/swapchain.c b/src/opengl/swapchain.c new file mode 100644 index 0000000..46d5f9e --- /dev/null +++ b/src/opengl/swapchain.c @@ -0,0 +1,278 @@ +/* + * This file is part of libplacebo. + * + * libplacebo is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * libplacebo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "common.h" +#include "formats.h" +#include "gpu.h" +#include "swapchain.h" +#include "utils.h" +#include "pl_thread.h" + +struct priv { + struct pl_sw_fns impl; + + struct pl_opengl_swapchain_params params; + pl_opengl gl; + pl_mutex lock; + bool has_sync; + + // current parameters + pl_tex fb; + bool frame_started; + + // vsync fences + int swapchain_depth; + PL_ARRAY(GLsync) vsync_fences; +}; + +static const struct pl_sw_fns opengl_swapchain; + +pl_swapchain pl_opengl_create_swapchain(pl_opengl pl_gl, + const struct pl_opengl_swapchain_params *params) +{ + pl_gpu gpu = pl_gl->gpu; + + if (params->max_swapchain_depth < 0) { + PL_ERR(gpu, "Tried specifying negative swapchain depth?"); + return NULL; + } + + if (!gl_make_current(pl_gl)) + return NULL; + + struct pl_swapchain_t *sw = pl_zalloc_obj(NULL, sw, struct priv); + sw->log = gpu->log; + sw->gpu = gpu; + + struct priv *p = PL_PRIV(sw); + pl_mutex_init(&p->lock); + p->impl = opengl_swapchain; + p->params = *params; + p->has_sync = pl_opengl_has_ext(pl_gl, "GL_ARB_sync"); + p->gl = pl_gl; + + gl_release_current(pl_gl); + return sw; +} + +static void gl_sw_destroy(pl_swapchain sw) +{ + pl_gpu gpu = sw->gpu; + struct priv *p = PL_PRIV(sw); + + pl_gpu_flush(gpu); + pl_tex_destroy(gpu, &p->fb); + pl_mutex_destroy(&p->lock); + pl_free((void *) sw); +} + +static int gl_sw_latency(pl_swapchain sw) +{ + struct priv *p = PL_PRIV(sw); + return p->params.max_swapchain_depth; +} + +static bool gl_sw_resize(pl_swapchain sw, int *width, int *height) +{ + struct priv *p = PL_PRIV(sw); + const int w = *width, h = *height; + + pl_mutex_lock(&p->lock); + if (p->fb && w == p->fb->params.w && h == p->fb->params.h) { + pl_mutex_unlock(&p->lock); + return true; + } + + if (p->frame_started && (w || h)) { + PL_ERR(sw, "Tried resizing the swapchain while a frame was in progress! " + "Please submit the current frame first."); + pl_mutex_unlock(&p->lock); + return false; + } + + if (w && h) { + pl_tex_destroy(sw->gpu, &p->fb); + p->fb = pl_opengl_wrap(sw->gpu, pl_opengl_wrap_params( + .framebuffer = p->params.framebuffer.id, + .width = w, + .height = h, + )); + if (!p->fb) { + PL_ERR(sw, "Failed wrapping OpenGL framebuffer!"); + pl_mutex_unlock(&p->lock); + return false; + } + } + + if (!p->fb) { + PL_ERR(sw, "Tried calling `pl_swapchain_resize` with unknown size! " + "This is forbidden for OpenGL. The first call to " + "`pl_swapchain_resize` must include the width and height of the " + "swapchain, because there's no way to figure this out from " + "within the API."); + pl_mutex_unlock(&p->lock); + return false; + } + + *width = p->fb->params.w; + *height = p->fb->params.h; + pl_mutex_unlock(&p->lock); + return true; +} + +void pl_opengl_swapchain_update_fb(pl_swapchain sw, + const struct pl_opengl_framebuffer *fb) +{ + struct priv *p = PL_PRIV(sw); + pl_mutex_lock(&p->lock); + if (p->frame_started) { + PL_ERR(sw,"Tried calling `pl_opengl_swapchain_update_fb` while a frame " + "was in progress! Please submit the current frame first."); + pl_mutex_unlock(&p->lock); + return; + } + + if (p->params.framebuffer.id != fb->id) + pl_tex_destroy(sw->gpu, &p->fb); + + p->params.framebuffer = *fb; + pl_mutex_unlock(&p->lock); +} + +static bool gl_sw_start_frame(pl_swapchain sw, + struct pl_swapchain_frame *out_frame) +{ + struct priv *p = PL_PRIV(sw); + pl_mutex_lock(&p->lock); + bool ok = false; + + if (!p->fb) { + PL_ERR(sw, "Unknown framebuffer size. Please call `pl_swapchain_resize` " + "before `pl_swapchain_start_frame` for OpenGL swapchains!"); + goto error; + } + + if (p->frame_started) { + PL_ERR(sw, "Attempted calling `pl_swapchain_start` while a frame was " + "already in progress! Call `pl_swapchain_submit_frame` first."); + goto error; + } + + if (!gl_make_current(p->gl)) + goto error; + + *out_frame = (struct pl_swapchain_frame) { + .fbo = p->fb, + .flipped = !p->params.framebuffer.flipped, + .color_repr = { + .sys = PL_COLOR_SYSTEM_RGB, + .levels = PL_COLOR_LEVELS_FULL, + .alpha = p->fb->params.format->num_components == 4 + ? PL_ALPHA_PREMULTIPLIED + : PL_ALPHA_UNKNOWN, + .bits = { + // Just use the red channel in the absence of anything more + // sane to do, because the red channel is both guaranteed to + // exist and also typically has the minimum number of bits + // (which is arguably what matters for dithering) + .sample_depth = p->fb->params.format->component_depth[0], + .color_depth = p->fb->params.format->component_depth[0], + }, + }, + .color_space = pl_color_space_monitor, + }; + + p->frame_started = gl_check_err(sw->gpu, "gl_sw_start_frame"); + if (!p->frame_started) + goto error; + + // keep p->lock held + gl_release_current(p->gl); + return true; + +error: + gl_release_current(p->gl); + pl_mutex_unlock(&p->lock); + return ok; +} + +static bool gl_sw_submit_frame(pl_swapchain sw) +{ + struct priv *p = PL_PRIV(sw); + struct gl_ctx *glctx = PL_PRIV(p->gl); + const gl_funcs *gl = &glctx->func; + if (!gl_make_current(p->gl)) { + p->frame_started = false; + pl_mutex_unlock(&p->lock); + return false; + } + + pl_assert(p->frame_started); + if (p->has_sync && p->params.max_swapchain_depth) { + GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + if (fence) + PL_ARRAY_APPEND(sw, p->vsync_fences, fence); + } + + gl->Flush(); + p->frame_started = false; + bool ok = gl_check_err(sw->gpu, "gl_sw_submit_frame"); + gl_release_current(p->gl); + pl_mutex_unlock(&p->lock); + + return ok; +} + +static void gl_sw_swap_buffers(pl_swapchain sw) +{ + struct priv *p = PL_PRIV(sw); + struct gl_ctx *glctx = PL_PRIV(p->gl); + const gl_funcs *gl = &glctx->func; + if (!p->params.swap_buffers) { + PL_ERR(sw, "`pl_swapchain_swap_buffers` called but no " + "`params.swap_buffers` callback set!"); + return; + } + + pl_mutex_lock(&p->lock); + if (!gl_make_current(p->gl)) { + pl_mutex_unlock(&p->lock); + return; + } + + p->params.swap_buffers(p->params.priv); + + const int max_depth = p->params.max_swapchain_depth; + while (max_depth && p->vsync_fences.num >= max_depth) { + gl->ClientWaitSync(p->vsync_fences.elem[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9); + gl->DeleteSync(p->vsync_fences.elem[0]); + PL_ARRAY_REMOVE_AT(p->vsync_fences, 0); + } + + gl_check_err(sw->gpu, "gl_sw_swap_buffers"); + gl_release_current(p->gl); + pl_mutex_unlock(&p->lock); +} + +static const struct pl_sw_fns opengl_swapchain = { + .destroy = gl_sw_destroy, + .latency = gl_sw_latency, + .resize = gl_sw_resize, + .start_frame = gl_sw_start_frame, + .submit_frame = gl_sw_submit_frame, + .swap_buffers = gl_sw_swap_buffers, +}; |