fix(native): unblock linux startup — provider GL loader + wayland-dlopen
Two Linux startup blockers: 1. Skia's Interface::new_native() dlopens libGL/GLX, which fails on the EGL contexts this project always creates on Linux — GL init errored and the app exited (the tracked LINUX_GPU_SKIA_LOADER_TBD gap). GlContextProvider now exposes gl_proc_address; the glutin provider resolves through its display (eglGetProcAddress on EGL) and SharedSkiaContext builds the interface via new_load_with, falling back to new_native for providers without a loader. The Linux GPU smoke tests are un-ignored (they soft-skip without a working EGL stack unless STEP1A_REQUIRE_GPU=1). 2. Dropping casement's default features also dropped wayland-dlopen, making libwayland-client a DT_NEEDED hard dependency — the binary could not even load on X11-only hosts. Restore the feature so libwayland loads at runtime when present.
This commit is contained in:
parent
061296c779
commit
bde00122d5
|
|
@ -164,6 +164,10 @@ skia-safe = { version = "0.97.0", default-features = false, features = [
|
|||
winit = { package = "casement", path = "../../vendor/casement", default-features = false, features = [
|
||||
"x11",
|
||||
"wayland",
|
||||
# dlopen libwayland at runtime so X11-only hosts can still start the
|
||||
# binary (without this, libwayland-client becomes a DT_NEEDED hard
|
||||
# dependency and the dynamic linker refuses to load on X11-only boxes).
|
||||
"wayland-dlopen",
|
||||
"wayland-csd-adwaita",
|
||||
"rwh_06",
|
||||
] }
|
||||
|
|
|
|||
|
|
@ -165,6 +165,10 @@ glutin = { version = "0.32.3", optional = true }
|
|||
winit = { package = "casement", path = "../../vendor/casement", default-features = false, optional = true, features = [
|
||||
"x11",
|
||||
"wayland",
|
||||
# dlopen libwayland at runtime so X11-only hosts can still start the
|
||||
# binary (without this, libwayland-client becomes a DT_NEEDED hard
|
||||
# dependency and the dynamic linker refuses to load on X11-only boxes).
|
||||
"wayland-dlopen",
|
||||
"wayland-csd-adwaita",
|
||||
"rwh_06",
|
||||
] }
|
||||
|
|
|
|||
|
|
@ -107,6 +107,18 @@ pub trait GlContextProvider {
|
|||
/// asynchronously, so an explicit release path makes the
|
||||
/// 100-iteration RSS sanity test deterministic.
|
||||
fn release(&mut self) -> ProviderResult<()>;
|
||||
|
||||
/// Resolve a GL symbol through the API that created this context
|
||||
/// (EGL / GLX / WGL / CGL). Skia consumes this via
|
||||
/// `Interface::new_load_with` so symbol resolution matches the
|
||||
/// context's API — `Interface::new_native()` dlopens libGL/GLX on
|
||||
/// Linux, which fails when the current context is EGL (the
|
||||
/// LINUX_GPU_SKIA_LOADER_TBD gap). `None` = this provider has no
|
||||
/// loader; `SharedSkiaContext::new` falls back to `new_native()`.
|
||||
fn gl_proc_address(&self, symbol: &std::ffi::CStr) -> Option<*const std::ffi::c_void> {
|
||||
let _ = symbol;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -324,6 +336,17 @@ impl GlContextProvider for GlutinProvider {
|
|||
0
|
||||
}
|
||||
|
||||
/// Route Skia's symbol lookups through the same glutin display the
|
||||
/// glow function table was loaded from, so EGL contexts resolve via
|
||||
/// eglGetProcAddress instead of `new_native()`'s GLX path.
|
||||
fn gl_proc_address(&self, symbol: &std::ffi::CStr) -> Option<*const std::ffi::c_void> {
|
||||
use glutin::display::GetGlDisplay;
|
||||
use glutin::prelude::*;
|
||||
self.context
|
||||
.as_ref()
|
||||
.map(|ctx| ctx.display().get_proc_address(symbol))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
fn resize(&mut self, width: u32, height: u32) -> ProviderResult<()> {
|
||||
use glutin::prelude::*;
|
||||
|
|
|
|||
|
|
@ -97,8 +97,7 @@ impl SharedSkiaContext {
|
|||
|
||||
let glow_handle = provider.glow();
|
||||
|
||||
let interface =
|
||||
skia_safe::gpu::gl::Interface::new_native().ok_or(SharedSkiaError::GlInterface)?;
|
||||
let interface = provider_gl_interface(&provider).ok_or(SharedSkiaError::GlInterface)?;
|
||||
let mut direct_context = skia_safe::gpu::direct_contexts::make_gl(interface, None)
|
||||
.ok_or(SharedSkiaError::DirectContext)?;
|
||||
|
||||
|
|
@ -325,6 +324,25 @@ impl Drop for SharedSkiaContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Build the Skia GL interface for a provider. Prefer the provider's
|
||||
/// own symbol loader so resolution matches the API that created the
|
||||
/// context (EGL vs GLX vs WGL vs CGL) — `new_native()` dlopens
|
||||
/// libGL/GLX on Linux and fails on EGL-current contexts (the
|
||||
/// LINUX_GPU_SKIA_LOADER_TBD gap). Providers without a loader return
|
||||
/// `None` per symbol, the load comes back empty, and the chain falls
|
||||
/// through to `new_native()`.
|
||||
fn provider_gl_interface(
|
||||
provider: &dyn GlContextProvider,
|
||||
) -> Option<skia_safe::gpu::gl::Interface> {
|
||||
skia_safe::gpu::gl::Interface::new_load_with(|name| {
|
||||
std::ffi::CString::new(name)
|
||||
.ok()
|
||||
.and_then(|sym| provider.gl_proc_address(&sym))
|
||||
.unwrap_or(std::ptr::null())
|
||||
})
|
||||
.or_else(skia_safe::gpu::gl::Interface::new_native)
|
||||
}
|
||||
|
||||
/// Query GL state for the current viewport + sample/stencil bits.
|
||||
///
|
||||
/// Phase A Gate round 2 BLOCK 1 fix: `SharedSkiaContext::new` is now
|
||||
|
|
|
|||
|
|
@ -213,6 +213,15 @@ pub mod egl_pbuffer {
|
|||
0
|
||||
}
|
||||
|
||||
/// Resolve symbols via eglGetProcAddress — the loader path that
|
||||
/// unblocks Skia on EGL pbuffer + llvmpipe (previously the
|
||||
/// LINUX_GPU_SKIA_LOADER_TBD gap: `new_native()` went through
|
||||
/// GLX and found nothing).
|
||||
fn gl_proc_address(&self, symbol: &std::ffi::CStr) -> Option<*const c_void> {
|
||||
let name = symbol.to_str().ok()?;
|
||||
self.egl.get_proc_address(name).map(|p| p as *const c_void)
|
||||
}
|
||||
|
||||
fn resize(&mut self, _w: u32, _h: u32) -> ProviderResult<()> {
|
||||
// Pbuffer can't be resized once created — tests build the
|
||||
// surface at the size they need.
|
||||
|
|
|
|||
|
|
@ -288,11 +288,11 @@ fn gpu_chrome_stub_composition() {
|
|||
platform::run();
|
||||
}
|
||||
|
||||
// Linux GPU chrome+stub composition deferred: same root cause as
|
||||
// LINUX_GPU_SKIA_LOADER_TBD in gpu_smoke.rs.
|
||||
// Linux GPU chrome+stub composition: unblocked by the
|
||||
// `GlContextProvider::gl_proc_address` loader path (see gpu_smoke.rs);
|
||||
// soft-skips without a working EGL stack unless STEP1A_REQUIRE_GPU=1.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
#[ignore = "LINUX_GPU_SKIA_LOADER_TBD: skia-safe Interface::new_native cannot resolve GL syms from EGL pbuffer + llvmpipe (see gpu_smoke.rs)"]
|
||||
fn gpu_chrome_stub_composition() {
|
||||
platform::run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,14 +290,14 @@ fn gpu_smoke() {
|
|||
platform::run();
|
||||
}
|
||||
|
||||
// Linux GPU smoke deferred: skia-safe `Interface::new_native()` dlopens
|
||||
// libGL.so + glXGetProcAddress, which fails on EGL pbuffer + llvmpipe.
|
||||
// Wiring `Interface::new_load_with(eglGetProcAddress)` requires a new
|
||||
// `GlContextProvider::get_proc_address` method (spec §3.1 mini-patch
|
||||
// follow-up). Tracked LINUX_GPU_SKIA_LOADER_TBD.
|
||||
// Linux GPU smoke: Skia now loads its GL interface through
|
||||
// `GlContextProvider::gl_proc_address` (eglGetProcAddress for the EGL
|
||||
// pbuffer provider), closing the old LINUX_GPU_SKIA_LOADER_TBD gap
|
||||
// where `Interface::new_native()` went through GLX and found nothing.
|
||||
// On hosts without a working EGL/Mesa stack the test soft-skips
|
||||
// (INCONCLUSIVE eprintln) unless `STEP1A_REQUIRE_GPU=1`.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
#[ignore = "LINUX_GPU_SKIA_LOADER_TBD: skia-safe Interface::new_native cannot resolve GL syms from EGL pbuffer + llvmpipe; needs new_load_with(eglGetProcAddress) loader path (spec §3.1 follow-up)"]
|
||||
fn gpu_smoke() {
|
||||
platform::run();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue