From c7d1457bbbcdfd35bb8339df21506e89e6765bee Mon Sep 17 00:00:00 2001 From: KhooLy <73142442+KhooLy@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:23:44 +0300 Subject: [PATCH] chore: add ANGLE/EGL diagnostic example cargo run --example egl_diag recreates the player's ANGLE D3D11 GLES context and prints the EGL client/display extensions, GL extensions, and every check mpv's hwdec_d3d11egl probe performs, to diagnose why zero- copy d3d11va falls back to d3d11va-copy on a given machine. Co-Authored-By: Claude Fable 5 --- src-tauri/examples/egl_diag.rs | 188 +++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src-tauri/examples/egl_diag.rs diff --git a/src-tauri/examples/egl_diag.rs b/src-tauri/examples/egl_diag.rs new file mode 100644 index 0000000..6ef4834 --- /dev/null +++ b/src-tauri/examples/egl_diag.rs @@ -0,0 +1,188 @@ +#![cfg(target_os = "windows")] + +use std::ffi::{c_char, c_void, CStr, CString}; +use std::ptr; +use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress, LoadLibraryExA}; +use windows_sys::Win32::UI::WindowsAndMessaging::{CreateWindowExA, WS_POPUP}; + +const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: u32 = 0x0000_1000; +const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: u32 = 0x0000_0100; + +const EGL_NONE: i32 = 0x3038; +const EGL_OPENGL_ES2_BIT: i32 = 0x0004; +const EGL_OPENGL_ES3_BIT_KHR: i32 = 0x0040; +const EGL_SURFACE_TYPE: i32 = 0x3033; +const EGL_WINDOW_BIT: i32 = 0x0004; +const EGL_RENDERABLE_TYPE: i32 = 0x3040; +const EGL_RED_SIZE: i32 = 0x3024; +const EGL_GREEN_SIZE: i32 = 0x3023; +const EGL_BLUE_SIZE: i32 = 0x3022; +const EGL_ALPHA_SIZE: i32 = 0x3021; +const EGL_CONTEXT_CLIENT_VERSION: i32 = 0x3098; +const EGL_EXTENSIONS: i32 = 0x3055; +const EGL_VENDOR: i32 = 0x3053; +const EGL_VERSION: i32 = 0x3054; +const EGL_PLATFORM_ANGLE_ANGLE: i32 = 0x3202; +const EGL_PLATFORM_ANGLE_TYPE_ANGLE: i32 = 0x3203; +const EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: i32 = 0x3208; +const GL_VENDOR: u32 = 0x1F00; +const GL_RENDERER: u32 = 0x1F01; +const GL_VERSION: u32 = 0x1F02; +const GL_EXTENSIONS: u32 = 0x1F03; + +type FnGetProcAddress = unsafe extern "system" fn(*const c_char) -> *mut c_void; +type FnGetPlatformDisplayExt = unsafe extern "system" fn(i32, *mut c_void, *const i32) -> *mut c_void; +type FnInitialize = unsafe extern "system" fn(*mut c_void, *mut i32, *mut i32) -> i32; +type FnChooseConfig = unsafe extern "system" fn(*mut c_void, *const i32, *mut *mut c_void, i32, *mut i32) -> i32; +type FnCreateContext = unsafe extern "system" fn(*mut c_void, *mut c_void, *mut c_void, *const i32) -> *mut c_void; +type FnCreateWindowSurface = unsafe extern "system" fn(*mut c_void, *mut c_void, isize, *const i32) -> *mut c_void; +type FnMakeCurrent = unsafe extern "system" fn(*mut c_void, *mut c_void, *mut c_void, *mut c_void) -> i32; +type FnQueryString = unsafe extern "system" fn(*mut c_void, i32) -> *const c_char; +type FnGlGetString = unsafe extern "system" fn(u32) -> *const u8; + +fn load_dll(name: &str) -> isize { + let manifest_lib = format!("{}\\lib\\{}", env!("CARGO_MANIFEST_DIR"), name); + for path in [manifest_lib.as_str(), name] { + if let Ok(cpath) = CString::new(path) { + let flags = if path.contains('\\') { + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR + } else { + 0 + }; + let handle = unsafe { LoadLibraryExA(cpath.as_ptr() as *const u8, 0, flags) }; + if handle != 0 { + println!("loaded {path}"); + return handle; + } + } + } + panic!("failed to load {name}"); +} + +fn proc(module: isize, name: &str) -> T { + let cname = CString::new(name).unwrap(); + let addr = unsafe { GetProcAddress(module as _, cname.as_ptr() as *const u8) } + .unwrap_or_else(|| panic!("missing export {name}")); + unsafe { std::mem::transmute_copy::<_, T>(&addr) } +} + +fn has(haystack: &str, ext: &str) -> bool { + haystack.split_ascii_whitespace().any(|e| e == ext) +} + +fn cstr(p: *const c_char) -> String { + if p.is_null() { + "".to_string() + } else { + unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned() + } +} + +fn main() { + let egl = load_dll("libEGL.dll"); + let get_proc: FnGetProcAddress = proc(egl, "eglGetProcAddress"); + let initialize: FnInitialize = proc(egl, "eglInitialize"); + let choose_config: FnChooseConfig = proc(egl, "eglChooseConfig"); + let create_context: FnCreateContext = proc(egl, "eglCreateContext"); + let create_surface: FnCreateWindowSurface = proc(egl, "eglCreateWindowSurface"); + let make_current: FnMakeCurrent = proc(egl, "eglMakeCurrent"); + let query_string: FnQueryString = proc(egl, "eglQueryString"); + + let client_exts = cstr(unsafe { query_string(ptr::null_mut(), EGL_EXTENSIONS) }); + println!("\n== EGL client extensions ==\n{client_exts}"); + + let get_platform_display: FnGetPlatformDisplayExt = { + let name = CString::new("eglGetPlatformDisplayEXT").unwrap(); + let raw = unsafe { get_proc(name.as_ptr()) }; + assert!(!raw.is_null(), "no eglGetPlatformDisplayEXT"); + unsafe { std::mem::transmute(raw) } + }; + + let display_attribs = [EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_NONE]; + let display = unsafe { get_platform_display(EGL_PLATFORM_ANGLE_ANGLE, ptr::null_mut(), display_attribs.as_ptr()) }; + assert!(!display.is_null(), "eglGetPlatformDisplayEXT failed"); + + let (mut major, mut minor) = (0i32, 0i32); + assert!(unsafe { initialize(display, &mut major, &mut minor) } != 0, "eglInitialize failed"); + println!("\nEGL {major}.{minor} vendor: {} version: {}", cstr(unsafe { query_string(display, EGL_VENDOR) }), cstr(unsafe { query_string(display, EGL_VERSION) })); + + let display_exts = cstr(unsafe { query_string(display, EGL_EXTENSIONS) }); + println!("\n== EGL display extensions (ANGLE D3D11) ==\n{display_exts}"); + + let hwnd = unsafe { + let class = CString::new("STATIC").unwrap(); + let title = CString::new("egl-diag").unwrap(); + CreateWindowExA(0, class.as_ptr() as *const u8, title.as_ptr() as *const u8, WS_POPUP, 0, 0, 32, 32, 0 as _, 0 as _, GetModuleHandleA(ptr::null()) as _, ptr::null()) + }; + assert!(hwnd as isize != 0, "CreateWindowExA failed"); + + let config_attribs = [ + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT_KHR, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, + EGL_NONE, + ]; + let mut config: *mut c_void = ptr::null_mut(); + let mut num = 0i32; + assert!(unsafe { choose_config(display, config_attribs.as_ptr(), &mut config, 1, &mut num) } != 0 && num > 0, "eglChooseConfig failed"); + + let surface = unsafe { create_surface(display, config, hwnd as isize, ptr::null()) }; + assert!(!surface.is_null(), "eglCreateWindowSurface failed"); + + let gles = load_dll("libGLESv2.dll"); + let gl_get_string: FnGlGetString = proc(gles, "glGetString"); + + for client_version in [3, 2] { + let attribs = [EGL_CONTEXT_CLIENT_VERSION, client_version, EGL_NONE]; + let context = unsafe { create_context(display, config, ptr::null_mut(), attribs.as_ptr()) }; + if context.is_null() { + println!("\n== GLES{client_version}: context creation FAILED =="); + continue; + } + assert!(unsafe { make_current(display, surface, surface, context) } != 0, "eglMakeCurrent failed"); + + let version = cstr(unsafe { gl_get_string(GL_VERSION) } as *const c_char); + let renderer = cstr(unsafe { gl_get_string(GL_RENDERER) } as *const c_char); + let vendor = cstr(unsafe { gl_get_string(GL_VENDOR) } as *const c_char); + let gl_exts = cstr(unsafe { gl_get_string(GL_EXTENSIONS) } as *const c_char); + + println!("\n== GLES{client_version} context =="); + println!("GL_VERSION: {version}"); + println!("GL_RENDERER: {renderer}"); + println!("GL_VENDOR: {vendor}"); + + println!("\n-- mpv hwdec_d3d11egl probe checklist (GLES{client_version}) --"); + for ext in [ + "EGL_ANGLE_d3d_share_handle_client_buffer", + "EGL_ANGLE_stream_producer_d3d_texture", + "EGL_EXT_device_query", + "EGL_KHR_stream", + "EGL_KHR_stream_consumer_gltexture", + "EGL_ANGLE_device_d3d", + "EGL_ANGLE_device_d3d11", + ] { + println!("display {ext}: {}", if has(&display_exts, ext) { "YES" } else { "MISSING" }); + } + for ext in ["GL_OES_EGL_image_external", "GL_OES_EGL_image_external_essl3", "GL_EXT_texture_rg"] { + println!("gl {ext}: {}", if has(&gl_exts, ext) { "YES" } else { "MISSING" }); + } + + for name in [ + "eglCreateStreamKHR", + "eglDestroyStreamKHR", + "eglStreamConsumerAcquireKHR", + "eglStreamConsumerReleaseKHR", + "eglStreamConsumerGLTextureExternalAttribsNV", + "eglCreateStreamProducerD3DTextureANGLE", + "eglStreamPostD3DTextureANGLE", + "eglQueryDisplayAttribEXT", + "eglQueryDeviceAttribEXT", + ] { + let cname = CString::new(name).unwrap(); + let addr = unsafe { get_proc(cname.as_ptr()) }; + println!("fn {name}: {}", if addr.is_null() { "MISSING" } else { "YES" }); + } + + unsafe { make_current(display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()) }; + } +}