renderer: Cache textures in SurfaceAttributes

This commit is contained in:
Victor Brekenfeld 2021-05-16 20:00:45 +02:00
parent e4f72f7516
commit c41cc9828f
5 changed files with 99 additions and 59 deletions

View File

@ -84,7 +84,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() {
match renderer.import_buffer(&buffer, egl_buffer_reader) { match renderer.import_buffer(&buffer, Some(&attributes), egl_buffer_reader) {
Ok(m) => { Ok(m) => {
data.texture = Some(Box::new(BufferTextures { buffer, texture: m }) data.texture = Some(Box::new(BufferTextures { buffer, texture: m })
as Box<dyn std::any::Any + 'static>) as Box<dyn std::any::Any + 'static>)

View File

@ -2,6 +2,8 @@ use std::collections::HashSet;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::SurfaceAttributes;
use cgmath::Matrix3; use cgmath::Matrix3;
use drm::buffer::PlanarBuffer; use drm::buffer::PlanarBuffer;
use drm::control::{connector, crtc, framebuffer, plane, Device, Mode}; use drm::control::{connector, crtc, framebuffer, plane, Device, Mode};
@ -353,10 +355,11 @@ where
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>,
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error> { ) -> Result<Self::TextureId, Self::Error> {
self.renderer self.renderer
.import_buffer(buffer, egl) .import_buffer(buffer, surface, egl)
.map_err(Error::RenderError) .map_err(Error::RenderError)
} }

View File

@ -28,6 +28,8 @@ use crate::backend::egl::{
}; };
use crate::backend::SwapBuffersError; use crate::backend::SwapBuffersError;
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::SurfaceAttributes;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm}; use wayland_server::protocol::{wl_buffer, wl_shm};
@ -163,6 +165,10 @@ pub enum Gles2Error {
#[error("Error accessing the buffer ({0:?})")] #[error("Error accessing the buffer ({0:?})")]
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
EGLBufferAccessError(crate::backend::egl::BufferAccessError), EGLBufferAccessError(crate::backend::egl::BufferAccessError),
/// The buffer backend is unknown or unsupported
#[error("Error accessing the buffer")]
#[cfg(feature = "wayland_frontend")]
UnknownBufferType,
/// This rendering operation was called without a previous `begin`-call /// This rendering operation was called without a previous `begin`-call
#[error("Call begin before doing any rendering operations")] #[error("Call begin before doing any rendering operations")]
UnconstraintRenderingOperation, UnconstraintRenderingOperation,
@ -180,6 +186,7 @@ impl From<Gles2Error> for SwapBuffersError {
x @ Gles2Error::FramebufferBindingError x @ Gles2Error::FramebufferBindingError
| x @ Gles2Error::BindBufferEGLError(_) | x @ Gles2Error::BindBufferEGLError(_)
| x @ Gles2Error::UnsupportedPixelFormat(_) | x @ Gles2Error::UnsupportedPixelFormat(_)
| x @ Gles2Error::UnknownBufferType
| x @ Gles2Error::BufferAccessError(_) | x @ Gles2Error::BufferAccessError(_)
| x @ Gles2Error::EGLBufferAccessError(_) => SwapBuffersError::TemporaryFailure(Box::new(x)), | x @ Gles2Error::EGLBufferAccessError(_) => SwapBuffersError::TemporaryFailure(Box::new(x)),
} }
@ -418,17 +425,11 @@ struct BufferCache {
} }
enum BufferCacheVariant { enum BufferCacheVariant {
Egl(Option<EglCache>), Egl(Option<EGLBuffer>),
Shm(Option<ShmCache>),
} }
struct EglCache { struct SurfaceCache {
buf: Option<EGLBuffer>, texture: Vec<Option<Gles2Texture>>,
texture: Gles2Texture,
}
struct ShmCache {
texture: Gles2Texture,
} }
impl Gles2Renderer { impl Gles2Renderer {
@ -436,7 +437,7 @@ impl Gles2Renderer {
fn import_shm( fn import_shm(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
cache: &mut Option<ShmCache>, cache: &mut Option<Gles2Texture>,
) -> Result<Gles2Texture, Gles2Error> { ) -> Result<Gles2Texture, Gles2Error> {
use crate::wayland::shm::with_buffer_contents; use crate::wayland::shm::with_buffer_contents;
@ -463,7 +464,7 @@ impl Gles2Renderer {
format => return Err(Gles2Error::UnsupportedPixelFormat(format)), format => return Err(Gles2Error::UnsupportedPixelFormat(format)),
}; };
let texture = cache.as_ref().map(|x| x.texture.clone()).unwrap_or_else(|| { let texture = cache.as_ref().cloned().unwrap_or_else(|| {
let mut tex = 0; let mut tex = 0;
unsafe { self.gl.GenTextures(1, &mut tex) }; unsafe { self.gl.GenTextures(1, &mut tex) };
Gles2Texture(Rc::new(Gles2TextureInternal { Gles2Texture(Rc::new(Gles2TextureInternal {
@ -503,10 +504,7 @@ impl Gles2Renderer {
self.egl.unbind()?; self.egl.unbind()?;
*cache = Some(ShmCache { *cache = Some(texture.clone());
texture: texture.clone(),
});
Ok(texture) Ok(texture)
}) })
.map_err(Gles2Error::BufferAccessError)? .map_err(Gles2Error::BufferAccessError)?
@ -517,53 +515,58 @@ impl Gles2Renderer {
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
reader: &EGLBufferReader, reader: &EGLBufferReader,
cache: &mut Option<EglCache>, buffer_cache: &mut Option<EGLBuffer>,
texture_cache: &mut Option<Gles2Texture>,
) -> Result<Gles2Texture, Gles2Error> { ) -> Result<Gles2Texture, Gles2Error> {
if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") { if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") {
return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"])); return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"]));
} }
self.make_current()?; self.make_current()?;
let egl_buffer = cache let old_buffer = buffer_cache.take();
.as_mut() let new_buffer = reader
.and_then(|x| x.buf.take()) .egl_buffer_contents(&buffer)
.map(Ok)
.unwrap_or_else(|| reader.egl_buffer_contents(&buffer))
.map_err(Gles2Error::EGLBufferAccessError)?; .map_err(Gles2Error::EGLBufferAccessError)?;
// we do not need to re-import external textures // we do not need to re-import external textures
if egl_buffer.format == EGLFormat::External && cache.is_some() { if let Some(old_buffer) = old_buffer {
return Ok(cache.as_ref().map(|x| x.texture.clone()).unwrap()); if old_buffer.format == EGLFormat::External
&& new_buffer.format == EGLFormat::External
&& old_buffer.image(0) == new_buffer.image(0)
// good enough
{
if let Some(texture) = texture_cache.as_ref().cloned() {
*buffer_cache = Some(new_buffer);
return Ok(texture);
}
}
} }
let tex = self.import_egl_image( let tex = self.import_egl_image(
egl_buffer.image(0).unwrap(), new_buffer.image(0).unwrap(),
egl_buffer.format == EGLFormat::External, new_buffer.format == EGLFormat::External,
cache.as_ref().map(|x| x.texture.0.texture), texture_cache.as_ref().map(|x| x.0.texture),
)?; )?;
let texture = cache.as_ref().map(|x| x.texture.clone()).unwrap_or_else(|| { let texture = texture_cache.as_ref().cloned().unwrap_or_else(|| {
Gles2Texture(Rc::new(Gles2TextureInternal { Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex, texture: tex,
texture_kind: match egl_buffer.format { texture_kind: match new_buffer.format {
EGLFormat::RGB => 3, EGLFormat::RGB => 3,
EGLFormat::RGBA => 2, EGLFormat::RGBA => 2,
EGLFormat::External => 4, EGLFormat::External => 4,
_ => unreachable!("EGLBuffer currenly does not expose multi-planar buffers to us"), _ => unreachable!("EGLBuffer currenly does not expose multi-planar buffers to us"),
}, },
is_external: egl_buffer.format == EGLFormat::External, is_external: new_buffer.format == EGLFormat::External,
y_inverted: egl_buffer.y_inverted, y_inverted: new_buffer.y_inverted,
width: egl_buffer.width, width: new_buffer.width,
height: egl_buffer.height, height: new_buffer.height,
destruction_callback_sender: self.destruction_callback_sender.clone(), destruction_callback_sender: self.destruction_callback_sender.clone(),
})) }))
}); });
self.egl.unbind()?; self.egl.unbind()?;
*cache = Some(EglCache { *buffer_cache = Some(new_buffer);
buf: Some(egl_buffer), *texture_cache = Some(texture.clone());
texture: texture.clone(),
});
Ok(texture) Ok(texture)
} }
@ -812,10 +815,11 @@ impl Renderer for Gles2Renderer {
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>,
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error> { ) -> Result<Self::TextureId, Self::Error> {
// init cache if not existing // init cache if not existing
let cache_cell = match buffer.as_ref().user_data().get::<Rc<RefCell<BufferCache>>>() { let buffer_cell = match buffer.as_ref().user_data().get::<Rc<RefCell<BufferCache>>>() {
Some(cache) => cache.clone(), Some(cache) => cache.clone(),
None => { None => {
let cache = BufferCache { let cache = BufferCache {
@ -828,30 +832,57 @@ impl Renderer for Gles2Renderer {
result result
} }
}; };
let mut cache = buffer_cell.borrow_mut();
// init cache while cache.cache.len() <= self.id {
let mut cache = cache_cell.borrow_mut();
while cache.cache.len() < self.id {
cache.cache.push(None); cache.cache.push(None);
} }
if cache.cache.len() == self.id { if let Some(attributes) = surface {
cache.cache.push(Some( let texture_cell = match attributes.user_data.get::<Rc<RefCell<SurfaceCache>>>() {
Some(cache) => cache.clone(),
None => {
let cache = SurfaceCache {
texture: Vec::with_capacity(self.id + 1),
};
let data: Rc<RefCell<SurfaceCache>> = Rc::new(RefCell::new(cache));
let result = data.clone();
attributes.user_data.insert_if_missing(|| data);
result
}
};
let mut cache = texture_cell.borrow_mut();
while cache.texture.len() <= self.id {
cache.texture.push(None);
}
}
// init buffer cache variants
if cache.cache[self.id].is_none() {
if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() { if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() {
BufferCacheVariant::Egl(None) cache.cache[self.id] = Some(BufferCacheVariant::Egl(None));
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() { }
BufferCacheVariant::Shm(None)
} else {
unreachable!("Completely unknown buffer format. How did we got here?");
},
));
} }
// delegate for different buffer types // delegate for different buffer types
match cache.cache[self.id].as_mut() { let mut texture_cache_tmp = surface
Some(BufferCacheVariant::Egl(cache)) => self.import_egl(&buffer, egl.unwrap(), cache), .and_then(|a| a.user_data.get::<Rc<RefCell<SurfaceCache>>>())
Some(BufferCacheVariant::Shm(cache)) => self.import_shm(&buffer, cache), .map(|cache| cache.borrow_mut());
let mut temporary_none = None;
let texture_cache = texture_cache_tmp
.as_mut()
.map(|cache| &mut cache.texture[self.id])
.unwrap_or(&mut temporary_none);
if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() {
let buffer_cache = match &mut cache.cache[self.id] {
Some(BufferCacheVariant::Egl(cache)) => cache,
_ => unreachable!(), _ => unreachable!(),
};
self.import_egl(&buffer, egl.unwrap(), buffer_cache, texture_cache)
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
self.import_shm(&buffer, texture_cache)
} else {
Err(Gles2Error::UnknownBufferType)
} }
} }

View File

@ -10,6 +10,8 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::SurfaceAttributes;
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};
@ -178,6 +180,7 @@ pub trait Renderer {
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>,
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error>; ) -> Result<Self::TextureId, Self::Error>;

View File

@ -14,6 +14,8 @@ use crate::backend::{
Bind, Renderer, Transform, Bind, Renderer, Transform,
}, },
}; };
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::SurfaceAttributes;
use cgmath::Matrix3; use cgmath::Matrix3;
use std::{cell::RefCell, rc::Rc, time::Instant}; use std::{cell::RefCell, rc::Rc, time::Instant};
use wayland_egl as wegl; use wayland_egl as wegl;
@ -294,9 +296,10 @@ impl Renderer for WinitGraphicsBackend {
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>,
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error> { ) -> Result<Self::TextureId, Self::Error> {
self.renderer.import_buffer(buffer, egl) self.renderer.import_buffer(buffer, surface, egl)
} }
fn begin( fn begin(