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

View File

@ -1,12 +1,21 @@
//! EGL context related structs
use std::collections::HashSet;
use std::os::raw::c_int;
use std::sync::atomic::Ordering;
use std::{
collections::HashSet,
os::raw::c_int,
sync::{atomic::Ordering, Arc},
};
use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::allocator::Format as DrmFormat;
use crate::backend::egl::display::{EGLDisplay, PixelFormat};
use crate::backend::egl::EGLSurface;
use crate::{
backend::{
allocator::Format as DrmFormat,
egl::{
display::{EGLDisplay, PixelFormat},
EGLSurface,
},
},
utils::user_data::UserDataMap,
};
use slog::{info, o, trace};
@ -17,6 +26,7 @@ pub struct EGLContext {
pub(crate) display: EGLDisplay,
config_id: ffi::egl::types::EGLConfig,
pixel_format: Option<PixelFormat>,
user_data: Arc<UserDataMap>,
}
// EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {}
@ -162,6 +172,11 @@ impl EGLContext {
display: display.clone(),
config_id,
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> {
&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 {

View File

@ -173,7 +173,9 @@ impl 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
}
}

View File

@ -172,6 +172,16 @@ impl EGLSurface {
pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
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 {

View File

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