diff --git a/build/egl.rs b/build/egl.rs index eef31de4..bfba180d 100644 --- a/build/egl.rs +++ b/build/egl.rs @@ -79,6 +79,7 @@ fn write_egl_procs(f: &mut W) -> anyhow::Result<()> { "()", &[("target", "GLenum"), ("image", "GLeglImageOES")][..], ), + ("glGetGraphicsResetStatusKHR", "GLenum", &[][..]), ]; writeln!(f, "use std::ptr;")?; diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 33012c6f..ba5b9733 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -9,7 +9,7 @@ use { Backend, BackendEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent, KeyState, TransformMatrix, }, - backends::metal::video::{MetalDrmDevice, PendingDrmDevice}, + backends::metal::video::{MetalDrmDevice, MetalRenderContext, PendingDrmDevice}, dbus::{DbusError, SignalHandler}, libinput::{ consts::{ @@ -44,6 +44,7 @@ use { cell::{Cell, RefCell}, error::Error, ffi::{CStr, CString}, + fmt::{Debug, Formatter}, future::pending, mem, rc::Rc, @@ -82,7 +83,7 @@ pub enum MetalError { NoModeForConnector, #[error("Could not allocate scanout buffer")] ScanoutBuffer(#[source] GbmError), - #[error("Could not create a framebuffer")] + #[error("addfb2 failed")] Framebuffer(#[source] DrmError), #[error("Could not import a framebuffer into EGL")] ImportFb(#[source] RenderError), @@ -124,6 +125,13 @@ pub struct MetalBackend { drm_ids: DrmIds, pause_handler: Cell>, resume_handler: Cell>, + ctx: CloneCell>>, +} + +impl Debug for MetalBackend { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MetalBackend").finish_non_exhaustive() + } } impl MetalBackend { @@ -250,6 +258,7 @@ pub async fn create(state: &Rc) -> Result, MetalError> { drm_ids: Default::default(), pause_handler: Default::default(), resume_handler: Default::default(), + ctx: Default::default(), }); metal.pause_handler.set(Some({ let mtl = metal.clone(); diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index 281baf1c..ff10fca6 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -235,8 +235,16 @@ impl MetalBackend { } } - fn handle_drm_change(self: &Rc, _dev: UdevDevice) -> Option<()> { - // TODO: Handle monitor connections and connector hotplug + fn handle_drm_change(self: &Rc, dev: UdevDevice) -> Option<()> { + 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 } diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 04384e22..7162f4c0 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -23,7 +23,7 @@ use { DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, 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, }, }, @@ -37,6 +37,7 @@ use { }, uapi::c, }; +use crate::render::ResetStatus; pub struct PendingDrmDevice { pub id: DrmId, @@ -44,6 +45,11 @@ pub struct PendingDrmDevice { pub devnode: CString, } +pub struct MetalRenderContext { + pub dev: Rc, + pub egl: Rc, +} + #[derive(Debug)] pub struct MetalDrmDeviceStatic { pub id: DrmId, @@ -58,7 +64,6 @@ pub struct MetalDrmDeviceStatic { pub min_height: u32, pub max_height: u32, pub gbm: GbmDevice, - pub egl: Rc, pub async_fd: AsyncFd, pub handle_events: HandleEvents, } @@ -86,6 +91,9 @@ pub struct MetalConnector { pub master: Rc, pub state: Rc, + pub dev: Rc, + pub backend: Rc, + pub connector_id: ConnectorId, pub crtcs: AHashMap>, @@ -162,6 +170,9 @@ impl MetalConnector { } pub fn present(&self) { + if !self.backend.check_render_context() { + return; + } let crtc = match self.crtc.get() { Some(crtc) => crtc, _ => return, @@ -172,14 +183,14 @@ impl MetalConnector { if !crtc.active.value.get() { return; } - let buffers = match self.buffers.get() { - None => return, - Some(b) => b, - }; let plane = match self.primary_plane.get() { Some(p) => p, _ => return, }; + let buffers = match self.buffers.get() { + Some(b) => b, + _ => return, + }; let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()]; if let Some(node) = self.state.root.outputs.get(&self.connector_id) { let mut rr = self.render_result.borrow_mut(); @@ -295,7 +306,7 @@ pub struct MetalPlane { } fn get_connectors( - state: &Rc, + backend: &Rc, dev: &Rc, ids: &[DrmConnector], ) -> Result< @@ -308,7 +319,7 @@ fn get_connectors( let mut connectors = AHashMap::new(); let mut futures = vec![]; for connector in ids { - match create_connector(state, *connector, dev) { + match create_connector(backend, *connector, dev) { Ok((con, fut)) => { connectors.insert(con.id, con); futures.push(fut); @@ -320,7 +331,7 @@ fn get_connectors( } fn create_connector( - state: &Rc, + backend: &Rc, connector: DrmConnector, dev: &Rc, ) -> Result<(Rc, ConnectorFutures), DrmError> { @@ -412,8 +423,10 @@ fn create_connector( let slf = Rc::new(MetalConnector { id: connector, master: dev.master.clone(), - state: state.clone(), - connector_id: state.connector_ids.next(), + state: backend.state.clone(), + dev: dev.clone(), + backend: backend.clone(), + connector_id: backend.state.connector_ids.next(), crtcs, mode: CloneCell::new(mode), refresh: Cell::new(refresh), @@ -440,7 +453,10 @@ fn create_connector( render_result: RefCell::new(Default::default()), }); 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)) } @@ -606,6 +622,67 @@ impl MutableProperty { } 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) -> 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, + ) -> Result, 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( self: &Rc, pending: PendingDrmDevice, @@ -650,10 +727,6 @@ impl MetalBackend { Ok(g) => g, 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()) { Ok(f) => f, Err(e) => return Err(MetalError::CreateAsyncFd(e)), @@ -672,14 +745,20 @@ impl MetalBackend { min_height: resources.min_height, max_height: resources.max_height, gbm, - egl: egl.clone(), async_fd, handle_events: HandleEvents { 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 { dev, @@ -730,8 +809,6 @@ impl MetalBackend { .spawn(self.clone().handle_drm_events(slf.clone())); slf.dev.handle_events.handle_events.set(Some(drm_handler)); - self.state.set_render_ctx(&egl); - Ok(slf) } @@ -891,6 +968,10 @@ impl MetalBackend { } fn init_drm_device(&self, dev: &Rc) -> Result<(), MetalError> { + let ctx = match self.ctx.get() { + Some(ctx) => ctx, + _ => return Ok(()), + }; let mut flags = 0; let mut changes = dev.dev.master.change(); if !self.can_use_current_drm_mode(dev) { @@ -905,7 +986,7 @@ impl MetalBackend { } self.reset_planes(dev, &mut changes); 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)); } } @@ -996,37 +1077,38 @@ impl MetalBackend { fn create_scanout_buffers( &self, - dev: &Rc, + dev: &Rc, format: &ModifiedFormat, width: i32, height: i32, + ctx: &MetalRenderContext, ) -> 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()?]) } fn create_scanout_buffer( &self, - dev: &Rc, + dev: &Rc, format: &ModifiedFormat, width: i32, height: i32, + ctx: &MetalRenderContext, ) -> Result { - let bo = dev.dev.gbm.create_bo( - width, - height, - format, - GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT, - ); + let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; + if ctx.dev.id != dev.id { + usage |= GBM_BO_USE_LINEAR; + }; + let bo = dev.gbm.create_bo(width, height, format, usage); let bo = match bo { Ok(b) => b, 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), 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, Err(e) => return Err(MetalError::ImportImage(e)), }; @@ -1085,9 +1167,9 @@ impl MetalBackend { fn assign_connector_plane( &self, - dev: &Rc, connector: &Rc, changes: &mut Change, + ctx: &MetalRenderContext, ) -> Result<(), MetalError> { let crtc = match connector.crtc.get() { Some(c) => c, @@ -1111,22 +1193,17 @@ impl MetalBackend { } return Err(MetalError::NoPrimaryPlaneForConnector); }; - connector.buffers.set(None); - let buffers = match connector.buffers.get() { - Some(b) => b, - None => { - let format = ModifiedFormat { - format: XRGB8888, - modifier: INVALID_MODIFIER, - }; - Rc::new(self.create_scanout_buffers( - dev, - &format, - mode.hdisplay as _, - mode.vdisplay as _, - )?) - } + let format = ModifiedFormat { + format: XRGB8888, + modifier: INVALID_MODIFIER, }; + 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| { c.change(primary_plane.fb_id, buffers[0].drm.id().0 as _); c.change(primary_plane.crtc_id.id, crtc.id.0 as _); diff --git a/src/backends/x.rs b/src/backends/x.rs index f7655d5c..f6990d60 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -279,7 +279,7 @@ impl XBackend { .eng .spawn2(Phase::Present, self.clone().present_handler()); - self.state.set_render_ctx(&self.ctx); + self.state.set_render_ctx(Some(&self.ctx)); pending().await } diff --git a/src/compositor.rs b/src/compositor.rs index 8745eb20..8b31a3d1 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -127,6 +127,7 @@ fn start_compositor2( eng: engine.clone(), el: el.clone(), render_ctx: Default::default(), + render_ctx_version: NumCell::new(1), cursors: Default::default(), wheel, clients: Clients::new(), diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index 915499f0..7e87a5ee 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; +use std::ops::Deref; use { crate::{ client::{Client, ClientError}, @@ -16,6 +18,9 @@ use { std::{cell::Cell, rc::Rc}, thiserror::Error, }; +use crate::utils::errorfmt::ErrorFmt; +use crate::video::dmabuf::DmaBuf; +use crate::wire::jay_screenshot::Dmabuf; pub enum WlBufferStorage { Shm { mem: ClientMemOffset, stride: i32 }, @@ -28,7 +33,9 @@ pub struct WlBuffer { pub client: Rc, pub rect: Rect, pub format: &'static Format, - pub storage: WlBufferStorage, + dmabuf: Option, + render_ctx_version: Cell, + pub storage: RefCell>, pub texture: CloneCell>>, pub famebuffer: CloneCell>>, width: i32, @@ -46,6 +53,7 @@ impl WlBuffer { id: WlBufferId, client: &Rc, format: &'static Format, + dmabuf: DmaBuf, img: &Rc, ) -> Self { let width = img.width(); @@ -60,7 +68,9 @@ impl WlBuffer { height, texture: CloneCell::new(None), 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(), } } @@ -92,7 +102,9 @@ impl WlBuffer { client: client.clone(), rect: Rect::new_sized(0, 0, width, height).unwrap(), 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, height, 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> { - 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 } => { self.texture.set(None); if let Some(ctx) = self.client.state.render_ctx.get() { @@ -122,7 +169,12 @@ impl WlBuffer { } 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 { .. } => { // nothing } diff --git a/src/ifs/wl_drm.rs b/src/ifs/wl_drm.rs index 6af8f443..d4ce898d 100644 --- a/src/ifs/wl_drm.rs +++ b/src/ifs/wl_drm.rs @@ -147,7 +147,7 @@ impl WlDrm { } } 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); self.client.add_client_obj(&buffer)?; Ok(()) diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 1d8367da..a44f56c4 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -189,7 +189,7 @@ impl WlOutputGlobal { continue; } 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| { fb.copy_to_shm( rect.x1(), diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index 9045089d..ffd07dd3 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -1,3 +1,4 @@ +use std::ops::Deref; use { crate::{ client::{Client, ClientError}, @@ -114,7 +115,7 @@ impl ZwlrScreencopyFrameV1 { return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat); } 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 { return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); } diff --git a/src/ifs/zwp_linux_buffer_params_v1.rs b/src/ifs/zwp_linux_buffer_params_v1.rs index 30e51b71..6feb0213 100644 --- a/src/ifs/zwp_linux_buffer_params_v1.rs +++ b/src/ifs/zwp_linux_buffer_params_v1.rs @@ -141,6 +141,7 @@ impl ZwpLinuxBufferParamsV1 { buffer_id, &self.parent.client, format.format, + dmabuf, &img, )); track!(self.parent.client, buffer); diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 87b2f272..8329abdc 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -167,7 +167,7 @@ impl TestBackend { Ok(ctx) => ctx, 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(()) } } diff --git a/src/render/egl/context.rs b/src/render/egl/context.rs index 9a6b6ef1..3e895cd7 100644 --- a/src/render/egl/context.rs +++ b/src/render/egl/context.rs @@ -3,9 +3,14 @@ use { egl::{ display::EglDisplay, sys::{eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE}, + PROCS, }, - ext::GlExt, - RenderError, + ext::{DisplayExt, GlExt}, + sys::{ + GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB, + GL_UNKNOWN_CONTEXT_RESET_ARB, + }, + RenderError, ResetStatus, }, std::rc::Rc, }; @@ -31,6 +36,27 @@ impl Drop for EglContext { static mut CURRENT: EGLContext = EGLContext::none(); impl EglContext { + pub fn reset_status(&self) -> Option { + 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] pub fn with_current Result>( &self, diff --git a/src/render/egl/display.rs b/src/render/egl/display.rs index b259080a..da6abd79 100644 --- a/src/render/egl/display.rs +++ b/src/render/egl/display.rs @@ -23,7 +23,10 @@ use { PROCS, }, 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, }, video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice, INVALID_MODIFIER}, @@ -104,7 +107,17 @@ impl EglDisplay { } pub fn create_context(self: &Rc) -> Result, 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 { let ctx = eglCreateContext( self.dpy, diff --git a/src/render/egl/sys.rs b/src/render/egl/sys.rs index 0c90bdaa..087c06a5 100644 --- a/src/render/egl/sys.rs +++ b/src/render/egl/sys.rs @@ -1,4 +1,4 @@ -use uapi::c; +use {crate::render::sys::GLenum, uapi::c}; pub type EGLint = i32; 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_PLATFORM_GBM_KHR: EGLint = 0x31D7; 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_HEIGHT: EGLint = 0x3056; diff --git a/src/render/ext.rs b/src/render/ext.rs index e88e462f..cae4fd35 100644 --- a/src/render/ext.rs +++ b/src/render/ext.rs @@ -76,6 +76,7 @@ bitflags::bitflags! { const MESA_CONFIGLESS_CONTEXT = 1 << 4; const KHR_SURFACELESS_CONTEXT = 1 << 5; 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, ), ("EGL_IMG_context_priority", DisplayExt::IMG_CONTEXT_PRIORITY), + ( + "EGL_EXT_create_context_robustness", + DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS, + ), ]; match get_dpy_extensions(dpy) { Some(exts) => get_typed_ext(&exts, DisplayExt::empty(), &map), diff --git a/src/render/renderer/context.rs b/src/render/renderer/context.rs index 2f5fec2a..895ce479 100644 --- a/src/render/renderer/context.rs +++ b/src/render/renderer/context.rs @@ -66,7 +66,19 @@ impl Debug for RenderContext { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ResetStatus { + Guilty, + Innocent, + Unknown, + Other(u32), +} + impl RenderContext { + pub fn reset_status(&self) -> Option { + self.ctx.reset_status() + } + pub fn from_drm_device(drm: &Drm) -> Result { let nodes = drm.get_nodes()?; let node = match nodes diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 6345dfa7..a4ca3599 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -198,7 +198,7 @@ impl Renderer<'_> { std::slice::from_ref(&pos.at_point(x, y)), &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 y = y + (pos.height() - tex.height()) / 2; self.render_texture(&tex, x, y, &ARGB8888); diff --git a/src/state.rs b/src/state.rs index d52a7927..f0b6369d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -29,7 +29,7 @@ use { theme::Theme, tree::{ ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, NodeVisitorBase, - OutputNode, ToplevelNode, WorkspaceNode, + OutputNode, PlaceholderNode, ToplevelNode, WorkspaceNode, }, utils::{ asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap, @@ -53,6 +53,7 @@ use { time::Duration, }, }; +use crate::ifs::wl_surface::WlSurface; pub struct State { pub xkb_ctx: XkbContext, @@ -62,6 +63,7 @@ pub struct State { pub eng: Rc, pub el: Rc, pub render_ctx: CloneCell>>, + pub render_ctx_version: NumCell, pub cursors: CloneCell>>, pub wheel: Rc, pub clients: Clients, @@ -175,34 +177,76 @@ pub struct OutputData { } impl State { - pub fn set_render_ctx(&self, ctx: &Rc) { - 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); - self.render_ctx.set(Some(ctx.clone())); + pub fn set_render_ctx(&self, ctx: Option<&Rc>) { + self.render_ctx.set(ctx.cloned()); + self.render_ctx_version.fetch_add(1); - struct Walker; - impl NodeVisitorBase for Walker { - fn visit_container(&mut self, node: &Rc) { - // log::info!("set_render_ctx"); - node.schedule_compute_render_data(); - node.node_visit_children(self); + { + struct Walker; + impl NodeVisitorBase for Walker { + fn visit_container(&mut self, node: &Rc) { + node.render_data.borrow_mut().titles.clear(); + node.node_visit_children(self); + } + fn visit_output(&mut self, node: &Rc) { + 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) { + node.title_texture.set(None); + node.node_visit_children(self); + } + fn visit_placeholder(&mut self, node: &Rc) { + node.texture.set(None); + node.node_visit_children(self); + } + fn visit_surface(&mut self, node: &Rc) { + if let Some(buffer) = node.buffer.get() { + buffer.handle_gfx_context_change(); + } + node.node_visit_children(self); + } } - fn visit_output(&mut self, node: &Rc) { - node.update_render_data(); - node.node_visit_children(self); - } - fn visit_float(&mut self, node: &Rc) { - node.schedule_render_titles(); - node.node_visit_children(self); + Walker.visit_display(&self.root); + for client in self.clients.clients.borrow_mut().values() { + for buffer in client.data.objects.buffers.lock().values() { + buffer.handle_gfx_context_change(); + } } } - 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) { + node.schedule_compute_render_data(); + node.node_visit_children(self); + } + fn visit_output(&mut self, node: &Rc) { + node.update_render_data(); + node.node_visit_children(self); + } + fn visit_float(&mut self, node: &Rc) { + node.schedule_render_titles(); + node.node_visit_children(self); + } + fn visit_placeholder(&mut self, node: &Rc) { + node.update_texture(); + node.node_visit_children(self); + } + } + Walker.visit_display(&self.root); + } let seats = self.globals.seats.lock(); for seat in seats.values() { diff --git a/src/tree/placeholder.rs b/src/tree/placeholder.rs index 6d4fb4a2..b8572c4a 100644 --- a/src/tree/placeholder.rs +++ b/src/tree/placeholder.rs @@ -22,7 +22,7 @@ pub struct PlaceholderNode { id: PlaceholderNodeId, toplevel: ToplevelData, destroyed: Cell, - texture: CloneCell>>, + pub texture: CloneCell>>, } impl PlaceholderNode { @@ -39,13 +39,34 @@ impl PlaceholderNode { } } - pub fn texture(&self) -> Option> { - self.texture.get() - } - pub fn is_destroyed(&self) -> bool { 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 { @@ -126,27 +147,7 @@ impl ToplevelNode for PlaceholderNode { if let Some(p) = self.toplevel.parent.get() { p.node_child_size_changed(self.deref(), rect.width(), rect.height()); } - self.texture.set(None); - 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)); - } - } - } - } + self.update_texture(); } fn tl_close(self: Rc) { diff --git a/src/video/drm/sys.rs b/src/video/drm/sys.rs index 4c508b9e..644e3ad3 100644 --- a/src/video/drm/sys.rs +++ b/src/video/drm/sys.rs @@ -966,7 +966,7 @@ pub fn mode_addfb2( offsets, modifiers, }; - log::info!("{:#?}", res); + // log::info!("{:#?}", res); unsafe { ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &mut res)?;