metal: handle gpu reset
Unfortunately this doesn't seem to work on amdgpu [1]. I've tested that it works on i915. [1] https://gitlab.freedesktop.org/drm/amd/-/issues/1749
This commit is contained in:
parent
4584dee160
commit
d2913449ea
21 changed files with 377 additions and 120 deletions
|
|
@ -79,6 +79,7 @@ fn write_egl_procs<W: Write>(f: &mut W) -> anyhow::Result<()> {
|
||||||
"()",
|
"()",
|
||||||
&[("target", "GLenum"), ("image", "GLeglImageOES")][..],
|
&[("target", "GLenum"), ("image", "GLeglImageOES")][..],
|
||||||
),
|
),
|
||||||
|
("glGetGraphicsResetStatusKHR", "GLenum", &[][..]),
|
||||||
];
|
];
|
||||||
|
|
||||||
writeln!(f, "use std::ptr;")?;
|
writeln!(f, "use std::ptr;")?;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use {
|
||||||
Backend, BackendEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
Backend, BackendEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
|
||||||
InputDeviceId, InputEvent, KeyState, TransformMatrix,
|
InputDeviceId, InputEvent, KeyState, TransformMatrix,
|
||||||
},
|
},
|
||||||
backends::metal::video::{MetalDrmDevice, PendingDrmDevice},
|
backends::metal::video::{MetalDrmDevice, MetalRenderContext, PendingDrmDevice},
|
||||||
dbus::{DbusError, SignalHandler},
|
dbus::{DbusError, SignalHandler},
|
||||||
libinput::{
|
libinput::{
|
||||||
consts::{
|
consts::{
|
||||||
|
|
@ -44,6 +44,7 @@ use {
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
error::Error,
|
error::Error,
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
future::pending,
|
future::pending,
|
||||||
mem,
|
mem,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
|
@ -82,7 +83,7 @@ pub enum MetalError {
|
||||||
NoModeForConnector,
|
NoModeForConnector,
|
||||||
#[error("Could not allocate scanout buffer")]
|
#[error("Could not allocate scanout buffer")]
|
||||||
ScanoutBuffer(#[source] GbmError),
|
ScanoutBuffer(#[source] GbmError),
|
||||||
#[error("Could not create a framebuffer")]
|
#[error("addfb2 failed")]
|
||||||
Framebuffer(#[source] DrmError),
|
Framebuffer(#[source] DrmError),
|
||||||
#[error("Could not import a framebuffer into EGL")]
|
#[error("Could not import a framebuffer into EGL")]
|
||||||
ImportFb(#[source] RenderError),
|
ImportFb(#[source] RenderError),
|
||||||
|
|
@ -124,6 +125,13 @@ pub struct MetalBackend {
|
||||||
drm_ids: DrmIds,
|
drm_ids: DrmIds,
|
||||||
pause_handler: Cell<Option<SignalHandler>>,
|
pause_handler: Cell<Option<SignalHandler>>,
|
||||||
resume_handler: Cell<Option<SignalHandler>>,
|
resume_handler: Cell<Option<SignalHandler>>,
|
||||||
|
ctx: CloneCell<Option<Rc<MetalRenderContext>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for MetalBackend {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("MetalBackend").finish_non_exhaustive()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetalBackend {
|
impl MetalBackend {
|
||||||
|
|
@ -250,6 +258,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
|
||||||
drm_ids: Default::default(),
|
drm_ids: Default::default(),
|
||||||
pause_handler: Default::default(),
|
pause_handler: Default::default(),
|
||||||
resume_handler: Default::default(),
|
resume_handler: Default::default(),
|
||||||
|
ctx: Default::default(),
|
||||||
});
|
});
|
||||||
metal.pause_handler.set(Some({
|
metal.pause_handler.set(Some({
|
||||||
let mtl = metal.clone();
|
let mtl = metal.clone();
|
||||||
|
|
|
||||||
|
|
@ -235,8 +235,16 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_drm_change(self: &Rc<Self>, _dev: UdevDevice) -> Option<()> {
|
fn handle_drm_change(self: &Rc<Self>, dev: UdevDevice) -> Option<()> {
|
||||||
// TODO: Handle monitor connections and connector hotplug
|
let dev = match self.device_holder.drm_devices.get(&dev.devnum()) {
|
||||||
|
Some(dev) => dev,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
for connector in dev.connectors.values() {
|
||||||
|
connector.can_present.set(true);
|
||||||
|
connector.has_damage.set(true);
|
||||||
|
connector.schedule_present();
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use {
|
||||||
DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK,
|
DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK,
|
||||||
DRM_MODE_PAGE_FLIP_EVENT,
|
DRM_MODE_PAGE_FLIP_EVENT,
|
||||||
},
|
},
|
||||||
gbm::{GbmDevice, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
|
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
|
||||||
ModifiedFormat, INVALID_MODIFIER,
|
ModifiedFormat, INVALID_MODIFIER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -37,6 +37,7 @@ use {
|
||||||
},
|
},
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
|
use crate::render::ResetStatus;
|
||||||
|
|
||||||
pub struct PendingDrmDevice {
|
pub struct PendingDrmDevice {
|
||||||
pub id: DrmId,
|
pub id: DrmId,
|
||||||
|
|
@ -44,6 +45,11 @@ pub struct PendingDrmDevice {
|
||||||
pub devnode: CString,
|
pub devnode: CString,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MetalRenderContext {
|
||||||
|
pub dev: Rc<MetalDrmDeviceStatic>,
|
||||||
|
pub egl: Rc<RenderContext>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MetalDrmDeviceStatic {
|
pub struct MetalDrmDeviceStatic {
|
||||||
pub id: DrmId,
|
pub id: DrmId,
|
||||||
|
|
@ -58,7 +64,6 @@ pub struct MetalDrmDeviceStatic {
|
||||||
pub min_height: u32,
|
pub min_height: u32,
|
||||||
pub max_height: u32,
|
pub max_height: u32,
|
||||||
pub gbm: GbmDevice,
|
pub gbm: GbmDevice,
|
||||||
pub egl: Rc<RenderContext>,
|
|
||||||
pub async_fd: AsyncFd,
|
pub async_fd: AsyncFd,
|
||||||
pub handle_events: HandleEvents,
|
pub handle_events: HandleEvents,
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +91,9 @@ pub struct MetalConnector {
|
||||||
pub master: Rc<DrmMaster>,
|
pub master: Rc<DrmMaster>,
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
|
|
||||||
|
pub dev: Rc<MetalDrmDeviceStatic>,
|
||||||
|
pub backend: Rc<MetalBackend>,
|
||||||
|
|
||||||
pub connector_id: ConnectorId,
|
pub connector_id: ConnectorId,
|
||||||
|
|
||||||
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
|
||||||
|
|
@ -162,6 +170,9 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn present(&self) {
|
pub fn present(&self) {
|
||||||
|
if !self.backend.check_render_context() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let crtc = match self.crtc.get() {
|
let crtc = match self.crtc.get() {
|
||||||
Some(crtc) => crtc,
|
Some(crtc) => crtc,
|
||||||
_ => return,
|
_ => return,
|
||||||
|
|
@ -172,14 +183,14 @@ impl MetalConnector {
|
||||||
if !crtc.active.value.get() {
|
if !crtc.active.value.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let buffers = match self.buffers.get() {
|
|
||||||
None => return,
|
|
||||||
Some(b) => b,
|
|
||||||
};
|
|
||||||
let plane = match self.primary_plane.get() {
|
let plane = match self.primary_plane.get() {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
let buffers = match self.buffers.get() {
|
||||||
|
Some(b) => b,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()];
|
let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()];
|
||||||
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
||||||
let mut rr = self.render_result.borrow_mut();
|
let mut rr = self.render_result.borrow_mut();
|
||||||
|
|
@ -295,7 +306,7 @@ pub struct MetalPlane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_connectors(
|
fn get_connectors(
|
||||||
state: &Rc<State>,
|
backend: &Rc<MetalBackend>,
|
||||||
dev: &Rc<MetalDrmDeviceStatic>,
|
dev: &Rc<MetalDrmDeviceStatic>,
|
||||||
ids: &[DrmConnector],
|
ids: &[DrmConnector],
|
||||||
) -> Result<
|
) -> Result<
|
||||||
|
|
@ -308,7 +319,7 @@ fn get_connectors(
|
||||||
let mut connectors = AHashMap::new();
|
let mut connectors = AHashMap::new();
|
||||||
let mut futures = vec![];
|
let mut futures = vec![];
|
||||||
for connector in ids {
|
for connector in ids {
|
||||||
match create_connector(state, *connector, dev) {
|
match create_connector(backend, *connector, dev) {
|
||||||
Ok((con, fut)) => {
|
Ok((con, fut)) => {
|
||||||
connectors.insert(con.id, con);
|
connectors.insert(con.id, con);
|
||||||
futures.push(fut);
|
futures.push(fut);
|
||||||
|
|
@ -320,7 +331,7 @@ fn get_connectors(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_connector(
|
fn create_connector(
|
||||||
state: &Rc<State>,
|
backend: &Rc<MetalBackend>,
|
||||||
connector: DrmConnector,
|
connector: DrmConnector,
|
||||||
dev: &Rc<MetalDrmDeviceStatic>,
|
dev: &Rc<MetalDrmDeviceStatic>,
|
||||||
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
|
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
|
||||||
|
|
@ -412,8 +423,10 @@ fn create_connector(
|
||||||
let slf = Rc::new(MetalConnector {
|
let slf = Rc::new(MetalConnector {
|
||||||
id: connector,
|
id: connector,
|
||||||
master: dev.master.clone(),
|
master: dev.master.clone(),
|
||||||
state: state.clone(),
|
state: backend.state.clone(),
|
||||||
connector_id: state.connector_ids.next(),
|
dev: dev.clone(),
|
||||||
|
backend: backend.clone(),
|
||||||
|
connector_id: backend.state.connector_ids.next(),
|
||||||
crtcs,
|
crtcs,
|
||||||
mode: CloneCell::new(mode),
|
mode: CloneCell::new(mode),
|
||||||
refresh: Cell::new(refresh),
|
refresh: Cell::new(refresh),
|
||||||
|
|
@ -440,7 +453,10 @@ fn create_connector(
|
||||||
render_result: RefCell::new(Default::default()),
|
render_result: RefCell::new(Default::default()),
|
||||||
});
|
});
|
||||||
let futures = ConnectorFutures {
|
let futures = ConnectorFutures {
|
||||||
present: state.eng.spawn2(Phase::Present, slf.clone().present_loop()),
|
present: backend
|
||||||
|
.state
|
||||||
|
.eng
|
||||||
|
.spawn2(Phase::Present, slf.clone().present_loop()),
|
||||||
};
|
};
|
||||||
Ok((slf, futures))
|
Ok((slf, futures))
|
||||||
}
|
}
|
||||||
|
|
@ -606,6 +622,67 @@ impl<T: Copy> MutableProperty<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetalBackend {
|
impl MetalBackend {
|
||||||
|
fn check_render_context(&self) -> bool {
|
||||||
|
let ctx = match self.ctx.get() {
|
||||||
|
Some(ctx) => ctx,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
let reset = match ctx.egl.reset_status() {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
log::error!("EGL context has been reset: {:?}", reset);
|
||||||
|
if reset != ResetStatus::Innocent {
|
||||||
|
fatal!("We are not innocent. Terminating.");
|
||||||
|
}
|
||||||
|
log::info!("Trying to create a new context");
|
||||||
|
self.state.set_render_ctx(None);
|
||||||
|
let mut old_buffers = vec![];
|
||||||
|
for dev in self.device_holder.drm_devices.lock().values() {
|
||||||
|
for connector in dev.connectors.values() {
|
||||||
|
old_buffers.push(connector.buffers.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.install_render_context(&ctx.dev) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for dev in self.device_holder.drm_devices.lock().values() {
|
||||||
|
if let Err(e) = self.init_drm_device(dev) {
|
||||||
|
log::error!("Could not re-initialize device: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_render_context(&self, dev: &Rc<MetalDrmDeviceStatic>) -> bool {
|
||||||
|
let ctx = match self.create_render_context(dev) {
|
||||||
|
Ok(ctx) => ctx,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create a render context: {}", ErrorFmt(e));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.state.set_render_ctx(Some(&ctx.egl));
|
||||||
|
self.ctx.set(Some(ctx));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_render_context(
|
||||||
|
&self,
|
||||||
|
dev: &Rc<MetalDrmDeviceStatic>,
|
||||||
|
) -> Result<Rc<MetalRenderContext>, MetalError> {
|
||||||
|
let egl = match RenderContext::from_drm_device(&dev.master) {
|
||||||
|
Ok(r) => Rc::new(r),
|
||||||
|
Err(e) => return Err(MetalError::CreateRenderContex(e)),
|
||||||
|
};
|
||||||
|
let ctx = Rc::new(MetalRenderContext {
|
||||||
|
dev: dev.clone(),
|
||||||
|
egl,
|
||||||
|
});
|
||||||
|
self.ctx.set(Some(ctx.clone()));
|
||||||
|
Ok(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_drm_device(
|
pub fn create_drm_device(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
pending: PendingDrmDevice,
|
pending: PendingDrmDevice,
|
||||||
|
|
@ -650,10 +727,6 @@ impl MetalBackend {
|
||||||
Ok(g) => g,
|
Ok(g) => g,
|
||||||
Err(e) => return Err(MetalError::GbmDevice(e)),
|
Err(e) => return Err(MetalError::GbmDevice(e)),
|
||||||
};
|
};
|
||||||
let egl = match RenderContext::from_drm_device(master) {
|
|
||||||
Ok(r) => Rc::new(r),
|
|
||||||
Err(e) => return Err(MetalError::CreateRenderContex(e)),
|
|
||||||
};
|
|
||||||
let async_fd = match self.state.eng.fd(master.fd()) {
|
let async_fd = match self.state.eng.fd(master.fd()) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => return Err(MetalError::CreateAsyncFd(e)),
|
Err(e) => return Err(MetalError::CreateAsyncFd(e)),
|
||||||
|
|
@ -672,14 +745,20 @@ impl MetalBackend {
|
||||||
min_height: resources.min_height,
|
min_height: resources.min_height,
|
||||||
max_height: resources.max_height,
|
max_height: resources.max_height,
|
||||||
gbm,
|
gbm,
|
||||||
egl: egl.clone(),
|
|
||||||
async_fd,
|
async_fd,
|
||||||
handle_events: HandleEvents {
|
handle_events: HandleEvents {
|
||||||
handle_events: Cell::new(None),
|
handle_events: Cell::new(None),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let (connectors, futures) = get_connectors(&self.state, &dev, &resources.connectors)?;
|
if self.ctx.get().is_none() {
|
||||||
|
self.install_render_context(&dev);
|
||||||
|
for dev in self.device_holder.drm_devices.lock().values() {
|
||||||
|
let _ = self.init_drm_device(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (connectors, futures) = get_connectors(&self, &dev, &resources.connectors)?;
|
||||||
|
|
||||||
let slf = Rc::new(MetalDrmDevice {
|
let slf = Rc::new(MetalDrmDevice {
|
||||||
dev,
|
dev,
|
||||||
|
|
@ -730,8 +809,6 @@ impl MetalBackend {
|
||||||
.spawn(self.clone().handle_drm_events(slf.clone()));
|
.spawn(self.clone().handle_drm_events(slf.clone()));
|
||||||
slf.dev.handle_events.handle_events.set(Some(drm_handler));
|
slf.dev.handle_events.handle_events.set(Some(drm_handler));
|
||||||
|
|
||||||
self.state.set_render_ctx(&egl);
|
|
||||||
|
|
||||||
Ok(slf)
|
Ok(slf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,6 +968,10 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_drm_device(&self, dev: &Rc<MetalDrmDevice>) -> Result<(), MetalError> {
|
fn init_drm_device(&self, dev: &Rc<MetalDrmDevice>) -> Result<(), MetalError> {
|
||||||
|
let ctx = match self.ctx.get() {
|
||||||
|
Some(ctx) => ctx,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
let mut flags = 0;
|
let mut flags = 0;
|
||||||
let mut changes = dev.dev.master.change();
|
let mut changes = dev.dev.master.change();
|
||||||
if !self.can_use_current_drm_mode(dev) {
|
if !self.can_use_current_drm_mode(dev) {
|
||||||
|
|
@ -905,7 +986,7 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
self.reset_planes(dev, &mut changes);
|
self.reset_planes(dev, &mut changes);
|
||||||
for connector in dev.connectors.values() {
|
for connector in dev.connectors.values() {
|
||||||
if let Err(e) = self.assign_connector_plane(dev, connector, &mut changes) {
|
if let Err(e) = self.assign_connector_plane(connector, &mut changes, &ctx) {
|
||||||
log::error!("Could not assign a plane: {}", ErrorFmt(e));
|
log::error!("Could not assign a plane: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -996,37 +1077,38 @@ impl MetalBackend {
|
||||||
|
|
||||||
fn create_scanout_buffers(
|
fn create_scanout_buffers(
|
||||||
&self,
|
&self,
|
||||||
dev: &Rc<MetalDrmDevice>,
|
dev: &Rc<MetalDrmDeviceStatic>,
|
||||||
format: &ModifiedFormat,
|
format: &ModifiedFormat,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
|
ctx: &MetalRenderContext,
|
||||||
) -> Result<[RenderBuffer; 2], MetalError> {
|
) -> Result<[RenderBuffer; 2], MetalError> {
|
||||||
let create = || self.create_scanout_buffer(dev, format, width, height);
|
let create = || self.create_scanout_buffer(dev, format, width, height, ctx);
|
||||||
Ok([create()?, create()?])
|
Ok([create()?, create()?])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_scanout_buffer(
|
fn create_scanout_buffer(
|
||||||
&self,
|
&self,
|
||||||
dev: &Rc<MetalDrmDevice>,
|
dev: &Rc<MetalDrmDeviceStatic>,
|
||||||
format: &ModifiedFormat,
|
format: &ModifiedFormat,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
|
ctx: &MetalRenderContext,
|
||||||
) -> Result<RenderBuffer, MetalError> {
|
) -> Result<RenderBuffer, MetalError> {
|
||||||
let bo = dev.dev.gbm.create_bo(
|
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
|
||||||
width,
|
if ctx.dev.id != dev.id {
|
||||||
height,
|
usage |= GBM_BO_USE_LINEAR;
|
||||||
format,
|
};
|
||||||
GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT,
|
let bo = dev.gbm.create_bo(width, height, format, usage);
|
||||||
);
|
|
||||||
let bo = match bo {
|
let bo = match bo {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
||||||
};
|
};
|
||||||
let drm_fb = match dev.dev.master.add_fb(bo.dmabuf()) {
|
let drm_fb = match dev.master.add_fb(bo.dmabuf()) {
|
||||||
Ok(fb) => Rc::new(fb),
|
Ok(fb) => Rc::new(fb),
|
||||||
Err(e) => return Err(MetalError::Framebuffer(e)),
|
Err(e) => return Err(MetalError::Framebuffer(e)),
|
||||||
};
|
};
|
||||||
let egl_img = match dev.dev.egl.dmabuf_img(bo.dmabuf()) {
|
let egl_img = match ctx.egl.dmabuf_img(bo.dmabuf()) {
|
||||||
Ok(img) => img,
|
Ok(img) => img,
|
||||||
Err(e) => return Err(MetalError::ImportImage(e)),
|
Err(e) => return Err(MetalError::ImportImage(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -1085,9 +1167,9 @@ impl MetalBackend {
|
||||||
|
|
||||||
fn assign_connector_plane(
|
fn assign_connector_plane(
|
||||||
&self,
|
&self,
|
||||||
dev: &Rc<MetalDrmDevice>,
|
|
||||||
connector: &Rc<MetalConnector>,
|
connector: &Rc<MetalConnector>,
|
||||||
changes: &mut Change,
|
changes: &mut Change,
|
||||||
|
ctx: &MetalRenderContext,
|
||||||
) -> Result<(), MetalError> {
|
) -> Result<(), MetalError> {
|
||||||
let crtc = match connector.crtc.get() {
|
let crtc = match connector.crtc.get() {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
|
|
@ -1111,22 +1193,17 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
return Err(MetalError::NoPrimaryPlaneForConnector);
|
return Err(MetalError::NoPrimaryPlaneForConnector);
|
||||||
};
|
};
|
||||||
connector.buffers.set(None);
|
let format = ModifiedFormat {
|
||||||
let buffers = match connector.buffers.get() {
|
format: XRGB8888,
|
||||||
Some(b) => b,
|
modifier: INVALID_MODIFIER,
|
||||||
None => {
|
|
||||||
let format = ModifiedFormat {
|
|
||||||
format: XRGB8888,
|
|
||||||
modifier: INVALID_MODIFIER,
|
|
||||||
};
|
|
||||||
Rc::new(self.create_scanout_buffers(
|
|
||||||
dev,
|
|
||||||
&format,
|
|
||||||
mode.hdisplay as _,
|
|
||||||
mode.vdisplay as _,
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
let buffers = Rc::new(self.create_scanout_buffers(
|
||||||
|
&connector.dev,
|
||||||
|
&format,
|
||||||
|
mode.hdisplay as _,
|
||||||
|
mode.vdisplay as _,
|
||||||
|
ctx,
|
||||||
|
)?);
|
||||||
changes.change_object(primary_plane.id, |c| {
|
changes.change_object(primary_plane.id, |c| {
|
||||||
c.change(primary_plane.fb_id, buffers[0].drm.id().0 as _);
|
c.change(primary_plane.fb_id, buffers[0].drm.id().0 as _);
|
||||||
c.change(primary_plane.crtc_id.id, crtc.id.0 as _);
|
c.change(primary_plane.crtc_id.id, crtc.id.0 as _);
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ impl XBackend {
|
||||||
.eng
|
.eng
|
||||||
.spawn2(Phase::Present, self.clone().present_handler());
|
.spawn2(Phase::Present, self.clone().present_handler());
|
||||||
|
|
||||||
self.state.set_render_ctx(&self.ctx);
|
self.state.set_render_ctx(Some(&self.ctx));
|
||||||
|
|
||||||
pending().await
|
pending().await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ fn start_compositor2(
|
||||||
eng: engine.clone(),
|
eng: engine.clone(),
|
||||||
el: el.clone(),
|
el: el.clone(),
|
||||||
render_ctx: Default::default(),
|
render_ctx: Default::default(),
|
||||||
|
render_ctx_version: NumCell::new(1),
|
||||||
cursors: Default::default(),
|
cursors: Default::default(),
|
||||||
wheel,
|
wheel,
|
||||||
clients: Clients::new(),
|
clients: Clients::new(),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ops::Deref;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
|
@ -16,6 +18,9 @@ use {
|
||||||
std::{cell::Cell, rc::Rc},
|
std::{cell::Cell, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
use crate::utils::errorfmt::ErrorFmt;
|
||||||
|
use crate::video::dmabuf::DmaBuf;
|
||||||
|
use crate::wire::jay_screenshot::Dmabuf;
|
||||||
|
|
||||||
pub enum WlBufferStorage {
|
pub enum WlBufferStorage {
|
||||||
Shm { mem: ClientMemOffset, stride: i32 },
|
Shm { mem: ClientMemOffset, stride: i32 },
|
||||||
|
|
@ -28,7 +33,9 @@ pub struct WlBuffer {
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub rect: Rect,
|
pub rect: Rect,
|
||||||
pub format: &'static Format,
|
pub format: &'static Format,
|
||||||
pub storage: WlBufferStorage,
|
dmabuf: Option<DmaBuf>,
|
||||||
|
render_ctx_version: Cell<u32>,
|
||||||
|
pub storage: RefCell<Option<WlBufferStorage>>,
|
||||||
pub texture: CloneCell<Option<Rc<Texture>>>,
|
pub texture: CloneCell<Option<Rc<Texture>>>,
|
||||||
pub famebuffer: CloneCell<Option<Rc<Framebuffer>>>,
|
pub famebuffer: CloneCell<Option<Rc<Framebuffer>>>,
|
||||||
width: i32,
|
width: i32,
|
||||||
|
|
@ -46,6 +53,7 @@ impl WlBuffer {
|
||||||
id: WlBufferId,
|
id: WlBufferId,
|
||||||
client: &Rc<Client>,
|
client: &Rc<Client>,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
|
dmabuf: DmaBuf,
|
||||||
img: &Rc<Image>,
|
img: &Rc<Image>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let width = img.width();
|
let width = img.width();
|
||||||
|
|
@ -60,7 +68,9 @@ impl WlBuffer {
|
||||||
height,
|
height,
|
||||||
texture: CloneCell::new(None),
|
texture: CloneCell::new(None),
|
||||||
famebuffer: Default::default(),
|
famebuffer: Default::default(),
|
||||||
storage: WlBufferStorage::Dmabuf(img.clone()),
|
dmabuf: Some(dmabuf),
|
||||||
|
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
||||||
|
storage: RefCell::new(Some(WlBufferStorage::Dmabuf(img.clone()))),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +102,9 @@ impl WlBuffer {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
rect: Rect::new_sized(0, 0, width, height).unwrap(),
|
rect: Rect::new_sized(0, 0, width, height).unwrap(),
|
||||||
format,
|
format,
|
||||||
storage: WlBufferStorage::Shm { mem, stride },
|
dmabuf: None,
|
||||||
|
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
||||||
|
storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
texture: CloneCell::new(None),
|
texture: CloneCell::new(None),
|
||||||
|
|
@ -101,8 +113,43 @@ impl WlBuffer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_gfx_context_change(&self) {
|
||||||
|
let ctx_version = self.client.state.render_ctx_version.get();
|
||||||
|
if self.render_ctx_version.replace(ctx_version) == ctx_version {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.texture.set(None);
|
||||||
|
self.famebuffer.set(None);
|
||||||
|
let mut storage = self.storage.borrow_mut();
|
||||||
|
if let Some(storage) = &mut *storage {
|
||||||
|
if let WlBufferStorage::Shm { .. } = storage {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*storage = None;
|
||||||
|
let ctx = match self.client.state.render_ctx.get() {
|
||||||
|
Some(ctx) => ctx,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if let Some(dmabuf) = &self.dmabuf {
|
||||||
|
let image = match ctx.dmabuf_img(dmabuf) {
|
||||||
|
Ok(image) => image,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Cannot re-import wl_buffer after graphics context reset: {}", ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*storage = Some(WlBufferStorage::Dmabuf(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_texture(&self) -> Result<(), WlBufferError> {
|
pub fn update_texture(&self) -> Result<(), WlBufferError> {
|
||||||
match &self.storage {
|
let storage = self.storage.borrow_mut();
|
||||||
|
let storage = match storage.deref() {
|
||||||
|
Some(s) => s,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
match storage {
|
||||||
WlBufferStorage::Shm { mem, stride } => {
|
WlBufferStorage::Shm { mem, stride } => {
|
||||||
self.texture.set(None);
|
self.texture.set(None);
|
||||||
if let Some(ctx) = self.client.state.render_ctx.get() {
|
if let Some(ctx) = self.client.state.render_ctx.get() {
|
||||||
|
|
@ -122,7 +169,12 @@ impl WlBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_framebuffer(&self) -> Result<(), WlBufferError> {
|
pub fn update_framebuffer(&self) -> Result<(), WlBufferError> {
|
||||||
match &self.storage {
|
let storage = self.storage.borrow_mut();
|
||||||
|
let storage = match storage.deref() {
|
||||||
|
Some(s) => s,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
match storage {
|
||||||
WlBufferStorage::Shm { .. } => {
|
WlBufferStorage::Shm { .. } => {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ impl WlDrm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let img = ctx.dmabuf_img(&dmabuf)?;
|
let img = ctx.dmabuf_img(&dmabuf)?;
|
||||||
let buffer = Rc::new(WlBuffer::new_dmabuf(req.id, &self.client, format, &img));
|
let buffer = Rc::new(WlBuffer::new_dmabuf(req.id, &self.client, format, dmabuf, &img));
|
||||||
track!(self.client, buffer);
|
track!(self.client, buffer);
|
||||||
self.client.add_client_obj(&buffer)?;
|
self.client.add_client_obj(&buffer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ impl WlOutputGlobal {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let rect = capture.rect;
|
let rect = capture.rect;
|
||||||
if let WlBufferStorage::Shm { mem, .. } = &wl_buffer.storage {
|
if let Some(WlBufferStorage::Shm { mem, .. }) = wl_buffer.storage.borrow_mut().deref() {
|
||||||
let res = mem.access(|mem| {
|
let res = mem.access(|mem| {
|
||||||
fb.copy_to_shm(
|
fb.copy_to_shm(
|
||||||
rect.x1(),
|
rect.x1(),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ops::Deref;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
|
@ -114,7 +115,7 @@ impl ZwlrScreencopyFrameV1 {
|
||||||
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat);
|
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat);
|
||||||
}
|
}
|
||||||
buffer.update_framebuffer()?;
|
buffer.update_framebuffer()?;
|
||||||
if let WlBufferStorage::Shm { stride, .. } = &buffer.storage {
|
if let Some(WlBufferStorage::Shm { stride, .. }) = buffer.storage.borrow_mut().deref() {
|
||||||
if *stride != self.rect.width() * 4 {
|
if *stride != self.rect.width() * 4 {
|
||||||
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride);
|
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ impl ZwpLinuxBufferParamsV1 {
|
||||||
buffer_id,
|
buffer_id,
|
||||||
&self.parent.client,
|
&self.parent.client,
|
||||||
format.format,
|
format.format,
|
||||||
|
dmabuf,
|
||||||
&img,
|
&img,
|
||||||
));
|
));
|
||||||
track!(self.parent.client, buffer);
|
track!(self.parent.client, buffer);
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ impl TestBackend {
|
||||||
Ok(ctx) => ctx,
|
Ok(ctx) => ctx,
|
||||||
Err(e) => return Err(TestBackendError::RenderContext(e)),
|
Err(e) => return Err(TestBackendError::RenderContext(e)),
|
||||||
};
|
};
|
||||||
self.state.set_render_ctx(&Rc::new(ctx));
|
self.state.set_render_ctx(Some(&Rc::new(ctx)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,14 @@ use {
|
||||||
egl::{
|
egl::{
|
||||||
display::EglDisplay,
|
display::EglDisplay,
|
||||||
sys::{eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE},
|
sys::{eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE},
|
||||||
|
PROCS,
|
||||||
},
|
},
|
||||||
ext::GlExt,
|
ext::{DisplayExt, GlExt},
|
||||||
RenderError,
|
sys::{
|
||||||
|
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB,
|
||||||
|
GL_UNKNOWN_CONTEXT_RESET_ARB,
|
||||||
|
},
|
||||||
|
RenderError, ResetStatus,
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
@ -31,6 +36,27 @@ impl Drop for EglContext {
|
||||||
static mut CURRENT: EGLContext = EGLContext::none();
|
static mut CURRENT: EGLContext = EGLContext::none();
|
||||||
|
|
||||||
impl EglContext {
|
impl EglContext {
|
||||||
|
pub fn reset_status(&self) -> Option<ResetStatus> {
|
||||||
|
if !self
|
||||||
|
.dpy
|
||||||
|
.exts
|
||||||
|
.contains(DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let status = self.with_current(|| unsafe {
|
||||||
|
let status = match PROCS.glGetGraphicsResetStatusKHR() {
|
||||||
|
0 => return Ok(None),
|
||||||
|
GL_GUILTY_CONTEXT_RESET_ARB => ResetStatus::Guilty,
|
||||||
|
GL_INNOCENT_CONTEXT_RESET_ARB => ResetStatus::Innocent,
|
||||||
|
GL_UNKNOWN_CONTEXT_RESET_ARB => ResetStatus::Unknown,
|
||||||
|
n => ResetStatus::Other(n),
|
||||||
|
};
|
||||||
|
Ok(Some(status))
|
||||||
|
});
|
||||||
|
status.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_current<T, F: FnOnce() -> Result<T, RenderError>>(
|
pub fn with_current<T, F: FnOnce() -> Result<T, RenderError>>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,10 @@ use {
|
||||||
PROCS,
|
PROCS,
|
||||||
},
|
},
|
||||||
ext::{get_display_ext, get_gl_ext, DisplayExt, GlExt},
|
ext::{get_display_ext, get_gl_ext, DisplayExt, GlExt},
|
||||||
sys::{eglInitialize, EGL_PLATFORM_GBM_KHR},
|
sys::{
|
||||||
|
eglInitialize, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
|
||||||
|
EGL_LOSE_CONTEXT_ON_RESET_EXT, EGL_PLATFORM_GBM_KHR,
|
||||||
|
},
|
||||||
RenderError,
|
RenderError,
|
||||||
},
|
},
|
||||||
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice, INVALID_MODIFIER},
|
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice, INVALID_MODIFIER},
|
||||||
|
|
@ -104,7 +107,17 @@ impl EglDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_context(self: &Rc<Self>) -> Result<Rc<EglContext>, RenderError> {
|
pub fn create_context(self: &Rc<Self>) -> Result<Rc<EglContext>, RenderError> {
|
||||||
let attrib = [EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE];
|
let mut attrib = vec![EGL_CONTEXT_CLIENT_VERSION, 2];
|
||||||
|
if self
|
||||||
|
.exts
|
||||||
|
.contains(DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS)
|
||||||
|
{
|
||||||
|
attrib.push(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
|
||||||
|
attrib.push(EGL_LOSE_CONTEXT_ON_RESET_EXT);
|
||||||
|
} else {
|
||||||
|
log::warn!("EGL display does not support gpu reset notifications");
|
||||||
|
}
|
||||||
|
attrib.push(EGL_NONE);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ctx = eglCreateContext(
|
let ctx = eglCreateContext(
|
||||||
self.dpy,
|
self.dpy,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use uapi::c;
|
use {crate::render::sys::GLenum, uapi::c};
|
||||||
|
|
||||||
pub type EGLint = i32;
|
pub type EGLint = i32;
|
||||||
pub type EGLenum = c::c_uint;
|
pub type EGLenum = c::c_uint;
|
||||||
|
|
@ -51,6 +51,12 @@ pub const EGL_BAD_DEVICE_EXT: EGLint = 0x322B;
|
||||||
pub const EGL_OPENGL_ES_API: EGLenum = 0x30A0;
|
pub const EGL_OPENGL_ES_API: EGLenum = 0x30A0;
|
||||||
pub const EGL_PLATFORM_GBM_KHR: EGLint = 0x31D7;
|
pub const EGL_PLATFORM_GBM_KHR: EGLint = 0x31D7;
|
||||||
pub const EGL_CONTEXT_CLIENT_VERSION: EGLint = 0x3098;
|
pub const EGL_CONTEXT_CLIENT_VERSION: EGLint = 0x3098;
|
||||||
|
pub const EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT: EGLint = 0x3138;
|
||||||
|
pub const EGL_LOSE_CONTEXT_ON_RESET_EXT: EGLint = 0x31BF;
|
||||||
|
|
||||||
|
pub const GL_GUILTY_CONTEXT_RESET_ARB: GLenum = 0x8253;
|
||||||
|
pub const GL_INNOCENT_CONTEXT_RESET_ARB: GLenum = 0x8254;
|
||||||
|
pub const GL_UNKNOWN_CONTEXT_RESET_ARB: GLenum = 0x8255;
|
||||||
|
|
||||||
pub const EGL_WIDTH: EGLint = 0x3057;
|
pub const EGL_WIDTH: EGLint = 0x3057;
|
||||||
pub const EGL_HEIGHT: EGLint = 0x3056;
|
pub const EGL_HEIGHT: EGLint = 0x3056;
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ bitflags::bitflags! {
|
||||||
const MESA_CONFIGLESS_CONTEXT = 1 << 4;
|
const MESA_CONFIGLESS_CONTEXT = 1 << 4;
|
||||||
const KHR_SURFACELESS_CONTEXT = 1 << 5;
|
const KHR_SURFACELESS_CONTEXT = 1 << 5;
|
||||||
const IMG_CONTEXT_PRIORITY = 1 << 6;
|
const IMG_CONTEXT_PRIORITY = 1 << 6;
|
||||||
|
const EXT_CREATE_CONTEXT_ROBUSTNESS = 1 << 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,6 +104,10 @@ pub(super) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt {
|
||||||
DisplayExt::KHR_SURFACELESS_CONTEXT,
|
DisplayExt::KHR_SURFACELESS_CONTEXT,
|
||||||
),
|
),
|
||||||
("EGL_IMG_context_priority", DisplayExt::IMG_CONTEXT_PRIORITY),
|
("EGL_IMG_context_priority", DisplayExt::IMG_CONTEXT_PRIORITY),
|
||||||
|
(
|
||||||
|
"EGL_EXT_create_context_robustness",
|
||||||
|
DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
match get_dpy_extensions(dpy) {
|
match get_dpy_extensions(dpy) {
|
||||||
Some(exts) => get_typed_ext(&exts, DisplayExt::empty(), &map),
|
Some(exts) => get_typed_ext(&exts, DisplayExt::empty(), &map),
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,19 @@ impl Debug for RenderContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ResetStatus {
|
||||||
|
Guilty,
|
||||||
|
Innocent,
|
||||||
|
Unknown,
|
||||||
|
Other(u32),
|
||||||
|
}
|
||||||
|
|
||||||
impl RenderContext {
|
impl RenderContext {
|
||||||
|
pub fn reset_status(&self) -> Option<ResetStatus> {
|
||||||
|
self.ctx.reset_status()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
|
pub fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
|
||||||
let nodes = drm.get_nodes()?;
|
let nodes = drm.get_nodes()?;
|
||||||
let node = match nodes
|
let node = match nodes
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ impl Renderer<'_> {
|
||||||
std::slice::from_ref(&pos.at_point(x, y)),
|
std::slice::from_ref(&pos.at_point(x, y)),
|
||||||
&Color::from_rgba_straight(20, 20, 20, 255),
|
&Color::from_rgba_straight(20, 20, 20, 255),
|
||||||
);
|
);
|
||||||
if let Some(tex) = placeholder.texture() {
|
if let Some(tex) = placeholder.texture.get() {
|
||||||
let x = x + (pos.width() - tex.width()) / 2;
|
let x = x + (pos.width() - tex.width()) / 2;
|
||||||
let y = y + (pos.height() - tex.height()) / 2;
|
let y = y + (pos.height() - tex.height()) / 2;
|
||||||
self.render_texture(&tex, x, y, &ARGB8888);
|
self.render_texture(&tex, x, y, &ARGB8888);
|
||||||
|
|
|
||||||
94
src/state.rs
94
src/state.rs
|
|
@ -29,7 +29,7 @@ use {
|
||||||
theme::Theme,
|
theme::Theme,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, NodeVisitorBase,
|
ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, NodeVisitorBase,
|
||||||
OutputNode, ToplevelNode, WorkspaceNode,
|
OutputNode, PlaceholderNode, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
|
|
@ -53,6 +53,7 @@ use {
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use crate::ifs::wl_surface::WlSurface;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub xkb_ctx: XkbContext,
|
pub xkb_ctx: XkbContext,
|
||||||
|
|
@ -62,6 +63,7 @@ pub struct State {
|
||||||
pub eng: Rc<AsyncEngine>,
|
pub eng: Rc<AsyncEngine>,
|
||||||
pub el: Rc<EventLoop>,
|
pub el: Rc<EventLoop>,
|
||||||
pub render_ctx: CloneCell<Option<Rc<RenderContext>>>,
|
pub render_ctx: CloneCell<Option<Rc<RenderContext>>>,
|
||||||
|
pub render_ctx_version: NumCell<u32>,
|
||||||
pub cursors: CloneCell<Option<Rc<ServerCursors>>>,
|
pub cursors: CloneCell<Option<Rc<ServerCursors>>>,
|
||||||
pub wheel: Rc<Wheel>,
|
pub wheel: Rc<Wheel>,
|
||||||
pub clients: Clients,
|
pub clients: Clients,
|
||||||
|
|
@ -175,34 +177,76 @@ pub struct OutputData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn set_render_ctx(&self, ctx: &Rc<RenderContext>) {
|
pub fn set_render_ctx(&self, ctx: Option<&Rc<RenderContext>>) {
|
||||||
let cursors = match ServerCursors::load(ctx) {
|
self.render_ctx.set(ctx.cloned());
|
||||||
Ok(c) => Some(Rc::new(c)),
|
self.render_ctx_version.fetch_add(1);
|
||||||
Err(e) => {
|
|
||||||
log::error!("Could not load the cursors: {}", ErrorFmt(e));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.cursors.set(cursors);
|
|
||||||
self.render_ctx.set(Some(ctx.clone()));
|
|
||||||
|
|
||||||
struct Walker;
|
{
|
||||||
impl NodeVisitorBase for Walker {
|
struct Walker;
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
impl NodeVisitorBase for Walker {
|
||||||
// log::info!("set_render_ctx");
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
node.schedule_compute_render_data();
|
node.render_data.borrow_mut().titles.clear();
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
|
node.render_data.borrow_mut().titles.clear();
|
||||||
|
node.render_data.borrow_mut().status.take();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
|
node.title_texture.set(None);
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
|
node.texture.set(None);
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||||
|
if let Some(buffer) = node.buffer.get() {
|
||||||
|
buffer.handle_gfx_context_change();
|
||||||
|
}
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
Walker.visit_display(&self.root);
|
||||||
node.update_render_data();
|
for client in self.clients.clients.borrow_mut().values() {
|
||||||
node.node_visit_children(self);
|
for buffer in client.data.objects.buffers.lock().values() {
|
||||||
}
|
buffer.handle_gfx_context_change();
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
}
|
||||||
node.schedule_render_titles();
|
|
||||||
node.node_visit_children(self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Walker.visit_display(&self.root);
|
|
||||||
|
if let Some(ctx) = ctx {
|
||||||
|
let cursors = match ServerCursors::load(ctx) {
|
||||||
|
Ok(c) => Some(Rc::new(c)),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not load the cursors: {}", ErrorFmt(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.cursors.set(cursors);
|
||||||
|
|
||||||
|
struct Walker;
|
||||||
|
impl NodeVisitorBase for Walker {
|
||||||
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
|
node.schedule_compute_render_data();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
|
node.update_render_data();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
|
node.schedule_render_titles();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
|
node.update_texture();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Walker.visit_display(&self.root);
|
||||||
|
}
|
||||||
|
|
||||||
let seats = self.globals.seats.lock();
|
let seats = self.globals.seats.lock();
|
||||||
for seat in seats.values() {
|
for seat in seats.values() {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub struct PlaceholderNode {
|
||||||
id: PlaceholderNodeId,
|
id: PlaceholderNodeId,
|
||||||
toplevel: ToplevelData,
|
toplevel: ToplevelData,
|
||||||
destroyed: Cell<bool>,
|
destroyed: Cell<bool>,
|
||||||
texture: CloneCell<Option<Rc<Texture>>>,
|
pub texture: CloneCell<Option<Rc<Texture>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaceholderNode {
|
impl PlaceholderNode {
|
||||||
|
|
@ -39,13 +39,34 @@ impl PlaceholderNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture(&self) -> Option<Rc<Texture>> {
|
|
||||||
self.texture.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_destroyed(&self) -> bool {
|
pub fn is_destroyed(&self) -> bool {
|
||||||
self.destroyed.get()
|
self.destroyed.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_texture(&self) {
|
||||||
|
self.texture.set(None);
|
||||||
|
if let Some(ctx) = self.toplevel.state.render_ctx.get() {
|
||||||
|
let rect = self.toplevel.pos.get();
|
||||||
|
if rect.width() != 0 && rect.height() != 0 {
|
||||||
|
let font = format!("monospace {}", rect.width() / 10);
|
||||||
|
match text::render_fitting(
|
||||||
|
&ctx,
|
||||||
|
rect.height(),
|
||||||
|
&font,
|
||||||
|
"Fullscreen",
|
||||||
|
Color::GREY,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
Ok(t) => {
|
||||||
|
self.texture.set(Some(t));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not render fullscreen texture: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for PlaceholderNode {
|
impl Node for PlaceholderNode {
|
||||||
|
|
@ -126,27 +147,7 @@ impl ToplevelNode for PlaceholderNode {
|
||||||
if let Some(p) = self.toplevel.parent.get() {
|
if let Some(p) = self.toplevel.parent.get() {
|
||||||
p.node_child_size_changed(self.deref(), rect.width(), rect.height());
|
p.node_child_size_changed(self.deref(), rect.width(), rect.height());
|
||||||
}
|
}
|
||||||
self.texture.set(None);
|
self.update_texture();
|
||||||
if let Some(ctx) = self.toplevel.state.render_ctx.get() {
|
|
||||||
if rect.width() != 0 && rect.height() != 0 {
|
|
||||||
let font = format!("monospace {}", rect.width() / 10);
|
|
||||||
match text::render_fitting(
|
|
||||||
&ctx,
|
|
||||||
rect.height(),
|
|
||||||
&font,
|
|
||||||
"Fullscreen",
|
|
||||||
Color::GREY,
|
|
||||||
false,
|
|
||||||
) {
|
|
||||||
Ok(t) => {
|
|
||||||
self.texture.set(Some(t));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Could not render fullscreen texture: {}", ErrorFmt(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_close(self: Rc<Self>) {
|
fn tl_close(self: Rc<Self>) {
|
||||||
|
|
|
||||||
|
|
@ -966,7 +966,7 @@ pub fn mode_addfb2(
|
||||||
offsets,
|
offsets,
|
||||||
modifiers,
|
modifiers,
|
||||||
};
|
};
|
||||||
log::info!("{:#?}", res);
|
// log::info!("{:#?}", res);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &mut res)?;
|
ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &mut res)?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue