forked from Foxyz/sync_replay_project
Vendor media_kit_video 2.0.1 with the X11/EGL fix (hardware rendering on X11 with Flutter >= 3.38)
Flutter >= 3.38 keeps its EGL contexts on the raster thread, so media_kit's video output found none on the platform thread and fell back to software rendering (no shaders, ~5 fps) on X11. The patched output opens Flutter's EGL display itself, steps GDK's GLX context aside on X11 and releases its own context. Patch from a friend's agent, kept in third_party/media_kit_video/starsync/. Wayland/NVIDIA still renders on the GPU (checked); the X11 path was verified on the friend's machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
25b19f2d6a
commit
a87ee98faa
131 files changed
+15532
-6
No files matched your search
+20
@@ -0,0 +1,20 @@
|
||||
# Starsync's copy of media_kit_video
|
||||
|
||||
Upstream: `media_kit_video` 2.0.1 from pub.dev (media-kit/media-kit, MIT,
|
||||
see ../LICENSE), vendored because upstream has not released since
|
||||
December 2025. The `example/` folder is left out.
|
||||
|
||||
Patches (apply with `patch -p7` from the package root, as recorded):
|
||||
- `egl-platform-display.patch`: Flutter >= 3.38 keeps its EGL contexts on
|
||||
the raster thread, so nothing is current on the platform thread where
|
||||
the video output is created and media_kit fell back to software
|
||||
rendering (no shaders, slow). The output now opens Flutter's EGL display
|
||||
itself (eglGetPlatformDisplayEXT on the GDK X11/Wayland display) with
|
||||
Flutter's config attributes; on X11 it steps GDK's GLX context aside
|
||||
while its EGL context is in use (libglvnd refuses EGL on a thread
|
||||
holding GLX), and releases its context instead of leaving it current.
|
||||
Written by a friend's agent from a real X11 failure.
|
||||
|
||||
To update: copy the new upstream version over this folder (minus
|
||||
`example/`), re-apply the patches, run the app under X11
|
||||
(`GDK_BACKEND=x11`) and Wayland.
|
||||
@@ -0,0 +1,237 @@
|
||||
diff -ru /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv_orig/linux/include/media_kit_video/video_output.h /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv/linux/include/media_kit_video/video_output.h
|
||||
--- /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv_orig/linux/include/media_kit_video/video_output.h 2026-10-15 22:51:53.800899923 +0200
|
||||
+++ /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv/linux/include/media_kit_video/video_output.h 2026-10-15 22:54:01.866997836 +0200
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
#include <epoxy/egl.h>
|
||||
+#include <epoxy/glx.h>
|
||||
|
||||
#include "mpv/client.h"
|
||||
#include "mpv/render.h"
|
||||
@@ -94,6 +95,27 @@
|
||||
|
||||
EGLDisplay video_output_get_egl_display(VideoOutput* self);
|
||||
|
||||
+// GDK's GLX context of the calling thread, set aside while an EGL context
|
||||
+// is used there.
|
||||
+typedef struct {
|
||||
+ Display* display;
|
||||
+ GLXDrawable draw;
|
||||
+ GLXDrawable read;
|
||||
+ GLXContext context;
|
||||
+} VideoOutputGlxState;
|
||||
+
|
||||
+void video_output_glx_release(VideoOutputGlxState* state);
|
||||
+
|
||||
+void video_output_glx_restore(VideoOutputGlxState* state);
|
||||
+
|
||||
+// Makes |context| current again, or, when it is EGL_NO_CONTEXT, releases
|
||||
+// whatever context of |own_display| this thread holds.
|
||||
+void video_output_restore_egl_context(EGLDisplay own_display,
|
||||
+ EGLDisplay display,
|
||||
+ EGLSurface draw,
|
||||
+ EGLSurface read,
|
||||
+ EGLContext context);
|
||||
+
|
||||
EGLContext video_output_get_egl_context(VideoOutput* self);
|
||||
|
||||
EGLSurface video_output_get_egl_surface(VideoOutput* self);
|
||||
diff -ru /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv_orig/linux/texture_gl.cc /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv/linux/texture_gl.cc
|
||||
--- /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv_orig/linux/texture_gl.cc 2026-10-15 22:51:53.800854599 +0200
|
||||
+++ /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv/linux/texture_gl.cc 2026-10-15 22:54:01.866903241 +0200
|
||||
@@ -64,6 +64,9 @@
|
||||
TextureGL* self = TEXTURE_GL(object);
|
||||
VideoOutput* video_output = self->video_output;
|
||||
|
||||
+ VideoOutputGlxState glx_state;
|
||||
+ video_output_glx_release(&glx_state);
|
||||
+
|
||||
// Save current context
|
||||
EGLDisplay current_display = eglGetCurrentDisplay();
|
||||
EGLContext current_context = eglGetCurrentContext();
|
||||
@@ -101,10 +104,14 @@
|
||||
}
|
||||
|
||||
// Restore previous context
|
||||
- eglMakeCurrent(current_display, current_draw, current_read, current_context);
|
||||
+ video_output_restore_egl_context(egl_display, current_display,
|
||||
+ current_draw, current_read,
|
||||
+ current_context);
|
||||
}
|
||||
}
|
||||
|
||||
+ video_output_glx_restore(&glx_state);
|
||||
+
|
||||
self->current_width = 1;
|
||||
self->current_height = 1;
|
||||
self->video_output = NULL;
|
||||
diff -ru /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv_orig/linux/video_output.cc /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv/linux/video_output.cc
|
||||
--- /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv_orig/linux/video_output.cc 2026-10-15 22:51:53.800808124 +0200
|
||||
+++ /tmp/claude-1000/-home-funmaker-Desktop-kek-starsync-debug-sync-replay-project/d3b87c25-a0fb-470b-98fb-f17446c5db6d/scratchpad/mkv/linux/video_output.cc 2026-10-15 22:53:45.478604032 +0200
|
||||
@@ -15,6 +15,74 @@
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
+// Flutter >= 3.38 keeps its EGL contexts on the raster thread, so nothing is
|
||||
+// current on the platform thread (where |video_output_new| runs). Open the
|
||||
+// display the way Flutter's FlOpenGLManager does: EGL hands back the same
|
||||
+// EGLDisplay for the same platform and native display, which the EGLImage
|
||||
+// shared in |texture_gl_populate_texture| needs. eglGetPlatformDisplayEXT is
|
||||
+// looked up by hand: epoxy's dispatch for it needs a current display.
|
||||
+static EGLDisplay flutter_egl_display() {
|
||||
+ typedef EGLDisplay (*GetPlatformDisplay)(EGLenum, void*, const EGLint*);
|
||||
+ GetPlatformDisplay get_platform_display =
|
||||
+ (GetPlatformDisplay)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
+ if (get_platform_display == NULL) {
|
||||
+ return EGL_NO_DISPLAY;
|
||||
+ }
|
||||
+ EGLDisplay egl_display = EGL_NO_DISPLAY;
|
||||
+ GdkDisplay* display = gdk_display_get_default();
|
||||
+ if (GDK_IS_WAYLAND_DISPLAY(display)) {
|
||||
+ egl_display = get_platform_display(
|
||||
+ EGL_PLATFORM_WAYLAND_EXT, gdk_wayland_display_get_wl_display(display),
|
||||
+ NULL);
|
||||
+ } else if (GDK_IS_X11_DISPLAY(display)) {
|
||||
+ egl_display = get_platform_display(
|
||||
+ EGL_PLATFORM_X11_EXT, gdk_x11_display_get_xdisplay(display), NULL);
|
||||
+ }
|
||||
+ if (egl_display != EGL_NO_DISPLAY &&
|
||||
+ !eglInitialize(egl_display, NULL, NULL)) {
|
||||
+ egl_display = EGL_NO_DISPLAY;
|
||||
+ }
|
||||
+ return egl_display;
|
||||
+}
|
||||
+
|
||||
+void video_output_glx_release(VideoOutputGlxState* state) {
|
||||
+ *state = VideoOutputGlxState{};
|
||||
+ // On X11, GDK draws Flutter's view with GLX on the platform thread, and
|
||||
+ // libglvnd refuses an EGL context (EGL_BAD_ACCESS) on a thread holding a
|
||||
+ // GLX one: step it aside while ours is in use.
|
||||
+ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default()) ||
|
||||
+ glXGetCurrentContext() == NULL) {
|
||||
+ return;
|
||||
+ }
|
||||
+ state->display = glXGetCurrentDisplay();
|
||||
+ state->draw = glXGetCurrentDrawable();
|
||||
+ state->read = glXGetCurrentReadDrawable();
|
||||
+ state->context = glXGetCurrentContext();
|
||||
+ glXMakeContextCurrent(state->display, None, None, NULL);
|
||||
+}
|
||||
+
|
||||
+void video_output_glx_restore(VideoOutputGlxState* state) {
|
||||
+ if (state->context != NULL) {
|
||||
+ glXMakeContextCurrent(state->display, state->draw, state->read,
|
||||
+ state->context);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void video_output_restore_egl_context(EGLDisplay own_display,
|
||||
+ EGLDisplay display,
|
||||
+ EGLSurface draw,
|
||||
+ EGLSurface read,
|
||||
+ EGLContext context) {
|
||||
+ // Nothing was current (the platform thread): release ours, or it stays
|
||||
+ // current here and the raster thread cannot make it current (BadAccess).
|
||||
+ if (context == EGL_NO_CONTEXT) {
|
||||
+ eglMakeCurrent(own_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
+ EGL_NO_CONTEXT);
|
||||
+ } else {
|
||||
+ eglMakeCurrent(display, draw, read, context);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
struct _VideoOutput {
|
||||
GObject parent_instance;
|
||||
TextureGL* texture_gl;
|
||||
@@ -59,6 +127,8 @@
|
||||
|
||||
// Free mpv_render_context with our own isolated EGL context
|
||||
if (self->render_context != NULL) {
|
||||
+ VideoOutputGlxState glx_state;
|
||||
+ video_output_glx_release(&glx_state);
|
||||
if (self->egl_context != EGL_NO_CONTEXT) {
|
||||
eglMakeCurrent(self->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, self->egl_context);
|
||||
}
|
||||
@@ -66,9 +136,10 @@
|
||||
self->render_context = NULL;
|
||||
|
||||
// Restore Flutter's context
|
||||
- if (flutter_context != EGL_NO_CONTEXT) {
|
||||
- eglMakeCurrent(current_display, flutter_draw_surface, flutter_read_surface, flutter_context);
|
||||
- }
|
||||
+ video_output_restore_egl_context(self->egl_display, current_display,
|
||||
+ flutter_draw_surface,
|
||||
+ flutter_read_surface, flutter_context);
|
||||
+ video_output_glx_restore(&glx_state);
|
||||
}
|
||||
|
||||
// Clean up EGL resources
|
||||
@@ -140,14 +211,20 @@
|
||||
// mpv_set_option_string(self->handle, "video-timing-offset", "0");
|
||||
gboolean hardware_acceleration_supported = FALSE;
|
||||
if (self->configuration.enable_hardware_acceleration) {
|
||||
+ VideoOutputGlxState glx_state;
|
||||
+ video_output_glx_release(&glx_state);
|
||||
// Get Flutter's current EGL display (DO NOT share context)
|
||||
EGLDisplay flutter_display = eglGetCurrentDisplay();
|
||||
EGLContext flutter_context = eglGetCurrentContext();
|
||||
EGLSurface flutter_draw_surface = eglGetCurrentSurface(EGL_DRAW);
|
||||
EGLSurface flutter_read_surface = eglGetCurrentSurface(EGL_READ);
|
||||
|
||||
- if (flutter_display != EGL_NO_DISPLAY && flutter_context != EGL_NO_CONTEXT) {
|
||||
- self->egl_display = flutter_display;
|
||||
+ EGLDisplay egl_display = flutter_context != EGL_NO_CONTEXT
|
||||
+ ? flutter_display
|
||||
+ : flutter_egl_display();
|
||||
+
|
||||
+ if (egl_display != EGL_NO_DISPLAY) {
|
||||
+ self->egl_display = egl_display;
|
||||
|
||||
// Bind OpenGL ES API (Flutter uses OpenGL ES on Linux)
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
@@ -156,7 +233,21 @@
|
||||
EGLConfig config = NULL;
|
||||
EGLint config_id = 0;
|
||||
|
||||
- if (eglQueryContext(self->egl_display, flutter_context, EGL_CONFIG_ID, &config_id)) {
|
||||
+ if (flutter_context == EGL_NO_CONTEXT) {
|
||||
+ // The attributes FlOpenGLManager chooses Flutter's config with.
|
||||
+ EGLint num_configs = 0;
|
||||
+ const EGLint config_attribs[] = {
|
||||
+ EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
|
||||
+ EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
|
||||
+ EGL_NONE,
|
||||
+ };
|
||||
+ if (!eglChooseConfig(self->egl_display, config_attribs, &config, 1,
|
||||
+ &num_configs) ||
|
||||
+ num_configs == 0) {
|
||||
+ g_printerr("media_kit: VideoOutput: Failed to choose an EGL config.\n");
|
||||
+ config = NULL;
|
||||
+ }
|
||||
+ } else if (eglQueryContext(self->egl_display, flutter_context, EGL_CONFIG_ID, &config_id)) {
|
||||
g_print("media_kit: VideoOutput: Flutter's EGL config ID: %d\n", config_id);
|
||||
|
||||
// Get Flutter's exact config
|
||||
@@ -239,7 +330,9 @@
|
||||
}
|
||||
|
||||
// Restore Flutter's context
|
||||
- eglMakeCurrent(flutter_display, flutter_draw_surface, flutter_read_surface, flutter_context);
|
||||
+ video_output_restore_egl_context(
|
||||
+ self->egl_display, flutter_display, flutter_draw_surface,
|
||||
+ flutter_read_surface, flutter_context);
|
||||
} else {
|
||||
g_printerr("media_kit: VideoOutput: Failed to make isolated EGL context current. Error: 0x%x\n", eglGetError());
|
||||
}
|
||||
@@ -250,8 +343,9 @@
|
||||
g_printerr("media_kit: VideoOutput: Could not obtain Flutter's EGL config.\n");
|
||||
}
|
||||
} else {
|
||||
- g_printerr("media_kit: VideoOutput: EGL display or context is invalid.\n");
|
||||
+ g_printerr("media_kit: VideoOutput: EGL display is invalid.\n");
|
||||
}
|
||||
+ video_output_glx_restore(&glx_state);
|
||||
}
|
||||
#ifdef MPV_RENDER_API_TYPE_SW
|
||||
if (!hardware_acceleration_supported) {
|
||||
Reference in new issue
Block a user