1
0
Fork 0
forked from wry/wry

Merge pull request #253 from mahkoh/jorth/output-formats

metal: allow configuring framebuffer formats
This commit is contained in:
mahkoh 2024-09-05 08:44:51 +02:00 committed by GitHub
commit 588fce4832
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 980 additions and 232 deletions

View file

@ -25,7 +25,7 @@ use {
timer::Timer, timer::Timer,
video::{ video::{
connector_type::{ConnectorType, CON_UNKNOWN}, connector_type::{ConnectorType, CON_UNKNOWN},
Connector, DrmDevice, GfxApi, Mode, TearingMode, Transform, VrrMode, Connector, DrmDevice, Format, GfxApi, Mode, TearingMode, Transform, VrrMode,
}, },
Axis, Direction, ModifiedKeySym, PciId, Workspace, Axis, Direction, ModifiedKeySym, PciId, Workspace,
}, },
@ -754,6 +754,10 @@ impl Client {
self.send(&ClientMessage::ConnectorSetScale { connector, scale }); self.send(&ClientMessage::ConnectorSetScale { connector, scale });
} }
pub fn connector_set_format(&self, connector: Connector, format: Format) {
self.send(&ClientMessage::ConnectorSetFormat { connector, format });
}
pub fn connector_get_scale(&self, connector: Connector) -> f64 { pub fn connector_get_scale(&self, connector: Connector) -> f64 {
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector }); let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
get_response!(res, 1.0, ConnectorGetScale { scale }); get_response!(res, 1.0, ConnectorGetScale { scale });

View file

@ -9,8 +9,8 @@ use {
theme::{colors::Colorable, sized::Resizable, Color}, theme::{colors::Colorable, sized::Resizable, Color},
timer::Timer, timer::Timer,
video::{ video::{
connector_type::ConnectorType, Connector, DrmDevice, GfxApi, TearingMode, Transform, connector_type::ConnectorType, Connector, DrmDevice, Format, GfxApi, TearingMode,
VrrMode, Transform, VrrMode,
}, },
Axis, Direction, PciId, Workspace, Axis, Direction, PciId, Workspace,
_private::{PollableId, WireMode}, _private::{PollableId, WireMode},
@ -509,6 +509,10 @@ pub enum ClientMessage<'a> {
SetEiSocketEnabled { SetEiSocketEnabled {
enabled: bool, enabled: bool,
}, },
ConnectorSetFormat {
connector: Connector,
format: Format,
},
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View file

@ -267,6 +267,11 @@ impl Connector {
pub fn set_tearing_mode(self, mode: TearingMode) { pub fn set_tearing_mode(self, mode: TearingMode) {
get!().set_tearing_mode(Some(self), mode) get!().set_tearing_mode(Some(self), mode)
} }
/// Sets the format to use for framebuffers.
pub fn set_format(self, format: Format) {
get!().connector_set_format(self, format);
}
} }
/// Returns all available DRM devices. /// Returns all available DRM devices.
@ -612,3 +617,38 @@ impl TearingMode {
pub fn set_tearing_mode(mode: TearingMode) { pub fn set_tearing_mode(mode: TearingMode) {
get!().set_tearing_mode(None, mode) get!().set_tearing_mode(None, mode)
} }
/// A graphics format.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Format(pub u32);
impl Format {
pub const ARGB8888: Self = Self(0);
pub const XRGB8888: Self = Self(1);
pub const ABGR8888: Self = Self(2);
pub const XBGR8888: Self = Self(3);
pub const R8: Self = Self(4);
pub const GR88: Self = Self(5);
pub const RGB888: Self = Self(6);
pub const BGR888: Self = Self(7);
pub const RGBA4444: Self = Self(8);
pub const RGBX4444: Self = Self(9);
pub const BGRA4444: Self = Self(10);
pub const BGRX4444: Self = Self(11);
pub const RGB565: Self = Self(12);
pub const BGR565: Self = Self(13);
pub const RGBA5551: Self = Self(14);
pub const RGBX5551: Self = Self(15);
pub const BGRA5551: Self = Self(16);
pub const BGRX5551: Self = Self(17);
pub const ARGB1555: Self = Self(18);
pub const XRGB1555: Self = Self(19);
pub const ARGB2101010: Self = Self(20);
pub const XRGB2101010: Self = Self(21);
pub const ABGR2101010: Self = Self(22);
pub const XBGR2101010: Self = Self(23);
pub const ABGR16161616: Self = Self(24);
pub const XBGR16161616: Self = Self(25);
pub const ABGR16161616F: Self = Self(26);
pub const XBGR16161616F: Self = Self(27);
}

View file

@ -3,14 +3,18 @@ use {
async_engine::SpawnedFuture, async_engine::SpawnedFuture,
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
format::Format,
gfx_api::{GfxFramebuffer, SyncFile}, gfx_api::{GfxFramebuffer, SyncFile},
ifs::wl_seat::{ ifs::{
tablet::{ wl_output::OutputId,
PadButtonState, TabletInit, TabletPadId, TabletPadInit, TabletRingEventSource, wl_seat::{
TabletStripEventSource, TabletToolChanges, TabletToolId, TabletToolInit, tablet::{
ToolButtonState, PadButtonState, TabletInit, TabletPadId, TabletPadInit, TabletRingEventSource,
TabletStripEventSource, TabletToolChanges, TabletToolId, TabletToolInit,
ToolButtonState,
},
wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
}, },
wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
}, },
libinput::consts::DeviceCapability, libinput::consts::DeviceCapability,
video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion}, video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion},
@ -64,9 +68,7 @@ pub struct Mode {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MonitorInfo { pub struct MonitorInfo {
pub modes: Vec<Mode>, pub modes: Vec<Mode>,
pub manufacturer: String, pub output_id: Rc<OutputId>,
pub product: String,
pub serial_number: String,
pub initial_mode: Mode, pub initial_mode: Mode,
pub width_mm: i32, pub width_mm: i32,
pub height_mm: i32, pub height_mm: i32,
@ -115,6 +117,9 @@ pub trait Connector {
fn set_tearing_enabled(&self, enabled: bool) { fn set_tearing_enabled(&self, enabled: bool) {
let _ = enabled; let _ = enabled;
} }
fn set_fb_format(&self, format: &'static Format) {
let _ = format;
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -127,6 +132,7 @@ pub enum ConnectorEvent {
Unavailable, Unavailable,
Available, Available,
VrrChanged(bool), VrrChanged(bool),
FormatsChanged(Rc<Vec<&'static Format>>, &'static Format),
} }
pub trait HardwareCursor: Debug { pub trait HardwareCursor: Debug {

View file

@ -11,12 +11,16 @@ use {
}, },
backends::metal::video::{ backends::metal::video::{
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice, MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
PersistentDisplayData,
}, },
dbus::{DbusError, SignalHandler}, dbus::{DbusError, SignalHandler},
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
gfx_api::GfxError, gfx_api::GfxError,
ifs::wl_seat::tablet::{ ifs::{
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit, wl_output::OutputId,
wl_seat::tablet::{
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
},
}, },
libinput::{ libinput::{
consts::{ consts::{
@ -144,6 +148,7 @@ pub struct MetalBackend {
resume_handler: Cell<Option<SignalHandler>>, resume_handler: Cell<Option<SignalHandler>>,
ctx: CloneCell<Option<Rc<MetalRenderContext>>>, ctx: CloneCell<Option<Rc<MetalRenderContext>>>,
default_feedback: CloneCell<Option<Rc<DrmFeedback>>>, default_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
persistent_display_data: CopyHashMap<Rc<OutputId>, Rc<PersistentDisplayData>>,
} }
impl Debug for MetalBackend { impl Debug for MetalBackend {
@ -317,6 +322,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
resume_handler: Default::default(), resume_handler: Default::default(),
ctx: Default::default(), ctx: Default::default(),
default_feedback: Default::default(), default_feedback: Default::default(),
persistent_display_data: Default::default(),
}); });
metal.pause_handler.set(Some({ metal.pause_handler.set(Some({
let mtl = metal.clone(); let mtl = metal.clone();

View file

@ -15,7 +15,10 @@ use {
AcquireSync, BufferResv, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, AcquireSync, BufferResv, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass,
GfxTexture, ReleaseSync, SyncFile, GfxTexture, ReleaseSync, SyncFile,
}, },
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, ifs::{
wl_output::OutputId,
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
},
renderer::RenderResult, renderer::RenderResult,
state::State, state::State,
theme::Color, theme::Color,
@ -23,9 +26,8 @@ use {
udev::UdevDevice, udev::UdevDevice,
utils::{ utils::{
asyncevent::AsyncEvent, bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, asyncevent::AsyncEvent, bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell,
copyhashmap::CopyHashMap, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, on_change::OnChange,
on_change::OnChange, opaque_cell::OpaqueCell, oserror::OsError, opaque_cell::OpaqueCell, oserror::OsError, transform_ext::TransformExt,
transform_ext::TransformExt,
}, },
video::{ video::{
dmabuf::DmaBufId, dmabuf::DmaBufId,
@ -295,40 +297,37 @@ pub struct MetalDrmDeviceData {
pub unprocessed_change: Cell<bool>, pub unprocessed_change: Cell<bool>,
} }
#[derive(Debug)]
pub struct PersistentDisplayData {
pub mode: RefCell<Option<DrmModeInfo>>,
pub vrr_requested: Cell<bool>,
pub format: Cell<&'static Format>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct ConnectorDisplayData { pub struct ConnectorDisplayData {
pub crtc_id: MutableProperty<DrmCrtc>, pub crtc_id: MutableProperty<DrmCrtc>,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>, pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub modes: Vec<DrmModeInfo>, pub modes: Vec<DrmModeInfo>,
pub mode: Option<DrmModeInfo>, pub mode: Option<DrmModeInfo>,
pub persistent: Rc<PersistentDisplayData>,
pub refresh: u32, pub refresh: u32,
pub non_desktop: bool, pub non_desktop: bool,
pub non_desktop_effective: bool, pub non_desktop_effective: bool,
pub vrr_capable: bool, pub vrr_capable: bool,
pub vrr_requested: bool,
pub monitor_manufacturer: String, pub connector_id: ConnectorKernelId,
pub monitor_name: String, pub output_id: Rc<OutputId>,
pub monitor_serial_number: String,
pub connection: ConnectorStatus, pub connection: ConnectorStatus,
pub mm_width: u32, pub mm_width: u32,
pub mm_height: u32, pub mm_height: u32,
pub _subpixel: u32, pub _subpixel: u32,
pub connector_type: ConnectorType,
pub connector_type_id: u32,
} }
impl ConnectorDisplayData { impl ConnectorDisplayData {
fn is_same_monitor(&self, other: &Self) -> bool {
self.monitor_manufacturer == other.monitor_manufacturer
&& self.monitor_name == other.monitor_name
&& self.monitor_serial_number == other.monitor_serial_number
}
fn should_enable_vrr(&self) -> bool { fn should_enable_vrr(&self) -> bool {
self.vrr_requested && self.vrr_capable self.persistent.vrr_requested.get() && self.vrr_capable
} }
} }
@ -417,6 +416,7 @@ pub struct MetalConnector {
pub connector_id: ConnectorId, pub connector_id: ConnectorId,
pub buffer_format: Cell<&'static Format>,
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>, pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub next_buffer: NumCell<usize>, pub next_buffer: NumCell<usize>,
@ -462,6 +462,7 @@ pub struct MetalConnector {
pub direct_scanout_active: Cell<bool>, pub direct_scanout_active: Cell<bool>,
pub tearing_requested: Cell<bool>, pub tearing_requested: Cell<bool>,
pub try_switch_format: Cell<bool>,
} }
impl Debug for MetalConnector { impl Debug for MetalConnector {
@ -643,6 +644,25 @@ impl MetalConnector {
} }
} }
fn send_formats(&self) {
match self.frontend_state.get() {
FrontState::Removed
| FrontState::Disconnected
| FrontState::Unavailable
| FrontState::Connected { non_desktop: true } => return,
FrontState::Connected { non_desktop: false } => {}
}
let mut formats = vec![];
if let Some(plane) = self.primary_plane.get() {
formats = plane.formats.values().map(|f| f.format).collect();
}
let formats = Rc::new(formats);
self.send_event(ConnectorEvent::FormatsChanged(
formats,
self.buffer_format.get(),
));
}
fn send_hardware_cursor(self: &Rc<Self>) { fn send_hardware_cursor(self: &Rc<Self>) {
match self.frontend_state.get() { match self.frontend_state.get() {
FrontState::Removed FrontState::Removed
@ -1266,6 +1286,17 @@ impl MetalConnector {
log::error!("Tried to send vrr-changed event in invalid state: {state:?}"); log::error!("Tried to send vrr-changed event in invalid state: {state:?}");
} }
}, },
ConnectorEvent::FormatsChanged(_, _) => match state {
FrontState::Connected { non_desktop: false } => {
self.on_change.send_event(event);
}
FrontState::Connected { non_desktop: true }
| FrontState::Removed
| FrontState::Disconnected
| FrontState::Unavailable => {
log::error!("Tried to send format-changed event in invalid state: {state:?}");
}
},
} }
} }
} }
@ -1276,11 +1307,7 @@ impl Connector for MetalConnector {
} }
fn kernel_id(&self) -> ConnectorKernelId { fn kernel_id(&self) -> ConnectorKernelId {
let dd = self.display.borrow_mut(); self.display.borrow().connector_id
ConnectorKernelId {
ty: dd.connector_type,
idx: dd.connector_type_id,
}
} }
fn event(&self) -> Option<ConnectorEvent> { fn event(&self) -> Option<ConnectorEvent> {
@ -1349,6 +1376,8 @@ impl Connector for MetalConnector {
return; return;
}; };
log::info!("Trying to change mode from {:?} to {:?}", prev, mode); log::info!("Trying to change mode from {:?} to {:?}", prev, mode);
let persistent = dd.persistent.clone();
*persistent.mode.borrow_mut() = Some(mode.clone());
dd.mode = Some(mode.clone()); dd.mode = Some(mode.clone());
drop(dd); drop(dd);
let Err(e) = self.backend.handle_drm_change_(&dev, true) else { let Err(e) = self.backend.handle_drm_change_(&dev, true) else {
@ -1356,6 +1385,7 @@ impl Connector for MetalConnector {
return; return;
}; };
log::warn!("Could not change mode: {}", ErrorFmt(&e)); log::warn!("Could not change mode: {}", ErrorFmt(&e));
*persistent.mode.borrow_mut() = prev.clone();
self.display.borrow_mut().mode = prev; self.display.borrow_mut().mode = prev;
if let MetalError::Modeset(DrmError::Atomic(OsError(c::EACCES))) = e { if let MetalError::Modeset(DrmError::Atomic(OsError(c::EACCES))) = e {
log::warn!("Failed due to access denied. Resetting in memory only."); log::warn!("Failed due to access denied. Resetting in memory only.");
@ -1396,7 +1426,7 @@ impl Connector for MetalConnector {
} }
let dd = &mut *self.display.borrow_mut(); let dd = &mut *self.display.borrow_mut();
let old_enabled = dd.should_enable_vrr(); let old_enabled = dd.should_enable_vrr();
dd.vrr_requested = enabled; dd.persistent.vrr_requested.set(enabled);
let new_enabled = dd.should_enable_vrr(); let new_enabled = dd.should_enable_vrr();
if old_enabled == new_enabled { if old_enabled == new_enabled {
return; return;
@ -1428,6 +1458,24 @@ impl Connector for MetalConnector {
log::debug!("{msg} tearing on output {}", self.kernel_id()); log::debug!("{msg} tearing on output {}", self.kernel_id());
} }
} }
fn set_fb_format(&self, format: &'static Format) {
{
let dd = self.display.borrow().persistent.clone();
dd.format.set(format);
if format == self.buffer_format.get() {
self.try_switch_format.set(false);
return;
}
self.try_switch_format.set(true);
}
if let Some(dev) = self.backend.device_holder.drm_devices.get(&self.dev.devnum) {
if let Err(e) = self.backend.handle_drm_change_(&dev, true) {
dev.unprocessed_change.set(true);
log::error!("Could not change format: {}", ErrorFmt(e));
}
}
}
} }
pub struct MetalCrtc { pub struct MetalCrtc {
@ -1547,6 +1595,7 @@ fn create_connector(
dev: dev.clone(), dev: dev.clone(),
backend: backend.clone(), backend: backend.clone(),
connector_id: backend.state.connector_ids.next(), connector_id: backend.state.connector_ids.next(),
buffer_format: Cell::new(XRGB8888),
buffers: Default::default(), buffers: Default::default(),
next_buffer: Default::default(), next_buffer: Default::default(),
enabled: Cell::new(true), enabled: Cell::new(true),
@ -1579,6 +1628,7 @@ fn create_connector(
direct_scanout_active: Cell::new(false), direct_scanout_active: Cell::new(false),
next_flip_nsec: Cell::new(0), next_flip_nsec: Cell::new(0),
tearing_requested: Cell::new(false), tearing_requested: Cell::new(false),
try_switch_format: Cell::new(false),
}); });
let futures = ConnectorFutures { let futures = ConnectorFutures {
_present: backend _present: backend
@ -1608,13 +1658,10 @@ fn create_connector_display_data(
let mut name = String::new(); let mut name = String::new();
let mut manufacturer = String::new(); let mut manufacturer = String::new();
let mut serial_number = String::new(); let mut serial_number = String::new();
let mode = info.modes.first().cloned(); let connector_id = ConnectorKernelId {
let refresh = mode ty: ConnectorType::from_drm(info.connector_type),
.as_ref() idx: info.connector_type_id,
.map(|m| 1_000_000_000_000u64 / (m.refresh_rate_millihz() as u64)) };
.unwrap_or(0) as u32;
let connector_type = ConnectorType::from_drm(info.connector_type);
let connector_name = debug_fn(|f| write!(f, "{}-{}", connector_type, info.connector_type_id));
'fetch_edid: { 'fetch_edid: {
if connection != ConnectorStatus::Connected { if connection != ConnectorStatus::Connected {
break 'fetch_edid; break 'fetch_edid;
@ -1624,7 +1671,7 @@ fn create_connector_display_data(
_ => { _ => {
log::warn!( log::warn!(
"Connector {} is connected but has no EDID blob", "Connector {} is connected but has no EDID blob",
connector_name, connector_id,
); );
break 'fetch_edid; break 'fetch_edid;
} }
@ -1634,7 +1681,7 @@ fn create_connector_display_data(
Err(e) => { Err(e) => {
log::error!( log::error!(
"Could not fetch edid property of connector {}: {}", "Could not fetch edid property of connector {}: {}",
connector_name, connector_id,
ErrorFmt(e) ErrorFmt(e)
); );
break 'fetch_edid; break 'fetch_edid;
@ -1645,7 +1692,7 @@ fn create_connector_display_data(
Err(e) => { Err(e) => {
log::error!( log::error!(
"Could not parse edid property of connector {}: {}", "Could not parse edid property of connector {}: {}",
connector_name, connector_id,
ErrorFmt(e) ErrorFmt(e)
); );
break 'fetch_edid; break 'fetch_edid;
@ -1666,43 +1713,77 @@ fn create_connector_display_data(
if name.is_empty() { if name.is_empty() {
log::warn!( log::warn!(
"The display attached to connector {} does not have a product name descriptor", "The display attached to connector {} does not have a product name descriptor",
connector_name, connector_id,
); );
} }
if serial_number.is_empty() { if serial_number.is_empty() {
log::warn!( log::warn!(
"The display attached to connector {} does not have a serial number descriptor", "The display attached to connector {} does not have a serial number descriptor",
connector_name, connector_id,
); );
serial_number = edid.base_block.id_serial_number.to_string(); serial_number = edid.base_block.id_serial_number.to_string();
} }
} }
let props = collect_properties(&dev.master, connector)?; let output_id = Rc::new(OutputId::new(
let connector_type = ConnectorType::from_drm(info.connector_type); connector_id.to_string(),
manufacturer,
name,
serial_number,
));
let desired_state = match dev.backend.persistent_display_data.get(&output_id) {
Some(ds) => {
log::info!("Reusing desired state for {:?}", output_id);
ds
}
None => {
let ds = Rc::new(PersistentDisplayData {
mode: RefCell::new(info.modes.first().cloned()),
vrr_requested: Default::default(),
format: Cell::new(XRGB8888),
});
dev.backend
.persistent_display_data
.set(output_id.clone(), ds.clone());
ds
}
};
let mut mode_opt = desired_state.mode.borrow_mut();
if let Some(mode) = &*mode_opt {
if !info.modes.contains(mode) {
log::warn!("Discarding previously desired mode");
*mode_opt = None;
}
}
if mode_opt.is_none() {
*mode_opt = info.modes.first().cloned();
}
let refresh = mode_opt
.as_ref()
.map(|m| 1_000_000_000_000u64 / (m.refresh_rate_millihz() as u64))
.unwrap_or(0) as u32;
let non_desktop = props.get("non-desktop")?.value.get() != 0; let non_desktop = props.get("non-desktop")?.value.get() != 0;
let vrr_capable = match props.get("vrr_capable") { let vrr_capable = match props.get("vrr_capable") {
Ok(c) => c.value.get() == 1, Ok(c) => c.value.get() == 1,
Err(_) => false, Err(_) => false,
}; };
let mode = mode_opt.clone();
drop(mode_opt);
Ok(ConnectorDisplayData { Ok(ConnectorDisplayData {
crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)), crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)),
crtcs, crtcs,
modes: info.modes, modes: info.modes,
mode, mode,
persistent: desired_state,
refresh, refresh,
non_desktop, non_desktop,
non_desktop_effective: non_desktop_override.unwrap_or(non_desktop), non_desktop_effective: non_desktop_override.unwrap_or(non_desktop),
vrr_capable, vrr_capable,
vrr_requested: false,
monitor_manufacturer: manufacturer,
monitor_name: name,
monitor_serial_number: serial_number,
connection, connection,
mm_width: info.mm_width, mm_width: info.mm_width,
mm_height: info.mm_height, mm_height: info.mm_height,
_subpixel: info.subpixel, _subpixel: info.subpixel,
connector_type, connector_id,
connector_type_id: info.connector_type_id, output_id,
}) })
} }
@ -2015,15 +2096,8 @@ impl MetalBackend {
} }
}; };
let mut old = c.display.borrow_mut(); let mut old = c.display.borrow_mut();
if old.is_same_monitor(&dd) {
if let Some(mode) = &old.mode {
if dd.modes.contains(mode) {
dd.mode = Some(mode.clone());
}
}
dd.vrr_requested = old.vrr_requested;
}
mem::swap(old.deref_mut(), &mut dd); mem::swap(old.deref_mut(), &mut dd);
let mut preserve_connector = false;
match c.frontend_state.get() { match c.frontend_state.get() {
FrontState::Removed | FrontState::Disconnected => {} FrontState::Removed | FrontState::Disconnected => {}
FrontState::Connected { .. } | FrontState::Unavailable => { FrontState::Connected { .. } | FrontState::Unavailable => {
@ -2042,7 +2116,7 @@ impl MetalBackend {
// Disconnect if the connector is no longer connected. // Disconnect if the connector is no longer connected.
disconnect |= old.connection != ConnectorStatus::Connected; disconnect |= old.connection != ConnectorStatus::Connected;
// Disconnect if the connected monitor changed. // Disconnect if the connected monitor changed.
disconnect |= !old.is_same_monitor(&dd); disconnect |= old.output_id != dd.output_id;
} }
if disconnect { if disconnect {
c.tearing_requested.set(false); c.tearing_requested.set(false);
@ -2055,10 +2129,16 @@ impl MetalBackend {
} }
c.send_event(ConnectorEvent::Disconnected); c.send_event(ConnectorEvent::Disconnected);
} else if preserve_any { } else if preserve_any {
preserve.connectors.insert(c.id); preserve_connector = true;
} }
} }
} }
if c.try_switch_format.get() && old.persistent.format.get() != c.buffer_format.get() {
preserve_connector = false;
}
if preserve_connector {
preserve.connectors.insert(c.id);
}
} }
for c in new_connectors { for c in new_connectors {
let (connector, future) = match create_connector(self, c, &dev.dev) { let (connector, future) = match create_connector(self, c, &dev.dev) {
@ -2103,9 +2183,7 @@ impl MetalBackend {
} }
connector.send_event(ConnectorEvent::Connected(MonitorInfo { connector.send_event(ConnectorEvent::Connected(MonitorInfo {
modes, modes,
manufacturer: dd.monitor_manufacturer.clone(), output_id: dd.output_id.clone(),
product: dd.monitor_name.clone(),
serial_number: dd.monitor_serial_number.clone(),
initial_mode: dd.mode.clone().unwrap().to_backend(), initial_mode: dd.mode.clone().unwrap().to_backend(),
width_mm: dd.mm_width as _, width_mm: dd.mm_width as _,
height_mm: dd.mm_height as _, height_mm: dd.mm_height as _,
@ -2114,6 +2192,7 @@ impl MetalBackend {
})); }));
connector.send_hardware_cursor(); connector.send_hardware_cursor();
connector.send_vrr_enabled(); connector.send_vrr_enabled();
connector.send_formats();
} }
pub fn create_drm_device( pub fn create_drm_device(
@ -2645,6 +2724,7 @@ impl MetalBackend {
connector.send_hardware_cursor(); connector.send_hardware_cursor();
connector.send_vrr_enabled(); connector.send_vrr_enabled();
connector.update_drm_feedback(); connector.update_drm_feedback();
connector.send_formats();
} }
Ok(()) Ok(())
} }
@ -2937,7 +3017,7 @@ impl MetalBackend {
ctx: &MetalRenderContext, ctx: &MetalRenderContext,
old_buffers: &mut Vec<Rc<dyn Any>>, old_buffers: &mut Vec<Rc<dyn Any>>,
) -> Result<(), MetalError> { ) -> Result<(), MetalError> {
let dd = connector.display.borrow_mut(); let dd = &mut *connector.display.borrow_mut();
let crtc = match connector.crtc.get() { let crtc = match connector.crtc.get() {
Some(c) => c, Some(c) => c,
_ => return Ok(()), _ => return Ok(()),
@ -2949,26 +3029,55 @@ impl MetalBackend {
return Ok(()); return Ok(());
} }
}; };
let (primary_plane, primary_modifiers) = 'primary_plane: { let allocate_primary_plane = |format: &'static Format| {
for plane in crtc.possible_planes.values() { let (primary_plane, primary_modifiers) = 'primary_plane: {
if plane.ty == PlaneType::Primary && !plane.assigned.get() && plane.lease.is_none() for plane in crtc.possible_planes.values() {
{ if plane.ty == PlaneType::Primary
if let Some(format) = plane.formats.get(&XRGB8888.drm) { && !plane.assigned.get()
break 'primary_plane (plane.clone(), &format.modifiers); && plane.lease.is_none()
{
if let Some(format) = plane.formats.get(&format.drm) {
break 'primary_plane (plane.clone(), &format.modifiers);
}
}
}
return Err(MetalError::NoPrimaryPlaneForConnector);
};
let buffers = Rc::new(self.create_scanout_buffers(
&connector.dev,
format,
primary_modifiers,
mode.hdisplay as _,
mode.vdisplay as _,
ctx,
false,
)?);
Ok((primary_plane, buffers))
};
let primary_plane;
let buffers;
let buffer_format;
'primary_plane: {
let format = dd.persistent.format.get();
if format != XRGB8888 {
match allocate_primary_plane(format) {
Ok(v) => {
(primary_plane, buffers) = v;
buffer_format = format;
break 'primary_plane;
}
Err(e) => {
log::error!(
"Could not allocate framebuffer with requested format {}: {}",
format.name,
ErrorFmt(e)
);
} }
} }
} }
return Err(MetalError::NoPrimaryPlaneForConnector); (primary_plane, buffers) = allocate_primary_plane(XRGB8888)?;
}; buffer_format = XRGB8888;
let buffers = Rc::new(self.create_scanout_buffers( }
&connector.dev,
XRGB8888,
primary_modifiers,
mode.hdisplay as _,
mode.vdisplay as _,
ctx,
false,
)?);
let mut cursor_plane = None; let mut cursor_plane = None;
let mut cursor_modifiers = &IndexSet::new(); let mut cursor_modifiers = &IndexSet::new();
for plane in crtc.possible_planes.values() { for plane in crtc.possible_planes.values() {
@ -3043,12 +3152,14 @@ impl MetalBackend {
} }
connector.cursor_plane.set(cursor_plane); connector.cursor_plane.set(cursor_plane);
connector.cursor_enabled.set(false); connector.cursor_enabled.set(false);
connector.buffer_format.set(buffer_format);
connector.try_switch_format.set(false);
Ok(()) Ok(())
} }
fn start_connector(&self, connector: &Rc<MetalConnector>, log_mode: bool) { fn start_connector(&self, connector: &Rc<MetalConnector>, log_mode: bool) {
let dd = connector.display.borrow_mut(); let dd = &*connector.display.borrow();
self.send_connected(connector, &dd); self.send_connected(connector, dd);
match connector.frontend_state.get() { match connector.frontend_state.get() {
FrontState::Connected { non_desktop: false } => {} FrontState::Connected { non_desktop: false } => {}
FrontState::Connected { non_desktop: true } FrontState::Connected { non_desktop: true }
@ -3058,9 +3169,8 @@ impl MetalBackend {
} }
if log_mode { if log_mode {
log::info!( log::info!(
"Initialized connector {}-{} with mode {:?}", "Initialized connector {} with mode {:?}",
dd.connector_type, dd.connector_id,
dd.connector_type_id,
dd.mode.as_ref().unwrap(), dd.mode.as_ref().unwrap(),
); );
} }

View file

@ -11,6 +11,7 @@ use {
fixed::Fixed, fixed::Fixed,
format::XRGB8888, format::XRGB8888,
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture}, gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
ifs::wl_output::OutputId,
renderer::RenderResult, renderer::RenderResult,
state::State, state::State,
utils::{ utils::{
@ -565,9 +566,12 @@ impl XBackend {
.push(BackendEvent::NewConnector(output.clone())); .push(BackendEvent::NewConnector(output.clone()));
output.events.push(ConnectorEvent::Connected(MonitorInfo { output.events.push(ConnectorEvent::Connected(MonitorInfo {
modes: vec![], modes: vec![],
manufacturer: "X.Org Foundation".to_string(), output_id: Rc::new(OutputId::new(
product: format!("X-Window-{}", output.window), String::new(),
serial_number: output.window.to_string(), "X.Org Foundation".to_string(),
format!("X-Window-{}", output.window),
output.window.to_string(),
)),
initial_mode: Mode { initial_mode: Mode {
width: output.width.get(), width: output.width.get(),
height: output.height.get(), height: output.height.get(),

View file

@ -17,10 +17,11 @@ use {
crate::{ crate::{
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs}, cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
compositor::start_compositor, compositor::start_compositor,
format::{ref_formats, Format},
portal, portal,
}, },
::log::Level, ::log::Level,
clap::{Args, Parser, Subcommand, ValueEnum}, clap::{builder::PossibleValue, Args, Parser, Subcommand, ValueEnum},
clap_complete::Shell, clap_complete::Shell,
}; };
@ -231,6 +232,16 @@ pub struct GenerateArgs {
shell: Shell, shell: Shell,
} }
impl ValueEnum for &'static Format {
fn value_variants<'a>() -> &'a [Self] {
ref_formats()
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(self.name))
}
}
pub fn main() { pub fn main() {
let cli = Jay::parse(); let cli = Jay::parse();
match cli.command { match cli.command {

View file

@ -1,6 +1,7 @@
use { use {
crate::{ crate::{
cli::GlobalArgs, cli::GlobalArgs,
format::{Format, XRGB8888},
scale::Scale, scale::Scale,
tools::tool_client::{with_tool_client, Handle, ToolClient}, tools::tool_client::{with_tool_client, Handle, ToolClient},
utils::{errorfmt::ErrorFmt, transform_ext::TransformExt}, utils::{errorfmt::ErrorFmt, transform_ext::TransformExt},
@ -44,6 +45,9 @@ pub struct ShowArgs {
/// Show all available modes. /// Show all available modes.
#[arg(long)] #[arg(long)]
pub modes: bool, pub modes: bool,
/// Show all available formats.
#[arg(long)]
pub formats: bool,
} }
#[derive(Args, Debug)] #[derive(Args, Debug)]
@ -122,6 +126,8 @@ pub enum OutputCommand {
Vrr(VrrArgs), Vrr(VrrArgs),
/// Change tearing settings. /// Change tearing settings.
Tearing(TearingArgs), Tearing(TearingArgs),
/// Change format settings.
Format(FormatSettings),
} }
#[derive(ValueEnum, Debug, Clone)] #[derive(ValueEnum, Debug, Clone)]
@ -177,6 +183,21 @@ pub struct CursorHzArgs {
pub rate: String, pub rate: String,
} }
#[derive(Args, Debug, Clone)]
pub struct FormatSettings {
#[clap(subcommand)]
pub command: FormatCommand,
}
#[derive(Subcommand, Debug, Clone)]
pub enum FormatCommand {
/// Sets the format of the framebuffer.
Set {
#[clap(value_enum)]
format: &'static Format,
},
}
#[derive(Args, Debug, Clone)] #[derive(Args, Debug, Clone)]
pub struct TearingArgs { pub struct TearingArgs {
#[clap(subcommand)] #[clap(subcommand)]
@ -318,6 +339,8 @@ struct Output {
pub vrr_mode: VrrMode, pub vrr_mode: VrrMode,
pub vrr_cursor_hz: Option<f64>, pub vrr_cursor_hz: Option<f64>,
pub tearing_mode: TearingMode, pub tearing_mode: TearingMode,
pub formats: Vec<String>,
pub format: Option<String>,
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -546,6 +569,20 @@ impl Randr {
} }
} }
} }
OutputCommand::Format(a) => {
self.handle_error(randr, move |msg| {
eprintln!("Could not change the framebuffer format: {}", msg);
});
match a.command {
FormatCommand::Set { format } => {
tc.send(jay_randr::SetFbFormat {
self_id: randr,
output: &args.output,
format: format.name,
});
}
}
}
} }
tc.round_trip().await; tc.round_trip().await;
} }
@ -609,7 +646,7 @@ impl Randr {
.collect(); .collect();
connectors.sort_by_key(|c| &c.name); connectors.sort_by_key(|c| &c.name);
for c in connectors { for c in connectors {
self.print_connector(c, args.modes); self.print_connector(c, args.modes, args.formats);
} }
} }
{ {
@ -622,7 +659,7 @@ impl Randr {
connectors.sort_by_key(|c| &c.name); connectors.sort_by_key(|c| &c.name);
println!("unbound connectors:"); println!("unbound connectors:");
for c in connectors { for c in connectors {
self.print_connector(c, args.modes); self.print_connector(c, args.modes, args.formats);
} }
} }
} }
@ -639,7 +676,7 @@ impl Randr {
} }
} }
fn print_connector(&self, connector: &Connector, modes: bool) { fn print_connector(&self, connector: &Connector, modes: bool, formats: bool) {
println!(" {}:", connector.name); println!(" {}:", connector.name);
let Some(o) = &connector.output else { let Some(o) = &connector.output else {
if !connector.enabled { if !connector.enabled {
@ -701,6 +738,11 @@ impl Randr {
print!(" mode: "); print!(" mode: ");
self.print_mode(mode, false); self.print_mode(mode, false);
} }
if let Some(format) = &o.format {
if format != XRGB8888.name {
println!(" format: {format}");
}
}
if o.scale != 1.0 { if o.scale != 1.0 {
println!(" scale: {}", o.scale); println!(" scale: {}", o.scale);
} }
@ -724,6 +766,12 @@ impl Randr {
self.print_mode(mode, true); self.print_mode(mode, true);
} }
} }
if o.formats.is_not_empty() && formats {
println!(" formats:");
for format in &o.formats {
println!(" {format}");
}
}
} }
fn print_mode(&self, m: &Mode, print_current: bool) { fn print_mode(&self, m: &Mode, print_current: bool) {
@ -788,6 +836,8 @@ impl Randr {
vrr_mode: VrrMode::NEVER, vrr_mode: VrrMode::NEVER,
vrr_cursor_hz: None, vrr_cursor_hz: None,
tearing_mode: TearingMode::NEVER, tearing_mode: TearingMode::NEVER,
formats: vec![],
format: None,
}); });
}); });
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| { jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
@ -813,6 +863,8 @@ impl Randr {
vrr_mode: VrrMode::NEVER, vrr_mode: VrrMode::NEVER,
vrr_cursor_hz: None, vrr_cursor_hz: None,
tearing_mode: TearingMode::NEVER, tearing_mode: TearingMode::NEVER,
formats: vec![],
format: None,
}); });
}); });
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| { jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
@ -835,6 +887,15 @@ impl Randr {
let output = c.output.as_mut().unwrap(); let output = c.output.as_mut().unwrap();
output.tearing_mode = TearingMode(msg.mode); output.tearing_mode = TearingMode(msg.mode);
}); });
jay_randr::FbFormat::handle(tc, randr, data.clone(), |data, msg| {
let mut data = data.borrow_mut();
let c = data.connectors.last_mut().unwrap();
let output = c.output.as_mut().unwrap();
output.formats.push(msg.name.to_string());
if msg.current != 0 {
output.format = Some(msg.name.to_string());
}
});
jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| { jay_randr::Mode::handle(tc, randr, data.clone(), |data, msg| {
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
let c = data.connectors.last_mut().unwrap(); let c = data.connectors.last_mut().unwrap();

View file

@ -425,7 +425,7 @@ fn init_fd_limit() {
fn create_dummy_output(state: &Rc<State>) { fn create_dummy_output(state: &Rc<State>) {
let output_id = Rc::new(OutputId { let output_id = Rc::new(OutputId {
connector: "jay-dummy-connector".to_string(), connector: Some("jay-dummy-connector".to_string()),
manufacturer: "jay".to_string(), manufacturer: "jay".to_string(),
model: "jay-dummy-output".to_string(), model: "jay-dummy-output".to_string(),
serial_number: "".to_string(), serial_number: "".to_string(),

View file

@ -7,6 +7,7 @@ use {
}, },
compositor::MAX_EXTENTS, compositor::MAX_EXTENTS,
config::ConfigProxy, config::ConfigProxy,
format::config_formats,
ifs::wl_seat::{SeatId, WlSeatGlobal}, ifs::wl_seat::{SeatId, WlSeatGlobal},
io_uring::TaskResultExt, io_uring::TaskResultExt,
output_schedule::map_cursor_hz, output_schedule::map_cursor_hz,
@ -49,8 +50,8 @@ use {
theme::{colors::Colorable, sized::Resizable}, theme::{colors::Colorable, sized::Resizable},
timer::Timer as JayTimer, timer::Timer as JayTimer,
video::{ video::{
Connector, DrmDevice, GfxApi, TearingMode as ConfigTearingMode, Transform, Connector, DrmDevice, Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode,
VrrMode as ConfigVrrMode, Transform, VrrMode as ConfigVrrMode,
}, },
Axis, Direction, Workspace, Axis, Direction, Workspace,
}, },
@ -973,7 +974,7 @@ impl ConfigProxyHandler {
fn handle_connector_model(&self, connector: Connector) -> Result<(), CphError> { fn handle_connector_model(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?; let connector = self.get_output(connector)?;
self.respond(Response::GetConnectorModel { self.respond(Response::GetConnectorModel {
model: connector.monitor_info.product.clone(), model: connector.monitor_info.output_id.model.clone(),
}); });
Ok(()) Ok(())
} }
@ -981,7 +982,7 @@ impl ConfigProxyHandler {
fn handle_connector_manufacturer(&self, connector: Connector) -> Result<(), CphError> { fn handle_connector_manufacturer(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?; let connector = self.get_output(connector)?;
self.respond(Response::GetConnectorManufacturer { self.respond(Response::GetConnectorManufacturer {
manufacturer: connector.monitor_info.manufacturer.clone(), manufacturer: connector.monitor_info.output_id.manufacturer.clone(),
}); });
Ok(()) Ok(())
} }
@ -989,7 +990,7 @@ impl ConfigProxyHandler {
fn handle_connector_serial_number(&self, connector: Connector) -> Result<(), CphError> { fn handle_connector_serial_number(&self, connector: Connector) -> Result<(), CphError> {
let connector = self.get_output(connector)?; let connector = self.get_output(connector)?;
self.respond(Response::GetConnectorSerialNumber { self.respond(Response::GetConnectorSerialNumber {
serial_number: connector.monitor_info.serial_number.clone(), serial_number: connector.monitor_info.output_id.serial_number.clone(),
}); });
Ok(()) Ok(())
} }
@ -1051,6 +1052,19 @@ impl ConfigProxyHandler {
Ok(()) Ok(())
} }
fn handle_connector_set_format(
&self,
connector: Connector,
format: ConfigFormat,
) -> Result<(), CphError> {
let Some(&format) = config_formats().get(&format) else {
return Err(CphError::UnknownFormat(format));
};
let connector = self.get_connector(connector)?;
connector.connector.set_fb_format(format);
Ok(())
}
fn handle_set_vrr_mode( fn handle_set_vrr_mode(
&self, &self,
connector: Option<Connector>, connector: Option<Connector>,
@ -1919,6 +1933,9 @@ impl ConfigProxyHandler {
ClientMessage::SetEiSocketEnabled { enabled } => { ClientMessage::SetEiSocketEnabled { enabled } => {
self.handle_set_ei_socket_enabled(enabled) self.handle_set_ei_socket_enabled(enabled)
} }
ClientMessage::ConnectorSetFormat { connector, format } => self
.handle_connector_set_format(connector, format)
.wrn("connector_set_format")?,
} }
Ok(()) Ok(())
} }
@ -1986,6 +2003,8 @@ enum CphError {
InvalidCursorHz(f64), InvalidCursorHz(f64),
#[error("Unknown tearing mode {0:?}")] #[error("Unknown tearing mode {0:?}")]
UnknownTearingMode(ConfigTearingMode), UnknownTearingMode(ConfigTearingMode),
#[error("The format {0:?} is unknown")]
UnknownFormat(ConfigFormat),
} }
trait WithRequestName { trait WithRequestName {

View file

@ -13,6 +13,7 @@ use {
}, },
ahash::AHashMap, ahash::AHashMap,
ash::vk, ash::vk,
jay_config::video::Format as ConfigFormat,
once_cell::sync::Lazy, once_cell::sync::Lazy,
std::fmt::{Debug, Write}, std::fmt::{Debug, Write},
}; };
@ -36,9 +37,10 @@ pub struct Format {
pub pipewire: SpaVideoFormat, pub pipewire: SpaVideoFormat,
pub opaque: Option<&'static Format>, pub opaque: Option<&'static Format>,
pub shm_info: Option<FormatShmInfo>, pub shm_info: Option<FormatShmInfo>,
pub config: ConfigFormat,
} }
const fn default() -> Format { const fn default(config: ConfigFormat) -> Format {
Format { Format {
name: "", name: "",
vk_format: vk::Format::UNDEFINED, vk_format: vk::Format::UNDEFINED,
@ -49,6 +51,7 @@ const fn default() -> Format {
pipewire: SPA_VIDEO_FORMAT_UNKNOWN, pipewire: SPA_VIDEO_FORMAT_UNKNOWN,
opaque: None, opaque: None,
shm_info: None, shm_info: None,
config,
} }
} }
@ -78,10 +81,30 @@ static PW_FORMATS_MAP: Lazy<AHashMap<SpaVideoFormat, &'static Format>> = Lazy::n
map map
}); });
static FORMATS_REFS: Lazy<Vec<&'static Format>> = Lazy::new(|| FORMATS.iter().collect());
static FORMATS_NAMES: Lazy<AHashMap<&'static str, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.name, format).is_none());
}
map
});
static FORMATS_CONFIG: Lazy<AHashMap<ConfigFormat, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.config, format).is_none());
}
map
});
#[test] #[test]
fn formats_dont_panic() { fn formats_dont_panic() {
formats(); formats();
pw_formats(); pw_formats();
named_formats();
config_formats();
} }
pub fn formats() -> &'static AHashMap<u32, &'static Format> { pub fn formats() -> &'static AHashMap<u32, &'static Format> {
@ -92,6 +115,18 @@ pub fn pw_formats() -> &'static AHashMap<SpaVideoFormat, &'static Format> {
&PW_FORMATS_MAP &PW_FORMATS_MAP
} }
pub fn ref_formats() -> &'static [&'static Format] {
&FORMATS_REFS
}
pub fn named_formats() -> &'static AHashMap<&'static str, &'static Format> {
&FORMATS_NAMES
}
pub fn config_formats() -> &'static AHashMap<ConfigFormat, &'static Format> {
&FORMATS_CONFIG
}
const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 { const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24) (a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
} }
@ -136,6 +171,7 @@ pub static ARGB8888: &Format = &Format {
has_alpha: true, has_alpha: true,
pipewire: SPA_VIDEO_FORMAT_BGRA, pipewire: SPA_VIDEO_FORMAT_BGRA,
opaque: Some(XRGB8888), opaque: Some(XRGB8888),
config: ConfigFormat::ARGB8888,
}; };
pub static XRGB8888: &Format = &Format { pub static XRGB8888: &Format = &Format {
@ -153,6 +189,7 @@ pub static XRGB8888: &Format = &Format {
has_alpha: false, has_alpha: false,
pipewire: SPA_VIDEO_FORMAT_BGRx, pipewire: SPA_VIDEO_FORMAT_BGRx,
opaque: None, opaque: None,
config: ConfigFormat::XRGB8888,
}; };
static ABGR8888: &Format = &Format { static ABGR8888: &Format = &Format {
@ -170,6 +207,7 @@ static ABGR8888: &Format = &Format {
has_alpha: true, has_alpha: true,
pipewire: SPA_VIDEO_FORMAT_RGBA, pipewire: SPA_VIDEO_FORMAT_RGBA,
opaque: Some(XBGR8888), opaque: Some(XBGR8888),
config: ConfigFormat::ABGR8888,
}; };
static XBGR8888: &Format = &Format { static XBGR8888: &Format = &Format {
@ -187,6 +225,7 @@ static XBGR8888: &Format = &Format {
has_alpha: false, has_alpha: false,
pipewire: SPA_VIDEO_FORMAT_RGBx, pipewire: SPA_VIDEO_FORMAT_RGBx,
opaque: None, opaque: None,
config: ConfigFormat::XBGR8888,
}; };
static R8: &Format = &Format { static R8: &Format = &Format {
@ -194,14 +233,14 @@ static R8: &Format = &Format {
vk_format: vk::Format::R8_UNORM, vk_format: vk::Format::R8_UNORM,
drm: fourcc_code('R', '8', ' ', ' '), drm: fourcc_code('R', '8', ' ', ' '),
pipewire: SPA_VIDEO_FORMAT_GRAY8, pipewire: SPA_VIDEO_FORMAT_GRAY8,
..default() ..default(ConfigFormat::R8)
}; };
static GR88: &Format = &Format { static GR88: &Format = &Format {
name: "gr88", name: "gr88",
vk_format: vk::Format::R8G8_UNORM, vk_format: vk::Format::R8G8_UNORM,
drm: fourcc_code('G', 'R', '8', '8'), drm: fourcc_code('G', 'R', '8', '8'),
..default() ..default(ConfigFormat::GR88)
}; };
static RGB888: &Format = &Format { static RGB888: &Format = &Format {
@ -209,7 +248,7 @@ static RGB888: &Format = &Format {
vk_format: vk::Format::B8G8R8_UNORM, vk_format: vk::Format::B8G8R8_UNORM,
drm: fourcc_code('R', 'G', '2', '4'), drm: fourcc_code('R', 'G', '2', '4'),
pipewire: SPA_VIDEO_FORMAT_BGR, pipewire: SPA_VIDEO_FORMAT_BGR,
..default() ..default(ConfigFormat::RGB888)
}; };
static BGR888: &Format = &Format { static BGR888: &Format = &Format {
@ -217,7 +256,7 @@ static BGR888: &Format = &Format {
vk_format: vk::Format::R8G8B8_UNORM, vk_format: vk::Format::R8G8B8_UNORM,
drm: fourcc_code('B', 'G', '2', '4'), drm: fourcc_code('B', 'G', '2', '4'),
pipewire: SPA_VIDEO_FORMAT_RGB, pipewire: SPA_VIDEO_FORMAT_RGB,
..default() ..default(ConfigFormat::BGR888)
}; };
static RGBA4444: &Format = &Format { static RGBA4444: &Format = &Format {
@ -226,14 +265,14 @@ static RGBA4444: &Format = &Format {
drm: fourcc_code('R', 'A', '1', '2'), drm: fourcc_code('R', 'A', '1', '2'),
has_alpha: true, has_alpha: true,
opaque: Some(RGBX4444), opaque: Some(RGBX4444),
..default() ..default(ConfigFormat::RGBA4444)
}; };
static RGBX4444: &Format = &Format { static RGBX4444: &Format = &Format {
name: "rgbx4444", name: "rgbx4444",
vk_format: vk::Format::R4G4B4A4_UNORM_PACK16, vk_format: vk::Format::R4G4B4A4_UNORM_PACK16,
drm: fourcc_code('R', 'X', '1', '2'), drm: fourcc_code('R', 'X', '1', '2'),
..default() ..default(ConfigFormat::RGBX4444)
}; };
static BGRA4444: &Format = &Format { static BGRA4444: &Format = &Format {
@ -242,14 +281,14 @@ static BGRA4444: &Format = &Format {
drm: fourcc_code('B', 'A', '1', '2'), drm: fourcc_code('B', 'A', '1', '2'),
has_alpha: true, has_alpha: true,
opaque: Some(BGRX4444), opaque: Some(BGRX4444),
..default() ..default(ConfigFormat::BGRA4444)
}; };
static BGRX4444: &Format = &Format { static BGRX4444: &Format = &Format {
name: "bgrx4444", name: "bgrx4444",
vk_format: vk::Format::B4G4R4A4_UNORM_PACK16, vk_format: vk::Format::B4G4R4A4_UNORM_PACK16,
drm: fourcc_code('B', 'X', '1', '2'), drm: fourcc_code('B', 'X', '1', '2'),
..default() ..default(ConfigFormat::BGRX4444)
}; };
static RGB565: &Format = &Format { static RGB565: &Format = &Format {
@ -257,7 +296,7 @@ static RGB565: &Format = &Format {
vk_format: vk::Format::R5G6B5_UNORM_PACK16, vk_format: vk::Format::R5G6B5_UNORM_PACK16,
drm: fourcc_code('R', 'G', '1', '6'), drm: fourcc_code('R', 'G', '1', '6'),
pipewire: SPA_VIDEO_FORMAT_BGR16, pipewire: SPA_VIDEO_FORMAT_BGR16,
..default() ..default(ConfigFormat::RGB565)
}; };
static BGR565: &Format = &Format { static BGR565: &Format = &Format {
@ -265,7 +304,7 @@ static BGR565: &Format = &Format {
vk_format: vk::Format::B5G6R5_UNORM_PACK16, vk_format: vk::Format::B5G6R5_UNORM_PACK16,
drm: fourcc_code('B', 'G', '1', '6'), drm: fourcc_code('B', 'G', '1', '6'),
pipewire: SPA_VIDEO_FORMAT_RGB16, pipewire: SPA_VIDEO_FORMAT_RGB16,
..default() ..default(ConfigFormat::BGR565)
}; };
static RGBA5551: &Format = &Format { static RGBA5551: &Format = &Format {
@ -274,14 +313,14 @@ static RGBA5551: &Format = &Format {
drm: fourcc_code('R', 'A', '1', '5'), drm: fourcc_code('R', 'A', '1', '5'),
has_alpha: true, has_alpha: true,
opaque: Some(RGBX5551), opaque: Some(RGBX5551),
..default() ..default(ConfigFormat::RGBA5551)
}; };
static RGBX5551: &Format = &Format { static RGBX5551: &Format = &Format {
name: "rgbx5551", name: "rgbx5551",
vk_format: vk::Format::R5G5B5A1_UNORM_PACK16, vk_format: vk::Format::R5G5B5A1_UNORM_PACK16,
drm: fourcc_code('R', 'X', '1', '5'), drm: fourcc_code('R', 'X', '1', '5'),
..default() ..default(ConfigFormat::RGBX5551)
}; };
static BGRA5551: &Format = &Format { static BGRA5551: &Format = &Format {
@ -290,14 +329,14 @@ static BGRA5551: &Format = &Format {
drm: fourcc_code('B', 'A', '1', '5'), drm: fourcc_code('B', 'A', '1', '5'),
has_alpha: true, has_alpha: true,
opaque: Some(BGRX5551), opaque: Some(BGRX5551),
..default() ..default(ConfigFormat::BGRA5551)
}; };
static BGRX5551: &Format = &Format { static BGRX5551: &Format = &Format {
name: "bgrx5551", name: "bgrx5551",
vk_format: vk::Format::B5G5R5A1_UNORM_PACK16, vk_format: vk::Format::B5G5R5A1_UNORM_PACK16,
drm: fourcc_code('B', 'X', '1', '5'), drm: fourcc_code('B', 'X', '1', '5'),
..default() ..default(ConfigFormat::BGRX5551)
}; };
static ARGB1555: &Format = &Format { static ARGB1555: &Format = &Format {
@ -306,7 +345,7 @@ static ARGB1555: &Format = &Format {
drm: fourcc_code('A', 'R', '1', '5'), drm: fourcc_code('A', 'R', '1', '5'),
has_alpha: true, has_alpha: true,
opaque: Some(XRGB1555), opaque: Some(XRGB1555),
..default() ..default(ConfigFormat::ARGB1555)
}; };
static XRGB1555: &Format = &Format { static XRGB1555: &Format = &Format {
@ -314,7 +353,7 @@ static XRGB1555: &Format = &Format {
vk_format: vk::Format::A1R5G5B5_UNORM_PACK16, vk_format: vk::Format::A1R5G5B5_UNORM_PACK16,
drm: fourcc_code('X', 'R', '1', '5'), drm: fourcc_code('X', 'R', '1', '5'),
pipewire: SPA_VIDEO_FORMAT_BGR15, pipewire: SPA_VIDEO_FORMAT_BGR15,
..default() ..default(ConfigFormat::XRGB1555)
}; };
static ARGB2101010: &Format = &Format { static ARGB2101010: &Format = &Format {
@ -324,7 +363,7 @@ static ARGB2101010: &Format = &Format {
has_alpha: true, has_alpha: true,
opaque: Some(XRGB2101010), opaque: Some(XRGB2101010),
pipewire: SPA_VIDEO_FORMAT_ARGB_210LE, pipewire: SPA_VIDEO_FORMAT_ARGB_210LE,
..default() ..default(ConfigFormat::ARGB2101010)
}; };
static XRGB2101010: &Format = &Format { static XRGB2101010: &Format = &Format {
@ -332,7 +371,7 @@ static XRGB2101010: &Format = &Format {
vk_format: vk::Format::A2R10G10B10_UNORM_PACK32, vk_format: vk::Format::A2R10G10B10_UNORM_PACK32,
drm: fourcc_code('X', 'R', '3', '0'), drm: fourcc_code('X', 'R', '3', '0'),
pipewire: SPA_VIDEO_FORMAT_xRGB_210LE, pipewire: SPA_VIDEO_FORMAT_xRGB_210LE,
..default() ..default(ConfigFormat::XRGB2101010)
}; };
static ABGR2101010: &Format = &Format { static ABGR2101010: &Format = &Format {
@ -342,7 +381,7 @@ static ABGR2101010: &Format = &Format {
has_alpha: true, has_alpha: true,
opaque: Some(XBGR2101010), opaque: Some(XBGR2101010),
pipewire: SPA_VIDEO_FORMAT_ABGR_210LE, pipewire: SPA_VIDEO_FORMAT_ABGR_210LE,
..default() ..default(ConfigFormat::ABGR2101010)
}; };
static XBGR2101010: &Format = &Format { static XBGR2101010: &Format = &Format {
@ -350,7 +389,7 @@ static XBGR2101010: &Format = &Format {
vk_format: vk::Format::A2B10G10R10_UNORM_PACK32, vk_format: vk::Format::A2B10G10R10_UNORM_PACK32,
drm: fourcc_code('X', 'B', '3', '0'), drm: fourcc_code('X', 'B', '3', '0'),
pipewire: SPA_VIDEO_FORMAT_xBGR_210LE, pipewire: SPA_VIDEO_FORMAT_xBGR_210LE,
..default() ..default(ConfigFormat::XBGR2101010)
}; };
static ABGR16161616: &Format = &Format { static ABGR16161616: &Format = &Format {
@ -359,14 +398,14 @@ static ABGR16161616: &Format = &Format {
drm: fourcc_code('A', 'B', '4', '8'), drm: fourcc_code('A', 'B', '4', '8'),
has_alpha: true, has_alpha: true,
opaque: Some(XBGR16161616), opaque: Some(XBGR16161616),
..default() ..default(ConfigFormat::ABGR16161616)
}; };
static XBGR16161616: &Format = &Format { static XBGR16161616: &Format = &Format {
name: "xbgr16161616", name: "xbgr16161616",
vk_format: vk::Format::R16G16B16A16_UNORM, vk_format: vk::Format::R16G16B16A16_UNORM,
drm: fourcc_code('X', 'B', '4', '8'), drm: fourcc_code('X', 'B', '4', '8'),
..default() ..default(ConfigFormat::XBGR16161616)
}; };
static ABGR16161616F: &Format = &Format { static ABGR16161616F: &Format = &Format {
@ -375,14 +414,14 @@ static ABGR16161616F: &Format = &Format {
drm: fourcc_code('A', 'B', '4', 'H'), drm: fourcc_code('A', 'B', '4', 'H'),
has_alpha: true, has_alpha: true,
opaque: Some(XBGR16161616F), opaque: Some(XBGR16161616F),
..default() ..default(ConfigFormat::ABGR16161616F)
}; };
static XBGR16161616F: &Format = &Format { static XBGR16161616F: &Format = &Format {
name: "xbgr16161616f", name: "xbgr16161616f",
vk_format: vk::Format::R16G16B16A16_SFLOAT, vk_format: vk::Format::R16G16B16A16_SFLOAT,
drm: fourcc_code('X', 'B', '4', 'H'), drm: fourcc_code('X', 'B', '4', 'H'),
..default() ..default(ConfigFormat::XBGR16161616F)
}; };
pub static FORMATS: &[Format] = &[ pub static FORMATS: &[Format] = &[

View file

@ -1,21 +1,21 @@
use { use {
crate::{ crate::gfx_apis::vulkan::{
format::ARGB8888, descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, shaders::VulkanShader,
gfx_apis::vulkan::{ util::OnDrop, VulkanError,
descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, shaders::VulkanShader,
util::OnDrop, VulkanError,
},
}, },
arrayvec::ArrayVec, arrayvec::ArrayVec,
ash::vk::{ ash::{
BlendFactor, BlendOp, ColorComponentFlags, CullModeFlags, DynamicState, FrontFace, vk,
GraphicsPipelineCreateInfo, Pipeline, PipelineCache, PipelineColorBlendAttachmentState, vk::{
PipelineColorBlendStateCreateInfo, PipelineDynamicStateCreateInfo, BlendFactor, BlendOp, ColorComponentFlags, CullModeFlags, DynamicState, FrontFace,
PipelineInputAssemblyStateCreateInfo, PipelineLayout, PipelineLayoutCreateInfo, GraphicsPipelineCreateInfo, Pipeline, PipelineCache, PipelineColorBlendAttachmentState,
PipelineMultisampleStateCreateInfo, PipelineRasterizationStateCreateInfo, PipelineColorBlendStateCreateInfo, PipelineDynamicStateCreateInfo,
PipelineRenderingCreateInfo, PipelineShaderStageCreateInfo, PipelineInputAssemblyStateCreateInfo, PipelineLayout, PipelineLayoutCreateInfo,
PipelineVertexInputStateCreateInfo, PipelineViewportStateCreateInfo, PolygonMode, PipelineMultisampleStateCreateInfo, PipelineRasterizationStateCreateInfo,
PrimitiveTopology, PushConstantRange, SampleCountFlags, ShaderStageFlags, PipelineRenderingCreateInfo, PipelineShaderStageCreateInfo,
PipelineVertexInputStateCreateInfo, PipelineViewportStateCreateInfo, PolygonMode,
PrimitiveTopology, PushConstantRange, SampleCountFlags, ShaderStageFlags,
},
}, },
std::{mem, rc::Rc, slice}, std::{mem, rc::Rc, slice},
}; };
@ -30,6 +30,7 @@ pub(super) struct VulkanPipeline {
} }
pub(super) struct PipelineCreateInfo { pub(super) struct PipelineCreateInfo {
pub(super) format: vk::Format,
pub(super) vert: Rc<VulkanShader>, pub(super) vert: Rc<VulkanShader>,
pub(super) frag: Rc<VulkanShader>, pub(super) frag: Rc<VulkanShader>,
pub(super) alpha: bool, pub(super) alpha: bool,
@ -128,7 +129,7 @@ impl VulkanDevice {
.viewport_count(1) .viewport_count(1)
.scissor_count(1); .scissor_count(1);
let mut pipeline_rendering_create_info = PipelineRenderingCreateInfo::default() let mut pipeline_rendering_create_info = PipelineRenderingCreateInfo::default()
.color_attachment_formats(slice::from_ref(&ARGB8888.vk_format)); .color_attachment_formats(slice::from_ref(&info.format));
let create_info = GraphicsPipelineCreateInfo::default() let create_info = GraphicsPipelineCreateInfo::default()
.push_next(&mut pipeline_rendering_create_info) .push_next(&mut pipeline_rendering_create_info)
.stages(&stages) .stages(&stages)

View file

@ -1,7 +1,7 @@
use { use {
crate::{ crate::{
async_engine::{AsyncEngine, SpawnedFuture}, async_engine::{AsyncEngine, SpawnedFuture},
format::Format, format::{Format, XRGB8888},
gfx_api::{ gfx_api::{
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer, AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer,
GfxTexture, ReleaseSync, SyncFile, GfxTexture, ReleaseSync, SyncFile,
@ -9,6 +9,7 @@ use {
gfx_apis::vulkan::{ gfx_apis::vulkan::{
allocator::VulkanAllocator, allocator::VulkanAllocator,
command::{VulkanCommandBuffer, VulkanCommandPool}, command::{VulkanCommandBuffer, VulkanCommandPool},
descriptor::VulkanDescriptorSetLayout,
device::VulkanDevice, device::VulkanDevice,
fence::VulkanFence, fence::VulkanFence,
image::{VulkanImage, VulkanImageMemory}, image::{VulkanImage, VulkanImageMemory},
@ -28,6 +29,7 @@ use {
}, },
ahash::AHashMap, ahash::AHashMap,
ash::{ ash::{
vk,
vk::{ vk::{
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy,
BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer, BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer,
@ -56,8 +58,7 @@ use {
pub struct VulkanRenderer { pub struct VulkanRenderer {
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>, pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
pub(super) device: Rc<VulkanDevice>, pub(super) device: Rc<VulkanDevice>,
pub(super) fill_pipeline: Rc<VulkanPipeline>, pub(super) pipelines: CopyHashMap<vk::Format, Rc<VulkanFormatPipelines>>,
pub(super) tex_pipelines: EnumMap<TexCopyType, EnumMap<TexSourceType, Rc<VulkanPipeline>>>,
pub(super) command_pool: Rc<VulkanCommandPool>, pub(super) command_pool: Rc<VulkanCommandPool>,
pub(super) command_buffers: Stack<Rc<VulkanCommandBuffer>>, pub(super) command_buffers: Stack<Rc<VulkanCommandBuffer>>,
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>, pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
@ -70,6 +71,13 @@ pub struct VulkanRenderer {
pub(super) buffer_resv_user: BufferResvUser, pub(super) buffer_resv_user: BufferResvUser,
pub(super) eng: Rc<AsyncEngine>, pub(super) eng: Rc<AsyncEngine>,
pub(super) ring: Rc<IoUring>, pub(super) ring: Rc<IoUring>,
pub(super) fill_vert_shader: Rc<VulkanShader>,
pub(super) fill_frag_shader: Rc<VulkanShader>,
pub(super) tex_vert_shader: Rc<VulkanShader>,
pub(super) tex_frag_shader: Rc<VulkanShader>,
pub(super) tex_frag_mult_opaque_shader: Rc<VulkanShader>,
pub(super) tex_frag_mult_alpha_shader: Rc<VulkanShader>,
pub(super) tex_descriptor_set_layout: Rc<VulkanDescriptorSetLayout>,
} }
pub(super) struct UsedTexture { pub(super) struct UsedTexture {
@ -112,46 +120,25 @@ pub(super) struct PendingFrame {
_release_fence: Option<Rc<VulkanFence>>, _release_fence: Option<Rc<VulkanFence>>,
} }
pub(super) struct VulkanFormatPipelines {
pub(super) fill: Rc<VulkanPipeline>,
pub(super) tex: EnumMap<TexCopyType, EnumMap<TexSourceType, Rc<VulkanPipeline>>>,
}
impl VulkanDevice { impl VulkanDevice {
pub fn create_renderer( pub fn create_renderer(
self: &Rc<Self>, self: &Rc<Self>,
eng: &Rc<AsyncEngine>, eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>, ring: &Rc<IoUring>,
) -> Result<Rc<VulkanRenderer>, VulkanError> { ) -> Result<Rc<VulkanRenderer>, VulkanError> {
let fill_pipeline = self.create_pipeline::<FillVertPushConstants, FillFragPushConstants>( let fill_vert_shader = self.create_shader(FILL_VERT)?;
PipelineCreateInfo { let fill_frag_shader = self.create_shader(FILL_FRAG)?;
vert: self.create_shader(FILL_VERT)?,
frag: self.create_shader(FILL_FRAG)?,
alpha: true,
frag_descriptor_set_layout: None,
},
)?;
let sampler = self.create_sampler()?; let sampler = self.create_sampler()?;
let tex_descriptor_set_layout = self.create_descriptor_set_layout(&sampler)?; let tex_descriptor_set_layout = self.create_descriptor_set_layout(&sampler)?;
let tex_vert_shader = self.create_shader(TEX_VERT)?; let tex_vert_shader = self.create_shader(TEX_VERT)?;
let tex_frag_shader = self.create_shader(TEX_FRAG)?; let tex_frag_shader = self.create_shader(TEX_FRAG)?;
let tex_frag_mult_opaque_shader = self.create_shader(TEX_FRAG_MULT_OPAQUE)?; let tex_frag_mult_opaque_shader = self.create_shader(TEX_FRAG_MULT_OPAQUE)?;
let tex_frag_mult_alpha_shader = self.create_shader(TEX_FRAG_MULT_ALPHA)?; let tex_frag_mult_alpha_shader = self.create_shader(TEX_FRAG_MULT_ALPHA)?;
let create_tex_pipeline = |alpha| {
self.create_pipeline::<TexVertPushConstants, ()>(PipelineCreateInfo {
vert: tex_vert_shader.clone(),
frag: tex_frag_shader.clone(),
alpha,
frag_descriptor_set_layout: Some(tex_descriptor_set_layout.clone()),
})
};
let create_tex_mult_pipeline = |frag: &Rc<VulkanShader>| {
self.create_pipeline::<TexVertPushConstants, TexFragPushConstants>(PipelineCreateInfo {
vert: tex_vert_shader.clone(),
frag: frag.clone(),
alpha: true,
frag_descriptor_set_layout: Some(tex_descriptor_set_layout.clone()),
})
};
let tex_opaque_pipeline = create_tex_pipeline(false)?;
let tex_alpha_pipeline = create_tex_pipeline(true)?;
let tex_mult_opaque_pipeline = create_tex_mult_pipeline(&tex_frag_mult_opaque_shader)?;
let tex_mult_alpha_pipeline = create_tex_mult_pipeline(&tex_frag_mult_alpha_shader)?;
let command_pool = self.create_command_pool()?; let command_pool = self.create_command_pool()?;
let formats: AHashMap<u32, _> = self let formats: AHashMap<u32, _> = self
.formats .formats
@ -178,20 +165,10 @@ impl VulkanDevice {
}) })
.collect(); .collect();
let allocator = self.create_allocator()?; let allocator = self.create_allocator()?;
Ok(Rc::new(VulkanRenderer { let render = Rc::new(VulkanRenderer {
formats: Rc::new(formats), formats: Rc::new(formats),
device: self.clone(), device: self.clone(),
fill_pipeline, pipelines: Default::default(),
tex_pipelines: enum_map! {
TexCopyType::Identity => enum_map! {
TexSourceType::HasAlpha => tex_alpha_pipeline.clone(),
TexSourceType::Opaque => tex_opaque_pipeline.clone(),
},
TexCopyType::Multiply => enum_map! {
TexSourceType::HasAlpha => tex_mult_alpha_pipeline.clone(),
TexSourceType::Opaque => tex_mult_opaque_pipeline.clone(),
},
},
command_pool, command_pool,
command_buffers: Default::default(), command_buffers: Default::default(),
wait_semaphores: Default::default(), wait_semaphores: Default::default(),
@ -204,11 +181,79 @@ impl VulkanDevice {
buffer_resv_user: Default::default(), buffer_resv_user: Default::default(),
eng: eng.clone(), eng: eng.clone(),
ring: ring.clone(), ring: ring.clone(),
})) fill_vert_shader,
fill_frag_shader,
tex_vert_shader,
tex_frag_shader,
tex_frag_mult_opaque_shader,
tex_frag_mult_alpha_shader,
tex_descriptor_set_layout,
});
render.get_or_create_pipelines(XRGB8888.vk_format)?;
Ok(render)
} }
} }
impl VulkanRenderer { impl VulkanRenderer {
fn get_or_create_pipelines(
&self,
format: vk::Format,
) -> Result<Rc<VulkanFormatPipelines>, VulkanError> {
if let Some(pl) = self.pipelines.get(&format) {
return Ok(pl);
}
let fill = self
.device
.create_pipeline::<FillVertPushConstants, FillFragPushConstants>(
PipelineCreateInfo {
format,
vert: self.fill_vert_shader.clone(),
frag: self.fill_frag_shader.clone(),
alpha: true,
frag_descriptor_set_layout: None,
},
)?;
let create_tex_pipeline = |alpha| {
self.device
.create_pipeline::<TexVertPushConstants, ()>(PipelineCreateInfo {
format,
vert: self.tex_vert_shader.clone(),
frag: self.tex_frag_shader.clone(),
alpha,
frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()),
})
};
let create_tex_mult_pipeline = |frag: &Rc<VulkanShader>| {
self.device
.create_pipeline::<TexVertPushConstants, TexFragPushConstants>(PipelineCreateInfo {
format,
vert: self.tex_vert_shader.clone(),
frag: frag.clone(),
alpha: true,
frag_descriptor_set_layout: Some(self.tex_descriptor_set_layout.clone()),
})
};
let tex_opaque = create_tex_pipeline(false)?;
let tex_alpha = create_tex_pipeline(true)?;
let tex_mult_opaque = create_tex_mult_pipeline(&self.tex_frag_mult_opaque_shader)?;
let tex_mult_alpha = create_tex_mult_pipeline(&self.tex_frag_mult_alpha_shader)?;
let pipelines = Rc::new(VulkanFormatPipelines {
fill,
tex: enum_map! {
TexCopyType::Identity => enum_map! {
TexSourceType::HasAlpha => tex_alpha.clone(),
TexSourceType::Opaque => tex_opaque.clone(),
},
TexCopyType::Multiply => enum_map! {
TexSourceType::HasAlpha => tex_mult_alpha.clone(),
TexSourceType::Opaque => tex_mult_opaque.clone(),
},
},
});
self.pipelines.set(format, pipelines.clone());
Ok(pipelines)
}
pub(super) fn allocate_point(&self) -> u64 { pub(super) fn allocate_point(&self) -> u64 {
self.last_point.fetch_add(1) + 1 self.last_point.fetch_add(1) + 1
} }
@ -350,7 +395,13 @@ impl VulkanRenderer {
} }
} }
fn record_draws(&self, buf: CommandBuffer, opts: &[GfxApiOpt]) -> Result<(), VulkanError> { fn record_draws(
&self,
buf: CommandBuffer,
fb: &VulkanImage,
opts: &[GfxApiOpt],
) -> Result<(), VulkanError> {
let pipelines = self.get_or_create_pipelines(fb.format.vk_format)?;
let dev = &self.device.device; let dev = &self.device.device;
let mut current_pipeline = None; let mut current_pipeline = None;
let mut bind = |pipeline: &VulkanPipeline| { let mut bind = |pipeline: &VulkanPipeline| {
@ -365,7 +416,7 @@ impl VulkanRenderer {
match opt { match opt {
GfxApiOpt::Sync => {} GfxApiOpt::Sync => {}
GfxApiOpt::FillRect(r) => { GfxApiOpt::FillRect(r) => {
bind(&self.fill_pipeline); bind(&pipelines.fill);
let vert = FillVertPushConstants { let vert = FillVertPushConstants {
pos: r.rect.to_points(), pos: r.rect.to_points(),
}; };
@ -375,16 +426,16 @@ impl VulkanRenderer {
unsafe { unsafe {
dev.cmd_push_constants( dev.cmd_push_constants(
buf, buf,
self.fill_pipeline.pipeline_layout, pipelines.fill.pipeline_layout,
ShaderStageFlags::VERTEX, ShaderStageFlags::VERTEX,
0, 0,
uapi::as_bytes(&vert), uapi::as_bytes(&vert),
); );
dev.cmd_push_constants( dev.cmd_push_constants(
buf, buf,
self.fill_pipeline.pipeline_layout, pipelines.fill.pipeline_layout,
ShaderStageFlags::FRAGMENT, ShaderStageFlags::FRAGMENT,
self.fill_pipeline.frag_push_offset, pipelines.fill.frag_push_offset,
uapi::as_bytes(&frag), uapi::as_bytes(&frag),
); );
dev.cmd_draw(buf, 4, 1, 0, 0); dev.cmd_draw(buf, 4, 1, 0, 0);
@ -400,7 +451,7 @@ impl VulkanRenderer {
true => TexSourceType::HasAlpha, true => TexSourceType::HasAlpha,
false => TexSourceType::Opaque, false => TexSourceType::Opaque,
}; };
let pipeline = &self.tex_pipelines[copy_type][source_type]; let pipeline = &pipelines.tex[copy_type][source_type];
bind(pipeline); bind(pipeline);
let vert = TexVertPushConstants { let vert = TexVertPushConstants {
pos: c.target.to_points(), pos: c.target.to_points(),
@ -944,7 +995,7 @@ impl VulkanRenderer {
self.initial_barriers(buf.buffer, fb); self.initial_barriers(buf.buffer, fb);
self.begin_rendering(buf.buffer, fb, clear); self.begin_rendering(buf.buffer, fb, clear);
self.set_viewport(buf.buffer, fb); self.set_viewport(buf.buffer, fb);
self.record_draws(buf.buffer, opts)?; self.record_draws(buf.buffer, fb, opts)?;
self.end_rendering(buf.buffer); self.end_rendering(buf.buffer);
self.copy_bridge_to_dmabuf(buf.buffer, fb); self.copy_bridge_to_dmabuf(buf.buffer, fb);
self.final_barriers(buf.buffer, fb); self.final_barriers(buf.buffer, fb);

View file

@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal {
} }
fn version(&self) -> u32 { fn version(&self) -> u32 {
7 8
} }
fn required_caps(&self) -> ClientCaps { fn required_caps(&self) -> ClientCaps {

View file

@ -3,6 +3,7 @@ use {
backend, backend,
client::{Client, ClientError}, client::{Client, ClientError},
compositor::MAX_EXTENTS, compositor::MAX_EXTENTS,
format::named_formats,
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
scale::Scale, scale::Scale,
@ -27,6 +28,7 @@ pub struct JayRandr {
const VRR_CAPABLE_SINCE: Version = Version(2); const VRR_CAPABLE_SINCE: Version = Version(2);
const TEARING_SINCE: Version = Version(3); const TEARING_SINCE: Version = Version(3);
const FORMAT_SINCE: Version = Version(8);
impl JayRandr { impl JayRandr {
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self { pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
@ -80,9 +82,9 @@ impl JayRandr {
None => { None => {
self.client.event(NonDesktopOutput { self.client.event(NonDesktopOutput {
self_id: self.id, self_id: self.id,
manufacturer: &output.monitor_info.manufacturer, manufacturer: &output.monitor_info.output_id.manufacturer,
product: &output.monitor_info.product, product: &output.monitor_info.output_id.model,
serial_number: &output.monitor_info.serial_number, serial_number: &output.monitor_info.output_id.serial_number,
width_mm: output.monitor_info.width_mm, width_mm: output.monitor_info.width_mm,
height_mm: output.monitor_info.height_mm, height_mm: output.monitor_info.height_mm,
}); });
@ -99,9 +101,9 @@ impl JayRandr {
x: pos.x1(), x: pos.x1(),
y: pos.y1(), y: pos.y1(),
transform: global.persistent.transform.get().to_wl(), transform: global.persistent.transform.get().to_wl(),
manufacturer: &output.monitor_info.manufacturer, manufacturer: &output.monitor_info.output_id.manufacturer,
product: &output.monitor_info.product, product: &output.monitor_info.output_id.model,
serial_number: &output.monitor_info.serial_number, serial_number: &output.monitor_info.output_id.serial_number,
width_mm: global.width_mm, width_mm: global.width_mm,
height_mm: global.height_mm, height_mm: global.height_mm,
}); });
@ -125,6 +127,23 @@ impl JayRandr {
mode: node.global.persistent.tearing_mode.get().to_config().0, mode: node.global.persistent.tearing_mode.get().to_config().0,
}); });
} }
if self.version >= FORMAT_SINCE {
let current = node.global.format.get();
self.client.event(FbFormat {
self_id: self.id,
name: current.name,
current: 1,
});
for &format in &*node.global.formats.get() {
if format != current {
self.client.event(FbFormat {
self_id: self.id,
name: format.name,
current: 0,
});
}
}
}
let current_mode = global.mode.get(); let current_mode = global.mode.get();
for mode in &global.modes { for mode in &global.modes {
self.client.event(Mode { self.client.event(Mode {
@ -365,6 +384,17 @@ impl JayRandrRequestHandler for JayRandr {
c.update_presentation_type(); c.update_presentation_type();
return Ok(()); return Ok(());
} }
fn set_fb_format(&self, req: SetFbFormat<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let Some(&format) = named_formats().get(req.format) else {
return Err(JayRandrError::UnknownFormat(req.format.to_string()));
};
let Some(c) = self.get_output_node(req.output) else {
return Ok(());
};
c.global.connector.connector.set_fb_format(format);
Ok(())
}
} }
object_base! { object_base! {
@ -384,5 +414,7 @@ pub enum JayRandrError {
UnknownVrrMode(u32), UnknownVrrMode(u32),
#[error("Unknown tearing mode {0}")] #[error("Unknown tearing mode {0}")]
UnknownTearingMode(u32), UnknownTearingMode(u32),
#[error("Unknown format {0}")]
UnknownFormat(String),
} }
efrom!(JayRandrError, ClientError); efrom!(JayRandrError, ClientError);

View file

@ -4,6 +4,7 @@ use {
crate::{ crate::{
backend, backend,
client::{Client, ClientError, ClientId}, client::{Client, ClientError, ClientId},
format::{Format, XRGB8888},
globals::{Global, GlobalName}, globals::{Global, GlobalName},
ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1}, ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1},
leaks::Tracker, leaks::Tracker,
@ -57,6 +58,8 @@ pub struct WlOutputGlobal {
pub output_id: Rc<OutputId>, pub output_id: Rc<OutputId>,
pub mode: Cell<backend::Mode>, pub mode: Cell<backend::Mode>,
pub modes: Vec<backend::Mode>, pub modes: Vec<backend::Mode>,
pub formats: CloneCell<Rc<Vec<&'static Format>>>,
pub format: Cell<&'static Format>,
pub width_mm: i32, pub width_mm: i32,
pub height_mm: i32, pub height_mm: i32,
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>, pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
@ -96,14 +99,30 @@ pub struct PersistentOutputState {
pub tearing_mode: Cell<&'static TearingMode>, pub tearing_mode: Cell<&'static TearingMode>,
} }
#[derive(Eq, PartialEq, Hash)] #[derive(Eq, PartialEq, Hash, Debug)]
pub struct OutputId { pub struct OutputId {
pub connector: String, pub connector: Option<String>,
pub manufacturer: String, pub manufacturer: String,
pub model: String, pub model: String,
pub serial_number: String, pub serial_number: String,
} }
impl OutputId {
pub fn new(
connector: String,
manufacturer: String,
model: String,
serial_number: String,
) -> Self {
Self {
connector: serial_number.is_empty().then_some(connector),
manufacturer,
model,
serial_number,
}
}
}
impl WlOutputGlobal { impl WlOutputGlobal {
pub fn clear(&self) { pub fn clear(&self) {
self.opt.clear(); self.opt.clear();
@ -136,6 +155,8 @@ impl WlOutputGlobal {
output_id: output_id.clone(), output_id: output_id.clone(),
mode: Cell::new(*mode), mode: Cell::new(*mode),
modes, modes,
formats: CloneCell::new(Rc::new(vec![])),
format: Cell::new(XRGB8888),
width_mm, width_mm,
height_mm, height_mm,
bindings: Default::default(), bindings: Default::default(),

View file

@ -11,8 +11,10 @@ use {
compositor::TestFuture, compositor::TestFuture,
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
format::XRGB8888,
gfx_api::GfxError, gfx_api::GfxError,
gfx_apis::create_vulkan_allocator, gfx_apis::create_vulkan_allocator,
ifs::wl_output::OutputId,
it::{ it::{
test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH, test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH,
}, },
@ -115,9 +117,12 @@ impl TestBackend {
}; };
let default_monitor_info = MonitorInfo { let default_monitor_info = MonitorInfo {
modes: vec![mode], modes: vec![mode],
manufacturer: "jay".to_string(), output_id: Rc::new(OutputId {
product: "TestConnector".to_string(), connector: None,
serial_number: default_connector.id.to_string(), manufacturer: "jay".to_string(),
model: "TestConnector".to_string(),
serial_number: default_connector.id.to_string(),
}),
initial_mode: mode, initial_mode: mode,
width_mm: 80, width_mm: 80,
height_mm: 60, height_mm: 60,

View file

@ -1,6 +1,7 @@
use { use {
crate::{ crate::{
backend::{BackendEvent, ConnectorEvent, ConnectorKernelId, Mode, MonitorInfo}, backend::{BackendEvent, ConnectorEvent, ConnectorKernelId, Mode, MonitorInfo},
ifs::wl_output::OutputId,
it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun}, it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun},
video::drm::ConnectorType, video::drm::ConnectorType,
}, },
@ -32,9 +33,12 @@ async fn test(run: Rc<TestRun>) -> TestResult {
}); });
let new_monitor_info = MonitorInfo { let new_monitor_info = MonitorInfo {
modes: vec![], modes: vec![],
manufacturer: "jay".to_string(), output_id: Rc::new(OutputId {
product: "jay second connector".to_string(), connector: None,
serial_number: "".to_string(), manufacturer: "jay".to_string(),
model: "jay second connector".to_string(),
serial_number: "".to_string(),
}),
initial_mode: Mode { initial_mode: Mode {
width: 400, width: 400,
height: 400, height: 400,

View file

@ -2,7 +2,7 @@ use {
crate::{ crate::{
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo}, backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
globals::GlobalName, globals::GlobalName,
ifs::wl_output::{OutputId, PersistentOutputState, WlOutputGlobal}, ifs::wl_output::{PersistentOutputState, WlOutputGlobal},
output_schedule::OutputSchedule, output_schedule::OutputSchedule,
state::{ConnectorData, OutputData, State}, state::{ConnectorData, OutputData, State},
tree::{move_ws_to_output, OutputNode, OutputRenderData, WsMoveConfig}, tree::{move_ws_to_output, OutputNode, OutputRenderData, WsMoveConfig},
@ -86,27 +86,17 @@ impl ConnectorHandler {
log::info!("Connector {} connected", self.data.connector.kernel_id()); log::info!("Connector {} connected", self.data.connector.kernel_id());
self.data.connected.set(true); self.data.connected.set(true);
let name = self.state.globals.name(); let name = self.state.globals.name();
let output_id = Rc::new(OutputId {
connector: self.data.name.clone(),
manufacturer: info.manufacturer.clone(),
model: info.product.clone(),
serial_number: info.serial_number.clone(),
});
if info.non_desktop { if info.non_desktop {
self.handle_non_desktop_connected(info).await; self.handle_non_desktop_connected(info).await;
} else { } else {
self.handle_desktop_connected(info, name, output_id).await; self.handle_desktop_connected(info, name).await;
} }
self.data.connected.set(false); self.data.connected.set(false);
log::info!("Connector {} disconnected", self.data.connector.kernel_id()); log::info!("Connector {} disconnected", self.data.connector.kernel_id());
} }
async fn handle_desktop_connected( async fn handle_desktop_connected(&self, info: MonitorInfo, name: GlobalName) {
&self, let output_id = info.output_id.clone();
info: MonitorInfo,
name: GlobalName,
output_id: Rc<OutputId>,
) {
let desired_state = match self.state.persistent_output_states.get(&output_id) { let desired_state = match self.state.persistent_output_states.get(&output_id) {
Some(ds) => ds, Some(ds) => ds,
_ => { _ => {
@ -260,6 +250,10 @@ impl ConnectorHandler {
ConnectorEvent::VrrChanged(enabled) => { ConnectorEvent::VrrChanged(enabled) => {
on.schedule.set_vrr_enabled(enabled); on.schedule.set_vrr_enabled(enabled);
} }
ConnectorEvent::FormatsChanged(formats, format) => {
on.global.formats.set(formats);
on.global.format.set(format);
}
ev => unreachable!("received unexpected event {:?}", ev), ev => unreachable!("received unexpected event {:?}", ev),
} }
} }

View file

@ -330,7 +330,7 @@ impl ToolClient {
self_id: s.registry, self_id: s.registry,
name: s.jay_compositor.0, name: s.jay_compositor.0,
interface: JayCompositor.name(), interface: JayCompositor.name(),
version: s.jay_compositor.1.min(6), version: s.jay_compositor.1.min(8),
id: id.into(), id: id.into(),
}); });
self.jay_compositor.set(Some(id)); self.jay_compositor.set(Some(id));

View file

@ -22,7 +22,7 @@ use {
logging::LogLevel, logging::LogLevel,
status::MessageFormat, status::MessageFormat,
theme::Color, theme::Color,
video::{GfxApi, TearingMode, Transform, VrrMode}, video::{Format, GfxApi, TearingMode, Transform, VrrMode},
Axis, Direction, Workspace, Axis, Direction, Workspace,
}, },
std::{ std::{
@ -208,6 +208,7 @@ pub struct Output {
pub mode: Option<Mode>, pub mode: Option<Mode>,
pub vrr: Option<Vrr>, pub vrr: Option<Vrr>,
pub tearing: Option<Tearing>, pub tearing: Option<Tearing>,
pub format: Option<Format>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -15,6 +15,7 @@ mod drm_device;
mod drm_device_match; mod drm_device_match;
mod env; mod env;
pub mod exec; pub mod exec;
mod format;
mod gfx_api; mod gfx_api;
mod idle; mod idle;
mod input; mod input;

View file

@ -0,0 +1,59 @@
use {
crate::{
config::parser::{DataType, ParseResult, Parser, UnexpectedDataType},
toml::toml_span::{Span, SpannedExt},
},
jay_config::video::Format,
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum FormatParserError {
#[error(transparent)]
Expected(#[from] UnexpectedDataType),
#[error("Unknown format {0}")]
UnknownFormat(String),
}
pub struct FormatParser;
impl Parser for FormatParser {
type Value = Format;
type Error = FormatParserError;
const EXPECTED: &'static [DataType] = &[DataType::String];
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
let format = match string {
"argb8888" => Format::ARGB8888,
"xrgb8888" => Format::XRGB8888,
"abgr8888" => Format::ABGR8888,
"xbgr8888" => Format::XBGR8888,
"r8" => Format::R8,
"gr88" => Format::GR88,
"rgb888" => Format::RGB888,
"bgr888" => Format::BGR888,
"rgba4444" => Format::RGBA4444,
"rgbx4444" => Format::RGBX4444,
"bgra4444" => Format::BGRA4444,
"bgrx4444" => Format::BGRX4444,
"rgb565" => Format::RGB565,
"bgr565" => Format::BGR565,
"rgba5551" => Format::RGBA5551,
"rgbx5551" => Format::RGBX5551,
"bgra5551" => Format::BGRA5551,
"bgrx5551" => Format::BGRX5551,
"argb1555" => Format::ARGB1555,
"xrgb1555" => Format::XRGB1555,
"argb2101010" => Format::ARGB2101010,
"xrgb2101010" => Format::XRGB2101010,
"abgr2101010" => Format::ABGR2101010,
"xbgr2101010" => Format::XBGR2101010,
"abgr16161616" => Format::ABGR16161616,
"xbgr16161616" => Format::XBGR16161616,
"abgr16161616f" => Format::ABGR16161616F,
"xbgr16161616f" => Format::XBGR16161616F,
_ => return Err(FormatParserError::UnknownFormat(string.to_string()).spanned(span)),
};
Ok(format)
}
}

View file

@ -5,6 +5,7 @@ use {
extractor::{fltorint, opt, recover, s32, str, val, Extractor, ExtractorError}, extractor::{fltorint, opt, recover, s32, str, val, Extractor, ExtractorError},
parser::{DataType, ParseResult, Parser, UnexpectedDataType}, parser::{DataType, ParseResult, Parser, UnexpectedDataType},
parsers::{ parsers::{
format::FormatParser,
mode::ModeParser, mode::ModeParser,
output_match::{OutputMatchParser, OutputMatchParserError}, output_match::{OutputMatchParser, OutputMatchParserError},
tearing::TearingParser, tearing::TearingParser,
@ -48,8 +49,8 @@ impl<'a> Parser for OutputParser<'a> {
table: &IndexMap<Spanned<String>, Spanned<Value>>, table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> { ) -> ParseResult<Self> {
let mut ext = Extractor::new(self.cx, span, table); let mut ext = Extractor::new(self.cx, span, table);
let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val) = let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val, format_val) = ext
ext.extract(( .extract((
opt(str("name")), opt(str("name")),
val("match"), val("match"),
recover(opt(s32("x"))), recover(opt(s32("x"))),
@ -59,6 +60,7 @@ impl<'a> Parser for OutputParser<'a> {
opt(val("mode")), opt(val("mode")),
opt(val("vrr")), opt(val("vrr")),
opt(val("tearing")), opt(val("tearing")),
opt(val("format")),
))?; ))?;
let transform = match transform { let transform = match transform {
None => None, None => None,
@ -119,6 +121,18 @@ impl<'a> Parser for OutputParser<'a> {
} }
} }
} }
let mut format = None;
if let Some(value) = format_val {
match value.parse(&mut FormatParser) {
Ok(v) => format = Some(v),
Err(e) => {
log::warn!(
"Could not parse framebuffer format setting: {}",
self.cx.error(e)
);
}
}
}
Ok(Output { Ok(Output {
name: name.despan().map(|v| v.to_string()), name: name.despan().map(|v| v.to_string()),
match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?, match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?,
@ -129,6 +143,7 @@ impl<'a> Parser for OutputParser<'a> {
mode, mode,
vrr, vrr,
tearing, tearing,
format,
}) })
} }
} }

View file

@ -573,6 +573,9 @@ impl Output {
c.set_tearing_mode(mode); c.set_tearing_mode(mode);
} }
} }
if let Some(format) = self.format {
c.set_format(format);
}
} }
} }

View file

@ -757,6 +757,40 @@
} }
] ]
}, },
"Format": {
"type": "string",
"description": "A graphics format.\n\nThese formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
"enum": [
"argb8888",
"xrgb8888",
"abgr8888",
"xbgr8888",
"r8",
"gr88",
"rgb888",
"bgr888",
"rgba4444",
"rgbx4444",
"bgra4444",
"bgrx4444",
"rgb565",
"bgr565",
"rgba5551",
"rgbx5551",
"bgra5551",
"bgrx5551",
"argb1555",
"xrgb1555",
"argb2101010",
"xrgb2101010",
"abgr2101010",
"xbgr2101010",
"abgr16161616",
"xbgr16161616",
"abgr16161616f",
"xbgr16161616f"
]
},
"GfxApi": { "GfxApi": {
"type": "string", "type": "string",
"description": "A graphics API used for rendering.", "description": "A graphics API used for rendering.",
@ -1066,6 +1100,10 @@
"tearing": { "tearing": {
"description": "Configures the tearing settings of this output.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n tearing.mode = \"never\"\n ```\n", "description": "Configures the tearing settings of this output.\n\nBy default, the tearing mode is `variant3`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n tearing.mode = \"never\"\n ```\n",
"$ref": "#/$defs/Tearing" "$ref": "#/$defs/Tearing"
},
"format": {
"description": "Configures the framebuffer format of this output.\n\nBy default, the format is `xrgb8888`.\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
"$ref": "#/$defs/Format"
} }
}, },
"required": [ "required": [

View file

@ -1507,6 +1507,111 @@ The table has the following fields:
The value of this field should be a boolean. The value of this field should be a boolean.
<a name="types-Format"></a>
### `Format`
A graphics format.
These formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
Values of this type should be strings.
The string should have one of the following values:
- `argb8888`:
- `xrgb8888`:
- `abgr8888`:
- `xbgr8888`:
- `r8`:
- `gr88`:
- `rgb888`:
- `bgr888`:
- `rgba4444`:
- `rgbx4444`:
- `bgra4444`:
- `bgrx4444`:
- `rgb565`:
- `bgr565`:
- `rgba5551`:
- `rgbx5551`:
- `bgra5551`:
- `bgrx5551`:
- `argb1555`:
- `xrgb1555`:
- `argb2101010`:
- `xrgb2101010`:
- `abgr2101010`:
- `xbgr2101010`:
- `abgr16161616`:
- `xbgr16161616`:
- `abgr16161616f`:
- `xbgr16161616f`:
<a name="types-GfxApi"></a> <a name="types-GfxApi"></a>
### `GfxApi` ### `GfxApi`
@ -2283,6 +2388,22 @@ The table has the following fields:
The value of this field should be a [Tearing](#types-Tearing). The value of this field should be a [Tearing](#types-Tearing).
- `format` (optional):
Configures the framebuffer format of this output.
By default, the format is `xrgb8888`.
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
The value of this field should be a [Format](#types-Format).
<a name="types-OutputMatch"></a> <a name="types-OutputMatch"></a>
### `OutputMatch` ### `OutputMatch`

View file

@ -1606,6 +1606,21 @@ Output:
match.serial-number = "33K03894SL0" match.serial-number = "33K03894SL0"
tearing.mode = "never" tearing.mode = "never"
``` ```
format:
ref: Format
required: false
description: |
Configures the framebuffer format of this output.
By default, the format is `xrgb8888`.
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
Transform: Transform:
@ -2490,3 +2505,76 @@ Libei:
Even if the socket is disabled, application can still request access via the portal. Even if the socket is disabled, application can still request access via the portal.
The default is `false`. The default is `false`.
Format:
description: |
A graphics format.
These formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h
- Example:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
kind: string
values:
- value: argb8888
description: ""
- value: xrgb8888
description: ""
- value: abgr8888
description: ""
- value: xbgr8888
description: ""
- value: r8
description: ""
- value: gr88
description: ""
- value: rgb888
description: ""
- value: bgr888
description: ""
- value: rgba4444
description: ""
- value: rgbx4444
description: ""
- value: bgra4444
description: ""
- value: bgrx4444
description: ""
- value: rgb565
description: ""
- value: bgr565
description: ""
- value: rgba5551
description: ""
- value: rgbx5551
description: ""
- value: bgra5551
description: ""
- value: bgrx5551
description: ""
- value: argb1555
description: ""
- value: xrgb1555
description: ""
- value: argb2101010
description: ""
- value: xrgb2101010
description: ""
- value: abgr2101010
description: ""
- value: xbgr2101010
description: ""
- value: abgr16161616
description: ""
- value: xbgr16161616
description: ""
- value: abgr16161616f
description: ""
- value: xbgr16161616f
description: ""

View file

@ -70,6 +70,11 @@ request set_tearing_mode (since = 3) {
mode: u32, mode: u32,
} }
request set_fb_format (since = 8) {
output: str,
format: str,
}
# events # events
event global { event global {
@ -141,3 +146,8 @@ event vrr_cursor_hz (since = 2) {
event tearing_state (since = 3) { event tearing_state (since = 3) {
mode: u32, mode: u32,
} }
event fb_format (since = 8) {
name: str,
current: u32,
}