1
0
Fork 0
forked from wry/wry
wry/src/backend/transaction.rs
2026-02-18 18:03:38 +01:00

251 lines
7.8 KiB
Rust

use {
crate::{
backend::{
BackendColorSpace, BackendConnectorState, BackendEotfs, Connector, ConnectorId,
ConnectorKernelId, Mode,
},
backends::metal::MetalError,
state::State,
utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt},
video::drm::DrmError,
},
ahash::AHashMap,
std::{
any::{Any, TypeId},
cell::{Cell, RefCell},
collections::hash_map::Entry,
hash::{Hash, Hasher},
rc::Rc,
},
thiserror::Error,
};
pub trait BackendConnectorTransactionType: Hash + Eq + Any {}
pub trait BackendConnectorTransactionTypeDyn: Any {
fn eq(&self, other: &dyn BackendConnectorTransactionTypeDyn) -> bool;
fn hash(&self, hasher: &mut dyn Hasher);
}
impl<T> BackendConnectorTransactionTypeDyn for T
where
T: BackendConnectorTransactionType,
{
fn eq(&self, other: &dyn BackendConnectorTransactionTypeDyn) -> bool {
let Some(other) = (other as &dyn Any).downcast_ref::<Self>() else {
return false;
};
self.eq(other)
}
fn hash(&self, hasher: &mut dyn Hasher) {
struct BufHasher<'a> {
buf: Vec<u8>,
clear: Cell<bool>,
any: Cell<bool>,
hasher: RefCell<&'a mut dyn Hasher>,
}
impl Hasher for BufHasher<'_> {
fn finish(&self) -> u64 {
let hasher = &mut *self.hasher.borrow_mut();
if self.any.take() {
self.clear.set(true);
hasher.write(&self.buf);
}
hasher.finish()
}
fn write(&mut self, bytes: &[u8]) {
if self.clear.take() {
self.buf.clear();
}
self.any.set(true);
self.buf.extend_from_slice(bytes);
}
}
let mut hasher = BufHasher {
buf: Default::default(),
clear: Cell::new(false),
any: Cell::new(false),
hasher: RefCell::new(hasher),
};
TypeId::of::<Self>().hash(&mut hasher);
self.hash(&mut hasher)
}
}
impl PartialEq for dyn BackendConnectorTransactionTypeDyn {
fn eq(&self, other: &Self) -> bool {
self.eq(other)
}
}
impl Eq for dyn BackendConnectorTransactionTypeDyn {}
impl Hash for dyn BackendConnectorTransactionTypeDyn {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}
#[derive(Debug, Error)]
pub enum BackendConnectorTransactionError {
#[error("The underlying DRM device of connector {} no longer exists", .0)]
MissingDrmDevice(ConnectorKernelId),
#[error("Connector {} does not support transactions", .0)]
TransactionsNotSupported(ConnectorKernelId),
#[error("Connector {} is not supported by this transaction", .0)]
UnsupportedConnectorType(ConnectorKernelId),
#[error("Connector {} cannot be modified because it is leased", .0)]
LeasedConnector(ConnectorKernelId),
#[error("Connector {} does not exist", .0)]
UnknownConnector(ConnectorKernelId),
#[error("Cannot initialize connector {} because no CRTC is available", .0)]
NoCrtcForConnector(ConnectorKernelId),
#[error("Cannot initialize connector {} because no primary plane is available", .0)]
NoPrimaryPlaneForConnector(ConnectorKernelId),
#[error("Connector {} does not support the requested mode {}", .0, .1)]
UnsupportedMode(ConnectorKernelId, Mode),
#[error("Connector {} does not support VRR", .0)]
NotVrrCapable(ConnectorKernelId),
#[error("Connector {} does not support tearing", .0)]
TearingNotSupported(ConnectorKernelId),
#[error("Connector {} does not support color space {:?}", .0, .1)]
ColorSpaceNotSupported(ConnectorKernelId, BackendColorSpace),
#[error("Connector {} does not support EOTF {:?}", .0, .1)]
EotfNotSupported(ConnectorKernelId, BackendEotfs),
#[error("Could not create an hdr metadata blob")]
CreateHdrMetadataBlob(#[source] DrmError),
#[error("Could not create a mode blob")]
CreateModeBlob(#[source] DrmError),
#[error("Could not allocate buffers for connector {}", .0)]
AllocateScanoutBuffers(ConnectorKernelId, #[source] Box<MetalError>),
#[error("Test commit failed")]
AtomicTestFailed(#[source] DrmError),
#[error("Commit failed")]
AtomicCommitFailed(#[source] DrmError),
#[error("Could not create a gamma lut blob")]
CreateGammaLutBlob(#[source] DrmError),
}
pub trait BackendConnectorTransaction {
fn add(
&mut self,
connector: &Rc<dyn Connector>,
change: BackendConnectorState,
) -> Result<(), BackendConnectorTransactionError>;
fn prepare(
self: Box<Self>,
) -> Result<Box<dyn BackendPreparedConnectorTransaction>, BackendConnectorTransactionError>;
}
pub trait BackendPreparedConnectorTransaction {
fn apply(
self: Box<Self>,
) -> Result<Box<dyn BackendAppliedConnectorTransaction>, BackendConnectorTransactionError>;
}
pub trait BackendAppliedConnectorTransaction {
fn commit(self: Box<Self>);
fn rollback(self: Box<Self>) -> Result<(), BackendConnectorTransactionError>;
}
struct Common {
state: Rc<State>,
states: AHashMap<ConnectorId, BackendConnectorState>,
}
pub struct ConnectorTransaction {
common: Common,
parts:
AHashMap<Box<dyn BackendConnectorTransactionTypeDyn>, Box<dyn BackendConnectorTransaction>>,
}
pub struct PreparedConnectorTransaction {
common: Common,
parts: Vec<Box<dyn BackendPreparedConnectorTransaction>>,
}
pub struct AppliedConnectorTransaction {
common: Common,
parts: Vec<Box<dyn BackendAppliedConnectorTransaction>>,
}
impl ConnectorTransaction {
pub fn new(state: &Rc<State>) -> Self {
Self {
common: Common {
state: state.clone(),
states: Default::default(),
},
parts: Default::default(),
}
}
pub fn add(
&mut self,
connector: &Rc<dyn Connector>,
mut state: BackendConnectorState,
) -> Result<(), BackendConnectorTransactionError> {
state.serial = self.common.state.backend_connector_state_serials.next();
let ty = connector.transaction_type();
let tran = match self.parts.entry(ty) {
Entry::Occupied(v) => v.into_mut(),
Entry::Vacant(v) => v.insert(connector.create_transaction()?),
};
tran.add(connector, state.clone())?;
self.common.states.insert(connector.id(), state);
Ok(())
}
pub fn prepare(
mut self,
) -> Result<PreparedConnectorTransaction, BackendConnectorTransactionError> {
let mut new = vec![];
for tran in self.parts.drain_values() {
new.push(tran.prepare()?);
}
Ok(PreparedConnectorTransaction {
common: self.common,
parts: new,
})
}
}
impl PreparedConnectorTransaction {
pub fn apply(self) -> Result<AppliedConnectorTransaction, BackendConnectorTransactionError> {
let mut applied = AppliedConnectorTransaction {
common: self.common,
parts: vec![],
};
for tran in self.parts {
applied.parts.push(tran.apply()?);
}
Ok(applied)
}
}
impl AppliedConnectorTransaction {
pub fn commit(mut self) {
for tran in self.parts.drain(..) {
tran.commit();
}
for (connector_id, state) in self.common.states.drain() {
if let Some(c) = self.common.state.connectors.get(&connector_id) {
c.set_state(&self.common.state, state);
}
}
}
}
impl Drop for AppliedConnectorTransaction {
fn drop(&mut self) {
for tran in self.parts.drain(..).rev() {
if let Err(e) = tran.rollback() {
log::error!("Could not roll back transaction: {}", ErrorFmt(e));
}
}
}
}