WayVR: Implement software texture blitting as an alternative to dmabuf (Closes #174) (#178)

This commit is contained in:
Aleksander
2025-03-11 23:42:37 +01:00
committed by GitHub
parent a1cc41f541
commit 6d39380ebc
8 changed files with 214 additions and 91 deletions
+43 -6
View File
@@ -1,4 +1,4 @@
use std::{cell::RefCell, rc::Rc};
use std::{cell::RefCell, rc::Rc, sync::Arc};
use smithay::{
backend::renderer::{
@@ -23,7 +23,7 @@ use crate::{
use super::{
client::WayVRCompositor, comp::send_frames_surface_tree, egl_data, event_queue::SyncEventQueue,
process, smithay_wrapper, time, window, WayVRSignal,
process, smithay_wrapper, time, window, BlitMethod, WayVRSignal,
};
fn generate_auth_key() -> String {
@@ -71,7 +71,8 @@ pub struct Display {
gles_texture: GlesTexture, // TODO: drop texture
egl_image: khronos_egl::Image,
egl_data: Rc<egl_data::EGLData>,
pub dmabuf_data: egl_data::DMAbufData,
pub render_data: egl_data::RenderData,
pub tasks: SyncEventQueue<DisplayTask>,
}
@@ -87,6 +88,7 @@ impl Drop for Display {
pub struct DisplayInitParams<'a> {
pub wm: Rc<RefCell<window::WindowManager>>,
pub config: &'a super::Config,
pub renderer: &'a mut GlesRenderer,
pub egl_data: Rc<egl_data::EGLData>,
pub wayland_env: super::WaylandEnv,
@@ -128,7 +130,13 @@ impl Display {
})?;
let egl_image = params.egl_data.create_egl_image(tex_id)?;
let dmabuf_data = params.egl_data.create_dmabuf_data(&egl_image)?;
let render_data = match params.config.blit_method {
BlitMethod::Dmabuf => {
egl_data::RenderData::Dmabuf(params.egl_data.create_dmabuf_data(&egl_image)?)
}
BlitMethod::Software => egl_data::RenderData::Software(None),
};
let opaque = false;
let size = (params.width as i32, params.height as i32).into();
@@ -145,7 +153,7 @@ impl Display {
wayland_env: params.wayland_env,
wm: params.wm,
displayed_windows: Vec::new(),
dmabuf_data,
render_data,
egl_image,
gles_texture,
last_pressed_time_ms: 0,
@@ -292,7 +300,7 @@ impl Display {
}
}
pub fn tick_render(&self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
pub fn tick_render(&mut self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
renderer.bind(self.gles_texture.clone())?;
let size = Size::from((self.width as i32, self.height as i32));
@@ -340,6 +348,35 @@ impl Display {
send_frames_surface_tree(window.toplevel.wl_surface(), time_ms as u32);
}
if let egl_data::RenderData::Software(_) = &self.render_data {
// Read OpenGL texture into memory. Slow!
let pixel_data = renderer.with_context(|gl| unsafe {
gl.BindTexture(ffi::TEXTURE_2D, self.gles_texture.tex_id());
let len = self.width as usize * self.height as usize * 4;
let mut data: Box<[u8]> = Box::new_uninit_slice(len).assume_init();
gl.ReadPixels(
0,
0,
self.width as i32,
self.height as i32,
ffi::RGBA,
ffi::UNSIGNED_BYTE,
data.as_mut_ptr().cast(),
);
let data: Arc<[u8]> = Arc::from(data);
data
})?;
self.render_data =
egl_data::RenderData::Software(Some(egl_data::RenderSoftwarePixelsData {
data: pixel_data,
width: self.width,
height: self.height,
}));
}
Ok(())
}
+21 -3
View File
@@ -1,3 +1,5 @@
use std::sync::Arc;
use super::egl_ex;
use anyhow::anyhow;
@@ -23,13 +25,26 @@ pub struct DMAbufModifierInfo {
}
#[derive(Debug, Clone)]
pub struct DMAbufData {
pub struct RenderDMAbufData {
pub fd: i32,
pub stride: i32,
pub offset: i32,
pub mod_info: DMAbufModifierInfo,
}
#[derive(Debug, Clone)]
pub struct RenderSoftwarePixelsData {
pub data: Arc<[u8]>,
pub width: u16,
pub height: u16,
}
#[derive(Debug, Clone)]
pub enum RenderData {
Dmabuf(RenderDMAbufData),
Software(Option<RenderSoftwarePixelsData>), // will be set if the next image data is available
}
impl EGLData {
pub fn load_func(&self, func_name: &str) -> anyhow::Result<extern "system" fn()> {
let raw_fn = self.egl.get_proc_address(func_name).ok_or(anyhow::anyhow!(
@@ -207,7 +222,10 @@ impl EGLData {
}
}
pub fn create_dmabuf_data(&self, egl_image: &khronos_egl::Image) -> anyhow::Result<DMAbufData> {
pub fn create_dmabuf_data(
&self,
egl_image: &khronos_egl::Image,
) -> anyhow::Result<RenderDMAbufData> {
use egl_ex::PFNEGLEXPORTDMABUFIMAGEMESAPROC as FUNC;
unsafe {
let egl_export_dmabuf_image_mesa =
@@ -235,7 +253,7 @@ impl EGLData {
let mod_info = self.query_dmabuf_mod_info()?;
Ok(DMAbufData {
Ok(RenderDMAbufData {
fd: fds[0],
stride: strides[0],
offset: offsets[0],
+27 -8
View File
@@ -90,11 +90,27 @@ pub enum WayVRSignal {
BroadcastStateChanged(packet_server::WvrStateChanged),
}
pub enum BlitMethod {
Dmabuf,
Software,
}
impl BlitMethod {
pub fn from_string(str: &str) -> Option<BlitMethod> {
match str {
"dmabuf" => Some(BlitMethod::Dmabuf),
"software" => Some(BlitMethod::Software),
_ => None,
}
}
}
pub struct Config {
pub click_freeze_time_ms: u32,
pub keyboard_repeat_delay_ms: u32,
pub keyboard_repeat_rate: u32,
pub auto_hide_delay: Option<u32>, // if None, auto-hide is disabled
pub blit_method: BlitMethod,
}
pub struct WayVRState {
@@ -104,7 +120,7 @@ pub struct WayVRState {
wm: Rc<RefCell<window::WindowManager>>,
egl_data: Rc<egl_data::EGLData>,
pub processes: process::ProcessVec,
config: Config,
pub config: Config,
dashboard_display: Option<display::DisplayHandle>,
pub tasks: SyncEventQueue<WayVRTask>,
pub signals: SyncEventQueue<WayVRSignal>,
@@ -256,7 +272,7 @@ impl WayVR {
Ok(Self { state, ipc_server })
}
pub fn tick_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<()> {
pub fn tick_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<bool> {
// millis since the start of wayvr
let display = self
.state
@@ -266,12 +282,12 @@ impl WayVR {
if !display.wants_redraw {
// Nothing changed, do not render
return Ok(());
return Ok(false);
}
if !display.visible {
// Display is invisible, do not render
return Ok(());
return Ok(false);
}
let time_ms = get_millis() - self.state.time_start;
@@ -279,7 +295,7 @@ impl WayVR {
display.tick_render(&mut self.state.manager.state.gles_renderer, time_ms)?;
display.wants_redraw = false;
Ok(())
Ok(true)
}
pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result<Vec<TickTask>> {
@@ -537,10 +553,13 @@ impl WayVRState {
}
}
pub fn get_dmabuf_data(&self, display: display::DisplayHandle) -> Option<egl_data::DMAbufData> {
pub fn get_render_data(
&self,
display: display::DisplayHandle,
) -> Option<&egl_data::RenderData> {
self.displays
.get(&display)
.map(|display| display.dmabuf_data.clone())
.map(|display| &display.render_data)
}
pub fn create_display(
@@ -555,12 +574,12 @@ impl WayVRState {
egl_data: self.egl_data.clone(),
renderer: &mut self.manager.state.gles_renderer,
wayland_env: self.manager.wayland_env.clone(),
config: &self.config,
width,
height,
name,
primary,
})?;
let handle = self.displays.add(display);
self.signals.send(WayVRSignal::BroadcastStateChanged(