1
0
Fork 0
forked from wry/wry

refactor: split cargo workspace

This commit is contained in:
kossLAN 2026-06-05 11:56:21 -04:00
parent 5db14936e7
commit 1c21bd1259
695 changed files with 32023 additions and 44964 deletions

View file

@ -0,0 +1,44 @@
use {
crate::{
copy_device::{CopyDevice, CopyDeviceRegistry},
utils::errorfmt::ErrorFmt,
},
std::{
cell::OnceCell,
fmt::{Debug, Formatter},
rc::Rc,
},
uapi::c::dev_t,
};
pub struct CopyDeviceHolder {
pub registry: Rc<CopyDeviceRegistry>,
pub devnum: dev_t,
pub dev: OnceCell<Option<Rc<CopyDevice>>>,
}
impl Debug for CopyDeviceHolder {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CopyDeviceHolder").finish_non_exhaustive()
}
}
impl CopyDeviceHolder {
pub fn get(&self) -> Option<Rc<CopyDevice>> {
self.dev
.get_or_init(
|| match self.registry.get(self.devnum)?.create_device().map(Some) {
Ok(d) => d,
Err(e) => {
log::error!(
"Could not get copy device for {}: {}",
self.devnum,
ErrorFmt(e),
);
None
}
},
)
.clone()
}
}

View file

@ -0,0 +1,660 @@
use {
super::{
ConnectorDisplayData, ConnectorFutures, FrontState, MetalConnector, MetalCrtc,
MetalDrmDevice, MetalEncoder, MetalPlane, PersistentDisplayData, PlaneFormat, PlaneType,
properties::{DefaultValue, collect_properties, create_default_properties},
},
crate::{
async_engine::Phase,
backend::{
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendLuminance,
ConnectorKernelId, Mode, OutputId,
},
backends::metal::{
MetalBackend,
present::DEFAULT_PRE_COMMIT_MARGIN,
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState},
},
cmm::cmm_primaries::Primaries,
edid::{CtaDataBlock, Descriptor, EdidExtension},
format::XRGB8888,
utils::{
binary_search_map::BinarySearchMap, bitflags::BitflagsExt, clonecell::CloneCell,
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, geometric_decay::GeometricDecay,
numcell::NumCell, ordered_float::F64,
},
video::{
INVALID_MODIFIER,
drm::{
ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, DrmEncoder,
DrmError, DrmFb, DrmMaster, DrmObject, DrmPlane, DrmPropertyType,
HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo, hdr_output_metadata,
},
},
},
ahash::AHashMap,
bstr::ByteSlice,
indexmap::indexset,
std::{
cell::{Cell, RefCell},
rc::Rc,
},
};
pub(super) fn get_connectors(
backend: &Rc<MetalBackend>,
dev: &Rc<MetalDrmDevice>,
ids: &[DrmConnector],
) -> Result<
(
CopyHashMap<DrmConnector, Rc<MetalConnector>>,
CopyHashMap<DrmConnector, ConnectorFutures>,
),
DrmError,
> {
let connectors = CopyHashMap::new();
let futures = CopyHashMap::new();
for connector in ids {
match create_connector(backend, *connector, dev) {
Ok((con, fut)) => {
let id = con.id;
connectors.set(id, con);
futures.set(id, fut);
}
Err(e) => return Err(DrmError::CreateConnector(Box::new(e))),
}
}
Ok((connectors, futures))
}
pub(super) fn create_connector(
backend: &Rc<MetalBackend>,
connector: DrmConnector,
dev: &Rc<MetalDrmDevice>,
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
let display = create_connector_display_data(connector, dev)?;
log::info!(
"Creating connector {} for device {}",
display.connector_id,
dev.devnode.as_bytes().as_bstr(),
);
let slf = Rc::new(MetalConnector {
id: connector,
kernel_id: Cell::new(display.connector_id),
master: dev.master.clone(),
state: backend.state.clone(),
dev: dev.clone(),
backend: backend.clone(),
connector_id: backend.state.connector_ids.next(),
buffers: Default::default(),
color_description: CloneCell::new(backend.state.color_manager.srgb_gamma22().clone()),
lease: Cell::new(None),
buffers_idle: Cell::new(true),
crtc_idle: Cell::new(true),
has_damage: NumCell::new(1),
primary_plane: Default::default(),
cursor_plane: Default::default(),
crtc: Default::default(),
on_change: Default::default(),
present_trigger: Default::default(),
cursor_x: Cell::new(0),
cursor_y: Cell::new(0),
cursor_enabled: Cell::new(false),
cursor_buffers: Default::default(),
display: RefCell::new(display),
frontend_state: Cell::new(FrontState::Removed),
cursor_changed: Cell::new(false),
cursor_damage: Cell::new(false),
cursor_swap_buffer: Cell::new(false),
cursor_sync: Default::default(),
drm_feedback: Default::default(),
scanout_buffers: Default::default(),
active_framebuffer: Default::default(),
next_framebuffer: Default::default(),
direct_scanout_active: Cell::new(false),
next_vblank_nsec: Cell::new(0),
version: Default::default(),
expected_sequence: Default::default(),
pre_commit_margin_decay: GeometricDecay::new(0.5, DEFAULT_PRE_COMMIT_MARGIN),
pre_commit_margin: Cell::new(DEFAULT_PRE_COMMIT_MARGIN),
post_commit_margin_decay: GeometricDecay::new(0.1, dev.min_post_commit_margin.get()),
post_commit_margin: Cell::new(dev.min_post_commit_margin.get()),
vblank_miss_sec: Cell::new(0),
vblank_miss_this_sec: Default::default(),
presentation_is_sync: Cell::new(false),
presentation_is_zero_copy: Cell::new(false),
});
let futures = ConnectorFutures {
_present: backend.state.eng.spawn2(
"present loop",
Phase::Present,
slf.clone().present_loop(),
),
};
Ok((slf, futures))
}
pub(super) fn create_connector_display_data(
connector: DrmConnector,
dev: &Rc<MetalDrmDevice>,
) -> Result<ConnectorDisplayData, DrmError> {
let info = dev.master.get_connector_info(connector, true)?;
let mut crtcs = BinarySearchMap::new();
for encoder in info.encoders {
if let Some(encoder) = dev.encoders.get(&encoder) {
for (_, crtc) in &encoder.crtcs {
crtcs.insert(crtc.id, crtc.clone());
}
}
}
let props = collect_properties(&dev.master, connector)?;
let connection = ConnectorStatus::from_drm(info.connection);
let mut name = String::new();
let mut manufacturer = String::new();
let mut serial_number = String::new();
let mut vrr_refresh_max_nsec = u64::MAX;
let connector_id = ConnectorKernelId {
ty: ConnectorType::from_drm(info.connector_type),
idx: info.connector_type_id,
};
let mut supports_bt2020 = false;
let mut supports_pq = false;
let mut luminance = None;
let mut primaries = Primaries::SRGB;
'fetch_edid: {
if connection != ConnectorStatus::Connected {
break 'fetch_edid;
}
let edid = match props.get("EDID") {
Ok(e) => e,
_ => {
log::warn!(
"Connector {} is connected but has no EDID blob",
connector_id,
);
break 'fetch_edid;
}
};
let blob = match dev.master.getblob_vec::<u8>(DrmBlob(edid.value as _)) {
Ok(b) => b,
Err(e) => {
log::error!(
"Could not fetch edid property of connector {}: {}",
connector_id,
ErrorFmt(e)
);
break 'fetch_edid;
}
};
let edid = match crate::edid::parse(&blob) {
Ok(e) => e,
Err(e) => {
log::error!(
"Could not parse edid property of connector {}: {}",
connector_id,
ErrorFmt(e)
);
break 'fetch_edid;
}
};
manufacturer = edid.base_block.id_manufacturer_name.to_string();
for descriptor in edid.base_block.descriptors.iter().flatten() {
match descriptor {
Descriptor::DisplayProductSerialNumber(s) => {
serial_number.clone_from(s);
}
Descriptor::DisplayProductName(s) => {
name.clone_from(s);
}
_ => {}
}
}
if name.is_empty() {
log::warn!(
"The display attached to connector {} does not have a product name descriptor",
connector_id,
);
}
if serial_number.is_empty() {
log::warn!(
"The display attached to connector {} does not have a serial number descriptor",
connector_id,
);
serial_number = edid.base_block.id_serial_number.to_string();
}
let min_vrr_hz = 'fetch_min_hz: {
for ext in &edid.extension_blocks {
if let EdidExtension::CtaV3(cta) = ext {
for data_block in &cta.data_blocks {
if let CtaDataBlock::VendorAmd(amd) = data_block {
break 'fetch_min_hz amd.minimum_refresh_hz as u64;
}
}
}
}
for desc in &edid.base_block.descriptors {
if let Some(desc) = desc
&& let Descriptor::DisplayRangeLimitsAndAdditionalTiming(timings) = desc
{
break 'fetch_min_hz timings.vertical_field_rate_min as u64;
}
}
0
};
if min_vrr_hz > 0 {
vrr_refresh_max_nsec = 1_000_000_000 / min_vrr_hz;
}
let cc = &edid.base_block.chromaticity_coordinates;
let map = |c: u16| F64(c as f64 / 1024.0);
primaries = Primaries {
r: (map(cc.red_x), map(cc.red_y)),
g: (map(cc.green_x), map(cc.green_y)),
b: (map(cc.blue_x), map(cc.blue_y)),
wp: (map(cc.white_x), map(cc.white_y)),
};
for ext in &edid.extension_blocks {
if let EdidExtension::CtaV3(cta) = ext {
for data_block in &cta.data_blocks {
match data_block {
CtaDataBlock::Colorimetry(c) => {
if c.bt2020_rgb {
supports_bt2020 = true;
}
}
CtaDataBlock::StaticHdrMetadata(h) => {
if h.smpte_st_2084 {
supports_pq = true;
}
if let Some(max) = h.max_luminance {
luminance = Some(BackendLuminance {
min: h.min_luminance.unwrap_or(0.0),
max,
max_fall: h.max_luminance.unwrap_or(max),
});
}
}
_ => {}
}
}
}
}
}
let output_id = OutputId::new(connector_id.to_string(), manufacturer, name, serial_number);
let first_mode = info
.modes
.first()
.cloned()
.map(|m| m.to_backend())
.unwrap_or_default();
let persistent = match dev.backend.persistent_display_data.get(&output_id) {
Some(ds) => {
if connection != ConnectorStatus::Disconnected {
log::info!("Reusing desired state for {:?}", output_id);
}
ds
}
None => {
let ds = Rc::new(PersistentDisplayData {
state: RefCell::new(BackendConnectorState {
serial: dev.backend.state.backend_connector_state_serials.next(),
enabled: true,
active: true,
mode: first_mode,
non_desktop_override: None,
vrr: false,
tearing: false,
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
}),
});
dev.backend
.persistent_display_data
.set(output_id.clone(), ds.clone());
ds
}
};
let mut desired_state = persistent.state.borrow_mut();
if desired_state.mode == Mode::default() {
desired_state.mode = first_mode;
} else if info
.modes
.iter()
.all(|m| m.to_backend() != desired_state.mode)
{
log::warn!("Discarding previously desired mode");
desired_state.mode = first_mode;
}
let non_desktop = props.get("non-desktop")?.value != 0;
let vrr_capable = match props.get("vrr_capable") {
Ok(c) => c.value == 1,
Err(_) => false,
};
if !vrr_capable && desired_state.vrr {
log::warn!("Connector has lost VRR capability");
desired_state.vrr = false;
}
{
let viable = match desired_state.eotf {
BackendEotfs::Default => true,
BackendEotfs::Pq => supports_pq,
};
if !viable {
log::warn!("Discarding previously desired EOTF");
desired_state.eotf = BackendEotfs::Default;
}
}
{
let viable = match desired_state.color_space {
BackendColorSpace::Default => true,
BackendColorSpace::Bt2020 => supports_bt2020,
};
if !viable {
log::warn!("Discarding previously desired color space");
desired_state.color_space = BackendColorSpace::Default;
}
}
drop(desired_state);
let default_properties = create_default_properties(
&props,
&[
("Broadcast RGB", DefaultValue::Enum("Automatic")),
("HDR_SOURCE_METADATA", DefaultValue::Fixed(0)),
("Output format", DefaultValue::Enum("Default")),
("WRITEBACK_FB_ID", DefaultValue::Fixed(0)),
("WRITEBACK_OUT_FENCE_PTR", DefaultValue::Fixed(0)),
("content type", DefaultValue::Enum("No Data")),
("dither", DefaultValue::Enum("off")),
("max bpc", DefaultValue::RangeMax),
],
);
let hdr_metadata_prop = props
.get("HDR_OUTPUT_METADATA")
.map(|p| p.map(|v| DrmBlob(v as _)))
.ok();
let mut hdr_metadata = None;
let mut hdr_metadata_blob_id = DrmBlob::NONE;
if let Some(p) = &hdr_metadata_prop {
hdr_metadata_blob_id = p.value;
hdr_metadata = Some(hdr_output_metadata::from_eotf(
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
));
if p.value.is_some() {
match dev.master.getblob::<hdr_output_metadata>(p.value) {
Ok(m) => hdr_metadata = Some(m),
_ => {
log::debug!("Could not retrieve hdr output metadata");
}
}
}
}
let colorspace_prop = props.get("Colorspace").ok();
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
let drm_state = DrmConnectorState {
crtc_id: crtc_id.value,
color_space: colorspace_prop.map(|p| p.value),
hdr_metadata,
hdr_metadata_blob_id,
hdr_metadata_blob: None,
locked: true,
fb: DrmFb::NONE,
fb_idx: 0,
cursor_fb: DrmFb::NONE,
cursor_fb_idx: 0,
cursor_x: 0,
cursor_y: 0,
out_fd: None,
src_w: 0,
src_h: 0,
crtc_x: 0,
crtc_y: 0,
crtc_w: 0,
crtc_h: 0,
};
Ok(ConnectorDisplayData {
crtc_id: props.get("CRTC_ID")?.id,
crtcs,
first_mode,
modes: info.modes,
persistent,
refresh: 0,
non_desktop,
non_desktop_effective: non_desktop,
vrr_capable,
_vrr_refresh_max_nsec: vrr_refresh_max_nsec,
default_properties,
untyped_properties: props.to_untyped(),
connection,
mm_width: info.mm_width,
mm_height: info.mm_height,
_subpixel: info.subpixel,
supports_bt2020,
supports_pq,
primaries,
luminance,
connector_id,
output_id,
colorspace: colorspace_prop.map(|p| p.id),
hdr_metadata: hdr_metadata_prop.map(|p| p.id),
drm_state,
})
}
pub(super) fn create_encoder(
encoder: DrmEncoder,
master: &Rc<DrmMaster>,
crtcs: &AHashMap<DrmCrtc, Rc<MetalCrtc>>,
) -> Result<MetalEncoder, DrmError> {
let info = master.get_encoder_info(encoder)?;
let mut possible = AHashMap::new();
for crtc in crtcs.values() {
if info.possible_crtcs.contains(1 << crtc.idx) {
possible.insert(crtc.id, crtc.clone());
}
}
Ok(MetalEncoder {
id: encoder,
crtcs: possible,
})
}
pub(super) fn create_crtc(
crtc: DrmCrtc,
idx: usize,
master: &Rc<DrmMaster>,
planes: &AHashMap<DrmPlane, Rc<MetalPlane>>,
) -> Result<MetalCrtc, DrmError> {
let mask = 1 << idx;
let mut possible_planes = BinarySearchMap::new();
for plane in planes.values() {
if plane.possible_crtcs.contains(mask) {
possible_planes.insert(plane.id, plane.clone());
}
}
let props = collect_properties(master, crtc)?;
let default_properties = create_default_properties(
&props,
&[
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
("CTM", DefaultValue::Fixed(0)),
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
("OUT_FENCE_PTR", DefaultValue::Fixed(0)),
],
);
let active = props.get("ACTIVE")?.map(|v| v == 1);
let mode_id = props.get("MODE_ID")?.map(|v| DrmBlob(v as u32));
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
let out_fence_ptr = props.get("OUT_FENCE_PTR")?;
let gamma_lut = props
.get("GAMMA_LUT")
.ok()
.map(|v| v.map(|v| DrmBlob(v as u32)));
let mut gamma_lut_size = None;
if gamma_lut.is_some() {
gamma_lut_size = props.get("GAMMA_LUT_SIZE").ok().map(|v| v.value as u32);
}
let mut mode = None;
if mode_id.value.is_some() {
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
Ok(m) => mode = Some(m.into()),
_ => {
log::debug!("Could not retrieve current mode of connector");
}
}
}
let state = DrmCrtcState {
active: active.value,
mode,
mode_blob_id: mode_id.value,
mode_blob: None,
vrr_enabled: vrr_enabled.value,
assigned_connector: DrmConnector::NONE,
gamma_lut: None,
gamma_lut_blob_id: gamma_lut.map_or(DrmBlob::NONE, |v| v.value),
gamma_lut_blob: None,
};
Ok(MetalCrtc {
id: crtc,
idx,
master: master.clone(),
default_properties,
untyped_properties: RefCell::new(props.to_untyped()),
lease: Cell::new(None),
possible_planes,
connector: Default::default(),
pending_flip: Default::default(),
drm_state: RefCell::new(state),
active: active.id,
mode_id: mode_id.id,
vrr_enabled: vrr_enabled.id,
out_fence_ptr: out_fence_ptr.id,
gamma_lut: gamma_lut.map(|v| v.id),
gamma_lut_size,
sequence: Cell::new(0),
have_queued_sequence: Cell::new(false),
needs_vblank_emulation: Cell::new(false),
})
}
pub(super) fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
let info = master.get_plane_info(plane)?;
let props = collect_properties(master, plane)?;
let mut formats = AHashMap::new();
if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) {
for format in master.get_in_formats(*v as _)? {
if format.modifiers.is_empty() {
continue;
}
if let Some(f) = crate::format::formats().get(&format.format) {
formats.insert(
format.format,
PlaneFormat {
format: f,
modifiers: format.modifiers,
},
);
}
}
} else {
for format in info.format_types {
if let Some(f) = crate::format::formats().get(&format) {
formats.insert(
format,
PlaneFormat {
format: f,
modifiers: indexset![INVALID_MODIFIER],
},
);
}
}
}
let ty = match props.props.get(b"type".as_bstr()) {
Some((def, val)) => match &def.ty {
DrmPropertyType::Enum { values, .. } => 'ty: {
for v in values {
if v.value == *val {
match v.name.as_bytes() {
b"Overlay" => break 'ty PlaneType::Overlay,
b"Primary" => break 'ty PlaneType::Primary,
b"Cursor" => break 'ty PlaneType::Cursor,
_ => return Err(DrmError::UnknownPlaneType(v.name.to_owned())),
}
}
}
return Err(DrmError::InvalidPlaneType(*val));
}
_ => return Err(DrmError::InvalidPlaneTypeProperty),
},
_ => {
return Err(DrmError::MissingProperty(
"type".to_string().into_boxed_str(),
));
}
};
let default_properties = create_default_properties(
&props,
&[
("AMD_PLANE_BLEND_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_BLEND_TF", DefaultValue::Enum("Default")),
("AMD_PLANE_CTM", DefaultValue::Fixed(0)),
("AMD_PLANE_DEGAMMA_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_HDR_MULT", DefaultValue::Fixed(0)),
("AMD_PLANE_LUT3D", DefaultValue::Fixed(0)),
("AMD_PLANE_SHAPER_LUT", DefaultValue::Fixed(0)),
("AMD_PLANE_SHAPER_TF", DefaultValue::Enum("Default")),
("alpha", DefaultValue::RangeMax),
("pixel blend mode", DefaultValue::Enum("Pre-multiplied")),
("rotation", DefaultValue::Bitmask(&["rotate-0"])),
],
);
let fb_id = props.get("FB_ID")?.map(|v| DrmFb(v as _));
let crtc_id = props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _));
let crtc_x = props.get("CRTC_X")?.map(|v| v as i32);
let crtc_y = props.get("CRTC_Y")?.map(|v| v as i32);
let crtc_w = props.get("CRTC_W")?.map(|v| v as i32);
let crtc_h = props.get("CRTC_H")?.map(|v| v as i32);
let src_x = props.get("SRC_X")?.map(|v| v as u32);
let src_y = props.get("SRC_Y")?.map(|v| v as u32);
let src_w = props.get("SRC_W")?.map(|v| v as u32);
let src_h = props.get("SRC_H")?.map(|v| v as u32);
let in_fence_fd = props.get("IN_FENCE_FD")?;
let state = DrmPlaneState {
fb_id: fb_id.value,
src_x: src_x.value,
src_y: src_y.value,
src_w: src_w.value,
src_h: src_h.value,
assigned_crtc: DrmCrtc::NONE,
crtc_id: crtc_id.value,
crtc_x: crtc_x.value,
crtc_y: crtc_y.value,
crtc_w: crtc_w.value,
crtc_h: crtc_h.value,
buffers: None,
};
Ok(MetalPlane {
id: plane,
master: master.clone(),
default_properties,
untyped_properties: RefCell::new(props.to_untyped()),
ty,
possible_crtcs: info.possible_crtcs,
formats,
drm_state: RefCell::new(state),
fb_id: fb_id.id,
crtc_id: crtc_id.id,
crtc_x: crtc_x.id,
crtc_y: crtc_y.id,
crtc_w: crtc_w.id,
crtc_h: crtc_h.id,
src_x: src_x.id,
src_y: src_y.id,
src_w: src_w.id,
src_h: src_h.id,
in_fence_fd: in_fence_fd.id,
mode_w: Cell::new(0),
mode_h: Cell::new(0),
lease: Cell::new(None),
})
}

View file

@ -0,0 +1,64 @@
use {
super::MetalConnector,
crate::{
backend::{HardwareCursor, HardwareCursorUpdate},
backends::metal::allocator::RenderBuffer,
gfx_api::{FdSync, GfxFramebuffer},
},
std::{
fmt::{Debug, Formatter},
rc::Rc,
},
};
pub struct MetalHardwareCursor {
pub connector: Rc<MetalConnector>,
}
pub struct MetalHardwareCursorChange<'a> {
pub cursor_swap_buffer: Option<Option<FdSync>>,
pub cursor_enabled: bool,
pub cursor_x: i32,
pub cursor_y: i32,
pub cursor_buffer: &'a RenderBuffer,
pub cursor_size: (i32, i32),
}
impl Debug for MetalHardwareCursor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetalHardwareCursor")
.finish_non_exhaustive()
}
}
impl HardwareCursor for MetalHardwareCursor {
fn damage(&self) {
self.connector.cursor_damage.set(true);
if self.connector.buffers_idle.get() && self.connector.crtc_idle.get() {
self.connector.schedule_present();
}
}
}
impl HardwareCursorUpdate for MetalHardwareCursorChange<'_> {
fn set_enabled(&mut self, enabled: bool) {
self.cursor_enabled = enabled;
}
fn get_buffer(&self) -> Rc<dyn GfxFramebuffer> {
self.cursor_buffer.render.fb.clone()
}
fn set_position(&mut self, x: i32, y: i32) {
self.cursor_x = x;
self.cursor_y = y;
}
fn swap_buffer(&mut self, sync: Option<FdSync>) {
self.cursor_swap_buffer = Some(sync);
}
fn size(&self) -> (i32, i32) {
self.cursor_size
}
}

View file

@ -0,0 +1,84 @@
use {
super::{FrontState, MetalConnector, MetalCrtc, MetalDrmDevice, MetalLeaseId, MetalPlane},
crate::{
backend::{BackendDrmLease, BackendDrmLessee, ConnectorEvent},
utils::errorfmt::ErrorFmt,
video::drm::DrmLease,
},
std::{cell::Cell, rc::Rc},
uapi::OwnedFd,
};
pub struct MetalLeaseData {
pub lease: DrmLease,
pub _lessee: Rc<dyn BackendDrmLessee>,
pub connectors: Vec<Rc<MetalConnector>>,
pub crtcs: Vec<Rc<MetalCrtc>>,
pub planes: Vec<Rc<MetalPlane>>,
pub revoked: Cell<bool>,
}
impl MetalLeaseData {
pub(super) fn try_revoke(&self) -> bool {
if self.revoked.get() {
return true;
}
let res = self.lease.try_revoke();
if res {
self.revoked.set(res);
for c in &self.connectors {
c.lease.take();
if let Err(e) = c.update_properties() {
log::error!("Could not update connector properties: {}", ErrorFmt(e));
}
}
for c in &self.crtcs {
c.lease.take();
if let Err(e) = c.update_properties() {
log::error!("Could not update crtc properties: {}", ErrorFmt(e));
}
}
for p in &self.planes {
p.lease.take();
if let Err(e) = p.update_properties() {
log::error!("Could not update plane properties: {}", ErrorFmt(e));
}
}
}
res
}
}
pub struct MetalLease {
pub(super) dev: Rc<MetalDrmDevice>,
pub(super) id: MetalLeaseId,
pub(super) fd: Rc<OwnedFd>,
}
impl Drop for MetalLease {
fn drop(&mut self) {
if let Some(lease) = self.dev.leases.remove(&self.id) {
if !self.dev.paused.get() {
for c in &lease.connectors {
match c.frontend_state.get() {
FrontState::Removed
| FrontState::Disconnected
| FrontState::Connected { .. } => {}
FrontState::Unavailable => {
c.send_event(ConnectorEvent::Available);
}
}
}
}
if !lease.try_revoke() {
self.dev.leases_to_break.set(self.id, lease);
}
}
}
}
impl BackendDrmLease for MetalLease {
fn fd(&self) -> &Rc<OwnedFd> {
&self.fd
}
}

View file

@ -0,0 +1,364 @@
use {
super::{copy_device::CopyDeviceHolder, lease::MetalLeaseData, properties::DefaultProperty},
crate::{
async_engine::SpawnedFuture,
backend::{
BackendConnectorState, BackendLuminance, ConnectorEvent, ConnectorId,
ConnectorKernelId, DrmDeviceId, DrmEvent, Mode, OutputId,
},
backends::metal::{
MetalBackend,
allocator::RenderBuffer,
present::{DirectScanoutCache, PresentFb},
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState},
},
cmm::{cmm_description::ColorDescription, cmm_primaries::Primaries},
drm_feedback::DrmFeedback,
format::Format,
gfx_api::{FdSync, GfxContext},
state::State,
utils::{
asyncevent::AsyncEvent, binary_search_map::BinarySearchMap, clonecell::CloneCell,
copyhashmap::CopyHashMap, geometric_decay::GeometricDecay, numcell::NumCell,
on_change::OnChange, opaque_cell::OpaqueCell,
},
video::{
Modifier,
dmabuf::DmaBufId,
drm::{
ConnectorStatus, DrmConnector, DrmCrtc, DrmEncoder, DrmMaster, DrmModeInfo,
DrmObject, DrmPlane, DrmProperty,
},
gbm::GbmDevice,
},
},
ahash::AHashMap,
indexmap::IndexSet,
std::{
cell::{Cell, RefCell},
ffi::CString,
fmt::{Debug, Formatter},
rc::Rc,
},
uapi::c,
};
pub struct PendingDrmDevice {
pub id: DrmDeviceId,
pub devnum: c::dev_t,
pub devnode: CString,
}
#[derive(Debug)]
pub struct MetalRenderContext {
pub dev_id: DrmDeviceId,
pub gfx: Rc<dyn GfxContext>,
pub gbm: Rc<GbmDevice>,
pub devnode: CString,
pub copy_device: Rc<CopyDeviceHolder>,
}
pub struct MetalDrmDevice {
pub backend: Rc<MetalBackend>,
pub id: DrmDeviceId,
pub devnum: c::dev_t,
pub devnode: CString,
pub master: Rc<DrmMaster>,
pub supports_kms: bool,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub encoders: AHashMap<DrmEncoder, Rc<MetalEncoder>>,
pub planes: AHashMap<DrmPlane, Rc<MetalPlane>>,
pub cursor_width: u64,
pub cursor_height: u64,
pub supports_async_commit: bool,
pub gbm: Rc<GbmDevice>,
pub handle_events: HandleEvents,
pub ctx: CloneCell<Rc<MetalRenderContext>>,
pub copy_device: Rc<CopyDeviceHolder>,
pub on_change: OnChange<DrmEvent>,
pub direct_scanout_enabled: Cell<Option<bool>>,
pub is_nvidia: bool,
pub _is_amd: bool,
pub lease_ids: MetalLeaseIds,
pub leases: CopyHashMap<MetalLeaseId, MetalLeaseData>,
pub leases_to_break: CopyHashMap<MetalLeaseId, MetalLeaseData>,
pub paused: Cell<bool>,
pub min_post_commit_margin: Cell<u64>,
}
impl Debug for MetalDrmDevice {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetalDrmDevice").finish_non_exhaustive()
}
}
impl MetalDrmDevice {
pub fn is_render_device(&self) -> bool {
if let Some(ctx) = self.backend.ctx.get() {
return ctx.dev_id == self.id;
}
false
}
}
pub struct HandleEvents {
pub handle_events: Cell<Option<SpawnedFuture<()>>>,
}
impl Debug for HandleEvents {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HandleEvents").finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct MetalDrmDeviceData {
pub dev: Rc<MetalDrmDevice>,
pub connectors: CopyHashMap<DrmConnector, Rc<MetalConnector>>,
pub futures: CopyHashMap<DrmConnector, ConnectorFutures>,
}
#[derive(Debug)]
pub struct PersistentDisplayData {
pub state: RefCell<BackendConnectorState>,
}
#[derive(Debug)]
pub struct ConnectorDisplayData {
pub crtc_id: DrmProperty,
pub crtcs: BinarySearchMap<DrmCrtc, Rc<MetalCrtc>, 8>,
pub first_mode: Mode,
pub modes: Vec<DrmModeInfo>,
pub persistent: Rc<PersistentDisplayData>,
pub refresh: u32,
pub non_desktop: bool,
pub non_desktop_effective: bool,
pub vrr_capable: bool,
pub _vrr_refresh_max_nsec: u64,
pub default_properties: Vec<DefaultProperty>,
pub untyped_properties: AHashMap<DrmProperty, u64>,
pub connector_id: ConnectorKernelId,
pub output_id: Rc<OutputId>,
pub connection: ConnectorStatus,
pub mm_width: u32,
pub mm_height: u32,
pub _subpixel: u32,
pub supports_bt2020: bool,
pub supports_pq: bool,
pub primaries: Primaries,
pub luminance: Option<BackendLuminance>,
pub colorspace: Option<DrmProperty>,
pub hdr_metadata: Option<DrmProperty>,
pub drm_state: DrmConnectorState,
}
impl ConnectorDisplayData {
fn update_refresh(&mut self, dev: &MetalDrmDevice) {
self.refresh = 0;
if self.drm_state.crtc_id.is_none() {
return;
}
let Some(crtc) = dev.crtcs.get(&self.drm_state.crtc_id) else {
return;
};
let drm_state = &*crtc.drm_state.borrow();
let Some(mode) = &drm_state.mode else {
return;
};
let refresh_rate_mhz = mode.refresh_rate_millihz();
if refresh_rate_mhz != 0 {
self.refresh = (1_000_000_000_000u64 / refresh_rate_mhz as u64) as u32;
}
}
fn update_non_desktop_effective(&mut self) {
let state = &*self.persistent.state.borrow();
self.non_desktop_effective =
!state.enabled || state.non_desktop_override.unwrap_or(self.non_desktop);
}
pub fn update_cached_fields(&mut self, dev: &MetalDrmDevice) {
self.update_refresh(dev);
self.update_non_desktop_effective();
}
}
linear_ids!(MetalLeaseIds, MetalLeaseId, u64);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FrontState {
Removed,
Disconnected,
Connected { non_desktop: bool },
Unavailable,
}
pub struct MetalConnector {
pub id: DrmConnector,
pub kernel_id: Cell<ConnectorKernelId>,
pub master: Rc<DrmMaster>,
pub state: Rc<State>,
pub dev: Rc<MetalDrmDevice>,
pub backend: Rc<MetalBackend>,
pub connector_id: ConnectorId,
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub color_description: CloneCell<Rc<ColorDescription>>,
pub lease: Cell<Option<MetalLeaseId>>,
pub buffers_idle: Cell<bool>,
pub crtc_idle: Cell<bool>,
pub has_damage: NumCell<u64>,
pub cursor_changed: Cell<bool>,
pub cursor_damage: Cell<bool>,
pub next_vblank_nsec: Cell<u64>,
pub display: RefCell<ConnectorDisplayData>,
pub frontend_state: Cell<FrontState>,
pub primary_plane: CloneCell<Option<Rc<MetalPlane>>>,
pub cursor_plane: CloneCell<Option<Rc<MetalPlane>>>,
pub crtc: CloneCell<Option<Rc<MetalCrtc>>>,
pub on_change: OnChange<ConnectorEvent>,
pub present_trigger: AsyncEvent,
pub cursor_x: Cell<i32>,
pub cursor_y: Cell<i32>,
pub cursor_enabled: Cell<bool>,
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub cursor_swap_buffer: Cell<bool>,
pub cursor_sync: CloneCell<Option<FdSync>>,
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
pub active_framebuffer: RefCell<Option<PresentFb>>,
pub next_framebuffer: OpaqueCell<Option<PresentFb>>,
pub direct_scanout_active: Cell<bool>,
pub version: NumCell<u64>,
pub expected_sequence: Cell<Option<u64>>,
pub pre_commit_margin: Cell<u64>,
pub pre_commit_margin_decay: GeometricDecay,
pub post_commit_margin: Cell<u64>,
pub post_commit_margin_decay: GeometricDecay,
pub vblank_miss_sec: Cell<u32>,
pub vblank_miss_this_sec: NumCell<u32>,
pub presentation_is_sync: Cell<bool>,
pub presentation_is_zero_copy: Cell<bool>,
}
impl Debug for MetalConnector {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetalConnnector").finish_non_exhaustive()
}
}
pub struct ConnectorFutures {
pub _present: SpawnedFuture<()>,
}
impl Debug for ConnectorFutures {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConnectorFutures").finish_non_exhaustive()
}
}
pub struct MetalCrtc {
pub id: DrmCrtc,
pub idx: usize,
pub master: Rc<DrmMaster>,
pub default_properties: Vec<DefaultProperty>,
pub untyped_properties: RefCell<AHashMap<DrmProperty, u64>>,
pub lease: Cell<Option<MetalLeaseId>>,
pub possible_planes: BinarySearchMap<DrmPlane, Rc<MetalPlane>, 8>,
pub connector: CloneCell<Option<Rc<MetalConnector>>>,
pub pending_flip: CloneCell<Option<Rc<MetalConnector>>>,
pub active: DrmProperty,
pub mode_id: DrmProperty,
pub vrr_enabled: DrmProperty,
pub out_fence_ptr: DrmProperty,
pub gamma_lut: Option<DrmProperty>,
pub gamma_lut_size: Option<u32>,
pub drm_state: RefCell<DrmCrtcState>,
pub sequence: Cell<u64>,
pub have_queued_sequence: Cell<bool>,
pub needs_vblank_emulation: Cell<bool>,
}
impl Debug for MetalCrtc {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetalCrtc").finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct MetalEncoder {
pub id: DrmEncoder,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PlaneType {
Overlay,
Primary,
Cursor,
}
#[derive(Debug)]
pub struct PlaneFormat {
pub format: &'static Format,
pub modifiers: IndexSet<Modifier>,
}
pub struct MetalPlane {
pub id: DrmPlane,
pub master: Rc<DrmMaster>,
pub default_properties: Vec<DefaultProperty>,
pub untyped_properties: RefCell<AHashMap<DrmProperty, u64>>,
pub ty: PlaneType,
pub possible_crtcs: u32,
pub formats: AHashMap<u32, PlaneFormat>,
pub lease: Cell<Option<MetalLeaseId>>,
pub mode_w: Cell<i32>,
pub mode_h: Cell<i32>,
pub crtc_id: DrmProperty,
pub crtc_x: DrmProperty,
pub crtc_y: DrmProperty,
pub crtc_w: DrmProperty,
pub crtc_h: DrmProperty,
pub src_x: DrmProperty,
pub src_y: DrmProperty,
pub src_w: DrmProperty,
pub src_h: DrmProperty,
pub in_fence_fd: DrmProperty,
pub fb_id: DrmProperty,
pub drm_state: RefCell<DrmPlaneState>,
}
impl Debug for MetalPlane {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetalPlane").finish_non_exhaustive()
}
}

View file

@ -0,0 +1,144 @@
use {
crate::video::drm::{
DrmError, DrmMaster, DrmObject, DrmProperty, DrmPropertyDefinition, DrmPropertyType,
},
ahash::AHashMap,
bstr::{BString, ByteSlice},
std::rc::Rc,
};
#[derive(Debug)]
pub struct DefaultProperty {
pub name: &'static str,
pub prop: DrmProperty,
pub value: u64,
}
#[derive(Copy, Clone)]
pub(super) enum DefaultValue {
Fixed(u64),
Enum(&'static str),
Bitmask(&'static [&'static str]),
RangeMax,
}
pub(super) fn create_default_properties(
props: &CollectedProperties,
defaults: &[(&'static str, DefaultValue)],
) -> Vec<DefaultProperty> {
let mut res = vec![];
let mut defaults = defaults.iter();
'outer: loop {
let Some(&(name, def)) = defaults.next() else {
break;
};
if let Some((definition, _)) = props.props.get(name.as_bytes().as_bstr()) {
let value = match def {
DefaultValue::Fixed(v) => v,
DefaultValue::Enum(e) => match &definition.ty {
DrmPropertyType::Enum {
values,
bitmask: false,
} => match values.iter().find(|v| v.name == e) {
None => continue,
Some(v) => v.value,
},
_ => continue,
},
DefaultValue::Bitmask(e) => match &definition.ty {
DrmPropertyType::Enum {
values,
bitmask: true,
} => {
let mut res = 0;
for &e in e {
match values.iter().find(|v| v.name == e) {
None => continue 'outer,
Some(v) => res |= 1 << v.value,
}
}
res
}
_ => continue,
},
DefaultValue::RangeMax => match &definition.ty {
DrmPropertyType::Range { max, .. } => *max,
DrmPropertyType::SignedRange { max, .. } => *max as u64,
_ => continue,
},
};
res.push(DefaultProperty {
name,
prop: definition.id,
value,
});
}
}
res
}
pub(super) fn collect_properties<T: DrmObject>(
master: &Rc<DrmMaster>,
t: T,
) -> Result<CollectedProperties, DrmError> {
let mut props = AHashMap::new();
for prop in master.get_properties(t)? {
let def = master.get_property(prop.id)?;
props.insert(def.name.clone(), (def, prop.value));
}
Ok(CollectedProperties { props })
}
pub(super) fn collect_untyped_properties<T: DrmObject>(
master: &Rc<DrmMaster>,
t: T,
props: &mut AHashMap<DrmProperty, u64>,
) -> Result<(), DrmError> {
props.clear();
for prop in master.get_properties(t)? {
props.insert(prop.id, prop.value);
}
Ok(())
}
pub(super) struct CollectedProperties {
pub(super) props: AHashMap<BString, (DrmPropertyDefinition, u64)>,
}
impl CollectedProperties {
pub(super) fn get(&self, name: &str) -> Result<TypedProperty<u64>, DrmError> {
match self.props.get(name.as_bytes().as_bstr()) {
Some((def, value)) => Ok(TypedProperty {
id: def.id,
value: *value,
}),
_ => Err(DrmError::MissingProperty(name.to_string().into_boxed_str())),
}
}
pub(super) fn to_untyped(&self) -> AHashMap<DrmProperty, u64> {
let mut res = AHashMap::new();
for (def, val) in self.props.values() {
res.insert(def.id, *val);
}
res
}
}
#[derive(Copy, Clone, Debug)]
pub struct TypedProperty<T> {
pub id: DrmProperty,
pub value: T,
}
impl<T: Copy> TypedProperty<T> {
pub(super) fn map<U, F>(self, f: F) -> TypedProperty<U>
where
F: FnOnce(T) -> U,
{
TypedProperty {
id: self.id,
value: f(self.value),
}
}
}