From fb30c830d7bb28bfc287507a6ca63ed540d40d81 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Wed, 7 Apr 2021 01:15:03 +0200 Subject: [PATCH] Fixup anvil for winit rendering - Since a lot of functionality is now in smithay's rendering module we can get rid of shm_load, shaders and the glium dependency. - glium_drawer becomes drawing and only features some code to get from surfaces to textures for any(!) renderer. (Should probably moved into smithay at some point as well.) - buffer_utils is only necessary to query the buffer size anymore. - disable egl-buffer support temporarily --- anvil/Cargo.toml | 7 +- anvil/src/buffer_utils.rs | 179 +---------------- anvil/src/drawing.rs | 198 +++++++++++++++++++ anvil/src/glium_drawer.rs | 398 -------------------------------------- anvil/src/main.rs | 8 +- anvil/src/shaders.rs | 120 ------------ anvil/src/shell.rs | 2 +- anvil/src/shm_load.rs | 55 ------ anvil/src/udev.rs | 8 +- anvil/src/winit.rs | 46 +++-- 10 files changed, 238 insertions(+), 783 deletions(-) create mode 100644 anvil/src/drawing.rs delete mode 100644 anvil/src/glium_drawer.rs delete mode 100644 anvil/src/shaders.rs delete mode 100644 anvil/src/shm_load.rs diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index eea64c4..77c2abb 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -8,18 +8,17 @@ edition = "2018" [dependencies] bitflags = "1.2.1" -glium = { version = "0.29.0", default-features = false } input = { version = "0.5.0", features = ["udev"], optional = true } rand = "0.8" slog = { version = "2.1.1" } -slog-term = "2.3" +slog-term = "2.8" slog-async = "2.2" xkbcommon = "0.4.0" [dependencies.smithay] path = ".." default-features = false -features = [ "renderer_glium", "backend_egl", "wayland_frontend" ] +features = [ "renderer_gl", "backend_egl", "wayland_frontend" ] [dependencies.x11rb] optional = true @@ -34,7 +33,7 @@ gl_generator = "0.14" default = [ "winit", "egl", "udev", "logind", "xwayland" ] egl = [ "smithay/use_system_lib" ] winit = [ "smithay/backend_winit" ] -udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm_atomic", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_eglstream", "smithay/backend_drm_egl", "smithay/backend_session", "input" ] +udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input" ] logind = [ "smithay/backend_session_logind" ] elogind = ["logind", "smithay/backend_session_elogind" ] xwayland = [ "smithay/xwayland", "x11rb" ] diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs index 8509c11..64ffb12 100644 --- a/anvil/src/buffer_utils.rs +++ b/anvil/src/buffer_utils.rs @@ -1,11 +1,3 @@ -use glium::texture::Texture2d; -#[cfg(feature = "egl")] -use glium::{ - texture::{MipmapsOption, UncompressedFloatFormat}, - GlObject, -}; -use slog::Logger; -use std::collections::HashMap; #[cfg(feature = "egl")] use std::{cell::RefCell, rc::Rc}; @@ -14,25 +6,22 @@ use smithay::backend::egl::{ display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format, }; use smithay::{ - backend::graphics::gl::GLGraphicsBackend, reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError}, }; -use crate::glium_drawer::GliumDrawer; - /// Utilities for working with `WlBuffer`s. #[derive(Clone)] pub struct BufferUtils { #[cfg(feature = "egl")] egl_buffer_reader: Rc>>, - log: Logger, + log: ::slog::Logger, } impl BufferUtils { /// Creates a new `BufferUtils`. #[cfg(feature = "egl")] - pub fn new(egl_buffer_reader: Rc>>, log: Logger) -> Self { + pub fn new(egl_buffer_reader: Rc>>, log: ::slog::Logger) -> Self { Self { egl_buffer_reader, log, @@ -41,7 +30,7 @@ impl BufferUtils { /// Creates a new `BufferUtils`. #[cfg(not(feature = "egl"))] - pub fn new(log: Logger) -> Self { + pub fn new(log: ::slog::Logger) -> Self { Self { log } } @@ -69,166 +58,4 @@ impl BufferUtils { err }) } - - #[cfg(feature = "egl")] - pub fn load_buffer(&self, buffer: WlBuffer) -> Result { - // try to retrieve the egl contents of this buffer - let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() { - display.egl_buffer_contents(&buffer) - } else { - return Err(buffer); - }; - - match images { - Ok(images) => { - // we have an EGL buffer - Ok(BufferTextures { - buffer, - textures: HashMap::new(), - fragment: crate::shaders::BUFFER_RGBA, - y_inverted: images.y_inverted, - dimensions: (images.width, images.height), - images: Some(images), // I guess we need to keep this alive ? - logger: self.log.clone(), - }) - } - Err(EGLBufferAccessError::NotManaged(_)) => { - // this is not an EGL buffer, try SHM - self.load_shm_buffer(buffer) - } - Err(err) => { - error!(self.log, "EGL error"; "err" => format!("{:?}", err)); - Err(buffer) - } - } - } - - #[cfg(not(feature = "egl"))] - pub fn load_buffer(&self, buffer: WlBuffer) -> Result { - self.load_shm_buffer(buffer) - } - - fn load_shm_buffer(&self, buffer: WlBuffer) -> Result { - let (width, height, format) = - match shm_buffer_contents(&buffer, |_, data| (data.width, data.height, data.format)) { - Ok(x) => x, - Err(err) => { - warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); - return Err(buffer); - } - }; - let shader = match crate::shm_load::load_format(format) { - Ok(x) => x.1, - Err(format) => { - warn!(self.log, "Unable to load buffer format: {:?}", format); - return Err(buffer); - } - }; - Ok(BufferTextures { - buffer, - textures: HashMap::new(), - fragment: shader, - y_inverted: false, - dimensions: (width as u32, height as u32), - #[cfg(feature = "egl")] - images: None, - logger: self.log.clone(), - }) - } -} - -pub struct BufferTextures { - buffer: WlBuffer, - pub textures: HashMap, - pub fragment: usize, - pub y_inverted: bool, - pub dimensions: (u32, u32), - #[cfg(feature = "egl")] - images: Option, - logger: slog::Logger, -} - -impl BufferTextures { - #[cfg(feature = "egl")] - pub fn load_texture<'a, F: GLGraphicsBackend + 'static>( - &'a mut self, - drawer: &GliumDrawer, - ) -> Result<&'a Texture2d, ()> { - if self.textures.contains_key(&drawer.id) { - return Ok(&self.textures[&drawer.id]); - } - - if let Some(images) = self.images.as_ref() { - //EGL buffer - let format = match images.format { - Format::RGB => UncompressedFloatFormat::U8U8U8, - Format::RGBA => UncompressedFloatFormat::U8U8U8U8, - _ => { - warn!(self.logger, "Unsupported EGL buffer format"; "format" => format!("{:?}", images.format)); - return Err(()); - } - }; - - let opengl_texture = Texture2d::empty_with_format( - &drawer.display, - format, - MipmapsOption::NoMipmap, - images.width, - images.height, - ) - .unwrap(); - - unsafe { - images - .bind_to_texture(0, opengl_texture.get_id(), &*drawer.display.borrow()) - .expect("Failed to bind to texture"); - } - - self.textures.insert(drawer.id, opengl_texture); - Ok(&self.textures[&drawer.id]) - } else { - self.load_shm_texture(drawer) - } - } - - #[cfg(not(feature = "egl"))] - pub fn load_texture<'a, F: GLGraphicsBackend + 'static>( - &'a mut self, - drawer: &GliumDrawer, - ) -> Result<&'a Texture2d, ()> { - if self.textures.contains_key(&drawer.id) { - return Ok(&self.textures[&drawer.id]); - } - - self.load_shm_texture(drawer) - } - - fn load_shm_texture<'a, F: GLGraphicsBackend + 'static>( - &'a mut self, - drawer: &GliumDrawer, - ) -> Result<&'a Texture2d, ()> { - match shm_buffer_contents(&self.buffer, |slice, data| { - crate::shm_load::load_shm_buffer(data, slice) - .map(|(image, _kind)| Texture2d::new(&drawer.display, image).unwrap()) - }) { - Ok(Ok(texture)) => { - self.textures.insert(drawer.id, texture); - Ok(&self.textures[&drawer.id]) - } - Ok(Err(format)) => { - warn!(self.logger, "Unsupported SHM buffer format"; "format" => format!("{:?}", format)); - Err(()) - } - Err(err) => { - warn!(self.logger, "Unable to load buffer contents"; "err" => format!("{:?}", err)); - Err(()) - } - } - } -} - -impl Drop for BufferTextures { - fn drop(&mut self) { - self.buffer.release() - } } diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs new file mode 100644 index 0000000..cc5f4f9 --- /dev/null +++ b/anvil/src/drawing.rs @@ -0,0 +1,198 @@ +use std::cell::RefCell; + +use slog::Logger; +use smithay::{ + backend::renderer::{Renderer, Frame, Transform, Texture}, + reexports::{wayland_server::protocol::wl_surface}, + utils::Rectangle, + wayland::{ + compositor::{roles::Role, SubsurfaceRole, TraversalAction}, + data_device::DnDIconRole, + seat::CursorImageRole, + }, +}; + +use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; + +pub fn draw_cursor( + renderer: &mut R, + frame: &mut F, + surface: &wl_surface::WlSurface, + (x, y): (i32, i32), + token: MyCompositorToken, + log: &Logger, +) + where + R: Renderer, + F: Frame, + E: std::error::Error, + T: Texture + 'static, +{ + let (dx, dy) = match token.with_role_data::(surface, |data| data.hotspot) { + Ok(h) => h, + Err(_) => { + warn!( + log, + "Trying to display as a cursor a surface that does not have the CursorImage role." + ); + (0, 0) + } + }; + draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), token, log); +} + +fn draw_surface_tree( + renderer: &mut R, + frame: &mut F, + root: &wl_surface::WlSurface, + location: (i32, i32), + compositor_token: MyCompositorToken, + log: &Logger, +) + where + R: Renderer, + F: Frame, + E: std::error::Error, + T: Texture + 'static, +{ + compositor_token.with_surface_tree_upward( + root, + location, + |_surface, attributes, role, &(mut x, mut y)| { + // Pull a new buffer if available + if let Some(data) = attributes.user_data.get::>() { + let mut data = data.borrow_mut(); + if data.texture.is_none() { + if let Some(buffer) = data.current_state.buffer.take() { + match renderer.import_shm(&buffer) { + Ok(m) => data.texture = Some(Box::new(m) as Box), + // there was an error reading the buffer, release it, we + // already logged the error + Err(err) => { + warn!(log, "Error loading buffer: {}", err); + }, + }; + buffer.release(); + } + } + // Now, should we be drawn ? + if data.texture.is_some() { + // if yes, also process the children + if Role::::has(role) { + x += data.current_state.sub_location.0; + y += data.current_state.sub_location.1; + } + TraversalAction::DoChildren((x, y)) + } else { + // we are not displayed, so our children are neither + TraversalAction::SkipChildren + } + } else { + // we are not displayed, so our children are neither + TraversalAction::SkipChildren + } + }, + |_surface, attributes, role, &(mut x, mut y)| { + if let Some(ref data) = attributes.user_data.get::>() { + let mut data = data.borrow_mut(); + let (sub_x, sub_y) = data.current_state.sub_location; + if let Some(buffer_texture) = data.texture.as_ref().and_then(|x| x.downcast_ref::()) { + // we need to re-extract the subsurface offset, as the previous closure + // only passes it to our children + if Role::::has(role) { + x += sub_x; + y += sub_y; + } + frame.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0); + } + } + }, + |_, _, _, _| true, + ); +} + +pub fn draw_windows( + renderer: &mut R, + frame: &mut F, + window_map: &MyWindowMap, + output_rect: Option, + compositor_token: MyCompositorToken, + log: &::slog::Logger, +) + where + R: Renderer, + F: Frame, + E: std::error::Error, + T: Texture + 'static, +{ + // redraw the frame, in a simple but inneficient way + { + window_map.with_windows_from_bottom_to_top( + |toplevel_surface, mut initial_place, bounding_box| { + // skip windows that do not overlap with a given output + if let Some(output) = output_rect { + if !output.overlaps(bounding_box) { + return; + } + initial_place.0 -= output.x; + } + if let Some(wl_surface) = toplevel_surface.get_surface() { + // this surface is a root of a subsurface tree that needs to be drawn + draw_surface_tree( + renderer, + frame, + &wl_surface, + initial_place, + compositor_token, + log, + ); + } + }, + ); + } +} + +pub fn draw_dnd_icon( + renderer: &mut R, + frame: &mut F, + surface: &wl_surface::WlSurface, + (x, y): (i32, i32), + token: MyCompositorToken, + log: &::slog::Logger, +) + where + R: Renderer, + F: Frame, + E: std::error::Error, + T: Texture + 'static, +{ + if !token.has_role::(surface) { + warn!( + log, + "Trying to display as a dnd icon a surface that does not have the DndIcon role." + ); + } + draw_surface_tree(renderer, frame, surface, (x, y), token, log); +} + +/* +pub fn schedule_initial_render( + renderer: Rc>, + evt_handle: &LoopHandle, +) { + let mut frame = renderer.draw(); + frame.clear_color(0.8, 0.8, 0.9, 1.0); + if let Err(err) = frame.set_finish() { + match err { + SwapBuffersError::AlreadySwapped => {} + SwapBuffersError::TemporaryFailure(err) => { + // TODO dont reschedule after 3(?) retries + warn!(renderer.log, "Failed to submit page_flip: {}", err); + let handle = evt_handle.clone(); + evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle)); + } + SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err), + } + } +} +*/ diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs deleted file mode 100644 index bcf2a76..0000000 --- a/anvil/src/glium_drawer.rs +++ /dev/null @@ -1,398 +0,0 @@ -use std::{ - cell::{Ref, RefCell}, - rc::Rc, - sync::atomic::{AtomicBool, AtomicUsize, Ordering}, -}; - -use glium::{self, index::PrimitiveType, texture::Texture2d, Surface}; -use slog::Logger; - -use smithay::{ - backend::graphics::{ - gl::GLGraphicsBackend, - glium::{Frame, GliumGraphicsBackend}, - CursorBackend, SwapBuffersError, - }, - reexports::{calloop::LoopHandle, wayland_server::protocol::wl_surface}, - utils::Rectangle, - wayland::{ - compositor::{roles::Role, SubsurfaceRole, TraversalAction}, - data_device::DnDIconRole, - seat::CursorImageRole, - }, -}; - -use crate::buffer_utils::BufferUtils; -use crate::shaders; -use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; - -pub static BACKEND_COUNTER: AtomicUsize = AtomicUsize::new(0); - -#[derive(Copy, Clone)] -struct Vertex { - position: [f32; 2], - tex_coords: [f32; 2], -} - -mod implement_vertex { - #![allow(clippy::unneeded_field_pattern)] - // Module to scope the clippy lint disabling - use super::Vertex; - implement_vertex!(Vertex, position, tex_coords); -} - -pub struct GliumDrawer { - pub id: usize, - pub display: GliumGraphicsBackend, - vertex_buffer: glium::VertexBuffer, - index_buffer: glium::IndexBuffer, - programs: [glium::Program; shaders::FRAGMENT_COUNT], - buffer_loader: BufferUtils, - pub hardware_cursor: AtomicBool, - log: Logger, -} - -impl GliumDrawer { - pub fn borrow(&self) -> Ref<'_, F> { - self.display.borrow() - } -} - -impl> + GLGraphicsBackend + 'static> GliumDrawer { - pub fn init(backend: T, buffer_loader: BufferUtils, log: Logger) -> GliumDrawer { - let display = backend.into(); - - // building the vertex buffer, which contains all the vertices that we will draw - let vertex_buffer = glium::VertexBuffer::new( - &display, - &[ - Vertex { - position: [0.0, 0.0], - tex_coords: [0.0, 0.0], - }, - Vertex { - position: [0.0, 1.0], - tex_coords: [0.0, 1.0], - }, - Vertex { - position: [1.0, 1.0], - tex_coords: [1.0, 1.0], - }, - Vertex { - position: [1.0, 0.0], - tex_coords: [1.0, 0.0], - }, - ], - ) - .unwrap(); - - // building the index buffer - let index_buffer = - glium::IndexBuffer::new(&display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap(); - - let programs = opengl_programs!(&display); - - GliumDrawer { - id: BACKEND_COUNTER.fetch_add(1, Ordering::AcqRel), - display, - vertex_buffer, - index_buffer, - programs, - buffer_loader, - hardware_cursor: AtomicBool::new(false), - log, - } - } -} - -impl GliumDrawer { - pub fn draw_hardware_cursor( - &self, - cursor: &::CursorFormat, - hotspot: (u32, u32), - position: (i32, i32), - ) { - let (x, y) = position; - let _ = self.display.borrow().set_cursor_position(x as u32, y as u32); - if !self.hardware_cursor.swap(true, Ordering::SeqCst) - && self - .display - .borrow() - .set_cursor_representation(cursor, hotspot) - .is_err() - { - warn!(self.log, "Failed to upload hardware cursor",); - } - } - - pub fn draw_software_cursor( - &self, - frame: &mut Frame, - surface: &wl_surface::WlSurface, - (x, y): (i32, i32), - token: MyCompositorToken, - ) { - let (dx, dy) = match token.with_role_data::(surface, |data| data.hotspot) { - Ok(h) => h, - Err(_) => { - warn!( - self.log, - "Trying to display as a cursor a surface that does not have the CursorImage role." - ); - (0, 0) - } - }; - let screen_dimensions = self.borrow().get_framebuffer_dimensions(); - self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions); - self.clear_cursor() - } - - pub fn clear_cursor(&self) { - if self.hardware_cursor.swap(false, Ordering::SeqCst) - && self.display.borrow().clear_cursor_representation().is_err() - { - warn!(self.log, "Failed to clear cursor"); - } - } -} - -// I would love to do this (check on !CursorBackend), but this is essentially specialization... -// And since this is just an example compositor, it seems we require now, -// that for the use of software cursors we need the hardware cursor trait (to do automatic cleanup..) -/* -impl GliumDrawer { - pub fn draw_software_cursor( - &self, - frame: &mut Frame, - surface: &wl_surface::WlSurface, - (x, y): (i32, i32), - token: MyCompositorToken, - ) { - let (dx, dy) = match token.with_role_data::(surface, |data| data.hotspot) { - Ok(h) => h, - Err(_) => { - warn!( - self.log, - "Trying to display as a cursor a surface that does not have the CursorImage role." - ); - (0, 0) - } - }; - let screen_dimensions = self.borrow().get_framebuffer_dimensions(); - self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions); - } -} -*/ - -impl GliumDrawer { - pub fn render_texture(&self, target: &mut Frame, spec: RenderTextureSpec<'_>) { - let xscale = 2.0 * (spec.surface_dimensions.0 as f32) / (spec.screen_size.0 as f32); - let mut yscale = -2.0 * (spec.surface_dimensions.1 as f32) / (spec.screen_size.1 as f32); - - let x = 2.0 * (spec.surface_location.0 as f32) / (spec.screen_size.0 as f32) - 1.0; - let mut y = 1.0 - 2.0 * (spec.surface_location.1 as f32) / (spec.screen_size.1 as f32); - - if spec.y_inverted { - yscale = -yscale; - y -= spec.surface_dimensions.1 as f32; - } - - let uniforms = uniform! { - matrix: [ - [xscale, 0.0 , 0.0, 0.0], - [ 0.0 , yscale , 0.0, 0.0], - [ 0.0 , 0.0 , 1.0, 0.0], - [ x , y , 0.0, 1.0] - ], - tex: spec.texture, - }; - - target - .draw( - &self.vertex_buffer, - &self.index_buffer, - &self.programs[spec.texture_kind], - &uniforms, - &glium::DrawParameters { - blend: spec.blending, - ..Default::default() - }, - ) - .unwrap(); - } - - #[inline] - pub fn draw(&self) -> Frame { - self.display.draw() - } -} - -pub struct RenderTextureSpec<'a> { - texture: &'a Texture2d, - texture_kind: usize, - y_inverted: bool, - surface_dimensions: (u32, u32), - surface_location: (i32, i32), - screen_size: (u32, u32), - blending: glium::Blend, -} - -impl GliumDrawer { - fn draw_surface_tree( - &self, - frame: &mut Frame, - root: &wl_surface::WlSurface, - location: (i32, i32), - compositor_token: MyCompositorToken, - screen_dimensions: (u32, u32), - ) { - compositor_token.with_surface_tree_upward( - root, - location, - |_surface, attributes, role, &(mut x, mut y)| { - // Pull a new buffer if available - if let Some(data) = attributes.user_data.get::>() { - let mut data = data.borrow_mut(); - if data.texture.is_none() { - if let Some(buffer) = data.current_state.buffer.take() { - match self.buffer_loader.load_buffer(buffer) { - Ok(m) => data.texture = Some(m), - // there was an error reading the buffer, release it, we - // already logged the error - Err(buffer) => buffer.release(), - }; - } - } - // Now, should we be drawn ? - if data.texture.is_some() { - // if yes, also process the children - if Role::::has(role) { - x += data.current_state.sub_location.0; - y += data.current_state.sub_location.1; - } - TraversalAction::DoChildren((x, y)) - } else { - // we are not displayed, so our children are neither - TraversalAction::SkipChildren - } - } else { - // we are not displayed, so our children are neither - TraversalAction::SkipChildren - } - }, - |_surface, attributes, role, &(mut x, mut y)| { - if let Some(ref data) = attributes.user_data.get::>() { - let mut data = data.borrow_mut(); - let (sub_x, sub_y) = data.current_state.sub_location; - if let Some(buffer_textures) = data.texture.as_mut() { - let texture_kind = buffer_textures.fragment; - let y_inverted = buffer_textures.y_inverted; - let surface_dimensions = buffer_textures.dimensions; - if let Ok(ref texture) = buffer_textures.load_texture(&self) { - // we need to re-extract the subsurface offset, as the previous closure - // only passes it to our children - if Role::::has(role) { - x += sub_x; - y += sub_y; - } - self.render_texture( - frame, - RenderTextureSpec { - texture: &texture, - texture_kind, - y_inverted, - surface_dimensions, - surface_location: (x, y), - screen_size: screen_dimensions, - blending: ::glium::Blend { - color: ::glium::BlendingFunction::Addition { - source: ::glium::LinearBlendingFactor::One, - destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - alpha: ::glium::BlendingFunction::Addition { - source: ::glium::LinearBlendingFactor::One, - destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - ..Default::default() - }, - }, - ); - } - } - } - }, - |_, _, _, _| true, - ); - } - - pub fn draw_windows( - &self, - frame: &mut Frame, - window_map: &MyWindowMap, - output_rect: Option, - compositor_token: MyCompositorToken, - ) { - // redraw the frame, in a simple but inneficient way - { - let screen_dimensions = self.borrow().get_framebuffer_dimensions(); - window_map.with_windows_from_bottom_to_top( - |toplevel_surface, mut initial_place, bounding_box| { - // skip windows that do not overlap with a given output - if let Some(output) = output_rect { - if !output.overlaps(bounding_box) { - return; - } - initial_place.0 -= output.x; - } - if let Some(wl_surface) = toplevel_surface.get_surface() { - // this surface is a root of a subsurface tree that needs to be drawn - self.draw_surface_tree( - frame, - &wl_surface, - initial_place, - compositor_token, - screen_dimensions, - ); - } - }, - ); - } - } - - pub fn draw_dnd_icon( - &self, - frame: &mut Frame, - surface: &wl_surface::WlSurface, - (x, y): (i32, i32), - token: MyCompositorToken, - ) { - if !token.has_role::(surface) { - warn!( - self.log, - "Trying to display as a dnd icon a surface that does not have the DndIcon role." - ); - } - let screen_dimensions = self.borrow().get_framebuffer_dimensions(); - self.draw_surface_tree(frame, surface, (x, y), token, screen_dimensions); - } -} - -pub fn schedule_initial_render( - renderer: Rc>, - evt_handle: &LoopHandle, -) { - let mut frame = renderer.draw(); - frame.clear_color(0.8, 0.8, 0.9, 1.0); - if let Err(err) = frame.set_finish() { - match err { - SwapBuffersError::AlreadySwapped => {} - SwapBuffersError::TemporaryFailure(err) => { - // TODO dont reschedule after 3(?) retries - warn!(renderer.log, "Failed to submit page_flip: {}", err); - let handle = evt_handle.clone(); - evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle)); - } - SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err), - } - } -} diff --git a/anvil/src/main.rs b/anvil/src/main.rs index 99211e1..20f58bf 100644 --- a/anvil/src/main.rs +++ b/anvil/src/main.rs @@ -1,7 +1,5 @@ #![warn(rust_2018_idioms)] -#[macro_use] -extern crate glium; #[macro_use] extern crate slog; #[macro_use(define_roles)] @@ -12,13 +10,10 @@ use std::{cell::RefCell, rc::Rc}; use slog::Drain; use smithay::reexports::{calloop::EventLoop, wayland_server::Display}; -#[macro_use] -mod shaders; mod buffer_utils; -mod glium_drawer; +mod drawing; mod input_handler; mod shell; -mod shm_load; mod state; #[cfg(feature = "udev")] mod udev; @@ -41,6 +36,7 @@ fn main() { // A logger facility, here we use the terminal here let log = slog::Logger::root( slog_async::Async::default(slog_term::term_full().fuse()).fuse(), + //std::sync::Mutex::new(slog_term::term_full().fuse()).fuse(), o!(), ); diff --git a/anvil/src/shaders.rs b/anvil/src/shaders.rs deleted file mode 100644 index 3e6e4c2..0000000 --- a/anvil/src/shaders.rs +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is the single point of definition of the opengl shaders - * and their indexes. - * - * The opengl_programs!() macro must call make_program!() in the correct - * order matching the indices stored in the BUFFER_* constants, if it - * does not, things will be drawn on screen with wrong colors. - */ - -// create a set of shaders for various loading types -macro_rules! make_program( - ($display: expr, $fragment_shader:expr) => { - program!($display, - 100 => { - vertex: crate::shaders::VERTEX_SHADER, - fragment: $fragment_shader, - }, - ).unwrap() - } -); - -#[macro_escape] -macro_rules! opengl_programs( - ($display: expr) => { - [ - make_program!($display, crate::shaders::FRAGMENT_SHADER_RGBA), - make_program!($display, crate::shaders::FRAGMENT_SHADER_ABGR), - make_program!($display, crate::shaders::FRAGMENT_SHADER_XBGR), - make_program!($display, crate::shaders::FRAGMENT_SHADER_BGRA), - make_program!($display, crate::shaders::FRAGMENT_SHADER_BGRX), - ] - } -); - -/* - * OpenGL Shaders - */ - -pub const VERTEX_SHADER: &str = r#" -#version 100 -uniform lowp mat4 matrix; -attribute lowp vec2 position; -attribute lowp vec2 tex_coords; -varying lowp vec2 v_tex_coords; -void main() { - gl_Position = matrix * vec4(position, 0.0, 1.0); - v_tex_coords = tex_coords; -}"#; - -pub const FRAGMENT_COUNT: usize = 5; - -pub const BUFFER_RGBA: usize = 0; -pub const FRAGMENT_SHADER_RGBA: &str = r#" -#version 100 -uniform lowp sampler2D tex; -varying lowp vec2 v_tex_coords; -void main() { - lowp vec4 color = texture2D(tex, v_tex_coords); - gl_FragColor.r = color.x; - gl_FragColor.g = color.y; - gl_FragColor.b = color.z; - gl_FragColor.a = color.w; -} -"#; - -pub const BUFFER_ABGR: usize = 1; -pub const FRAGMENT_SHADER_ABGR: &str = r#" -#version 100 -uniform lowp sampler2D tex; -varying lowp vec2 v_tex_coords; -void main() { - lowp vec4 color = texture2D(tex, v_tex_coords); - gl_FragColor.r = color.w; - gl_FragColor.g = color.z; - gl_FragColor.b = color.y; - gl_FragColor.a = color.x; -} -"#; - -pub const BUFFER_XBGR: usize = 2; -pub const FRAGMENT_SHADER_XBGR: &str = r#" -#version 100 -uniform lowp sampler2D tex; -varying lowp vec2 v_tex_coords; -void main() { - lowp vec4 color = texture2D(tex, v_tex_coords); - gl_FragColor.r = color.w; - gl_FragColor.g = color.z; - gl_FragColor.b = color.y; - gl_FragColor.a = 1.0; -} -"#; - -pub const BUFFER_BGRA: usize = 3; -pub const FRAGMENT_SHADER_BGRA: &str = r#" -#version 100 -uniform lowp sampler2D tex; -varying lowp vec2 v_tex_coords; -void main() { - lowp vec4 color = texture2D(tex, v_tex_coords); - gl_FragColor.r = color.z; - gl_FragColor.g = color.y; - gl_FragColor.b = color.x; - gl_FragColor.a = color.w; -} -"#; - -pub const BUFFER_BGRX: usize = 4; -pub const FRAGMENT_SHADER_BGRX: &str = r#" -#version 100 -uniform lowp sampler2D tex; -varying lowp vec2 v_tex_coords; -void main() { - lowp vec4 color = texture2D(tex, v_tex_coords); - gl_FragColor.r = color.z; - gl_FragColor.g = color.y; - gl_FragColor.b = color.x; - gl_FragColor.a = 1.0; -} -"#; diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index cb94ebd..c531ecd 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -672,7 +672,7 @@ pub struct CommitedState { #[derive(Default)] pub struct SurfaceData { - pub texture: Option, + pub texture: Option>, pub geometry: Option, pub resize_state: ResizeState, /// Minimum width and height, as requested by the surface. diff --git a/anvil/src/shm_load.rs b/anvil/src/shm_load.rs deleted file mode 100644 index abb41c6..0000000 --- a/anvil/src/shm_load.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::borrow::Cow; - -use smithay::{reexports::wayland_server::protocol::wl_shm::Format, wayland::shm::BufferData}; - -use glium::texture::{ClientFormat, RawImage2d}; - -pub fn load_shm_buffer(data: BufferData, pool: &[u8]) -> Result<(RawImage2d<'_, u8>, usize), Format> { - let offset = data.offset as usize; - let width = data.width as usize; - let height = data.height as usize; - let stride = data.stride as usize; - - // number of bytes per pixel - // TODO: compute from data.format - let pixelsize = 4; - - // ensure consistency, the SHM handler of smithay should ensure this - assert!(offset + (height - 1) * stride + width * pixelsize <= pool.len()); - - let slice: Cow<'_, [u8]> = if stride == width * pixelsize { - // the buffer is cleanly continuous, use as-is - Cow::Borrowed(&pool[offset..(offset + height * width * pixelsize)]) - } else { - // the buffer is discontinuous or lines overlap - // we need to make a copy as unfortunately Glium does not - // expose the OpenGL APIs we would need to load this buffer :/ - let mut data = Vec::with_capacity(height * width * pixelsize); - for i in 0..height { - data.extend(&pool[(offset + i * stride)..(offset + i * stride + width * pixelsize)]); - } - Cow::Owned(data) - }; - - // sharders format need to be reversed to account for endianness - let (client_format, fragment) = load_format(data.format)?; - Ok(( - RawImage2d { - data: slice, - width: width as u32, - height: height as u32, - format: client_format, - }, - fragment, - )) -} - -pub fn load_format(format: Format) -> Result<(ClientFormat, usize), Format> { - Ok(match format { - Format::Argb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRA), - Format::Xrgb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRX), - Format::Rgba8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_ABGR), - Format::Rgbx8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_XBGR), - _ => return Err(format), - }) -} diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index e5a8b59..14d72a2 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -16,7 +16,7 @@ use slog::Logger; use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; use smithay::{ backend::{ - drm::{ + drm_old::{ atomic::{AtomicDrmDevice, AtomicDrmSurface}, common::fallback::{FallbackDevice, FallbackSurface}, device_bind, @@ -679,9 +679,9 @@ impl DrmRenderer { match err { SwapBuffersError::AlreadySwapped => false, SwapBuffersError::TemporaryFailure(err) => { - match err.downcast_ref::() { - Some(&smithay::backend::drm::common::Error::DeviceInactive) => false, - Some(&smithay::backend::drm::common::Error::Access { + match err.downcast_ref::() { + Some(&smithay::backend::drm_old::common::Error::DeviceInactive) => false, + Some(&smithay::backend::drm_old::common::Error::Access { ref source, .. }) if matches!(source.get_ref(), drm::SystemError::PermissionDenied) => false, _ => true, diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index eafbfcf..aa52584 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -1,9 +1,9 @@ use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration}; -#[cfg(feature = "egl")] -use smithay::backend::egl::EGLGraphicsBackend; +//#[cfg(feature = "egl")] +//use smithay::backend::egl::EGLGraphicsBackend; use smithay::{ - backend::{graphics::gl::GLGraphicsBackend, input::InputBackend, winit}, + backend::{renderer::Frame, input::InputBackend, winit, SwapBuffersError}, reexports::{ calloop::EventLoop, wayland_server::{protocol::wl_output, Display}, @@ -17,19 +17,20 @@ use smithay::{ use slog::Logger; -use crate::buffer_utils::BufferUtils; -use crate::glium_drawer::GliumDrawer; use crate::state::AnvilState; +use crate::buffer_utils::BufferUtils; +use crate::drawing::*; pub fn run_winit( display: Rc>, event_loop: &mut EventLoop, log: Logger, ) -> Result<(), ()> { - let (renderer, mut input) = winit::init(log.clone()).map_err(|err| { + let (mut renderer, mut input) = winit::init(log.clone()).map_err(|err| { slog::crit!(log, "Failed to initialize Winit backend: {}", err); })?; + /* #[cfg(feature = "egl")] let egl_buffer_reader = Rc::new(RefCell::new( if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display.borrow()) { @@ -43,10 +44,10 @@ pub fn run_winit( #[cfg(feature = "egl")] let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone()); #[cfg(not(feature = "egl"))] + */ let buffer_utils = BufferUtils::new(log.clone()); - let (w, h) = renderer.get_framebuffer_dimensions(); - let drawer = GliumDrawer::init(renderer, buffer_utils.clone(), log.clone()); + let (w, h): (u32, u32) = renderer.window_size().physical_size.into(); /* * Initialize the globals @@ -94,9 +95,13 @@ pub fn run_winit( info!(log, "Initialization completed, starting the main loop."); while state.running.load(Ordering::SeqCst) { - input + if input .dispatch_new_events(|event, _| state.process_input_event(event)) - .unwrap(); + .is_err() + { + state.running.store(false, Ordering::SeqCst); + break; + } // Send frame events so that client start drawing their next frame state @@ -107,12 +112,11 @@ pub fn run_winit( // drawing logic { - use glium::Surface; - let mut frame = drawer.draw(); - frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); + let mut frame = renderer.begin().expect("Failed to render frame"); + frame.clear([0.8, 0.8, 0.9, 1.0]); // draw the windows - drawer.draw_windows(&mut frame, &*state.window_map.borrow(), None, state.ctoken); + draw_windows(&mut renderer, &mut frame, &*state.window_map.borrow(), None, state.ctoken, &log); let (x, y) = *state.pointer_location.borrow(); // draw the dnd icon if any @@ -120,7 +124,7 @@ pub fn run_winit( let guard = state.dnd_icon.lock().unwrap(); if let Some(ref surface) = *guard { if surface.as_ref().is_alive() { - drawer.draw_dnd_icon(&mut frame, surface, (x as i32, y as i32), state.ctoken); + draw_dnd_icon(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log); } } } @@ -135,19 +139,23 @@ pub fn run_winit( if reset { *guard = CursorImageStatus::Default; } + // draw as relevant if let CursorImageStatus::Image(ref surface) = *guard { - drawer.draw_software_cursor(&mut frame, surface, (x as i32, y as i32), state.ctoken); + renderer.window().set_cursor_visible(false); + draw_cursor(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log); } else { - drawer.draw_hardware_cursor(&CursorIcon::Default, (0, 0), (x as i32, y as i32)); + renderer.window().set_cursor_visible(true); } } - if let Err(err) = frame.finish() { - error!(log, "Error during rendering: {:?}", err); + if let Err(SwapBuffersError::ContextLost(err)) = frame.finish() { + error!(log, "Critical Rendering Error: {}", err); + state.running.store(false, Ordering::SeqCst); } } + if event_loop .dispatch(Some(Duration::from_millis(16)), &mut state) .is_err()