diff --git a/video/out/opengl/context_sdl.c b/video/out/opengl/context_sdl.c new file mode 100644 index 0000000000..bfe46e03bf --- /dev/null +++ b/video/out/opengl/context_sdl.c @@ -0,0 +1,244 @@ +/* + * This file is part of mpv. + * + * mpv 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. + * + * mpv 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 mpv. If not, see . + */ + +#include + +#include "common/common.h" +#include "common/msg.h" +#include "video/mp_image.h" + +#include "context.h" +#include "ra_gl.h" + +struct priv { + GL gl; + SDL_Window *window; + SDL_GLContext glcontext; + int w, h; +}; + +static void sdl_uninit(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + + ra_gl_ctx_uninit(ctx); + + if (p->glcontext) { + SDL_GL_DeleteContext(p->glcontext); + p->glcontext = NULL; + } + + if (p->window) { + SDL_DestroyWindow(p->window); + p->window = NULL; + } + + SDL_Quit(); +} + +static void sdl_swap_buffers(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + SDL_GL_SwapWindow(p->window); +} + +static bool sdl_gl_create_window(struct ra_ctx *ctx, int gl_version) +{ + struct priv *p = ctx->priv; + struct vo *vo = ctx->vo; + + // For maximum compatibility, don't request specific GL version + // Let SDL/system provide whatever OpenGL is available + // This is especially important for macOS 10.6 which doesn't support Core profile + + // Reset all GL attributes to defaults first + SDL_GL_ResetAttributes(); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + // Create window + int w = vo->dwidth; + int h = vo->dheight; + + Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + + // Use vo_get_window_title if available, otherwise use "mpv" + const char *title = "mpv"; + + p->window = SDL_CreateWindow(title, + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + w, h, flags); + if (!p->window) { + MP_FATAL(vo, "Failed to create SDL window: %s\n", SDL_GetError()); + return false; + } + + p->w = w; + p->h = h; + + // Create OpenGL context + p->glcontext = SDL_GL_CreateContext(p->window); + if (!p->glcontext) { + MP_FATAL(vo, "Failed to create OpenGL context: %s\n", SDL_GetError()); + SDL_DestroyWindow(p->window); + p->window = NULL; + return false; + } + + // Make context current + if (SDL_GL_MakeCurrent(p->window, p->glcontext) < 0) { + MP_FATAL(vo, "Failed to make OpenGL context current: %s\n", SDL_GetError()); + return false; + } + + // Get actual GL version + int actual_major = 0, actual_minor = 0; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &actual_major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &actual_minor); + + MP_VERBOSE(vo, "OpenGL context created: %d.%d (auto-detected)\n", + actual_major, actual_minor); + + return true; +} + +static bool sdl_reconfig(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + struct vo *vo = ctx->vo; + + int w = vo->dwidth; + int h = vo->dheight; + + if (p->window) { + SDL_SetWindowSize(p->window, w, h); + p->w = w; + p->h = h; + } + + ra_gl_ctx_resize(ctx->swapchain, w, h, 0); + return true; +} + +static int sdl_control(struct ra_ctx *ctx, int *events, int request, void *arg) +{ + struct priv *p = ctx->priv; + + switch (request) { + case VOCTRL_CHECK_EVENTS: { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + // Signal window close + *events |= VO_EVENT_EXPOSE; + break; + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_RESIZED || + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + p->w = event.window.data1; + p->h = event.window.data2; + *events |= VO_EVENT_RESIZE; + } else if (event.window.event == SDL_WINDOWEVENT_EXPOSED) { + *events |= VO_EVENT_EXPOSE; + } + break; + } + } + return VO_TRUE; + } + case VOCTRL_SET_CURSOR_VISIBILITY: + if (p->window) + SDL_ShowCursor(*(bool *)arg ? SDL_ENABLE : SDL_DISABLE); + return VO_TRUE; + case VOCTRL_UPDATE_WINDOW_TITLE: + if (p->window) + SDL_SetWindowTitle(p->window, (const char *)arg); + return VO_TRUE; + } + + return VO_NOTIMPL; +} + +static void sdl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info) +{ + // SDL doesn't provide detailed vsync info + info->vsync_duration = 0; + info->skipped_vsyncs = 0; + info->last_queue_display_time = 0; +} + +static bool sdl_init(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); + struct vo *vo = ctx->vo; + GL *gl = &p->gl; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + MP_FATAL(vo, "SDL_Init failed: %s\n", SDL_GetError()); + return false; + } + + // Try to create OpenGL context (auto-detect version) + if (!sdl_gl_create_window(ctx, 0)) { + MP_FATAL(vo, "Failed to create SDL window with OpenGL context\n"); + SDL_Quit(); + return false; + } + + mpgl_load_functions(gl, (void *)SDL_GL_GetProcAddress, NULL, vo->log); + + // Set vsync + SDL_GL_SetSwapInterval(1); + + struct ra_ctx_params params = { + .swap_buffers = sdl_swap_buffers, + .get_vsync = sdl_get_vsync, + }; + + if (!ra_gl_ctx_init(ctx, gl, params)) { + MP_FATAL(vo, "Failed to initialize rendering abstraction\n"); + if (p->glcontext) { + SDL_GL_DeleteContext(p->glcontext); + p->glcontext = NULL; + } + if (p->window) { + SDL_DestroyWindow(p->window); + p->window = NULL; + } + SDL_Quit(); + return false; + } + + return true; +} + +const struct ra_ctx_fns ra_ctx_sdl = { + .type = "opengl", + .name = "sdl", + .description = "SDL2 OpenGL", + .reconfig = sdl_reconfig, + .control = sdl_control, + .init = sdl_init, + .uninit = sdl_uninit, +}; diff --git a/meson.build b/meson.build index 5ffe559768..a9ecac43fe 100644 --- a/meson.build +++ b/meson.build @@ -999,8 +999,10 @@ sdl2_video = get_option('sdl2-video').require( ) features += {'sdl2-video': sdl2_video.allowed()} if features['sdl2-video'] - sources += files('video/out/vo_sdl.c') + sources += files('video/out/vo_sdl.c', + 'video/out/opengl/context_sdl.c') dependencies += sdl2 + features += {'gl': true} endif shaderc = dependency('shaderc', required: diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c index 60c0641a70..a4d64ba155 100644 --- a/video/out/gpu/context.c +++ b/video/out/gpu/context.c @@ -42,6 +42,7 @@ extern const struct ra_ctx_fns ra_ctx_wgl; extern const struct ra_ctx_fns ra_ctx_angle; extern const struct ra_ctx_fns ra_ctx_dxgl; extern const struct ra_ctx_fns ra_ctx_android; +extern const struct ra_ctx_fns ra_ctx_sdl; /* Vulkan */ extern const struct ra_ctx_fns ra_ctx_vulkan_wayland; @@ -120,6 +121,9 @@ static const struct ra_ctx_fns *const contexts[] = { #if HAVE_GL_X11 &ra_ctx_glx, #endif +#if HAVE_SDL2_VIDEO + &ra_ctx_sdl, +#endif #if HAVE_EGL_DRM &ra_ctx_drm_egl, #endif