Merge pull request #253 from mahkoh/jorth/output-formats
metal: allow configuring framebuffer formats
This commit is contained in:
commit
588fce4832
30 changed files with 980 additions and 232 deletions
|
|
@ -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 });
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
13
src/cli.rs
13
src/cli.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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] = &[
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
59
toml-config/src/config/parsers/format.rs
Normal file
59
toml-config/src/config/parsers/format.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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": [
|
||||||
|
|
|
||||||
|
|
@ -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`
|
||||||
|
|
|
||||||
|
|
@ -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: ""
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue