mirror of
https://github.com/FluxaMedia/fluxa-desktop.git
synced 2026-08-05 08:18:39 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
parent
14a8e5b434
commit
c7d1457bbb
1 changed files with 188 additions and 0 deletions
188
src-tauri/examples/egl_diag.rs
Normal file
188
src-tauri/examples/egl_diag.rs
Normal file
|
|
@ -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<T: Copy>(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() {
|
||||
"<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()) };
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue