refactor: split cargo workspace
This commit is contained in:
parent
5db14936e7
commit
1c21bd1259
695 changed files with 32023 additions and 44964 deletions
44
src/backends/metal/video/copy_device.rs
Normal file
44
src/backends/metal/video/copy_device.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
660
src/backends/metal/video/discovery.rs
Normal file
660
src/backends/metal/video/discovery.rs
Normal 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),
|
||||
})
|
||||
}
|
||||
64
src/backends/metal/video/hardware_cursor.rs
Normal file
64
src/backends/metal/video/hardware_cursor.rs
Normal 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
|
||||
}
|
||||
}
|
||||
84
src/backends/metal/video/lease.rs
Normal file
84
src/backends/metal/video/lease.rs
Normal 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
|
||||
}
|
||||
}
|
||||
364
src/backends/metal/video/model.rs
Normal file
364
src/backends/metal/video/model.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
144
src/backends/metal/video/properties.rs
Normal file
144
src/backends/metal/video/properties.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue