Merge pull request #460 from Smithay/feature/egl_userdata

This commit is contained in:
Victoria Brekenfeld 2022-01-14 11:03:14 +01:00 committed by GitHub
commit d554c7d2b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 306 additions and 93 deletions

View File

@ -168,24 +168,57 @@ pub fn run_winit(log: Logger) {
.and_then(|_| { .and_then(|_| {
backend backend
.renderer() .renderer()
.render(size, Transform::Flipped180, |renderer, frame| { .render(
render_layers_and_windows( output_geometry
renderer, .size
frame, .to_f64()
&*state.window_map.borrow(), .to_physical(output_scale as f64)
output_geometry, .to_i32_round(),
output_scale, Transform::Flipped180,
&log, |renderer, frame| {
)?; render_layers_and_windows(
renderer,
frame,
&*state.window_map.borrow(),
output_geometry,
output_scale,
&log,
)?;
let (x, y) = state.pointer_location.into(); let (x, y) = state.pointer_location.into();
// draw the dnd icon if any // draw the dnd icon if any
{ {
let guard = state.dnd_icon.lock().unwrap(); let guard = state.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard { if let Some(ref surface) = *guard {
if surface.as_ref().is_alive() { if surface.as_ref().is_alive() {
draw_dnd_icon( draw_dnd_icon(
renderer,
frame,
surface,
(x as i32, y as i32).into(),
output_scale,
&log,
)?;
}
}
}
// draw the cursor as relevant
{
let mut guard = state.cursor_status.lock().unwrap();
// reset the cursor if the surface is no longer alive
let mut reset = false;
if let CursorImageStatus::Image(ref surface) = *guard {
reset = !surface.as_ref().is_alive();
}
if reset {
*guard = CursorImageStatus::Default;
}
// draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard {
cursor_visible = false;
draw_cursor(
renderer, renderer,
frame, frame,
surface, surface,
@ -193,52 +226,27 @@ pub fn run_winit(log: Logger) {
output_scale, output_scale,
&log, &log,
)?; )?;
} else {
cursor_visible = true;
} }
} }
}
// draw the cursor as relevant
{
let mut guard = state.cursor_status.lock().unwrap();
// reset the cursor if the surface is no longer alive
let mut reset = false;
if let CursorImageStatus::Image(ref surface) = *guard {
reset = !surface.as_ref().is_alive();
}
if reset {
*guard = CursorImageStatus::Default;
}
// draw as relevant #[cfg(feature = "debug")]
if let CursorImageStatus::Image(ref surface) = *guard { {
cursor_visible = false; let fps = state.backend_data.fps.avg().round() as u32;
draw_cursor(
draw_fps(
renderer, renderer,
frame, frame,
surface, &state.backend_data.fps_texture,
(x as i32, y as i32).into(), output_scale as f64,
output_scale, fps,
&log,
)?; )?;
} else {
cursor_visible = true;
} }
}
#[cfg(feature = "debug")] Ok(())
{ },
let fps = state.backend_data.fps.avg().round() as u32; )
draw_fps(
renderer,
frame,
&state.backend_data.fps_texture,
output_scale as f64,
fps,
)?;
}
Ok(())
})
.map_err(Into::<SwapBuffersError>::into) .map_err(Into::<SwapBuffersError>::into)
.and_then(|x| x) .and_then(|x| x)
}) })

View File

@ -1,12 +1,21 @@
//! EGL context related structs //! EGL context related structs
use std::collections::HashSet; use std::{
use std::os::raw::c_int; collections::HashSet,
use std::sync::atomic::Ordering; os::raw::c_int,
sync::{atomic::Ordering, Arc},
};
use super::{ffi, wrap_egl_call, Error, MakeCurrentError}; use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::allocator::Format as DrmFormat; use crate::{
use crate::backend::egl::display::{EGLDisplay, PixelFormat}; backend::{
use crate::backend::egl::EGLSurface; allocator::Format as DrmFormat,
egl::{
display::{EGLDisplay, PixelFormat},
EGLSurface,
},
},
utils::user_data::UserDataMap,
};
use slog::{info, o, trace}; use slog::{info, o, trace};
@ -17,6 +26,7 @@ pub struct EGLContext {
pub(crate) display: EGLDisplay, pub(crate) display: EGLDisplay,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
pixel_format: Option<PixelFormat>, pixel_format: Option<PixelFormat>,
user_data: Arc<UserDataMap>,
} }
// EGLContexts can be moved between threads safely // EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {} unsafe impl Send for EGLContext {}
@ -162,6 +172,11 @@ impl EGLContext {
display: display.clone(), display: display.clone(),
config_id, config_id,
pixel_format, pixel_format,
user_data: if let Some(shared) = shared {
shared.user_data.clone()
} else {
Arc::new(UserDataMap::default())
},
}) })
} }
@ -241,6 +256,21 @@ impl EGLContext {
pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> { pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {
&self.display.dmabuf_import_formats &self.display.dmabuf_import_formats
} }
/// Retrieve user_data associated with this context
///
/// *Note:* UserData is shared between shared context, if constructed with
/// [`new_shared`](EGLContext::new_shared) or [`new_shared_with_config`](EGLContext::new_shared_with_config).
pub fn user_data(&self) -> &UserDataMap {
&*self.user_data
}
/// Get a raw handle to the underlying context.
///
/// The pointer will become invalid, when this struct is destroyed.
pub fn get_context_handle(&self) -> ffi::egl::types::EGLContext {
self.context
}
} }
impl Drop for EGLContext { impl Drop for EGLContext {

View File

@ -173,7 +173,9 @@ impl EGLDevice {
} }
/// Returns the pointer to the raw [`EGLDevice`]. /// Returns the pointer to the raw [`EGLDevice`].
pub fn inner(&self) -> *const c_void { ///
/// The pointer will become invalid, when this struct is destroyed.
pub fn get_device_handle(&self) -> *const c_void {
self.inner self.inner
} }
} }

View File

@ -172,6 +172,16 @@ impl EGLSurface {
pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool { pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
self.native.resize(width, height, dx, dy) self.native.resize(width, height, dx, dy)
} }
/// Get a raw handle to the underlying surface
///
/// *Note*: The surface might get dynamically recreated during swap-buffers
/// causing the pointer to become invalid.
///
/// The pointer will become invalid, when this struct is destroyed.
pub fn get_surface_handle(&self) -> ffi::egl::types::EGLSurface {
self.surface.load(Ordering::SeqCst)
}
} }
impl Drop for EGLSurface { impl Drop for EGLSurface {

View File

@ -178,6 +178,8 @@ pub struct Gles2Renderer {
// This field is only accessed if the image or wayland_frontend features are active // This field is only accessed if the image or wayland_frontend features are active
#[allow(dead_code)] #[allow(dead_code)]
destruction_callback_sender: Sender<CleanupResource>, destruction_callback_sender: Sender<CleanupResource>,
min_filter: TextureFilter,
max_filter: TextureFilter,
logger_ptr: Option<*mut ::slog::Logger>, logger_ptr: Option<*mut ::slog::Logger>,
logger: ::slog::Logger, logger: ::slog::Logger,
_not_send: *mut (), _not_send: *mut (),
@ -186,11 +188,14 @@ pub struct Gles2Renderer {
/// Handle to the currently rendered frame during [`Gles2Renderer::render`](Renderer::render) /// Handle to the currently rendered frame during [`Gles2Renderer::render`](Renderer::render)
pub struct Gles2Frame { pub struct Gles2Frame {
current_projection: Matrix3<f32>, current_projection: Matrix3<f32>,
transform: Transform,
gl: ffi::Gles2, gl: ffi::Gles2,
tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT], tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT],
solid_program: Gles2SolidProgram, solid_program: Gles2SolidProgram,
vbos: [ffi::types::GLuint; 2], vbos: [ffi::types::GLuint; 2],
size: Size<i32, Physical>, size: Size<i32, Physical>,
min_filter: TextureFilter,
max_filter: TextureFilter,
} }
impl fmt::Debug for Gles2Frame { impl fmt::Debug for Gles2Frame {
@ -200,6 +205,8 @@ impl fmt::Debug for Gles2Frame {
.field("tex_programs", &self.tex_programs) .field("tex_programs", &self.tex_programs)
.field("solid_program", &self.solid_program) .field("solid_program", &self.solid_program)
.field("size", &self.size) .field("size", &self.size)
.field("min_filter", &self.min_filter)
.field("max_filter", &self.max_filter)
.finish_non_exhaustive() .finish_non_exhaustive()
} }
} }
@ -215,6 +222,8 @@ impl fmt::Debug for Gles2Renderer {
.field("solid_program", &self.solid_program) .field("solid_program", &self.solid_program)
// ffi::Gles2 does not implement Debug // ffi::Gles2 does not implement Debug
.field("egl", &self.egl) .field("egl", &self.egl)
.field("min_filter", &self.min_filter)
.field("max_filter", &self.max_filter)
.field("logger", &self.logger) .field("logger", &self.logger)
.finish() .finish()
} }
@ -521,7 +530,7 @@ impl Gles2Renderer {
gl.BindBuffer(ffi::ARRAY_BUFFER, 0); gl.BindBuffer(ffi::ARRAY_BUFFER, 0);
let (tx, rx) = channel(); let (tx, rx) = channel();
let mut renderer = Gles2Renderer { let renderer = Gles2Renderer {
gl, gl,
egl: context, egl: context,
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
@ -537,12 +546,12 @@ impl Gles2Renderer {
destruction_callback: rx, destruction_callback: rx,
destruction_callback_sender: tx, destruction_callback_sender: tx,
vbos, vbos,
min_filter: TextureFilter::Nearest,
max_filter: TextureFilter::Linear,
logger_ptr, logger_ptr,
logger: log, logger: log,
_not_send: std::ptr::null_mut(), _not_send: std::ptr::null_mut(),
}; };
renderer.downscale_filter(TextureFilter::Nearest)?;
renderer.upscale_filter(TextureFilter::Linear)?;
renderer.egl.unbind()?; renderer.egl.unbind()?;
Ok(renderer) Ok(renderer)
} }
@ -635,7 +644,6 @@ impl ImportShm for Gles2Renderer {
unsafe { unsafe {
self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture); self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture);
self.gl self.gl
.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); .TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32);
self.gl self.gl
@ -1017,12 +1025,25 @@ impl Drop for Gles2Renderer {
} }
impl Gles2Renderer { impl Gles2Renderer {
/// Get access to the underlying [`EGLContext`].
///
/// *Note*: Modifying the context state, might result in rendering issues.
/// The context state is considerd an implementation detail
/// and no guarantee is made about what can or cannot be changed.
/// To make sure a certain modification does not interfere with
/// the renderer's behaviour, check the source.
pub fn egl_context(&self) -> &EGLContext {
&self.egl
}
/// Run custom code in the GL context owned by this renderer. /// Run custom code in the GL context owned by this renderer.
/// ///
/// *Note*: Any changes to the GL state should be restored at the end of this function. /// The OpenGL state of the renderer is considered an implementation detail
/// Otherwise this can lead to rendering errors while using functions of this renderer. /// and no guarantee is made about what can or cannot be changed,
/// Relying on any state set by the renderer may break on any smithay update as the /// as such you should reset everything you change back to its previous value
/// details about how this renderer works are considered an implementation detail. /// or check the source code of the version of Smithay you are using to ensure
/// your changes don't interfere with the renderer's behavior.
/// Doing otherwise can lead to rendering errors while using other functions of this renderer.
pub fn with_context<F, R>(&mut self, func: F) -> Result<R, Gles2Error> pub fn with_context<F, R>(&mut self, func: F) -> Result<R, Gles2Error>
where where
F: FnOnce(&mut Self, &ffi::Gles2) -> R, F: FnOnce(&mut Self, &ffi::Gles2) -> R,
@ -1039,31 +1060,11 @@ impl Renderer for Gles2Renderer {
type Frame = Gles2Frame; type Frame = Gles2Frame;
fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> { fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
self.make_current()?; self.min_filter = filter;
unsafe {
self.gl.TexParameteri(
ffi::TEXTURE_2D,
ffi::TEXTURE_MIN_FILTER,
match filter {
TextureFilter::Nearest => ffi::NEAREST as i32,
TextureFilter::Linear => ffi::LINEAR as i32,
},
);
}
Ok(()) Ok(())
} }
fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> { fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> {
self.make_current()?; self.max_filter = filter;
unsafe {
self.gl.TexParameteri(
ffi::TEXTURE_2D,
ffi::TEXTURE_MAG_FILTER,
match filter {
TextureFilter::Nearest => ffi::NEAREST as i32,
TextureFilter::Linear => ffi::LINEAR as i32,
},
);
}
Ok(()) Ok(())
} }
@ -1116,8 +1117,11 @@ impl Renderer for Gles2Renderer {
solid_program: self.solid_program.clone(), solid_program: self.solid_program.clone(),
// output transformation passed in by the user // output transformation passed in by the user
current_projection: flip180 * transform.matrix() * renderer, current_projection: flip180 * transform.matrix() * renderer,
transform,
vbos: self.vbos, vbos: self.vbos,
size, size,
min_filter: self.min_filter,
max_filter: self.max_filter,
}; };
let result = rendering(self, &mut frame); let result = rendering(self, &mut frame);
@ -1305,6 +1309,10 @@ impl Frame for Gles2Frame {
self.render_texture(texture, mat, Some(&damage), tex_verts, alpha) self.render_texture(texture, mat, Some(&damage), tex_verts, alpha)
} }
fn transformation(&self) -> Transform {
self.transform
}
} }
impl Gles2Frame { impl Gles2Frame {
@ -1331,8 +1339,22 @@ impl Gles2Frame {
unsafe { unsafe {
self.gl.ActiveTexture(ffi::TEXTURE0); self.gl.ActiveTexture(ffi::TEXTURE0);
self.gl.BindTexture(target, tex.0.texture); self.gl.BindTexture(target, tex.0.texture);
self.gl self.gl.TexParameteri(
.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); ffi::TEXTURE_2D,
ffi::TEXTURE_MIN_FILTER,
match self.min_filter {
TextureFilter::Nearest => ffi::NEAREST as i32,
TextureFilter::Linear => ffi::LINEAR as i32,
},
);
self.gl.TexParameteri(
ffi::TEXTURE_2D,
ffi::TEXTURE_MAG_FILTER,
match self.max_filter {
TextureFilter::Nearest => ffi::NEAREST as i32,
TextureFilter::Linear => ffi::LINEAR as i32,
},
);
self.gl.UseProgram(self.tex_programs[tex.0.texture_kind].program); self.gl.UseProgram(self.tex_programs[tex.0.texture_kind].program);
self.gl self.gl
@ -1415,4 +1437,9 @@ impl Gles2Frame {
Ok(()) Ok(())
} }
/// Projection matrix for this frame
pub fn projection(&self) -> &[f32; 9] {
self.current_projection.as_ref()
}
} }

View File

@ -108,6 +108,36 @@ impl Transform {
size size
} }
} }
/// Transforms a rectangle inside an area of a given size by applying this transformation
pub fn transform_rect_in<N: Coordinate, Kind>(
&self,
rect: Rectangle<N, Kind>,
area: &Size<N, Kind>,
) -> Rectangle<N, Kind> {
let size = self.transform_size(rect.size);
let loc = match *self {
Transform::Normal => rect.loc,
Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(),
Transform::_180 => (
area.w - rect.loc.x - rect.size.w,
area.h - rect.loc.y - rect.size.h,
)
.into(),
Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(),
Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(),
Transform::Flipped90 => (rect.loc.y, rect.loc.x).into(),
Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(),
Transform::Flipped270 => (
area.h - rect.loc.y - rect.size.h,
area.w - rect.loc.x - rect.size.w,
)
.into(),
};
Rectangle::from_loc_and_size(loc, size)
}
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
@ -219,6 +249,9 @@ pub trait Frame {
src_transform: Transform, src_transform: Transform,
alpha: f32, alpha: f32,
) -> Result<(), Self::Error>; ) -> Result<(), Self::Error>;
/// Output transformation that is applied to this frame
fn transformation(&self) -> Transform;
} }
/// Abstraction of commonly used rendering operations for compositors. /// Abstraction of commonly used rendering operations for compositors.
@ -527,3 +560,102 @@ pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<Size<i32, Physi
crate::wayland::shm::with_buffer_contents(buffer, |_, data| (data.width, data.height).into()).ok() crate::wayland::shm::with_buffer_contents(buffer, |_, data| (data.width, data.height).into()).ok()
} }
#[cfg(test)]
mod tests {
use super::Transform;
use crate::utils::{Logical, Rectangle, Size};
#[test]
fn transform_rect_ident() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Normal;
assert_eq!(rect, transform.transform_rect_in(rect, &size))
}
#[test]
fn transform_rect_90() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_90;
assert_eq!(
Rectangle::from_loc_and_size((30, 10), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_180() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_180;
assert_eq!(
Rectangle::from_loc_and_size((30, 30), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_270() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_270;
assert_eq!(
Rectangle::from_loc_and_size((20, 30), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped;
assert_eq!(
Rectangle::from_loc_and_size((30, 20), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f90() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 80));
let transform = Transform::Flipped90;
assert_eq!(
Rectangle::from_loc_and_size((20, 10), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f180() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped180;
assert_eq!(
Rectangle::from_loc_and_size((10, 30), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f270() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped270;
assert_eq!(
Rectangle::from_loc_and_size((30, 30), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
}

View File

@ -112,6 +112,10 @@ impl Frame for DummyFrame {
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
Ok(()) Ok(())
} }
fn transformation(&self) -> Transform {
Transform::Normal
}
} }
pub struct DummyTexture { pub struct DummyTexture {