1
0
Fork 0
forked from wry/wry

metal: allow configuring framebuffer formats

This commit is contained in:
Julian Orth 2024-09-04 19:56:01 +02:00
parent 9bab4f7ce1
commit b4ca15fec0
24 changed files with 713 additions and 58 deletions

View file

@ -25,7 +25,7 @@ use {
timer::Timer,
video::{
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,
},
@ -754,6 +754,10 @@ impl Client {
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 {
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
get_response!(res, 1.0, ConnectorGetScale { scale });

View file

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

View file

@ -267,6 +267,11 @@ impl Connector {
pub fn set_tearing_mode(self, mode: TearingMode) {
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.
@ -612,3 +617,38 @@ impl TearingMode {
pub fn set_tearing_mode(mode: TearingMode) {
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,6 +3,7 @@ use {
async_engine::SpawnedFuture,
drm_feedback::DrmFeedback,
fixed::Fixed,
format::Format,
gfx_api::{GfxFramebuffer, SyncFile},
ifs::{
wl_output::OutputId,
@ -116,6 +117,9 @@ pub trait Connector {
fn set_tearing_enabled(&self, enabled: bool) {
let _ = enabled;
}
fn set_fb_format(&self, format: &'static Format) {
let _ = format;
}
}
#[derive(Debug)]
@ -128,6 +132,7 @@ pub enum ConnectorEvent {
Unavailable,
Available,
VrrChanged(bool),
FormatsChanged(Rc<Vec<&'static Format>>, &'static Format),
}
pub trait HardwareCursor: Debug {

View file

@ -301,6 +301,7 @@ pub struct MetalDrmDeviceData {
pub struct PersistentDisplayData {
pub mode: RefCell<Option<DrmModeInfo>>,
pub vrr_requested: Cell<bool>,
pub format: Cell<&'static Format>,
}
#[derive(Debug)]
@ -415,6 +416,7 @@ pub struct MetalConnector {
pub connector_id: ConnectorId,
pub buffer_format: Cell<&'static Format>,
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub next_buffer: NumCell<usize>,
@ -460,6 +462,7 @@ pub struct MetalConnector {
pub direct_scanout_active: Cell<bool>,
pub tearing_requested: Cell<bool>,
pub try_switch_format: Cell<bool>,
}
impl Debug for MetalConnector {
@ -641,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>) {
match self.frontend_state.get() {
FrontState::Removed
@ -1264,6 +1286,17 @@ impl MetalConnector {
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:?}");
}
},
}
}
}
@ -1425,6 +1458,24 @@ impl Connector for MetalConnector {
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 {
@ -1544,6 +1595,7 @@ fn create_connector(
dev: dev.clone(),
backend: backend.clone(),
connector_id: backend.state.connector_ids.next(),
buffer_format: Cell::new(XRGB8888),
buffers: Default::default(),
next_buffer: Default::default(),
enabled: Cell::new(true),
@ -1576,6 +1628,7 @@ fn create_connector(
direct_scanout_active: Cell::new(false),
next_flip_nsec: Cell::new(0),
tearing_requested: Cell::new(false),
try_switch_format: Cell::new(false),
});
let futures = ConnectorFutures {
_present: backend
@ -1686,6 +1739,7 @@ fn create_connector_display_data(
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
@ -2043,6 +2097,7 @@ impl MetalBackend {
};
let mut old = c.display.borrow_mut();
mem::swap(old.deref_mut(), &mut dd);
let mut preserve_connector = false;
match c.frontend_state.get() {
FrontState::Removed | FrontState::Disconnected => {}
FrontState::Connected { .. } | FrontState::Unavailable => {
@ -2074,10 +2129,16 @@ impl MetalBackend {
}
c.send_event(ConnectorEvent::Disconnected);
} 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 {
let (connector, future) = match create_connector(self, c, &dev.dev) {
@ -2131,6 +2192,7 @@ impl MetalBackend {
}));
connector.send_hardware_cursor();
connector.send_vrr_enabled();
connector.send_formats();
}
pub fn create_drm_device(
@ -2662,6 +2724,7 @@ impl MetalBackend {
connector.send_hardware_cursor();
connector.send_vrr_enabled();
connector.update_drm_feedback();
connector.send_formats();
}
Ok(())
}
@ -2954,7 +3017,7 @@ impl MetalBackend {
ctx: &MetalRenderContext,
old_buffers: &mut Vec<Rc<dyn Any>>,
) -> Result<(), MetalError> {
let dd = connector.display.borrow_mut();
let dd = &mut *connector.display.borrow_mut();
let crtc = match connector.crtc.get() {
Some(c) => c,
_ => return Ok(()),
@ -2966,26 +3029,55 @@ impl MetalBackend {
return Ok(());
}
};
let (primary_plane, primary_modifiers) = 'primary_plane: {
for plane in crtc.possible_planes.values() {
if plane.ty == PlaneType::Primary && !plane.assigned.get() && plane.lease.is_none()
{
if let Some(format) = plane.formats.get(&XRGB8888.drm) {
break 'primary_plane (plane.clone(), &format.modifiers);
let allocate_primary_plane = |format: &'static Format| {
let (primary_plane, primary_modifiers) = 'primary_plane: {
for plane in crtc.possible_planes.values() {
if plane.ty == PlaneType::Primary
&& !plane.assigned.get()
&& 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);
};
let buffers = Rc::new(self.create_scanout_buffers(
&connector.dev,
XRGB8888,
primary_modifiers,
mode.hdisplay as _,
mode.vdisplay as _,
ctx,
false,
)?);
(primary_plane, buffers) = allocate_primary_plane(XRGB8888)?;
buffer_format = XRGB8888;
}
let mut cursor_plane = None;
let mut cursor_modifiers = &IndexSet::new();
for plane in crtc.possible_planes.values() {
@ -3060,6 +3152,8 @@ impl MetalBackend {
}
connector.cursor_plane.set(cursor_plane);
connector.cursor_enabled.set(false);
connector.buffer_format.set(buffer_format);
connector.try_switch_format.set(false);
Ok(())
}

View file

@ -17,10 +17,11 @@ use {
crate::{
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
compositor::start_compositor,
format::{ref_formats, Format},
portal,
},
::log::Level,
clap::{Args, Parser, Subcommand, ValueEnum},
clap::{builder::PossibleValue, Args, Parser, Subcommand, ValueEnum},
clap_complete::Shell,
};
@ -231,6 +232,16 @@ pub struct GenerateArgs {
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() {
let cli = Jay::parse();
match cli.command {

View file

@ -1,6 +1,7 @@
use {
crate::{
cli::GlobalArgs,
format::{Format, XRGB8888},
scale::Scale,
tools::tool_client::{with_tool_client, Handle, ToolClient},
utils::{errorfmt::ErrorFmt, transform_ext::TransformExt},
@ -44,6 +45,9 @@ pub struct ShowArgs {
/// Show all available modes.
#[arg(long)]
pub modes: bool,
/// Show all available formats.
#[arg(long)]
pub formats: bool,
}
#[derive(Args, Debug)]
@ -122,6 +126,8 @@ pub enum OutputCommand {
Vrr(VrrArgs),
/// Change tearing settings.
Tearing(TearingArgs),
/// Change format settings.
Format(FormatSettings),
}
#[derive(ValueEnum, Debug, Clone)]
@ -177,6 +183,21 @@ pub struct CursorHzArgs {
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)]
pub struct TearingArgs {
#[clap(subcommand)]
@ -318,6 +339,8 @@ struct Output {
pub vrr_mode: VrrMode,
pub vrr_cursor_hz: Option<f64>,
pub tearing_mode: TearingMode,
pub formats: Vec<String>,
pub format: Option<String>,
}
#[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;
}
@ -609,7 +646,7 @@ impl Randr {
.collect();
connectors.sort_by_key(|c| &c.name);
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);
println!("unbound 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);
let Some(o) = &connector.output else {
if !connector.enabled {
@ -701,6 +738,11 @@ impl Randr {
print!(" mode: ");
self.print_mode(mode, false);
}
if let Some(format) = &o.format {
if format != XRGB8888.name {
println!(" format: {format}");
}
}
if o.scale != 1.0 {
println!(" scale: {}", o.scale);
}
@ -724,6 +766,12 @@ impl Randr {
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) {
@ -788,6 +836,8 @@ impl Randr {
vrr_mode: VrrMode::NEVER,
vrr_cursor_hz: None,
tearing_mode: TearingMode::NEVER,
formats: vec![],
format: None,
});
});
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
@ -813,6 +863,8 @@ impl Randr {
vrr_mode: VrrMode::NEVER,
vrr_cursor_hz: None,
tearing_mode: TearingMode::NEVER,
formats: vec![],
format: None,
});
});
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
@ -835,6 +887,15 @@ impl Randr {
let output = c.output.as_mut().unwrap();
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| {
let mut data = data.borrow_mut();
let c = data.connectors.last_mut().unwrap();

View file

@ -7,6 +7,7 @@ use {
},
compositor::MAX_EXTENTS,
config::ConfigProxy,
format::config_formats,
ifs::wl_seat::{SeatId, WlSeatGlobal},
io_uring::TaskResultExt,
output_schedule::map_cursor_hz,
@ -49,8 +50,8 @@ use {
theme::{colors::Colorable, sized::Resizable},
timer::Timer as JayTimer,
video::{
Connector, DrmDevice, GfxApi, TearingMode as ConfigTearingMode, Transform,
VrrMode as ConfigVrrMode,
Connector, DrmDevice, Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode,
Transform, VrrMode as ConfigVrrMode,
},
Axis, Direction, Workspace,
},
@ -1051,6 +1052,19 @@ impl ConfigProxyHandler {
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(
&self,
connector: Option<Connector>,
@ -1919,6 +1933,9 @@ impl ConfigProxyHandler {
ClientMessage::SetEiSocketEnabled { enabled } => {
self.handle_set_ei_socket_enabled(enabled)
}
ClientMessage::ConnectorSetFormat { connector, format } => self
.handle_connector_set_format(connector, format)
.wrn("connector_set_format")?,
}
Ok(())
}
@ -1986,6 +2003,8 @@ enum CphError {
InvalidCursorHz(f64),
#[error("Unknown tearing mode {0:?}")]
UnknownTearingMode(ConfigTearingMode),
#[error("The format {0:?} is unknown")]
UnknownFormat(ConfigFormat),
}
trait WithRequestName {

View file

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

View file

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

View file

@ -3,6 +3,7 @@ use {
backend,
client::{Client, ClientError},
compositor::MAX_EXTENTS,
format::named_formats,
leaks::Tracker,
object::{Object, Version},
scale::Scale,
@ -27,6 +28,7 @@ pub struct JayRandr {
const VRR_CAPABLE_SINCE: Version = Version(2);
const TEARING_SINCE: Version = Version(3);
const FORMAT_SINCE: Version = Version(8);
impl JayRandr {
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
@ -125,6 +127,23 @@ impl JayRandr {
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();
for mode in &global.modes {
self.client.event(Mode {
@ -365,6 +384,17 @@ impl JayRandrRequestHandler for JayRandr {
c.update_presentation_type();
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! {
@ -384,5 +414,7 @@ pub enum JayRandrError {
UnknownVrrMode(u32),
#[error("Unknown tearing mode {0}")]
UnknownTearingMode(u32),
#[error("Unknown format {0}")]
UnknownFormat(String),
}
efrom!(JayRandrError, ClientError);

View file

@ -4,6 +4,7 @@ use {
crate::{
backend,
client::{Client, ClientError, ClientId},
format::{Format, XRGB8888},
globals::{Global, GlobalName},
ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1},
leaks::Tracker,
@ -57,6 +58,8 @@ pub struct WlOutputGlobal {
pub output_id: Rc<OutputId>,
pub mode: Cell<backend::Mode>,
pub modes: Vec<backend::Mode>,
pub formats: CloneCell<Rc<Vec<&'static Format>>>,
pub format: Cell<&'static Format>,
pub width_mm: i32,
pub height_mm: i32,
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
@ -152,6 +155,8 @@ impl WlOutputGlobal {
output_id: output_id.clone(),
mode: Cell::new(*mode),
modes,
formats: CloneCell::new(Rc::new(vec![])),
format: Cell::new(XRGB8888),
width_mm,
height_mm,
bindings: Default::default(),

View file

@ -11,6 +11,7 @@ use {
compositor::TestFuture,
drm_feedback::DrmFeedback,
fixed::Fixed,
format::XRGB8888,
gfx_api::GfxError,
gfx_apis::create_vulkan_allocator,
ifs::wl_output::OutputId,

View file

@ -250,6 +250,10 @@ impl ConnectorHandler {
ConnectorEvent::VrrChanged(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),
}
}

View file

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

View file

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

View file

@ -15,6 +15,7 @@ mod drm_device;
mod drm_device_match;
mod env;
pub mod exec;
mod format;
mod gfx_api;
mod idle;
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},
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
parsers::{
format::FormatParser,
mode::ModeParser,
output_match::{OutputMatchParser, OutputMatchParserError},
tearing::TearingParser,
@ -48,8 +49,8 @@ impl<'a> Parser for OutputParser<'a> {
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let mut ext = Extractor::new(self.cx, span, table);
let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val) =
ext.extract((
let (name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val, format_val) = ext
.extract((
opt(str("name")),
val("match"),
recover(opt(s32("x"))),
@ -59,6 +60,7 @@ impl<'a> Parser for OutputParser<'a> {
opt(val("mode")),
opt(val("vrr")),
opt(val("tearing")),
opt(val("format")),
))?;
let transform = match transform {
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 {
name: name.despan().map(|v| v.to_string()),
match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?,
@ -129,6 +143,7 @@ impl<'a> Parser for OutputParser<'a> {
mode,
vrr,
tearing,
format,
})
}
}

View file

@ -573,6 +573,9 @@ impl Output {
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": {
"type": "string",
"description": "A graphics API used for rendering.",
@ -1066,6 +1100,10 @@
"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",
"$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": [

View file

@ -1507,6 +1507,111 @@ The table has the following fields:
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>
### `GfxApi`
@ -2283,6 +2388,22 @@ The table has the following fields:
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>
### `OutputMatch`

View file

@ -1606,6 +1606,21 @@ Output:
match.serial-number = "33K03894SL0"
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:
@ -2490,3 +2505,76 @@ Libei:
Even if the socket is disabled, application can still request access via the portal.
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,
}
request set_fb_format (since = 8) {
output: str,
format: str,
}
# events
event global {
@ -141,3 +146,8 @@ event vrr_cursor_hz (since = 2) {
event tearing_state (since = 3) {
mode: u32,
}
event fb_format (since = 8) {
name: str,
current: u32,
}