kms: Create socket variants for different gpus
To make it possible for clients to choose a gpu, we create special sockets per gpu, that advertise their gpu to clients.
This commit is contained in:
parent
2fb0fa8aef
commit
742780ddc3
2 changed files with 232 additions and 7 deletions
|
|
@ -47,7 +47,9 @@ use std::{
|
||||||
|
|
||||||
mod drm_helpers;
|
mod drm_helpers;
|
||||||
mod session_fd;
|
mod session_fd;
|
||||||
|
mod socket;
|
||||||
use session_fd::*;
|
use session_fd::*;
|
||||||
|
use socket::*;
|
||||||
|
|
||||||
pub struct KmsState {
|
pub struct KmsState {
|
||||||
devices: HashMap<DrmNode, Device>,
|
devices: HashMap<DrmNode, Device>,
|
||||||
|
|
@ -66,6 +68,7 @@ pub struct Device {
|
||||||
formats: HashSet<Format>,
|
formats: HashSet<Format>,
|
||||||
supports_atomic: bool,
|
supports_atomic: bool,
|
||||||
event_token: Option<RegistrationToken>,
|
event_token: Option<RegistrationToken>,
|
||||||
|
socket: Option<Socket>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Surface {
|
pub struct Surface {
|
||||||
|
|
@ -246,7 +249,7 @@ impl State {
|
||||||
.spaces
|
.spaces
|
||||||
.active_space_mut(&surface.output)
|
.active_space_mut(&surface.output)
|
||||||
.send_frames(
|
.send_frames(
|
||||||
state.common.start_time.elapsed().as_millis() as u32,
|
state.common.start_time.elapsed().as_millis() as u32
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(err) => slog_scope::warn!("Failed to submit frame: {}", err),
|
Err(err) => slog_scope::warn!("Failed to submit frame: {}", err),
|
||||||
|
|
@ -264,6 +267,18 @@ impl State {
|
||||||
.register_dispatcher(dispatcher.clone())
|
.register_dispatcher(dispatcher.clone())
|
||||||
.with_context(|| format!("Failed to add drm device to event loop: {}", dev))?;
|
.with_context(|| format!("Failed to add drm device to event loop: {}", dev))?;
|
||||||
|
|
||||||
|
let socket = match self.create_socket(render_node, formats.clone().into_iter()) {
|
||||||
|
Ok(socket) => Some(socket),
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::warn!(
|
||||||
|
"Failed to initialize hardware-acceleration for clients on {}: {}",
|
||||||
|
render_node,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut device = Device {
|
let mut device = Device {
|
||||||
render_node,
|
render_node,
|
||||||
surfaces: HashMap::new(),
|
surfaces: HashMap::new(),
|
||||||
|
|
@ -272,6 +287,7 @@ impl State {
|
||||||
formats,
|
formats,
|
||||||
supports_atomic,
|
supports_atomic,
|
||||||
event_token: Some(token),
|
event_token: Some(token),
|
||||||
|
socket,
|
||||||
};
|
};
|
||||||
|
|
||||||
let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
|
let outputs = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
|
||||||
|
|
@ -335,6 +351,9 @@ impl State {
|
||||||
if let Some(token) = device.event_token.take() {
|
if let Some(token) = device.event_token.take() {
|
||||||
self.common.event_loop_handle.remove(token);
|
self.common.event_loop_handle.remove(token);
|
||||||
}
|
}
|
||||||
|
if let Some(socket) = device.socket.take() {
|
||||||
|
self.common.event_loop_handle.remove(socket.token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -442,7 +461,11 @@ impl Device {
|
||||||
let backend = state.backend.kms();
|
let backend = state.backend.kms();
|
||||||
if let Some(device) = backend.devices.get_mut(&dev_id) {
|
if let Some(device) = backend.devices.get_mut(&dev_id) {
|
||||||
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
||||||
if let Err(err) = surface.render_output(&mut backend.api, &backend.primary, &device.render_node, &mut state.common) {
|
if let Err(err) = surface.render_output(
|
||||||
|
&mut backend.api,
|
||||||
|
&device.render_node,
|
||||||
|
&mut state.common,
|
||||||
|
) {
|
||||||
slog_scope::error!("Error rendering: {}", err);
|
slog_scope::error!("Error rendering: {}", err);
|
||||||
// TODO re-schedule?
|
// TODO re-schedule?
|
||||||
}
|
}
|
||||||
|
|
@ -471,18 +494,47 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_CPU_COPIES: usize = 3;
|
||||||
|
|
||||||
impl Surface {
|
impl Surface {
|
||||||
pub fn render_output(
|
pub fn render_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
api: &mut GpuManager<EglGlesBackend>,
|
api: &mut GpuManager<EglGlesBackend>,
|
||||||
render_node: &DrmNode,
|
|
||||||
target_node: &DrmNode,
|
target_node: &DrmNode,
|
||||||
state: &mut Common,
|
state: &mut Common,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut renderer = api
|
let nodes = state
|
||||||
.renderer(render_node, target_node)
|
.spaces
|
||||||
.unwrap();
|
.active_space(&self.output)
|
||||||
|
.windows()
|
||||||
|
.flat_map(|w| {
|
||||||
|
w.toplevel()
|
||||||
|
.get_surface()?
|
||||||
|
.as_ref()
|
||||||
|
.client()?
|
||||||
|
.data_map()
|
||||||
|
.get::<DrmNode>()
|
||||||
|
.cloned()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let render_node = if nodes.contains(&target_node) || nodes.len() < MAX_CPU_COPIES {
|
||||||
|
&target_node
|
||||||
|
} else {
|
||||||
|
nodes
|
||||||
|
.iter()
|
||||||
|
.fold(HashMap::new(), |mut count_map, node| {
|
||||||
|
let count = count_map.entry(node).or_insert(0);
|
||||||
|
*count += 1;
|
||||||
|
count_map
|
||||||
|
})
|
||||||
|
.into_iter()
|
||||||
|
.reduce(|a, b| if a.1 > b.1 { a } else { b })
|
||||||
|
.map(|(node, _)| node)
|
||||||
|
.unwrap_or(&target_node)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut renderer = api.renderer(render_node, &target_node).unwrap();
|
||||||
|
|
||||||
let (buffer, age) = self
|
let (buffer, age) = self
|
||||||
.surface
|
.surface
|
||||||
.next_buffer()
|
.next_buffer()
|
||||||
|
|
|
||||||
173
src/backend/kms/socket.rs
Normal file
173
src/backend/kms/socket.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use smithay::{
|
||||||
|
backend::{
|
||||||
|
allocator::Format,
|
||||||
|
drm::DrmNode,
|
||||||
|
renderer::{gles2::Gles2Renderbuffer, ImportDma},
|
||||||
|
},
|
||||||
|
reexports::{
|
||||||
|
calloop::{generic::Generic, Interest, Mode, PostAction, RegistrationToken},
|
||||||
|
wayland_protocols::unstable::linux_dmabuf,
|
||||||
|
wayland_server::Client,
|
||||||
|
},
|
||||||
|
wayland::dmabuf::init_dmabuf_global_with_filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
os::unix::{
|
||||||
|
io::{AsRawFd, IntoRawFd, RawFd},
|
||||||
|
net::UnixListener,
|
||||||
|
},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{state::State, utils::GlobalDrop, wayland::init_wl_drm_global};
|
||||||
|
|
||||||
|
pub struct Socket {
|
||||||
|
pub token: RegistrationToken,
|
||||||
|
pub drm_global: GlobalDrop<crate::wayland::wl_drm::WlDrm>,
|
||||||
|
pub dmabuf_global: GlobalDrop<linux_dmabuf::v1::server::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub(super) fn create_socket(
|
||||||
|
&mut self,
|
||||||
|
render_node: DrmNode,
|
||||||
|
formats: impl Iterator<Item = Format>,
|
||||||
|
) -> Result<Socket> {
|
||||||
|
let formats = formats.collect::<Vec<_>>();
|
||||||
|
let is_primary = self.backend.kms().primary == render_node;
|
||||||
|
let socket_path = PathBuf::from(env::var("XDG_RUNTIME_DIR").unwrap()).join(format!(
|
||||||
|
"{}-{}",
|
||||||
|
&self.common.socket.to_string_lossy(),
|
||||||
|
render_node
|
||||||
|
.dev_path()
|
||||||
|
.unwrap()
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
));
|
||||||
|
// HACK!
|
||||||
|
let _ = std::fs::remove_file(&socket_path);
|
||||||
|
|
||||||
|
let listener = UnixListener::bind(socket_path.clone())
|
||||||
|
.with_context(|| format!("Failed to bind socket to {}", socket_path.display()))?;
|
||||||
|
listener.set_nonblocking(true)?;
|
||||||
|
let listener = WaylandListener(listener);
|
||||||
|
let token = self
|
||||||
|
.common
|
||||||
|
.event_loop_handle
|
||||||
|
.insert_source(
|
||||||
|
Generic::new(listener, Interest::READ, Mode::Edge),
|
||||||
|
move |_, listener, state: &mut State| {
|
||||||
|
loop {
|
||||||
|
match listener.0.accept() {
|
||||||
|
Ok((stream, _)) => {
|
||||||
|
let display = state.common.display.clone();
|
||||||
|
let client = unsafe {
|
||||||
|
display
|
||||||
|
.borrow_mut()
|
||||||
|
.create_client(stream.into_raw_fd(), state)
|
||||||
|
};
|
||||||
|
client
|
||||||
|
.data_map()
|
||||||
|
.insert_if_missing_threadsafe(|| render_node);
|
||||||
|
}
|
||||||
|
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
// we have exhausted all the pending connections
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// this is a legitimate error
|
||||||
|
if let Ok(addr) = listener.0.local_addr() {
|
||||||
|
if let Some(path) = addr.as_pathname() {
|
||||||
|
slog_scope::error!(
|
||||||
|
"Error accepting connection on listening socket {} : {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slog_scope::error!(
|
||||||
|
"Error accepting connection on listening socket <unnamed> : {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PostAction::Continue)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.context("Failed to add gpu-wayland socket to the event loop")?;
|
||||||
|
|
||||||
|
// initialize globals
|
||||||
|
let filter = move |client: Client| {
|
||||||
|
let dev_id = client.data_map().get::<DrmNode>();
|
||||||
|
if dev_id.is_none() && is_primary {
|
||||||
|
client
|
||||||
|
.data_map()
|
||||||
|
.insert_if_missing_threadsafe(|| render_node);
|
||||||
|
}
|
||||||
|
dev_id.map(|x| *x == render_node).unwrap_or(is_primary)
|
||||||
|
};
|
||||||
|
|
||||||
|
let drm_global = init_wl_drm_global(
|
||||||
|
&mut *self.common.display.borrow_mut(),
|
||||||
|
render_node.dev_path().unwrap(),
|
||||||
|
formats.clone(),
|
||||||
|
filter.clone(),
|
||||||
|
);
|
||||||
|
let dmabuf_global = init_dmabuf_global_with_filter(
|
||||||
|
&mut *self.common.display.borrow_mut(),
|
||||||
|
formats,
|
||||||
|
move |buf, mut ddata| {
|
||||||
|
let state = ddata.get::<State>().unwrap();
|
||||||
|
state
|
||||||
|
.backend
|
||||||
|
.kms()
|
||||||
|
.api
|
||||||
|
.renderer::<Gles2Renderbuffer>(&render_node, &render_node)
|
||||||
|
.map(|mut renderer| renderer.import_dmabuf(buf, None).is_ok())
|
||||||
|
.unwrap_or(false)
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
slog_scope::info!(
|
||||||
|
"Adding socket at {} for gpu {}",
|
||||||
|
socket_path.display(),
|
||||||
|
render_node
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Socket {
|
||||||
|
token,
|
||||||
|
drm_global: GlobalDrop::from(drm_global),
|
||||||
|
dmabuf_global: GlobalDrop::from(dmabuf_global),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WaylandListener(UnixListener);
|
||||||
|
|
||||||
|
impl AsRawFd for WaylandListener {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WaylandListener {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Ok(socketaddr) = self.0.local_addr() {
|
||||||
|
if let Some(path) = socketaddr.as_pathname() {
|
||||||
|
let _ = ::std::fs::remove_file(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue