renderer: Properly cache shm-resource surface-global

This commit is contained in:
Victor Brekenfeld 2021-05-25 00:40:55 +02:00
parent 6bfe6e1f25
commit 67a9478293
3 changed files with 73 additions and 53 deletions

View File

@ -90,11 +90,7 @@ where
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() {
let damage = attributes.damage.iter().map(|dmg| match dmg { match renderer.import_buffer(&buffer, &attributes, egl_buffer_reader) {
Damage::Buffer(rect) => *rect,
Damage::Surface(rect) => rect.scale(attributes.buffer_scale),
}).collect::<Vec<_>>();
match renderer.import_buffer(&buffer, &damage, egl_buffer_reader) {
Ok(m) => { Ok(m) => {
let buffer = if smithay::wayland::shm::with_buffer_contents(&buffer, |_,_| ()).is_ok() { let buffer = if smithay::wayland::shm::with_buffer_contents(&buffer, |_,_| ()).is_ok() {
buffer.release(); buffer.release();

View File

@ -28,10 +28,13 @@ use crate::backend::egl::{
ffi::egl::{self as ffi_egl, types::EGLImage}, ffi::egl::{self as ffi_egl, types::EGLImage},
EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError, EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError,
}; };
use crate::{backend::SwapBuffersError, utils::Rectangle}; use crate::{backend::SwapBuffersError};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use crate::backend::egl::display::EGLBufferReader; use crate::{
backend::egl::display::EGLBufferReader,
wayland::compositor::{Damage, SurfaceAttributes},
};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_commons::user_data::UserDataMap; use wayland_commons::user_data::UserDataMap;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
@ -150,7 +153,7 @@ pub struct Gles2Renderer {
extensions: Vec<String>, extensions: Vec<String>,
programs: [Gles2Program; shaders::FRAGMENT_COUNT], programs: [Gles2Program; shaders::FRAGMENT_COUNT],
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
textures: HashMap<BufferEntry, Gles2Texture>, dmabuf_cache: HashMap<BufferEntry, Gles2Texture>,
egl: EGLContext, egl: EGLContext,
gl: ffi::Gles2, gl: ffi::Gles2,
destruction_callback: Receiver<CleanupResource>, destruction_callback: Receiver<CleanupResource>,
@ -457,7 +460,7 @@ impl Gles2Renderer {
target_surface: None, target_surface: None,
buffers: Vec::new(), buffers: Vec::new(),
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
textures: HashMap::new(), dmabuf_cache: HashMap::new(),
destruction_callback: rx, destruction_callback: rx,
destruction_callback_sender: tx, destruction_callback_sender: tx,
logger_ptr, logger_ptr,
@ -482,7 +485,7 @@ impl Gles2Renderer {
fn cleanup(&mut self) -> Result<(), Gles2Error> { fn cleanup(&mut self) -> Result<(), Gles2Error> {
self.make_current()?; self.make_current()?;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
self.textures self.dmabuf_cache
.retain(|entry, _tex| entry.buffer.as_ref().is_alive()); .retain(|entry, _tex| entry.buffer.as_ref().is_alive());
for resource in self.destruction_callback.try_iter() { for resource in self.destruction_callback.try_iter() {
match resource { match resource {
@ -503,7 +506,7 @@ impl Gles2Renderer {
fn import_shm( fn import_shm(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
mut damage: &[crate::utils::Rectangle], surface: &SurfaceAttributes,
) -> Result<Gles2Texture, Gles2Error> { ) -> Result<Gles2Texture, Gles2Error> {
use crate::wayland::shm::with_buffer_contents; use crate::wayland::shm::with_buffer_contents;
@ -530,31 +533,30 @@ impl Gles2Renderer {
format => return Err(Gles2Error::UnsupportedPixelFormat(format)), format => return Err(Gles2Error::UnsupportedPixelFormat(format)),
}; };
let texture = self.existing_texture(&buffer)?.unwrap_or_else(|| { let mut upload_full = false;
let mut tex = 0;
unsafe { self.gl.GenTextures(1, &mut tex) }; let texture = Gles2Texture(
// different buffer, upload in full // why not store a `Gles2Texture`? because the user might do so.
damage = &[]; // this is guaranteed a non-public internal type, so we are good.
let texture = Gles2Texture(Rc::new(Gles2TextureInternal { surface.user_data.get::<Rc<Gles2TextureInternal>>().cloned().unwrap_or_else(|| {
texture: tex, let mut tex = 0;
texture_kind: shader_idx, unsafe { self.gl.GenTextures(1, &mut tex) };
is_external: false, // new texture, upload in full
y_inverted: false, upload_full = true;
width: width as u32, let texture = Rc::new(Gles2TextureInternal {
height: height as u32, texture: tex,
buffer: Some(buffer.clone()), texture_kind: shader_idx,
egl_images: None, is_external: false,
destruction_callback_sender: self.destruction_callback_sender.clone(), y_inverted: false,
})); width: width as u32,
self.textures.insert( height: height as u32,
BufferEntry { buffer: Some(buffer.clone()),
id: buffer.as_ref().id(), egl_images: None,
buffer: buffer.clone(), destruction_callback_sender: self.destruction_callback_sender.clone(),
}, });
texture.clone(), texture
); })
texture );
});
unsafe { unsafe {
self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture); self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture);
@ -564,23 +566,44 @@ impl Gles2Renderer {
self.gl self.gl
.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); .TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32);
self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize); self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize);
for region in damage {
trace!(self.logger, "Uploading partial shm texture for {:?}", buffer);
self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, region.x); if upload_full {
self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, region.y); trace!(self.logger, "Uploading shm texture for {:?}", buffer);
self.gl.TexSubImage2D( self.gl.TexImage2D(
ffi::TEXTURE_2D, ffi::TEXTURE_2D,
0, 0,
region.x, gl_format as i32,
region.y, width,
region.width, height,
region.height, 0,
gl_format, gl_format,
ffi::UNSIGNED_BYTE as u32, ffi::UNSIGNED_BYTE as u32,
slice.as_ptr().offset(offset as isize) as *const _, slice.as_ptr().offset(offset as isize) as *const _,
); );
self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, 0); } else {
self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, 0); for region in surface.damage.iter().map(|dmg| match dmg {
Damage::Buffer(rect) => *rect,
// TODO also apply transformations
Damage::Surface(rect) => rect.scale(surface.buffer_scale),
}) {
trace!(self.logger, "Uploading partial shm texture for {:?}", buffer);
self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, region.x);
self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, region.y);
self.gl.TexSubImage2D(
ffi::TEXTURE_2D,
0,
region.x,
region.y,
region.width,
region.height,
gl_format,
ffi::UNSIGNED_BYTE as u32,
slice.as_ptr().offset(offset as isize) as *const _,
);
self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, 0);
self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, 0);
}
} }
self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0); self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0);
@ -632,14 +655,13 @@ impl Gles2Renderer {
destruction_callback_sender: self.destruction_callback_sender.clone(), destruction_callback_sender: self.destruction_callback_sender.clone(),
})); }));
self.egl.unbind()?;
Ok(texture) Ok(texture)
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn existing_texture(&self, buffer: &wl_buffer::WlBuffer) -> Result<Option<Gles2Texture>, Gles2Error> { fn existing_dmabuf_texture(&self, buffer: &wl_buffer::WlBuffer) -> Result<Option<Gles2Texture>, Gles2Error> {
let existing_texture = self let existing_texture = self
.textures .dmabuf_cache
.iter() .iter()
.find(|(old_buffer, _)| &old_buffer.buffer == buffer) .find(|(old_buffer, _)| &old_buffer.buffer == buffer)
.map(|(_, tex)| tex.clone()); .map(|(_, tex)| tex.clone());
@ -917,7 +939,7 @@ impl Renderer for Gles2Renderer {
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
damage: &[Rectangle], surface: &SurfaceAttributes,
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error> { ) -> Result<Self::TextureId, Self::Error> {
let texture = if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() { let texture = if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() {
@ -925,7 +947,7 @@ impl Renderer for Gles2Renderer {
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() { } else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
self.import_shm( self.import_shm(
&buffer, &buffer,
damage, surface,
) )
} else { } else {
Err(Gles2Error::UnknownBufferType) Err(Gles2Error::UnknownBufferType)

View File

@ -13,6 +13,8 @@ use std::error::Error;
use cgmath::{prelude::*, Matrix3, Vector2}; use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm}; use wayland_server::protocol::{wl_buffer, wl_shm};
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::SurfaceAttributes;
use crate::{backend::SwapBuffersError, utils::Rectangle}; use crate::{backend::SwapBuffersError, utils::Rectangle};
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
@ -240,7 +242,7 @@ pub trait Renderer {
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
damage: &[Rectangle], surface: &SurfaceAttributes,
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error>; ) -> Result<Self::TextureId, Self::Error>;