1
0
Fork 0
forked from wry/wry

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:
Julian Orth 2022-05-06 13:09:29 +02:00
parent 4584dee160
commit d2913449ea
21 changed files with 377 additions and 120 deletions

View file

@ -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;")?;

View file

@ -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();

View file

@ -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
} }

View file

@ -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 _);

View file

@ -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
} }

View file

@ -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(),

View file

@ -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
} }

View file

@ -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(())

View file

@ -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(),

View file

@ -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);
} }

View file

@ -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);

View file

@ -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(())
} }
} }

View file

@ -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,

View file

@ -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,

View file

@ -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;

View file

@ -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),

View file

@ -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

View file

@ -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);

View file

@ -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() {

View file

@ -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>) {

View file

@ -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)?;