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")]
use std::{cell::RefCell, rc::Rc};
use std::{cell::RefCell, rc::Rc, sync::mpsc::Sender};
#[cfg(feature = "udev")]
use smithay::backend::renderer::{Renderer, Texture};
@ -86,6 +86,7 @@ impl BufferUtils {
Ok(BufferTextures {
buffer,
textures: HashMap::new(),
callbacks: HashMap::new(),
egl: egl_buffer, // I guess we need to keep this alive ?
})
}
@ -103,6 +104,7 @@ impl BufferUtils {
pub struct BufferTextures<T> {
buffer: WlBuffer,
pub textures: HashMap<dev_t, T>,
callbacks: HashMap<dev_t, Sender<T>>,
#[cfg(feature = "egl")]
egl: Option<EGLBuffer>,
}
@ -110,10 +112,11 @@ pub struct BufferTextures<T> {
#[cfg(feature = "udev")]
impl<T: Texture> BufferTextures<T> {
#[cfg(feature = "egl")]
pub fn load_texture<'a, R: Renderer<Texture=T>>(
pub fn load_texture<'a, R: Renderer<TextureId=T>>(
&'a mut self,
id: u64,
renderer: &mut R,
texture_destruction_callback: &Sender<T>,
) -> Result<&'a T, R::Error> {
if self.textures.contains_key(&id) {
return Ok(&self.textures[&id]);
@ -122,10 +125,13 @@ impl<T: Texture> BufferTextures<T> {
if let Some(buffer) = self.egl.as_ref() {
//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])
} 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,
id: u64,
renderer: &mut R,
texture_destruction_callback: &Sender<T>,
) -> Result<&'a T, R::Error> {
if self.textures.contains_key(&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,
id: u64,
renderer: &mut R,
texture_destruction_callback: &Sender<T>,
) -> Result<&'a T, R::Error> {
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])
}
}
@ -157,6 +168,9 @@ impl<T: Texture> BufferTextures<T> {
#[cfg(feature = "udev")]
impl<T> Drop for BufferTextures<T> {
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::{
cell::RefCell,
rc::Rc,
sync::mpsc::Sender,
};
use slog::Logger;
@ -25,6 +26,7 @@ use crate::buffer_utils::{BufferUtils, BufferTextures};
pub fn draw_cursor<R, E, T>(
renderer: &mut R,
renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils,
surface: &wl_surface::WlSurface,
(x, y): (i32, i32),
@ -32,7 +34,7 @@ pub fn draw_cursor<R, E, T>(
log: &Logger,
)
where
R: Renderer<Error=E, Texture=T>,
R: Renderer<Error=E, TextureId=T>,
E: std::error::Error,
T: Texture + 'static,
{
@ -46,12 +48,13 @@ pub fn draw_cursor<R, E, T>(
(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>(
renderer: &mut R,
renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils,
root: &wl_surface::WlSurface,
location: (i32, i32),
@ -59,7 +62,7 @@ fn draw_surface_tree<R, E, T>(
log: &Logger,
)
where
R: Renderer<Error=E, Texture=T>,
R: Renderer<Error=E, TextureId=T>,
E: std::error::Error,
T: Texture + 'static,
{
@ -72,7 +75,7 @@ fn draw_surface_tree<R, E, T>(
let mut data = data.borrow_mut();
if data.texture.is_none() {
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>),
// there was an error reading the buffer, release it, we
// already logged the error
@ -133,7 +136,7 @@ fn draw_surface_tree<R, E, T>(
x += sub_x;
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);
}
}
@ -145,6 +148,7 @@ fn draw_surface_tree<R, E, T>(
pub fn draw_windows<R, E, T>(
renderer: &mut R,
renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils,
window_map: &MyWindowMap,
output_rect: Option<Rectangle>,
@ -152,7 +156,7 @@ pub fn draw_windows<R, E, T>(
log: &::slog::Logger,
)
where
R: Renderer<Error=E, Texture=T>,
R: Renderer<Error=E, TextureId=T>,
E: std::error::Error,
T: Texture + 'static,
{
@ -172,6 +176,7 @@ pub fn draw_windows<R, E, T>(
draw_surface_tree(
renderer,
renderer_id,
texture_destruction_callback,
buffer_utils,
&wl_surface,
initial_place,
@ -187,6 +192,7 @@ pub fn draw_windows<R, E, T>(
pub fn draw_dnd_icon<R, E, T>(
renderer: &mut R,
renderer_id: u64,
texture_destruction_callback: &Sender<T>,
buffer_utils: &BufferUtils,
surface: &wl_surface::WlSurface,
(x, y): (i32, i32),
@ -194,7 +200,7 @@ pub fn draw_dnd_icon<R, E, T>(
log: &::slog::Logger,
)
where
R: Renderer<Error=E, Texture=T>,
R: Renderer<Error=E, TextureId=T>,
E: std::error::Error,
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."
);
}
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>(

View File

@ -5,7 +5,7 @@ use std::{
os::unix::io::{AsRawFd, RawFd},
path::PathBuf,
rc::Rc,
sync::{atomic::Ordering, Arc, Mutex},
sync::{atomic::Ordering, Arc, Mutex, mpsc},
time::Duration,
};
@ -494,6 +494,8 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
&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 context = EGLContext::new_shared(&egl, &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(),
compositor_token: self.compositor_token,
backends: backends.clone(),
texture_destruction_callback: mpsc::channel(),
window_map: self.window_map.clone(),
output_map: self.output_map.clone(),
pointer_location: self.pointer_location.clone(),
@ -647,6 +650,7 @@ pub struct DrmRenderer {
buffer_utils: BufferUtils,
compositor_token: CompositorToken<Roles>,
backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
texture_destruction_callback: (mpsc::Sender<Gles2Texture>, mpsc::Receiver<Gles2Texture>),
window_map: Rc<RefCell<MyWindowMap>>,
output_map: Rc<RefCell<Vec<MyOutput>>>,
pointer_location: Rc<RefCell<(f64, f64)>>,
@ -672,6 +676,7 @@ impl DrmRenderer {
if let Some(surface) = self.backends.borrow().get(&crtc) {
let result = DrmRenderer::render_surface(
&mut *surface.borrow_mut(),
&self.texture_destruction_callback.0,
&self.buffer_utils,
self.device_id,
crtc,
@ -739,12 +744,17 @@ impl DrmRenderer {
self.window_map
.borrow()
.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(
surface: &mut RenderSurface,
texture_destruction_callback: &mpsc::Sender<Gles2Texture>,
buffer_utils: &BufferUtils,
device_id: dev_t,
crtc: crtc::Handle,
@ -777,6 +787,7 @@ impl DrmRenderer {
draw_windows(
surface,
device_id,
texture_destruction_callback,
buffer_utils,
window_map,
Some(Rectangle {
@ -800,7 +811,7 @@ impl DrmRenderer {
{
if let Some(ref wl_surface) = dnd_icon.as_ref() {
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(
surface,
device_id,
texture_destruction_callback,
buffer_utils,
wl_surface,
(ptr_x, ptr_y),

View File

@ -91,6 +91,7 @@ pub fn run_winit(
info!(log, "Initialization completed, starting the main loop.");
let (texture_send, texture_receive) = std::sync::mpsc::channel();
while state.running.load(Ordering::SeqCst) {
if input
.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");
// 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();
// draw the dnd icon if any
@ -121,7 +122,7 @@ pub fn run_winit(
let guard = state.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard {
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
if let CursorImageStatus::Image(ref surface) = *guard {
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 {
renderer.window().set_cursor_visible(true);
}
@ -162,6 +163,10 @@ pub fn run_winit(
display.borrow_mut().flush_clients(&mut state);
state.window_map.borrow_mut().refresh();
}
while let Ok(texture) = texture_receive.try_recv() {
let _ = renderer.destroy_texture(texture);
}
}
// Cleanup stuff