kms: New backend
New backend utilizing a thread per surface for precise frame scheduling.
This commit is contained in:
parent
3b7bba3add
commit
469a366207
24 changed files with 3219 additions and 1958 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -887,6 +887,7 @@ dependencies = [
|
|||
"sendfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"smithay",
|
||||
"smithay-egui",
|
||||
"thiserror",
|
||||
|
|
|
|||
|
|
@ -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
657
src/backend/kms/device.rs
Normal 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(())
|
||||
}
|
||||
|
|
@ -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
191
src/backend/kms/render/gles.rs
Normal file
191
src/backend/kms/render/gles.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
2
src/backend/kms/render/mod.rs
Normal file
2
src/backend/kms/render/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod gles;
|
||||
pub mod pixman;
|
||||
155
src/backend/kms/render/pixman.rs
Normal file
155
src/backend/kms/render/pixman.rs
Normal 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
|
||||
}
|
||||
}
|
||||
1358
src/backend/kms/surface/mod.rs
Normal file
1358
src/backend/kms/surface/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
262
src/backend/kms/surface/timings.rs
Normal file
262
src/backend/kms/surface/timings.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
|||
42
src/main.rs
42
src/main.rs
|
|
@ -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")
|
||||
};
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue