metal: allow configuring framebuffer formats
This commit is contained in:
parent
9bab4f7ce1
commit
b4ca15fec0
24 changed files with 713 additions and 58 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
13
src/cli.rs
13
src/cli.rs
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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] = &[
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
7
|
||||
8
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue