renderer: Cache textures in SurfaceAttributes
This commit is contained in:
parent
e4f72f7516
commit
c41cc9828f
|
@ -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>)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>>>() {
|
||||||
if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() {
|
Some(cache) => cache.clone(),
|
||||||
BufferCacheVariant::Egl(None)
|
None => {
|
||||||
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
|
let cache = SurfaceCache {
|
||||||
BufferCacheVariant::Shm(None)
|
texture: Vec::with_capacity(self.id + 1),
|
||||||
} else {
|
};
|
||||||
unreachable!("Completely unknown buffer format. How did we got here?");
|
|
||||||
},
|
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() {
|
||||||
|
cache.cache[self.id] = Some(BufferCacheVariant::Egl(None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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());
|
||||||
_ => unreachable!(),
|
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!(),
|
||||||
|
};
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue