kms: New backend

New backend utilizing a thread per surface for precise frame
scheduling.
This commit is contained in:
Victoria Brekenfeld 2024-06-07 20:04:39 +02:00 committed by Victoria Brekenfeld
parent 3b7bba3add
commit 469a366207
24 changed files with 3219 additions and 1958 deletions

1
Cargo.lock generated
View file

@ -887,6 +887,7 @@ dependencies = [
"sendfd",
"serde",
"serde_json",
"smallvec",
"smithay",
"smithay-egui",
"thiserror",

View file

@ -57,6 +57,7 @@ xkbcommon = "0.7"
zbus = "3.15.0"
profiling = { version = "1.0" }
rustix = { version = "0.38.32", features = ["process"] }
smallvec = "1.13.2"
[dependencies.id_tree]
branch = "feature/copy_clone"

657
src/backend/kms/device.rs Normal file
View file

@ -0,0 +1,657 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
config::{OutputConfig, OutputState},
shell::Shell,
utils::prelude::*,
};
use anyhow::{Context, Result};
use libc::dev_t;
use smithay::{
backend::{
allocator::gbm::GbmDevice,
drm::{DrmDevice, DrmDeviceFd, DrmEvent, DrmNode},
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
session::Session,
},
output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel},
reexports::{
calloop::{LoopHandle, RegistrationToken},
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
rustix::fs::OFlags,
wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Weak},
},
utils::{DevPath, DeviceFd, Transform},
wayland::drm_lease::{DrmLease, DrmLeaseState},
};
use tracing::{error, info, warn};
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
fmt,
path::{Path, PathBuf},
sync::{Arc, Condvar, Mutex, RwLock},
};
use super::{drm_helpers, socket::Socket, surface::Surface};
#[derive(Debug)]
pub struct EGLInternals {
pub display: EGLDisplay,
pub device: EGLDevice,
pub context: EGLContext,
}
pub struct Device {
pub dev_node: DrmNode,
pub render_node: DrmNode,
pub egl: Option<EGLInternals>,
pub outputs: HashMap<connector::Handle, Output>,
pub surfaces: HashMap<crtc::Handle, Surface>,
pub gbm: GbmDevice<DrmDeviceFd>,
pub drm: DrmDevice,
supports_atomic: bool,
pub leased_connectors: Vec<(connector::Handle, crtc::Handle)>,
pub leasing_global: Option<DrmLeaseState>,
pub active_leases: Vec<DrmLease>,
pub active_buffers: HashSet<Weak<WlBuffer>>,
event_token: Option<RegistrationToken>,
pub socket: Option<Socket>,
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Device")
.field("dev_node", &self.render_node)
.field("render_node", &self.render_node)
.field("outputs", &self.outputs)
.field("surfaces", &self.surfaces)
.field("drm", &self.drm)
.field("gbm", &self.gbm)
.field("egl", &self.egl)
.field("supports_atomic", &self.supports_atomic)
.field("leased_connectors", &self.leased_connectors)
.field("leasing_global", &self.leasing_global)
.field("active_leases", &self.active_leases)
.field("event_token", &self.event_token)
.field("socket", &self.socket)
.finish()
}
}
pub fn init_egl(gbm: &GbmDevice<DrmDeviceFd>) -> Result<EGLInternals> {
let path = gbm.dev_path();
let display = unsafe { EGLDisplay::new(gbm.clone()) }.with_context(|| {
format!(
"Failed to create EGLDisplay for device: {:?}",
path.as_deref().map(Path::display)
)
})?;
let device = EGLDevice::device_for_display(&display).with_context(|| {
format!(
"Unable to find matching egl device for {:?}",
path.as_deref().map(Path::display)
)
})?;
let context =
EGLContext::new_with_priority(&display, ContextPriority::High).with_context(|| {
format!(
"Failed to create EGLContext for device {:?}:{:?}",
device,
path.as_deref().map(Path::display),
)
})?;
Ok(EGLInternals {
display,
device,
context,
})
}
impl State {
pub fn device_added(&mut self, dev: dev_t, path: PathBuf, dh: &DisplayHandle) -> Result<()> {
if !self.backend.kms().session.is_active() {
return Ok(());
}
let fd = DrmDeviceFd::new(DeviceFd::from(
self.backend
.kms()
.session
.open(
&path,
OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,
)
.with_context(|| {
format!(
"Failed to optain file descriptor for drm device: {}",
path.display()
)
})?,
));
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
let drm_node = DrmNode::from_dev_id(dev)?;
let supports_atomic = drm.is_atomic();
let gbm = GbmDevice::new(fd)
.with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?;
let (render_node, render_formats) = {
let egl = init_egl(&gbm)?;
let render_node = egl
.device
.try_get_render_node()
.ok()
.and_then(std::convert::identity)
.unwrap_or(drm_node);
let render_formats = egl.context.dmabuf_texture_formats().clone();
(render_node, render_formats)
};
let token = self
.common
.event_loop_handle
.insert_source(
notifier,
move |event, metadata, state: &mut State| match event {
DrmEvent::VBlank(crtc) => {
if let Some(device) = state.backend.kms().drm_devices.get_mut(&drm_node) {
if let Some(surface) = device.surfaces.get_mut(&crtc) {
surface.on_vblank(metadata.take());
}
}
}
DrmEvent::Error(err) => {
warn!(?err, "Failed to read events of device {:?}.", dev);
}
},
)
.with_context(|| format!("Failed to add drm device to event loop: {}", dev))?;
let socket = match self.create_socket(dh, render_node, render_formats.clone().into_iter()) {
Ok(socket) => Some(socket),
Err(err) => {
warn!(
?err,
"Failed to initialize hardware-acceleration for clients on {}.", render_node,
);
None
}
};
let mut device = Device {
dev_node: drm_node,
render_node,
egl: None,
outputs: HashMap::new(),
surfaces: HashMap::new(),
gbm: gbm.clone(),
drm,
supports_atomic,
leased_connectors: Vec::new(),
leasing_global: DrmLeaseState::new::<State>(dh, &drm_node)
.map_err(|err| {
// TODO: replace with inspect_err, once stable
warn!(
?err,
"Failed to initialize drm lease global for: {}", drm_node
);
err
})
.ok(),
active_leases: Vec::new(),
active_buffers: HashSet::new(),
event_token: Some(token),
socket,
};
let connectors = device.enumerate_surfaces()?.added; // There are no removed outputs on newly added devices
let mut wl_outputs = Vec::new();
let mut w = self.common.shell.read().unwrap().global_space().size.w;
{
for (conn, maybe_crtc) in connectors {
match device.connector_added(
self.backend.kms().primary_node.as_ref(),
conn,
maybe_crtc,
(0, w),
&self.common.event_loop_handle,
self.common.shell.clone(),
self.common.startup_done.clone(),
) {
Ok((output, should_expose)) => {
if should_expose {
w += output.config().mode_size().w;
wl_outputs.push(output.clone());
}
device.outputs.insert(conn, output);
}
Err(err) => {
warn!(?err, "Failed to initialize output, skipping");
}
}
}
// TODO atomic commit all surfaces together and drop surfaces, if it fails due to bandwidth
self.backend.kms().drm_devices.insert(drm_node, device);
}
self.backend.kms().refresh_used_devices();
self.common
.output_configuration_state
.add_heads(wl_outputs.iter());
self.common.config.read_outputs(
&mut self.common.output_configuration_state,
&mut self.backend,
&self.common.shell,
&self.common.event_loop_handle,
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
self.common.startup_done.clone(),
);
self.common.refresh();
Ok(())
}
pub fn device_changed(&mut self, dev: dev_t) -> Result<()> {
if !self.backend.kms().session.is_active() {
return Ok(());
}
let drm_node = DrmNode::from_dev_id(dev)?;
let mut outputs_removed = Vec::new();
let mut outputs_added = Vec::new();
{
let backend = self.backend.kms();
if let Some(device) = backend.drm_devices.get_mut(&drm_node) {
let changes = device.enumerate_surfaces()?;
let mut w = self.common.shell.read().unwrap().global_space().size.w;
for conn in changes.removed {
// contains conns with updated crtcs, just drop the surface and re-create
if let Some(pos) = device
.leased_connectors
.iter()
.position(|(handle, _)| *handle == conn)
{
let _ = device.leased_connectors.remove(pos);
if let Some(leasing_state) = device.leasing_global.as_mut() {
leasing_state.withdraw_connector(conn);
}
} else if let Some(crtc) = device
.surfaces
.iter()
.find_map(|(crtc, surface)| (surface.connector == conn).then_some(crtc))
.cloned()
{
let surface = device.surfaces.remove(&crtc).unwrap();
// TODO: move up later outputs?
w -= surface.output.current_mode().map(|m| m.size.w).unwrap_or(0);
}
if !changes.added.iter().any(|(c, _)| c == &conn) {
outputs_removed.push(
device
.outputs
.remove(&conn)
.expect("Connector without output?"),
);
}
}
for (conn, maybe_crtc) in changes.added {
match device.connector_added(
backend.primary_node.as_ref(),
conn,
maybe_crtc,
(0, w),
&self.common.event_loop_handle,
self.common.shell.clone(),
self.common.startup_done.clone(),
) {
Ok((output, should_expose)) => {
if should_expose {
w += output.config().mode_size().w;
outputs_added.push(output.clone());
}
device.outputs.insert(conn, output);
}
Err(err) => {
warn!(?err, "Failed to initialize output, skipping");
}
}
}
}
}
self.backend.kms().refresh_used_devices();
self.common
.output_configuration_state
.remove_heads(outputs_removed.iter());
self.common
.output_configuration_state
.add_heads(outputs_added.iter());
{
self.common.config.read_outputs(
&mut self.common.output_configuration_state,
&mut self.backend,
&self.common.shell,
&self.common.event_loop_handle,
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
self.common.startup_done.clone(),
);
// Don't remove the outputs, before potentially new ones have been initialized.
// reading a new config means outputs might become enabled, that were previously disabled.
// This gives the shell more information on how to move outputs.
for output in outputs_removed {
self.common.remove_output(&output);
}
self.common.refresh();
}
Ok(())
}
pub fn device_removed(&mut self, dev: dev_t, dh: &DisplayHandle) -> Result<()> {
let drm_node = DrmNode::from_dev_id(dev)?;
let mut outputs_removed = Vec::new();
let backend = self.backend.kms();
if let Some(mut device) = backend.drm_devices.remove(&drm_node) {
if let Some(mut leasing_global) = device.leasing_global.take() {
leasing_global.disable_global::<State>();
}
for surface in device.surfaces.values_mut() {
outputs_removed.push(surface.output.clone());
}
if let Some(token) = device.event_token.take() {
self.common.event_loop_handle.remove(token);
}
if let Some(socket) = device.socket.take() {
self.common.event_loop_handle.remove(socket.token);
self.common
.dmabuf_state
.destroy_global::<State>(dh, socket.dmabuf_global);
dh.remove_global::<State>(socket.drm_global);
}
}
self.common
.output_configuration_state
.remove_heads(outputs_removed.iter());
if self.backend.kms().session.is_active() {
for output in outputs_removed {
self.common.remove_output(&output);
}
self.common.config.read_outputs(
&mut self.common.output_configuration_state,
&mut self.backend,
&self.common.shell,
&self.common.event_loop_handle,
&mut self.common.workspace_state.update(),
&self.common.xdg_activation_state,
self.common.startup_done.clone(),
);
self.common.refresh();
} else {
self.common.output_configuration_state.update();
}
Ok(())
}
}
pub struct OutputChanges {
pub added: Vec<(connector::Handle, Option<crtc::Handle>)>,
pub removed: Vec<connector::Handle>,
}
impl Device {
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
// enumerate our outputs
let config = drm_helpers::display_configuration(&mut self.drm, self.supports_atomic)?;
let surfaces = self
.surfaces
.iter()
.map(|(c, s)| (s.connector, *c))
.chain(
self.leased_connectors
.iter()
.map(|(conn, crtc)| (*conn, *crtc)),
)
.collect::<HashMap<connector::Handle, crtc::Handle>>();
let added = config
.iter()
.filter(|(conn, maybe)| match (surfaces.get(&conn), maybe) {
(Some(current_crtc), Some(new_crtc)) => current_crtc != new_crtc,
(None, _) => true,
_ => false,
})
.map(|(conn, crtc)| (*conn, *crtc))
.collect::<Vec<_>>();
let removed = self
.outputs
.iter()
.filter(|(conn, _)| match config.get(conn) {
Some(Some(c)) => surfaces.get(&conn).is_some_and(|crtc| c != crtc),
_ => true,
})
.map(|(conn, _)| *conn)
.collect::<Vec<_>>();
Ok(OutputChanges { added, removed })
}
pub fn connector_added(
&mut self,
primary_node: Option<&DrmNode>,
conn: connector::Handle,
maybe_crtc: Option<crtc::Handle>,
position: (i32, i32),
evlh: &LoopHandle<'static, State>,
shell: Arc<RwLock<Shell>>,
startup_done: Arc<(Mutex<bool>, Condvar)>,
) -> Result<(Output, bool)> {
let output = self
.outputs
.get(&conn)
.cloned()
.map(|output| Ok(output))
.unwrap_or_else(|| create_output_for_conn(&mut self.drm, conn))
.context("Failed to create `Output`")?;
let non_desktop = match drm_helpers::get_property_val(&self.drm, conn, "non-desktop") {
Ok((val_type, value)) => val_type.convert_value(value).as_boolean().unwrap(),
Err(err) => {
warn!(
?err,
"Failed to determine if connector is meant desktop usage, assuming so."
);
false
}
};
if non_desktop {
if let Some(crtc) = maybe_crtc {
self.leased_connectors.push((conn, crtc));
info!(
"Connector {} is non-desktop, setting up for leasing",
output.name()
);
if let Some(lease_state) = self.leasing_global.as_mut() {
let physical = output.physical_properties();
lease_state.add_connector::<State>(
conn,
output.name(),
format!("{} {}", physical.make, physical.model),
);
}
} else {
warn!(
"Connector {} is non-desktop, but we don't have a free crtc: not leasing",
output.name()
);
}
Ok((output, false))
} else {
output
.user_data()
.insert_if_missing(|| RefCell::new(OutputConfig::default()));
populate_modes(&mut self.drm, &output, conn, position)
.with_context(|| "Failed to enumerate connector modes")?;
let has_surface = if let Some(crtc) = maybe_crtc {
match Surface::new(
&output,
crtc,
conn,
primary_node.copied().unwrap_or(self.render_node),
self.dev_node,
self.render_node,
evlh,
shell,
startup_done,
) {
Ok(data) => {
self.surfaces.insert(crtc, data);
true
}
Err(err) => {
error!(?crtc, "Failed to initialize surface: {}", err);
false
}
}
} else {
false
};
if !has_surface {
output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut()
.enabled = OutputState::Disabled;
}
Ok((output, true))
}
}
pub fn in_use(&self, primary: Option<&DrmNode>) -> bool {
Some(&self.render_node) == primary
|| !self.surfaces.is_empty()
|| !self.active_buffers.is_empty()
}
}
fn create_output_for_conn(drm: &mut DrmDevice, conn: connector::Handle) -> Result<Output> {
let conn_info = drm
.get_connector(conn, false)
.with_context(|| "Failed to query connector info")?;
let interface = drm_helpers::interface_name(drm, conn)?;
let edid_info = drm_helpers::edid_info(drm, conn);
let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0));
Ok(Output::new(
interface,
PhysicalProperties {
size: (phys_w as i32, phys_h as i32).into(),
subpixel: match conn_info.subpixel() {
connector::SubPixel::HorizontalRgb => Subpixel::HorizontalRgb,
connector::SubPixel::HorizontalBgr => Subpixel::HorizontalBgr,
connector::SubPixel::VerticalRgb => Subpixel::VerticalRgb,
connector::SubPixel::VerticalBgr => Subpixel::VerticalBgr,
connector::SubPixel::None => Subpixel::None,
_ => Subpixel::Unknown,
},
make: edid_info
.as_ref()
.map(|info| info.manufacturer.clone())
.unwrap_or_else(|_| String::from("Unknown")),
model: edid_info
.as_ref()
.map(|info| info.model.clone())
.unwrap_or_else(|_| String::from("Unknown")),
},
))
}
fn populate_modes(
drm: &mut DrmDevice,
output: &Output,
conn: connector::Handle,
position: (i32, i32),
) -> Result<()> {
let conn_info = drm.get_connector(conn, false)?;
let max_bpc = drm_helpers::get_max_bpc(drm, conn)?.map(|(_val, range)| range.end.min(16));
let mode = conn_info
.modes()
.iter()
.find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED))
.copied()
.unwrap_or(conn_info.modes()[0]);
let refresh_rate = drm_helpers::calculate_refresh_rate(mode);
let output_mode = OutputMode {
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
refresh: refresh_rate as i32,
};
for mode in conn_info.modes() {
let refresh_rate = drm_helpers::calculate_refresh_rate(*mode);
let mode = OutputMode {
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
refresh: refresh_rate as i32,
};
output.add_mode(mode);
}
output.set_preferred(output_mode);
output.change_current_state(
Some(output_mode),
// TODO: Readout property for monitor rotation
Some(Transform::Normal),
None,
Some(position.into()),
);
let mut output_config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
*output_config = OutputConfig {
mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
position,
max_bpc,
..std::mem::take(&mut *output_config)
};
Ok(())
}

View file

@ -14,12 +14,13 @@ use std::{collections::HashMap, ops::Range};
pub fn display_configuration(
device: &mut impl ControlDevice,
supports_atomic: bool,
) -> Result<HashMap<connector::Handle, crtc::Handle>> {
) -> Result<HashMap<connector::Handle, Option<crtc::Handle>>> {
let res_handles = device.resource_handles()?;
let connectors = res_handles.connectors();
let mut map = HashMap::new();
let mut cleanup = Vec::new();
// We expect the previous running drm master (likely the login mananger)
// to leave the drm device in a sensible state.
// That means, to reduce flickering, we try to keep an established mapping.
@ -31,7 +32,7 @@ pub fn display_configuration(
if let Some(crtc) = device.get_encoder(enc)?.crtc() {
// If is is connected we found a mapping
if conn.state() == ConnectorState::Connected {
map.insert(conn.handle(), crtc);
map.insert(conn.handle(), Some(crtc));
// If not, the user just unplugged something,
// or the drm master did not cleanup?
// Well, I guess we cleanup after them.
@ -41,6 +42,7 @@ pub fn display_configuration(
}
}
}
// But just in case we try to match all remaining connectors.
for conn in connectors
.iter()
@ -56,12 +58,16 @@ pub fn display_configuration(
.flat_map(|encoder_handle| device.get_encoder(*encoder_handle))
{
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
if !map.values().any(|v| *v == crtc) {
map.insert(conn.handle(), crtc);
if !map.values().any(|v| *v == Some(crtc)) {
map.insert(conn.handle(), Some(crtc));
break 'outer;
}
}
}
if !map.contains_key(&conn.handle()) {
map.insert(conn.handle(), None);
}
}
// And then cleanup
@ -119,9 +125,11 @@ pub fn display_configuration(
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
} else {
for crtc in cleanup {
for crtc in res_handles.crtcs() {
#[allow(deprecated)]
let _ = device.set_cursor(crtc, Option::<&DumbBuffer>::None);
let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None);
}
for crtc in cleanup {
// null commit (necessary to trigger removal on the kernel side with the legacy api.)
let _ = device.set_crtc(crtc, None, (0, 0), &[], None);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::backend::{
allocator::{
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::GbmAllocator,
Allocator,
},
drm::{CreateDrmNodeError, DrmNode},
renderer::{
gles::GlesError,
glow::GlowRenderer,
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
Renderer,
},
SwapBuffersError,
};
use std::cell::Cell;
use std::{
collections::HashMap,
fmt,
os::unix::prelude::AsFd,
sync::atomic::{AtomicBool, Ordering},
};
use crate::backend::render::element::FromGlesError;
/// Errors raised by the [`GbmGlesBackend`]
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// OpenGL error
#[error(transparent)]
Gl(#[from] GlesError),
/// Error creating a drm node
#[error(transparent)]
DrmNode(#[from] CreateDrmNodeError),
}
impl From<Error> for SwapBuffersError {
fn from(err: Error) -> SwapBuffersError {
match err {
x @ Error::DrmNode(_) => SwapBuffersError::ContextLost(Box::new(x)),
Error::Gl(x) => x.into(),
}
}
}
/// A [`GraphicsApi`] utilizing user-provided GBM Devices and OpenGL ES for rendering.
pub struct GbmGlowBackend<A: AsFd + 'static> {
devices: HashMap<DrmNode, (GbmAllocator<A>, Cell<Option<GlowRenderer>>)>,
needs_enumeration: AtomicBool,
}
impl<A: AsFd + fmt::Debug + 'static> fmt::Debug for GbmGlowBackend<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GbmGlesBackend")
.field("devices", &self.devices.keys())
.field("needs_enumeration", &self.needs_enumeration)
.finish()
}
}
impl<A: AsFd + 'static> Default for GbmGlowBackend<A> {
fn default() -> Self {
GbmGlowBackend {
devices: HashMap::new(),
needs_enumeration: AtomicBool::new(true),
}
}
}
impl<A: AsFd + Clone + Send + 'static> GbmGlowBackend<A> {
pub fn new() -> Self {
GbmGlowBackend {
devices: HashMap::new(),
needs_enumeration: AtomicBool::new(false),
}
}
pub fn current_devices(&self) -> impl Iterator<Item = &DrmNode> {
self.devices.keys()
}
pub fn add_node(&mut self, node: DrmNode, gbm: GbmAllocator<A>, renderer: GlowRenderer) {
if self.devices.contains_key(&node) {
return;
}
self.devices.insert(node, (gbm, Cell::new(Some(renderer))));
self.needs_enumeration.store(true, Ordering::SeqCst);
}
/// Remove a given node from the api
pub fn remove_node(&mut self, node: &DrmNode) {
if self.devices.remove(node).is_some() {
self.needs_enumeration.store(true, Ordering::SeqCst);
}
}
}
impl<A: AsFd + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
type Device = GbmGlowDevice;
type Error = Error;
fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error> {
self.needs_enumeration.store(false, Ordering::SeqCst);
// remove old stuff
list.retain(|renderer| {
self.devices
.keys()
.any(|node| renderer.node.dev_id() == node.dev_id())
});
// add new stuff
let new_renderers = self
.devices
.iter()
// but don't replace already initialized renderers
.filter(|(node, _)| {
!list
.iter()
.any(|renderer| renderer.node.dev_id() == node.dev_id())
})
.flat_map(|(node, (allocator, renderer))| {
let renderer = renderer.replace(None)?;
Some(GbmGlowDevice {
node: *node,
renderer,
allocator: Box::new(DmabufAllocator(allocator.clone())),
})
})
.collect::<Vec<GbmGlowDevice>>();
list.extend(new_renderers);
Ok(())
}
fn needs_enumeration(&self) -> bool {
self.needs_enumeration.load(Ordering::Acquire)
}
fn identifier() -> &'static str {
"gbm_glow"
}
}
/// [`ApiDevice`] of the [`GbmGlesBackend`]
pub struct GbmGlowDevice {
node: DrmNode,
renderer: GlowRenderer,
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
}
impl fmt::Debug for GbmGlowDevice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GbmGlesDevice")
.field("node", &self.node)
.field("renderer", &self.renderer)
.finish_non_exhaustive()
}
}
impl ApiDevice for GbmGlowDevice {
type Renderer = GlowRenderer;
fn renderer(&self) -> &Self::Renderer {
&self.renderer
}
fn renderer_mut(&mut self) -> &mut Self::Renderer {
&mut self.renderer
}
fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError> {
self.allocator.as_mut()
}
fn node(&self) -> &DrmNode {
&self.node
}
}
impl<T: GraphicsApi, A: AsFd + Clone + 'static> FromGlesError for MultiError<GbmGlowBackend<A>, T>
where
T::Error: 'static,
<<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
{
#[inline]
fn from_gles_error(err: GlesError) -> MultiError<GbmGlowBackend<A>, T> {
MultiError::Render(err)
}
}

View file

@ -0,0 +1,2 @@
pub mod gles;
pub mod pixman;

View file

@ -0,0 +1,155 @@
use std::{
collections::HashMap,
fmt,
os::fd::AsFd,
sync::atomic::{AtomicBool, Ordering},
};
use smithay::backend::{
allocator::{
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
Allocator,
},
drm::DrmNode,
renderer::{
multigpu::{ApiDevice, GraphicsApi},
pixman::{PixmanError, PixmanRenderer},
},
};
use tracing::warn;
#[derive(Debug)]
pub struct GbmPixmanBackend<A: AsFd + 'static> {
devices: HashMap<DrmNode, GbmAllocator<A>>,
needs_enumeration: AtomicBool,
allocator_flags: GbmBufferFlags,
}
pub struct GbmPixmanDevice {
node: DrmNode,
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
renderer: PixmanRenderer,
}
impl fmt::Debug for GbmPixmanDevice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GbmPixmanDevice")
.field("node", &self.node)
.field("allocator", &"...")
.field("renderer", &self.renderer)
.finish()
}
}
impl<A: AsFd + 'static> GbmPixmanBackend<A> {
pub fn new() -> Self {
GbmPixmanBackend {
devices: HashMap::new(),
needs_enumeration: AtomicBool::new(false),
allocator_flags: GbmBufferFlags::RENDERING,
}
}
pub fn with_allocator_flags(allocator_flags: GbmBufferFlags) -> Self {
GbmPixmanBackend {
devices: HashMap::new(),
needs_enumeration: AtomicBool::new(false),
allocator_flags,
}
}
pub fn set_allocator_flags(&mut self, flags: GbmBufferFlags) {
self.allocator_flags = flags;
}
pub fn add_node(&mut self, node: DrmNode, gbm: GbmDevice<A>) {
if self.devices.contains_key(&node) {
return;
}
let allocator = GbmAllocator::new(gbm, self.allocator_flags);
self.devices.insert(node, allocator);
self.needs_enumeration.store(true, Ordering::SeqCst);
}
/// Remove a given node from the api
pub fn remove_node(&mut self, node: &DrmNode) {
if self.devices.remove(node).is_some() {
self.needs_enumeration.store(true, Ordering::SeqCst);
}
}
}
impl<A: AsFd + Clone + 'static> GraphicsApi for GbmPixmanBackend<A> {
type Device = GbmPixmanDevice;
type Error = PixmanError;
fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error> {
self.needs_enumeration.store(false, Ordering::SeqCst);
list.retain(|renderer| {
self.devices
.keys()
.any(|node| renderer.node.dev_id() == node.dev_id())
});
// add new stuff
let new_renderers = self
.devices
.iter()
.filter(|(node, _)| {
!list
.iter()
.any(|renderer| renderer.node.dev_id() == node.dev_id())
})
.map(|(node, gbm)| {
Ok(GbmPixmanDevice {
node: *node,
allocator: Box::new(DmabufAllocator(gbm.clone()))
as Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
renderer: PixmanRenderer::new()?,
})
})
.flat_map(|x: Result<GbmPixmanDevice, PixmanError>| match x {
Ok(x) => Some(x),
Err(x) => {
warn!("Skipping pixman device: {}", x);
None
}
})
.collect::<Vec<GbmPixmanDevice>>();
list.extend(new_renderers);
Ok(())
}
fn identifier() -> &'static str {
"gbm_pixman"
}
fn needs_enumeration(&self) -> bool {
self.needs_enumeration.load(Ordering::SeqCst)
}
}
impl ApiDevice for GbmPixmanDevice {
type Renderer = PixmanRenderer;
fn renderer(&self) -> &Self::Renderer {
&self.renderer
}
fn renderer_mut(&mut self) -> &mut Self::Renderer {
&mut self.renderer
}
fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError> {
&mut self.allocator
}
fn node(&self) -> &DrmNode {
&self.node
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,262 @@
use std::{collections::VecDeque, num::NonZeroU64, time::Duration};
use smithay::utils::{Clock, Monotonic, Time};
use tracing::error;
const MIN_RENDER_TIME: Duration = Duration::from_millis(5);
const FRAME_TIME_WINDOW: usize = 3;
pub struct Timings {
refresh_interval_ns: Option<NonZeroU64>,
vrr: bool,
pub pending_frame: Option<PendingFrame>,
pub previous_frames: VecDeque<Frame>,
}
#[derive(Debug)]
pub struct PendingFrame {
render_start: Time<Monotonic>,
render_duration_elements: Option<Duration>,
render_duration_draw: Option<Duration>,
presentation_submitted: Option<Time<Monotonic>>,
}
#[derive(Debug)]
pub struct Frame {
pub render_start: Time<Monotonic>,
pub render_duration_elements: Duration,
pub render_duration_draw: Duration,
pub presentation_submitted: Time<Monotonic>,
pub presentation_presented: Time<Monotonic>,
}
impl Frame {
fn render_time(&self) -> Duration {
self.render_duration_elements + self.render_duration_draw
}
fn frame_time(&self) -> Duration {
Time::elapsed(&self.render_start, self.presentation_submitted)
}
}
impl Timings {
const WINDOW_SIZE: usize = 360;
pub fn new(refresh_interval: Option<Duration>, vrr: bool) -> Self {
let refresh_interval_ns = if let Some(interval) = &refresh_interval {
assert_eq!(interval.as_secs(), 0);
Some(NonZeroU64::new(interval.subsec_nanos().into()).unwrap())
} else {
None
};
Self {
refresh_interval_ns,
vrr,
pending_frame: None,
previous_frames: VecDeque::new(),
}
}
pub fn refresh_interval(&self) -> Duration {
match self.refresh_interval_ns {
Some(ns) => Duration::from_nanos(ns.get()),
None => Duration::ZERO,
}
}
pub fn set_refresh_interval(&mut self, interval: Option<Duration>) {
self.refresh_interval_ns = interval
.map(|duration| duration.subsec_nanos() as u64)
.and_then(NonZeroU64::new);
self.previous_frames.clear();
}
pub fn set_vrr(&mut self, vrr: bool) {
self.vrr = vrr;
}
pub fn start_render(&mut self, clock: &Clock<Monotonic>) {
self.pending_frame = Some(PendingFrame {
render_start: clock.now(),
render_duration_elements: None,
render_duration_draw: None,
presentation_submitted: None,
});
}
pub fn elements_done(&mut self, clock: &Clock<Monotonic>) {
if let Some(frame) = self.pending_frame.as_mut() {
frame.render_duration_elements = Some(Time::elapsed(&frame.render_start, clock.now()));
}
}
pub fn draw_done(&mut self, clock: &Clock<Monotonic>) {
if let Some(frame) = self.pending_frame.as_mut() {
frame.render_duration_draw = Some(
Time::elapsed(&frame.render_start, clock.now())
- frame
.render_duration_elements
.clone()
.unwrap_or(Duration::ZERO),
);
}
}
pub fn submitted_for_presentation(&mut self, clock: &Clock<Monotonic>) {
if let Some(frame) = self.pending_frame.as_mut() {
frame.presentation_submitted = Some(clock.now());
}
}
pub fn presented(&mut self, value: Time<Monotonic>) {
if let Some(frame) = self.pending_frame.take() {
self.previous_frames.push_back(Frame {
render_start: frame.render_start,
render_duration_elements: frame.render_duration_elements.unwrap_or_default(),
render_duration_draw: frame.render_duration_draw.unwrap_or_default(),
presentation_submitted: frame.presentation_submitted.unwrap(),
presentation_presented: value,
});
while self.previous_frames.len() > Self::WINDOW_SIZE {
self.previous_frames.pop_front();
}
}
}
pub fn discard_current_frame(&mut self) {
let _ = self.pending_frame.take();
}
pub fn max_rendertime(&self) -> Duration {
self.previous_frames
.iter()
.map(|f| f.render_time())
.max()
.unwrap_or(Duration::ZERO)
}
pub fn min_rendertime(&self) -> Duration {
self.previous_frames
.iter()
.map(|f| f.render_time())
.min()
.unwrap_or(Duration::ZERO)
}
/*
pub fn max_time_to_display(&self) -> Duration {
self.previous_frames
.iter()
.map(|f| f.time_to_display())
.max()
.unwrap_or(Duration::ZERO)
}
pub fn min_time_to_display(&self) -> Duration {
self.previous_frames
.iter()
.map(|f| f.time_to_display())
.min()
.unwrap_or(Duration::ZERO)
}
*/
pub fn avg_rendertime(&self) -> Duration {
if self.previous_frames.is_empty() {
return Duration::ZERO;
}
self.previous_frames
.iter()
.map(|f| f.render_time())
.sum::<Duration>()
/ (self.previous_frames.len() as u32)
}
pub fn avg_frametime(&self, window: usize) -> Duration {
if self.previous_frames.is_empty() {
return MIN_RENDER_TIME;
}
self.previous_frames
.iter()
.rev()
.take(window)
.map(|f| f.frame_time())
.sum::<Duration>()
/ (window.min(self.previous_frames.len()) as u32)
}
pub fn avg_fps(&self) -> f64 {
if self.previous_frames.is_empty() {
return 0.0;
}
let secs = match (self.previous_frames.front(), self.previous_frames.back()) {
(Some(Frame { render_start, .. }), Some(end_frame)) => {
Time::elapsed(render_start, end_frame.render_start.clone()) + end_frame.frame_time()
}
_ => Duration::ZERO,
}
.as_secs_f64();
1.0 / (secs / self.previous_frames.len() as f64)
}
pub fn next_presentation_time(&self, clock: &Clock<Monotonic>) -> Duration {
let mut now = clock.now().into();
let Some(refresh_interval_ns) = self.refresh_interval_ns else {
return Duration::ZERO;
};
let Some(last_presentation_time): Option<Duration> = self
.previous_frames
.back()
.map(|frame| frame.presentation_presented.into())
else {
return Duration::ZERO;
};
let refresh_interval_ns = refresh_interval_ns.get();
if now <= last_presentation_time {
// Got an early VBlank.
let orig_now = now;
now += Duration::from_nanos(refresh_interval_ns);
if now < last_presentation_time {
// Not sure when this can happen.
error!(
now = ?orig_now,
?last_presentation_time,
"got a 2+ early VBlank, {:?} until presentation",
last_presentation_time - now,
);
now = last_presentation_time + Duration::from_nanos(refresh_interval_ns);
}
}
let since_last = now - last_presentation_time;
let since_last_ns =
since_last.as_secs() * 1_000_000_000 + u64::from(since_last.subsec_nanos());
let to_next_ns = (since_last_ns / refresh_interval_ns + 1) * refresh_interval_ns;
// If VRR is enabled and more than one frame passed since last presentation, assume that we
// can present immediately.
if self.vrr && to_next_ns > refresh_interval_ns {
Duration::ZERO
} else {
last_presentation_time + Duration::from_nanos(to_next_ns) - now
}
}
pub fn next_render_time(&self, clock: &Clock<Monotonic>) -> Duration {
let estimated_presentation_time = self.next_presentation_time(clock);
if estimated_presentation_time.is_zero() {
return Duration::ZERO;
}
estimated_presentation_time.saturating_sub(self.avg_frametime(FRAME_TIME_WINDOW))
}
}

View file

@ -8,7 +8,7 @@ use smithay::{
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
Element, Id, Kind, RenderElement, UnderlyingStorage,
},
gles::GlesTexture,
gles::{GlesError, GlesTexture},
glow::{GlowFrame, GlowRenderer},
utils::{CommitCounter, DamageSet, OpaqueRegions},
Frame, ImportAll, ImportMem, Renderer,
@ -465,3 +465,13 @@ impl<R: Renderer> RenderElement<R> for DamageElement {
Ok(())
}
}
pub trait FromGlesError {
fn from_gles_error(err: GlesError) -> Self;
}
impl FromGlesError for GlesError {
fn from_gles_error(err: GlesError) -> Self {
err
}
}

View file

@ -11,17 +11,15 @@ use std::{
#[cfg(feature = "debug")]
use crate::debug::fps_ui;
use crate::{
backend::render::element::DamageElement,
config::Config,
backend::{kms::render::gles::GbmGlowBackend, render::element::DamageElement},
shell::{
element::CosmicMappedKey,
focus::target::WindowGroup,
grabs::{SeatMenuGrabState, SeatMoveGrabState},
layout::tiling::ANIMATION_DURATION,
CosmicMapped, CosmicMappedRenderElement, OverviewMode, SeatExt, SessionLock, Trigger,
WorkspaceDelta, WorkspaceRenderElement,
CosmicMappedRenderElement, OverviewMode, SeatExt, SessionLock, Trigger, WorkspaceDelta,
WorkspaceRenderElement,
},
state::Fps,
utils::prelude::*,
wayland::{
handlers::{
@ -34,6 +32,7 @@ use crate::{
use cosmic::Theme;
use cosmic_comp_config::workspace::WorkspaceLayout;
use element::FromGlesError;
use keyframe::{ease, functions::EaseInOutCubic};
use smithay::{
backend::{
@ -51,8 +50,7 @@ use smithay::{
element::PixelShaderElement, GlesError, GlesPixelProgram, GlesRenderer, Uniform,
UniformName, UniformType,
},
glow::GlowRenderer,
multigpu::{gbm::GbmGlesBackend, Error as MultiError, MultiFrame, MultiRenderer},
multigpu::{Error as MultiError, MultiFrame, MultiRenderer},
sync::SyncPoint,
Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter,
},
@ -74,23 +72,11 @@ pub mod cursor;
pub mod element;
use self::element::{AsGlowRenderer, CosmicElement};
pub type GlMultiRenderer<'a> = MultiRenderer<
'a,
'a,
GbmGlesBackend<GlowRenderer, DrmDeviceFd>,
GbmGlesBackend<GlowRenderer, DrmDeviceFd>,
>;
pub type GlMultiFrame<'a, 'frame> = MultiFrame<
'a,
'a,
'frame,
GbmGlesBackend<GlowRenderer, DrmDeviceFd>,
GbmGlesBackend<GlowRenderer, DrmDeviceFd>,
>;
pub type GlMultiError = MultiError<
GbmGlesBackend<GlowRenderer, DrmDeviceFd>,
GbmGlesBackend<GlowRenderer, DrmDeviceFd>,
>;
pub type GlMultiRenderer<'a> =
MultiRenderer<'a, 'a, GbmGlowBackend<DrmDeviceFd>, GbmGlowBackend<DrmDeviceFd>>;
pub type GlMultiFrame<'a, 'frame> =
MultiFrame<'a, 'a, 'frame, GbmGlowBackend<DrmDeviceFd>, GbmGlowBackend<DrmDeviceFd>>;
pub type GlMultiError = MultiError<GbmGlowBackend<DrmDeviceFd>, GbmGlowBackend<DrmDeviceFd>>;
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
pub static OUTLINE_SHADER: &str = include_str!("./shaders/rounded_outline.frag");
@ -341,12 +327,9 @@ impl BackdropShader {
}
}
pub fn init_shaders<R: AsGlowRenderer>(renderer: &mut R) -> Result<(), GlesError> {
let glow_renderer = renderer.glow_renderer_mut();
let gles_renderer: &mut GlesRenderer = glow_renderer.borrow_mut();
pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
{
let egl_context = gles_renderer.egl_context();
let egl_context = renderer.egl_context();
if egl_context.user_data().get::<IndicatorShader>().is_some()
&& egl_context.user_data().get::<BackdropShader>().is_some()
{
@ -354,7 +337,7 @@ pub fn init_shaders<R: AsGlowRenderer>(renderer: &mut R) -> Result<(), GlesError
}
}
let outline_shader = gles_renderer.compile_custom_pixel_shader(
let outline_shader = renderer.compile_custom_pixel_shader(
OUTLINE_SHADER,
&[
UniformName::new("color", UniformType::_3f),
@ -362,7 +345,7 @@ pub fn init_shaders<R: AsGlowRenderer>(renderer: &mut R) -> Result<(), GlesError
UniformName::new("radius", UniformType::_1f),
],
)?;
let rectangle_shader = gles_renderer.compile_custom_pixel_shader(
let rectangle_shader = renderer.compile_custom_pixel_shader(
RECTANGLE_SHADER,
&[
UniformName::new("color", UniformType::_3f),
@ -370,7 +353,7 @@ pub fn init_shaders<R: AsGlowRenderer>(renderer: &mut R) -> Result<(), GlesError
],
)?;
let egl_context = gles_renderer.egl_context();
let egl_context = renderer.egl_context();
egl_context
.user_data()
.insert_if_missing(|| IndicatorShader(outline_shader));
@ -852,7 +835,7 @@ pub fn split_layer_elements<R>(
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
<R as Renderer>::Error: FromGlesError,
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{
@ -919,7 +902,7 @@ pub fn background_layer_elements<R>(
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
<R as Renderer>::Error: FromGlesError,
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{

View file

@ -139,9 +139,9 @@ pub fn init_backend(
event_loop: &mut EventLoop<State>,
state: &mut State,
) -> Result<()> {
let (mut backend, mut input) =
let (mut backend, mut input): (WinitGraphicsBackend<GlowRenderer>, _) =
winit::init().map_err(|e| anyhow!("Failed to initilize winit backend: {e:?}"))?;
init_shaders(backend.renderer()).context("Failed to initialize renderer")?;
init_shaders(backend.renderer().borrow_mut()).context("Failed to initialize renderer")?;
init_egl_client_side(dh, state, &mut backend)?;

View file

@ -342,7 +342,7 @@ pub fn init_backend(
let mut renderer =
unsafe { GlowRenderer::new(context) }.with_context(|| "Failed to initialize renderer")?;
init_shaders(&mut renderer).context("Failed to initialize renderer")?;
init_shaders(renderer.borrow_mut()).context("Failed to initialize renderer")?;
init_egl_client_side(dh, state, drm_node, &mut renderer)?;
state.backend = BackendData::X11(X11State {

View file

@ -16,7 +16,7 @@ pub fn init(evlh: &LoopHandle<'static, State>) -> Result<Vec<RegistrationToken>>
calloop::channel::Event::Msg(_) => {
let nodes = match &mut state.backend {
BackendData::Kms(kms) => {
kms.devices.keys().cloned().collect::<Vec<_>>()
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
}
_ => Vec::new(),
};

View file

@ -13,11 +13,7 @@ use state::State;
use std::{env, ffi::OsString, os::unix::process::CommandExt, process, sync::Arc};
use tracing::{error, info, warn};
use crate::{
shell::SeatExt,
state::{BackendData, ClientState},
wayland::handlers::compositor::client_compositor_state,
};
use crate::wayland::handlers::compositor::client_compositor_state;
pub mod backend;
pub mod config;
@ -91,6 +87,10 @@ fn main() -> Result<()> {
logger::init_logger()?;
info!("Cosmic starting up!");
#[cfg(feature = "profile-with-tracy")]
profiling::tracy_client::Client::start();
profiling::register_thread!("Main Thread");
utils::rlimit::increase_nofile_limit();
// init event loop
@ -111,11 +111,6 @@ fn main() -> Result<()> {
warn!(?err, "Failed to watch theme");
}
#[cfg(feature = "profile-with-tracy")]
profiling::tracy_client::Client::start();
profiling::register_thread!("Main Thread");
// run the event loop
event_loop.run(None, &mut state, |state| {
// shall we shut down?
@ -210,29 +205,12 @@ fn init_wayland_display(
event_loop
.handle()
.insert_source(source, |client_stream, _, state| {
let node = match &state.backend {
BackendData::Kms(kms_state) if kms_state.auto_assign => kms_state
.target_node_for_output(
&state
.common
.shell
.read()
.unwrap()
.seats
.last_active()
.active_output(),
),
_ => None,
};
let client_state = state.new_client_state();
if let Err(err) = state.common.display_handle.insert_client(
client_stream,
Arc::new(ClientState {
advertised_drm_node: node.or(client_state.advertised_drm_node),
..client_state
}),
) {
if let Err(err) = state
.common
.display_handle
.insert_client(client_stream, Arc::new(client_state))
{
warn!(?err, "Error adding wayland client")
};
})

View file

@ -342,11 +342,7 @@ impl BackendData {
// Winit has a very strict render-loop and skipping frames breaks atleast the wayland winit-backend.
// Swapping with damage (which should be empty on these frames) is likely good enough anyway.
BackendData::X11(ref mut state) => state.schedule_render(output),
BackendData::Kms(ref mut state) => {
if let Err(err) = state.schedule_render(loop_handle, output, None) {
error!(?err, "Failed to schedule event, are we shutting down?");
}
}
BackendData::Kms(ref mut state) => state.schedule_render(output),
_ => unreachable!("No backend was initialized"),
}
}
@ -554,7 +550,7 @@ impl State {
compositor_client_state: CompositorClientState::default(),
workspace_client_state: WorkspaceClientState::default(),
advertised_drm_node: match &self.backend {
BackendData::Kms(kms_state) => Some(kms_state.primary_node),
BackendData::Kms(kms_state) => kms_state.primary_node,
_ => None,
},
privileged: !enable_wayland_security(),

View file

@ -99,7 +99,7 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) {
let res = match &mut state.backend {
BackendData::Kms(kms) => {
let node = advertised_node_for_surface(&wl_surface, &state.common.display_handle)
.unwrap_or(kms.primary_node);
.unwrap_or(kms.primary_node.expect("No Software Rendering"));
kms.api
.single_renderer(&node)
.with_context(|| "Failed to get renderer for screenshot")

View file

@ -9,12 +9,12 @@ use smithay::{
impl BufferHandler for State {
fn buffer_destroyed(&mut self, buffer: &WlBuffer) {
if let BackendData::Kms(kms_state) = &mut self.backend {
for device in kms_state.devices.values_mut() {
for device in kms_state.drm_devices.values_mut() {
if device.active_buffers.remove(&buffer.downgrade()) {
if !device.in_use(&kms_state.primary_node) {
kms_state.api.as_mut().remove_node(&device.render_node);
if !device.in_use(kms_state.primary_node.as_ref()) {
kms_state.refresh_used_devices();
break;
}
break;
}
}
}

View file

@ -35,12 +35,13 @@ impl DmabufHandler for State {
if let BackendData::Kms(kms_state) = &mut self.backend {
if let Some(device) = kms_state
.devices
.drm_devices
.values_mut()
.find(|dev| dev.render_node == node)
{
device.active_buffers.insert(buffer.downgrade());
}
kms_state.refresh_used_devices();
}
}
Ok(None) => {

View file

@ -26,12 +26,14 @@ impl DrmHandler<Option<DrmNode>> for State {
// kms backend
if let BackendData::Kms(kms_state) = &mut self.backend {
if let Some(device) = kms_state
.devices
.drm_devices
.values_mut()
.find(|device| device.render_node == node)
{
device.active_buffers.insert(buffer.downgrade());
}
kms_state.refresh_used_devices();
}
}
}

View file

@ -13,7 +13,7 @@ impl DrmLeaseHandler for State {
fn drm_lease_state(&mut self, node: DrmNode) -> &mut DrmLeaseState {
self.backend
.kms()
.devices
.drm_devices
.get_mut(&node)
.unwrap()
.leasing_global
@ -29,14 +29,14 @@ impl DrmLeaseHandler for State {
let backend = self
.backend
.kms()
.devices
.drm_devices
.get_mut(&node)
.ok_or(LeaseRejected::default())?;
let mut builder = DrmLeaseBuilder::new(&backend.drm);
for conn in request.connectors {
if let Some((_, crtc)) = backend
.non_desktop_connectors
.leased_connectors
.iter()
.find(|(handle, _)| *handle == conn)
{
@ -63,14 +63,14 @@ impl DrmLeaseHandler for State {
}
fn new_active_lease(&mut self, node: DrmNode, lease: DrmLease) {
if let Some(backend) = self.backend.kms().devices.get_mut(&node) {
if let Some(backend) = self.backend.kms().drm_devices.get_mut(&node) {
backend.active_leases.push(lease);
}
// else the backend is gone, drop the lease
}
fn lease_destroyed(&mut self, node: DrmNode, lease: u32) {
if let Some(backend) = self.backend.kms().devices.get_mut(&node) {
if let Some(backend) = self.backend.kms().drm_devices.get_mut(&node) {
backend.active_leases.retain(|l| l.id() != lease);
}
}

View file

@ -359,7 +359,7 @@ fn constraints_for_output(output: &Output, backend: &mut BackendData) -> Option<
BackendData::Kms(ref mut kms) => {
let node = kms
.target_node_for_output(&output)
.unwrap_or(kms.primary_node);
.unwrap_or(kms.primary_node.expect("No Software Rendering"));
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
_kms_renderer.as_mut().unwrap().as_mut()
}
@ -388,7 +388,7 @@ fn constraints_for_toplevel(
})
.flatten();
let node = dma_node.unwrap_or(kms.primary_node);
let node = dma_node.unwrap_or(kms.primary_node.expect("No Software Rendering"));
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
_kms_renderer.as_mut().unwrap().as_mut()
}

View file

@ -9,7 +9,7 @@ use smithay::{
utils::{Relocate, RelocateRenderElement},
AsRenderElements, RenderElement,
},
gles::{GlesError, GlesRenderbuffer},
gles::GlesRenderbuffer,
sync::SyncPoint,
utils::with_renderer_surface_state,
Bind, Blit, BufferType, ExportMem, ImportAll, ImportMem, Offscreen, Renderer,
@ -35,7 +35,7 @@ use tracing::warn;
use crate::{
backend::render::{
cursor,
element::{AsGlowRenderer, CosmicElement, DamageElement},
element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError},
render_workspace, CursorMode, CLEAR_COLOR,
},
shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement},
@ -325,7 +325,7 @@ pub fn render_workspace_to_buffer(
BackendData::Kms(kms) => {
let render_node = kms
.target_node_for_output(&output)
.unwrap_or(kms.primary_node);
.unwrap_or(kms.primary_node.expect("No Software Rendering"));
let target_node = get_dmabuf(&buffer)
.ok()
.and_then(|dma| dma.node())
@ -609,7 +609,7 @@ pub fn render_window_to_buffer(
})
.flatten()
})
.unwrap_or(kms.primary_node);
.unwrap_or(kms.primary_node.expect("No Software Rendering"));
let mut multirenderer = match kms.api.single_renderer(&node) {
Ok(renderer) => renderer,
@ -800,7 +800,10 @@ pub fn render_cursor_to_buffer(
let common = &mut state.common;
let result = match &mut state.backend {
BackendData::Kms(kms) => {
let mut multirenderer = match kms.api.single_renderer(&kms.primary_node) {
let mut multirenderer = match kms
.api
.single_renderer(&kms.primary_node.expect("No Software Rendering"))
{
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Couldn't use node for screencopy");