diff options
| author | LLLL Colonq <llll@colonq> | 2026-10-06 21:31:19 -0400 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2026-10-06 21:31:19 -0400 |
| commit | b71e024a8ecd93a5ff1db38db68010af9775c679 (patch) | |
| tree | 40f28724902e0deed7b29eda53e8a5319a816720 /crates | |
| parent | 0bfb852f7a2784938f1b9724bc46d98bb5257f86 (diff) | |
Add texture sharing
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/teleia/Cargo.toml | 6 | ||||
| -rw-r--r-- | crates/teleia/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/teleia/src/net.rs | 3 | ||||
| -rw-r--r-- | crates/teleia/src/net/texture.rs | 154 | ||||
| -rw-r--r-- | crates/teleia/src/net/texture/ffi/egl.rs | 54 | ||||
| -rw-r--r-- | crates/teleia/src/net/texture/ffi/glfw.rs | 7 | ||||
| -rw-r--r-- | crates/teleia/src/net/texture/ffi/mod.rs | 2 | ||||
| -rw-r--r-- | crates/teleia/src/texture.rs | 3 |
8 files changed, 224 insertions, 6 deletions
diff --git a/crates/teleia/Cargo.toml b/crates/teleia/Cargo.toml index 26c08a9..1236b8c 100644 --- a/crates/teleia/Cargo.toml +++ b/crates/teleia/Cargo.toml @@ -14,7 +14,6 @@ tobj = "=4.0.1" # loader for .obj meshes loader ahash = "=0.8.10" # dependency of tobj gltf = { git = "https://github.com/lcolonq/gltf", features = ["extras", "import", "names", "utils"] } # loader for .gltf scenes image = { version = "0.25.10", default-features = false, features = ["jpeg", "png"] } # texture loader -# cosmic-text = { version = "*", default-features = false, features = ["std", "swash"] } # advanced text rendering glam = {version = "0.29.2", features = ["serde"]} # linear algebra log = "0.4.21" # logging rand = { version = "=0.8.5", features = ["small_rng"] } # rng @@ -23,8 +22,6 @@ serde_json = "1.0.128" # serialize JSON bincode = { version = "2.0.1", features = ["serde"] } # binary serialization enum-map = "2.7.3" # fast maps with enums as keys bimap = "0.6.3" # bijective maps -# reqwest = "*" # http requests -bytes = "*" # bytes for http responses bitflags = { version = "2.6.0", features = ["serde"] } # bitwise flags simple-eyre = { version = "0.3.1", default-features = false } # error reporting and formatting rapier3d = "0.23.1" # rigid-body physics @@ -51,4 +48,5 @@ glfw = { git = "https://github.com/lcolonq/glfw-rs", features = ["serde"] } # wi kira = { version = "=0.9.6", default-features = false, features = ["cpal", "ogg", "mp3", "wav"] } # audio directories = { git = "https://github.com/lcolonq/directories-rs" } # standard system directories polling = "3.11.0" # interface to epoll -tungstenite = { version = "0.24.0", features = ["native-tls"] } # websockets
\ No newline at end of file +tungstenite = { version = "0.24.0", features = ["native-tls"] } # websockets +libc = "0.2.190" # ffi
\ No newline at end of file diff --git a/crates/teleia/src/lib.rs b/crates/teleia/src/lib.rs index 498085f..1426737 100644 --- a/crates/teleia/src/lib.rs +++ b/crates/teleia/src/lib.rs @@ -1,4 +1,5 @@ #![feature(try_blocks)] +#![feature(unix_socket_ancillary_data)] pub mod utils; pub mod ui; diff --git a/crates/teleia/src/net.rs b/crates/teleia/src/net.rs index 0fee34d..d6c28db 100644 --- a/crates/teleia/src/net.rs +++ b/crates/teleia/src/net.rs @@ -2,3 +2,6 @@ pub mod ws; #[cfg(not(target_arch = "wasm32"))] pub mod fig; + +#[cfg(not(target_arch = "wasm32"))] +pub mod texture; diff --git a/crates/teleia/src/net/texture.rs b/crates/teleia/src/net/texture.rs new file mode 100644 index 0000000..86d6988 --- /dev/null +++ b/crates/teleia/src/net/texture.rs @@ -0,0 +1,154 @@ +mod ffi; + +use byteorder::{LE, ReadBytesExt, WriteBytesExt}; +use simple_eyre::eyre::Context; + +use std::io::{IoSlice, IoSliceMut}; +// use std::io::Write; +// use std::os::fd::FromRawFd; +use std::os::unix::net::{AncillaryData, SocketAncillary}; +use std::sync::mpsc::{Receiver, channel}; +use std::{os::unix::net::UnixDatagram, thread::{spawn, JoinHandle}}; + +// use glow::HasContext; +use ffi::egl; +use ffi::glfw; + +use crate::{context, texture, utils}; + +#[derive(Debug)] +struct ReceivedTexture { + fd: i32, + width: i32, height: i32, + fourcc: u32, offset: i32, stride: i32, +} + +#[allow(dead_code)] +pub struct Server { + pub tex: texture::Texture, + thread: JoinHandle<()>, + receiver: Receiver<ReceivedTexture>, +} +impl Server { + pub fn new(ctx: &context::Context, path: &str) -> Self { + let _ = std::fs::remove_file(path); + let sock = UnixDatagram::bind(path).expect("failed to bind texture server socket"); + + let (sender, receiver) = channel::<ReceivedTexture>(); + let thread = spawn(move || { + let mut buf = [0; 1024]; + let mut ancillary_buf = [0; 128]; + let mut ancillary = SocketAncillary::new(&mut ancillary_buf); + loop { + match sock.recv_vectored_with_ancillary(&mut [IoSliceMut::new(&mut buf)], &mut ancillary) { + Ok(_) => { + let res: utils::Erm<()> = try { + let mut cur = std::io::Cursor::new(&buf); + let width = cur.read_i32::<LE>().wrap_err("failed to read width")?; + let height = cur.read_i32::<LE>().wrap_err("failed to read height")?; + let fourcc = cur.read_u32::<LE>().wrap_err("failed to read fourcc")?; + let offset = cur.read_i32::<LE>().wrap_err("failed to read offset")?; + let stride = cur.read_i32::<LE>().wrap_err("failed to read stride")?; + for ar in ancillary.messages() { + match ar { + Ok(AncillaryData::ScmRights(mut sr)) => { + if let Some(fd) = sr.next() { + let rt = ReceivedTexture { + fd, width, height, + fourcc, offset, stride + }; + if let Err(e) = sender.send(rt) { + log::warn!("error on channel: {:?}", e); + break; + } + } + }, + Ok(_) => log::info!("message is not SCM_RIGHTS"), + Err(e) => log::warn!("failed to read ancillary message: {:?}", e), + } + }; + }; + if let Err(e) = res { + log::warn!("error when receiving texture: {}", e); + } + }, + Err(e) => log::warn!("texture server failed to receive: {}", e), + } + } + }); + Self { + tex: texture::Texture::new_empty(ctx), + thread, + receiver, + } + } + pub fn update(&self, ctx: &context::Context) { + if let Ok(rt) = self.receiver.try_recv() { + log::info!("received file descriptor: {:?}", rt); + unsafe { + let window = ctx.window.borrow_mut().0.ptr; + let d = glfw::glfwGetEGLDisplay(); + let c = glfw::glfwGetEGLContext(window); + log::info!("d: {:?}, c: {:?}", d, c); + let img = egl::eglCreateImage(d, std::ptr::null_mut(), egl::EGL_LINUX_DMA_BUF_EXT, std::ptr::null_mut(), [ + egl::EGL_WIDTH as egl::EGLAttrib, rt.width as _, + egl::EGL_HEIGHT as _, rt.height as _, + egl::EGL_LINUX_DRM_FOURCC_EXT as _, rt.fourcc as _, + egl::EGL_DMA_BUF_PLANE0_FD_EXT as _, rt.fd as _, + egl::EGL_DMA_BUF_PLANE0_OFFSET_EXT as _, rt.offset as _, + egl::EGL_DMA_BUF_PLANE0_PITCH_EXT as _, rt.stride as _, + egl::EGL_NONE as _, + ].as_ptr()); + let _ = libc::close(rt.fd); + log::info!("error here: {}", egl::eglGetError()); + self.tex.bind(ctx); + egl::glEGLImageTargetTexture2DOES(glow::TEXTURE_2D, img); + log::info!("set up texture: {}", egl::eglGetError()); + } + } + } +} + +pub fn share_texture(ctx: &context::Context, path: &str, tex: &texture::Texture) -> utils::Erm<()> { + unsafe { + #[allow(non_snake_case)] + let eglExportDMABUFImageQueryMESA = + std::mem::transmute::<egl::MustCastToFunction, egl::PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC>( + egl::eglGetProcAddress(c"eglExportDMABUFImageQueryMESA".as_ptr()) + ); + #[allow(non_snake_case)] + let eglExportDMABUFImageMESA = + std::mem::transmute::<egl::MustCastToFunction, egl::PFNEGLEXPORTDMABUFIMAGEMESAPROC>( + egl::eglGetProcAddress(c"eglExportDMABUFImageMESA".as_ptr()) + ); + let window = ctx.window.borrow_mut().0.ptr; + let d = glfw::glfwGetEGLDisplay(); + let c = glfw::glfwGetEGLContext(window); + let img = egl::eglCreateImage(d, c, egl::EGL_GL_TEXTURE_2D, + std::ptr::with_exposed_provenance_mut(tex.tex.0.get() as usize), + std::ptr::null_mut() + ); + let mut imgfd: std::ffi::c_int = 0; + let mut fourcc: std::ffi::c_int = 0; + let mut offset: egl::EGLint = 0; + let mut stride: egl::EGLint = 0; + eglExportDMABUFImageQueryMESA(d, img, &mut fourcc as *mut _, std::ptr::null_mut(), 0); + eglExportDMABUFImageMESA(d, img, + &mut imgfd as *mut _, &mut stride as *mut _, &mut offset as *mut _ + ); + log::info!("fd: {imgfd}, fourcc: {fourcc}, offset: {offset}, stride: {stride}"); + let mut buf: Vec<u8> = Vec::new(); + buf.write_i32::<LE>(tex.width)?; + buf.write_i32::<LE>(tex.height)?; + buf.write_u32::<LE>(fourcc as u32)?; + buf.write_i32::<LE>(offset)?; + buf.write_i32::<LE>(stride)?; + let mut ancillary_buf = [0; 128]; + let mut ancillary = SocketAncillary::new(&mut ancillary_buf[..]); + ancillary.add_fds(&[imgfd]); + let sock = UnixDatagram::unbound().expect("failed to create unix socket"); + sock.send_vectored_with_ancillary_to(&[IoSlice::new(&buf)], &mut ancillary, path)?; + let _ = libc::close(imgfd); + Ok(()) + } +} diff --git a/crates/teleia/src/net/texture/ffi/egl.rs b/crates/teleia/src/net/texture/ffi/egl.rs new file mode 100644 index 0000000..32c5356 --- /dev/null +++ b/crates/teleia/src/net/texture/ffi/egl.rs @@ -0,0 +1,54 @@ +// from <EGL/egl.h> +pub const EGL_NONE: EGLenum = 0x3038; +pub const EGL_WIDTH: EGLenum = 0x3057; +pub const EGL_HEIGHT: EGLenum = 0x3056; +pub const EGL_GL_TEXTURE_2D: EGLenum = 0x30B1; + +// from <EGL/eglext.h> +pub const EGL_LINUX_DMA_BUF_EXT: EGLenum = 0x3270; +pub const EGL_LINUX_DRM_FOURCC_EXT: EGLenum = 0x3271; +pub const EGL_DMA_BUF_PLANE0_FD_EXT: EGLenum = 0x3272; +pub const EGL_DMA_BUF_PLANE0_OFFSET_EXT: EGLenum = 0x3273; +pub const EGL_DMA_BUF_PLANE0_PITCH_EXT: EGLenum = 0x3274; + +#[repr(C)] +pub struct EGLDisplayTarget(()); +pub type EGLDisplay = *mut EGLDisplayTarget; + +#[repr(C)] +pub struct EGLContextTarget(()); +pub type EGLContext = *mut EGLContextTarget; + +#[repr(C)] +pub struct EGLClientBufferTarget(()); +pub type EGLClientBuffer = *mut EGLClientBufferTarget; + +#[repr(C)] +pub struct EGLImageTarget(()); +pub type EGLImage = *mut EGLImageTarget; + +#[repr(C)] +pub struct MustCastToFunctionTarget(()); +pub type MustCastToFunction = *mut MustCastToFunctionTarget; + +pub type EGLenum = core::ffi::c_uint; +pub type EGLBoolean = core::ffi::c_uint; + +pub type EGLint = core::ffi::c_int; + +pub type EGLAttrib = libc::intptr_t; + +pub type PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC = fn(EGLDisplay, EGLImage, *mut core::ffi::c_int, *mut core::ffi::c_int, u64) -> EGLBoolean; +pub type PFNEGLEXPORTDMABUFIMAGEMESAPROC = fn(EGLDisplay, EGLImage, *mut core::ffi::c_int, *mut EGLint, *mut EGLint) -> EGLBoolean; + +#[link(name = "EGL")] +unsafe extern "C" { + pub fn eglCreateImage(d: EGLDisplay, c: EGLContext, t: EGLenum, buf: EGLClientBuffer, attribs: *const EGLAttrib) -> EGLImage; + pub fn eglGetError() -> EGLint; + pub fn eglGetProcAddress(nm: *const core::ffi::c_char) -> MustCastToFunction; +} + +#[link(name = "GL")] +unsafe extern "C" { + pub fn glEGLImageTargetTexture2DOES(t: u32, img: EGLImage); +} diff --git a/crates/teleia/src/net/texture/ffi/glfw.rs b/crates/teleia/src/net/texture/ffi/glfw.rs new file mode 100644 index 0000000..eed135f --- /dev/null +++ b/crates/teleia/src/net/texture/ffi/glfw.rs @@ -0,0 +1,7 @@ +use super::egl::*; + +#[link(name = "glfw3")] +unsafe extern "C" { + pub fn glfwGetEGLDisplay() -> EGLDisplay; + pub fn glfwGetEGLContext(w: *mut glfw::ffi::GLFWwindow) -> EGLContext; +} diff --git a/crates/teleia/src/net/texture/ffi/mod.rs b/crates/teleia/src/net/texture/ffi/mod.rs new file mode 100644 index 0000000..a98fd71 --- /dev/null +++ b/crates/teleia/src/net/texture/ffi/mod.rs @@ -0,0 +1,2 @@ +pub mod egl; +pub mod glfw; diff --git a/crates/teleia/src/texture.rs b/crates/teleia/src/texture.rs index 71a39d8..95cb914 100644 --- a/crates/teleia/src/texture.rs +++ b/crates/teleia/src/texture.rs @@ -49,7 +49,6 @@ impl Texture { glow::UNSIGNED_BYTE, Some(pixels), ); - ctx.gl.generate_mipmap(glow::TEXTURE_2D); Self { tex, width: rgba.width() as i32, @@ -85,7 +84,6 @@ impl Texture { glow::UNSIGNED_BYTE, Some(data), ); - ctx.gl.generate_mipmap(glow::TEXTURE_2D); } self.width = width; self.height = height; @@ -100,6 +98,7 @@ impl Texture { pub fn set_anisotropic_filtering(&self, ctx: &context::Context) { unsafe { ctx.gl.bind_texture(glow::TEXTURE_2D, Some(self.tex)); + ctx.gl.generate_mipmap(glow::TEXTURE_2D); ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::REPEAT as i32); ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::REPEAT as i32); ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR_MIPMAP_LINEAR as i32); |
