anvil: Properly free textures

This commit is contained in:
Victor Brekenfeld 2021-04-28 20:27:38 +02:00
parent 85bef5fec6
commit 0661ebebb8
4 changed files with 58 additions and 21 deletions

View File

@ -1,5 +1,5 @@
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc, sync::mpsc::Sender};
#[cfg(feature = "udev")] #[cfg(feature = "udev")]
use smithay::backend::renderer::{Renderer, Texture}; use smithay::backend::renderer::{Renderer, Texture};
@ -86,6 +86,7 @@ impl BufferUtils {
Ok(BufferTextures { Ok(BufferTextures {
buffer, buffer,
textures: HashMap::new(), textures: HashMap::new(),
callbacks: HashMap::new(),
egl: egl_buffer, // I guess we need to keep this alive ? egl: egl_buffer, // I guess we need to keep this alive ?
}) })
} }
@ -103,6 +104,7 @@ impl BufferUtils {
pub struct BufferTextures<T> { pub struct BufferTextures<T> {
buffer: WlBuffer, buffer: WlBuffer,
pub textures: HashMap<dev_t, T>, pub textures: HashMap<dev_t, T>,
callbacks: HashMap<dev_t, Sender<T>>,
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
egl: Option<EGLBuffer>, egl: Option<EGLBuffer>,
} }
@ -110,10 +112,11 @@ pub struct BufferTextures<T> {
#[cfg(feature = "udev")] #[cfg(feature = "udev")]
impl<T: Texture> BufferTextures<T> { impl<T: Texture> BufferTextures<T> {
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
pub fn load_texture<'a, R: Renderer<Texture=T>>( pub fn load_texture<'a, R: Renderer<TextureId=T>>(
&'a mut self, &'a mut self,
id: u64, id: u64,
renderer: &mut R, renderer: &mut R,
texture_destruction_callback: &Sender<T>,
) -> Result<&'a T, R::Error> { ) -> Result<&'a T, R::Error> {
if self.textures.contains_key(&id) { if self.textures.contains_key(&id) {
return Ok(&self.textures[&id]); return Ok(&self.textures[&id]);
@ -122,10 +125,13 @@ impl<T: Texture> BufferTextures<T> {
if let Some(buffer) = self.egl.as_ref() { if let Some(buffer) = self.egl.as_ref() {
//EGL buffer //EGL buffer
let texture = renderer.import_egl(&buffer)?; let texture = renderer.import_egl(&buffer)?;
self.textures.insert(id, texture); if let Some(old_texture) = self.textures.insert(id, texture) {
let _ = renderer.destroy_texture(old_texture);
}
self.callbacks.insert(id, texture_destruction_callback.clone());
Ok(&self.textures[&id]) Ok(&self.textures[&id])
} else { } else {
self.load_shm_texture(id, renderer) self.load_shm_texture(id, renderer, texture_destruction_callback)
} }
} }
@ -134,22 +140,27 @@ impl<T: Texture> BufferTextures<T> {
&'a mut self, &'a mut self,
id: u64, id: u64,
renderer: &mut R, renderer: &mut R,
texture_destruction_callback: &Sender<T>,
) -> Result<&'a T, R::Error> { ) -> Result<&'a T, R::Error> {
if self.textures.contains_key(&id) { if self.textures.contains_key(&id) {
return Ok(&self.textures[&id]); return Ok(&self.textures[&id]);
} }
self.load_shm_texture(id, renderer) self.load_shm_texture(id, renderer, texture_destruction_callback)
} }
fn load_shm_texture<'a, R: Renderer<Texture=T>>( fn load_shm_texture<'a, R: Renderer<TextureId=T>>(
&'a mut self, &'a mut self,
id: u64, id: u64,
renderer: &mut R, renderer: &mut R,
texture_destruction_callback: &Sender<T>,
) -> Result<&'a T, R::Error> { ) -> Result<&'a T, R::Error> {
let texture = renderer.import_shm(&self.buffer)?; let texture = renderer.import_shm(&self.buffer)?;
self.textures.insert(id, texture); if let Some(old_texture) = self.textures.insert(id, texture) {
let _ = renderer.destroy_texture(old_texture)?;
}
self.callbacks.insert(id, texture_destruction_callback.clone());
Ok(&self.textures[&id]) Ok(&self.textures[&id])
} }
} }
@ -157,6 +168,9 @@ impl<T: Texture> BufferTextures<T> {
#[cfg(feature = "udev")] #[cfg(feature = "udev")]
impl<T> Drop for BufferTextures<T> { impl<T> Drop for BufferTextures<T> {
fn drop(&mut self) { fn drop(&mut self) {
self.buffer.release() self.buffer.release();
for (id, texture) in self.textures.drain() {
self.callbacks.get(&id).unwrap().send(texture).unwrap();
}
} }
} }

View File

@ -1,6 +1,7 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
rc::Rc, rc::Rc,
sync::mpsc::Sender,
}; };
use slog::Logger; use slog::Logger;
@ -25,6 +26,7 @@ use crate::buffer_utils::{BufferUtils, BufferTextures};
pub fn draw_cursor<R, E, T>( pub fn draw_cursor<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64, renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils, buffer_utils: &BufferUtils,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
(x, y): (i32, i32), (x, y): (i32, i32),
@ -32,7 +34,7 @@ pub fn draw_cursor<R, E, T>(
log: &Logger, log: &Logger,
) )
where where
R: Renderer<Error=E, Texture=T>, R: Renderer<Error=E, TextureId=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -46,12 +48,13 @@ pub fn draw_cursor<R, E, T>(
(0, 0) (0, 0)
} }
}; };
draw_surface_tree(renderer, renderer_id, buffer_utils, surface, (x - dx, y - dy), token, log); draw_surface_tree(renderer, renderer_id, texture_destruction_callback, buffer_utils, surface, (x - dx, y - dy), token, log);
} }
fn draw_surface_tree<R, E, T>( fn draw_surface_tree<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64, renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils, buffer_utils: &BufferUtils,
root: &wl_surface::WlSurface, root: &wl_surface::WlSurface,
location: (i32, i32), location: (i32, i32),
@ -59,7 +62,7 @@ fn draw_surface_tree<R, E, T>(
log: &Logger, log: &Logger,
) )
where where
R: Renderer<Error=E, Texture=T>, R: Renderer<Error=E, TextureId=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -72,7 +75,7 @@ fn draw_surface_tree<R, E, T>(
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
if data.texture.is_none() { if data.texture.is_none() {
if let Some(buffer) = data.current_state.buffer.take() { if let Some(buffer) = data.current_state.buffer.take() {
match buffer_utils.load_buffer::<R::Texture>(buffer) { match buffer_utils.load_buffer::<R::TextureId>(buffer) {
Ok(m) => data.texture = Some(Box::new(m) as Box<dyn std::any::Any + 'static>), Ok(m) => data.texture = Some(Box::new(m) as Box<dyn std::any::Any + 'static>),
// there was an error reading the buffer, release it, we // there was an error reading the buffer, release it, we
// already logged the error // already logged the error
@ -133,7 +136,7 @@ fn draw_surface_tree<R, E, T>(
x += sub_x; x += sub_x;
y += sub_y; y += sub_y;
} }
let texture = buffer_textures.load_texture(renderer_id, renderer).unwrap(); let texture = buffer_textures.load_texture(renderer_id, renderer, texture_destruction_callback).unwrap();
renderer.render_texture_at(texture, (x, y), Transform::Normal /* TODO */, 1.0); renderer.render_texture_at(texture, (x, y), Transform::Normal /* TODO */, 1.0);
} }
} }
@ -145,6 +148,7 @@ fn draw_surface_tree<R, E, T>(
pub fn draw_windows<R, E, T>( pub fn draw_windows<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64, renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils, buffer_utils: &BufferUtils,
window_map: &MyWindowMap, window_map: &MyWindowMap,
output_rect: Option<Rectangle>, output_rect: Option<Rectangle>,
@ -152,7 +156,7 @@ pub fn draw_windows<R, E, T>(
log: &::slog::Logger, log: &::slog::Logger,
) )
where where
R: Renderer<Error=E, Texture=T>, R: Renderer<Error=E, TextureId=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -172,6 +176,7 @@ pub fn draw_windows<R, E, T>(
draw_surface_tree( draw_surface_tree(
renderer, renderer,
renderer_id, renderer_id,
texture_destruction_callback,
buffer_utils, buffer_utils,
&wl_surface, &wl_surface,
initial_place, initial_place,
@ -187,6 +192,7 @@ pub fn draw_windows<R, E, T>(
pub fn draw_dnd_icon<R, E, T>( pub fn draw_dnd_icon<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64, renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils, buffer_utils: &BufferUtils,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
(x, y): (i32, i32), (x, y): (i32, i32),
@ -194,7 +200,7 @@ pub fn draw_dnd_icon<R, E, T>(
log: &::slog::Logger, log: &::slog::Logger,
) )
where where
R: Renderer<Error=E, Texture=T>, R: Renderer<Error=E, TextureId=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -204,7 +210,7 @@ pub fn draw_dnd_icon<R, E, T>(
"Trying to display as a dnd icon a surface that does not have the DndIcon role." "Trying to display as a dnd icon a surface that does not have the DndIcon role."
); );
} }
draw_surface_tree(renderer, renderer_id, buffer_utils, surface, (x, y), token, log); draw_surface_tree(renderer, renderer_id, texture_destruction_callback, buffer_utils, surface, (x, y), token, log);
} }
pub fn schedule_initial_render<R: Renderer + 'static, Data: 'static>( pub fn schedule_initial_render<R: Renderer + 'static, Data: 'static>(

View File

@ -5,7 +5,7 @@ use std::{
os::unix::io::{AsRawFd, RawFd}, os::unix::io::{AsRawFd, RawFd},
path::PathBuf, path::PathBuf,
rc::Rc, rc::Rc,
sync::{atomic::Ordering, Arc, Mutex}, sync::{atomic::Ordering, Arc, Mutex, mpsc},
time::Duration, time::Duration,
}; };
@ -494,6 +494,8 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
&self.logger, &self.logger,
))); )));
// we leak this texture (we would need to call `destroy_texture` on Drop of DrmRenderer),
// but only on shutdown anyway, because we do not support hot-pluggin, so it does not really matter.
let pointer_image = { let pointer_image = {
let context = EGLContext::new_shared(&egl, &context, self.logger.clone()).unwrap(); let context = EGLContext::new_shared(&egl, &context, self.logger.clone()).unwrap();
let mut renderer = unsafe { Gles2Renderer::new(context, self.logger.clone()).unwrap() }; let mut renderer = unsafe { Gles2Renderer::new(context, self.logger.clone()).unwrap() };
@ -508,6 +510,7 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
buffer_utils: self.buffer_utils.clone(), buffer_utils: self.buffer_utils.clone(),
compositor_token: self.compositor_token, compositor_token: self.compositor_token,
backends: backends.clone(), backends: backends.clone(),
texture_destruction_callback: mpsc::channel(),
window_map: self.window_map.clone(), window_map: self.window_map.clone(),
output_map: self.output_map.clone(), output_map: self.output_map.clone(),
pointer_location: self.pointer_location.clone(), pointer_location: self.pointer_location.clone(),
@ -647,6 +650,7 @@ pub struct DrmRenderer {
buffer_utils: BufferUtils, buffer_utils: BufferUtils,
compositor_token: CompositorToken<Roles>, compositor_token: CompositorToken<Roles>,
backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
texture_destruction_callback: (mpsc::Sender<Gles2Texture>, mpsc::Receiver<Gles2Texture>),
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
output_map: Rc<RefCell<Vec<MyOutput>>>, output_map: Rc<RefCell<Vec<MyOutput>>>,
pointer_location: Rc<RefCell<(f64, f64)>>, pointer_location: Rc<RefCell<(f64, f64)>>,
@ -672,6 +676,7 @@ impl DrmRenderer {
if let Some(surface) = self.backends.borrow().get(&crtc) { if let Some(surface) = self.backends.borrow().get(&crtc) {
let result = DrmRenderer::render_surface( let result = DrmRenderer::render_surface(
&mut *surface.borrow_mut(), &mut *surface.borrow_mut(),
&self.texture_destruction_callback.0,
&self.buffer_utils, &self.buffer_utils,
self.device_id, self.device_id,
crtc, crtc,
@ -739,12 +744,17 @@ impl DrmRenderer {
self.window_map self.window_map
.borrow() .borrow()
.send_frames(self.start_time.elapsed().as_millis() as u32); .send_frames(self.start_time.elapsed().as_millis() as u32);
while let Ok(texture) = self.texture_destruction_callback.1.try_recv() {
let _ = surface.borrow_mut().destroy_texture(texture);
}
} }
} }
} }
fn render_surface( fn render_surface(
surface: &mut RenderSurface, surface: &mut RenderSurface,
texture_destruction_callback: &mpsc::Sender<Gles2Texture>,
buffer_utils: &BufferUtils, buffer_utils: &BufferUtils,
device_id: dev_t, device_id: dev_t,
crtc: crtc::Handle, crtc: crtc::Handle,
@ -777,6 +787,7 @@ impl DrmRenderer {
draw_windows( draw_windows(
surface, surface,
device_id, device_id,
texture_destruction_callback,
buffer_utils, buffer_utils,
window_map, window_map,
Some(Rectangle { Some(Rectangle {
@ -800,7 +811,7 @@ impl DrmRenderer {
{ {
if let Some(ref wl_surface) = dnd_icon.as_ref() { if let Some(ref wl_surface) = dnd_icon.as_ref() {
if wl_surface.as_ref().is_alive() { if wl_surface.as_ref().is_alive() {
draw_dnd_icon(surface, device_id, buffer_utils, wl_surface, (ptr_x, ptr_y), compositor_token.clone(), logger); draw_dnd_icon(surface, device_id, texture_destruction_callback, buffer_utils, wl_surface, (ptr_x, ptr_y), compositor_token.clone(), logger);
} }
} }
} }
@ -819,6 +830,7 @@ impl DrmRenderer {
draw_cursor( draw_cursor(
surface, surface,
device_id, device_id,
texture_destruction_callback,
buffer_utils, buffer_utils,
wl_surface, wl_surface,
(ptr_x, ptr_y), (ptr_x, ptr_y),

View File

@ -91,6 +91,7 @@ pub fn run_winit(
info!(log, "Initialization completed, starting the main loop."); info!(log, "Initialization completed, starting the main loop.");
let (texture_send, texture_receive) = std::sync::mpsc::channel();
while state.running.load(Ordering::SeqCst) { while state.running.load(Ordering::SeqCst) {
if input if input
.dispatch_new_events(|event, _| state.process_input_event(event)) .dispatch_new_events(|event, _| state.process_input_event(event))
@ -113,7 +114,7 @@ pub fn run_winit(
renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame"); renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame");
// draw the windows // draw the windows
draw_windows(&mut renderer, 0, &buffer_utils, &*state.window_map.borrow(), None, state.ctoken, &log); draw_windows(&mut renderer, 0, &texture_send, &buffer_utils, &*state.window_map.borrow(), None, state.ctoken, &log);
let (x, y) = *state.pointer_location.borrow(); let (x, y) = *state.pointer_location.borrow();
// draw the dnd icon if any // draw the dnd icon if any
@ -121,7 +122,7 @@ pub fn run_winit(
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(&mut renderer, 0, &buffer_utils, surface, (x as i32, y as i32), state.ctoken, &log); draw_dnd_icon(&mut renderer, 0, &texture_send, &buffer_utils, surface, (x as i32, y as i32), state.ctoken, &log);
} }
} }
} }
@ -140,7 +141,7 @@ pub fn run_winit(
// draw as relevant // draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard { if let CursorImageStatus::Image(ref surface) = *guard {
renderer.window().set_cursor_visible(false); renderer.window().set_cursor_visible(false);
draw_cursor(&mut renderer, 0, &buffer_utils, surface, (x as i32, y as i32), state.ctoken, &log); draw_cursor(&mut renderer, 0, &texture_send, &buffer_utils, surface, (x as i32, y as i32), state.ctoken, &log);
} else { } else {
renderer.window().set_cursor_visible(true); renderer.window().set_cursor_visible(true);
} }
@ -162,6 +163,10 @@ pub fn run_winit(
display.borrow_mut().flush_clients(&mut state); display.borrow_mut().flush_clients(&mut state);
state.window_map.borrow_mut().refresh(); state.window_map.borrow_mut().refresh();
} }
while let Ok(texture) = texture_receive.try_recv() {
let _ = renderer.destroy_texture(texture);
}
} }
// Cleanup stuff // Cleanup stuff