renderer: allow caching resources on WlBuffer

- renderer: Change the renderer api to not import different buffer types directly,
but import any supported wl_buffer
- renderer: Remove destroy_texture call and move responsibility into implementation
- gles2: Cache egl images as well as textures on wl_buffer userdata
- gles2: Implement delayed destruction of textures to avoid leaking or changing global state on drop
This commit is contained in:
Victor Brekenfeld 2021-05-13 23:36:52 +02:00
parent 2200d09841
commit 75b2a2d801
4 changed files with 298 additions and 187 deletions

View File

@ -11,11 +11,9 @@ use wayland_server::protocol::{wl_buffer, wl_shm};
use super::{device::DevPath, surface::DrmSurfaceInternal, DrmError, DrmSurface}; use super::{device::DevPath, surface::DrmSurfaceInternal, DrmError, DrmSurface};
use crate::backend::{ use crate::backend::{
allocator::{ allocator::{dmabuf::{AsDmabuf, Dmabuf}, Allocator, Buffer, Format, Fourcc, Modifier, Slot, Swapchain},
dmabuf::{AsDmabuf, Dmabuf}, Allocator, Buffer, Format, Fourcc, Modifier, Slot, Swapchain, egl::display::EGLBufferReader,
},
}; };
use crate::backend::egl::EGLBuffer;
use crate::backend::renderer::{Bind, Renderer, Texture, Transform}; use crate::backend::renderer::{Bind, Renderer, Texture, Transform};
use crate::backend::SwapBuffersError; use crate::backend::SwapBuffersError;
@ -354,17 +352,8 @@ where
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::TextureId, Self::Error> { fn import_buffer(&mut self, buffer: &wl_buffer::WlBuffer, egl: Option<&EGLBufferReader>) -> Result<Self::TextureId, Self::Error> {
self.renderer.import_shm(buffer).map_err(Error::RenderError) self.renderer.import_buffer(buffer, egl).map_err(Error::RenderError)
}
#[cfg(feature = "wayland_frontend")]
fn import_egl(&mut self, buffer: &EGLBuffer) -> Result<Self::TextureId, Self::Error> {
self.renderer.import_egl(buffer).map_err(Error::RenderError)
}
fn destroy_texture(&mut self, texture: Self::TextureId) -> Result<(), Self::Error> {
self.renderer.destroy_texture(texture).map_err(Error::RenderError)
} }
fn begin(&mut self, width: u32, height: u32, _transform: Transform) -> Result<(), Error<E1, E2, E3>> { fn begin(&mut self, width: u32, height: u32, _transform: Transform) -> Result<(), Error<E1, E2, E3>> {

View File

@ -1,10 +1,15 @@
//! Implementation of the rendering traits using OpenGL ES 2 //! Implementation of the rendering traits using OpenGL ES 2
use std::{collections::HashSet, os::raw::c_char}; use std::{collections::HashSet, os::raw::c_char};
use std::cell::RefCell;
use std::fmt; use std::fmt;
use std::ffi::CStr; use std::ffi::CStr;
use std::ptr; use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{
atomic::{Ordering, AtomicUsize},
mpsc::{Sender, Receiver, channel}
};
use cgmath::{prelude::*, Matrix3}; use cgmath::{prelude::*, Matrix3};
@ -15,7 +20,7 @@ use crate::backend::allocator::{
Format, Format,
}; };
use crate::backend::egl::{ use crate::backend::egl::{
ffi::egl::types::EGLImage, EGLBuffer, EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError, ffi::egl::types::EGLImage, EGLBuffer, EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError, display::EGLBufferReader,
}; };
use crate::backend::SwapBuffersError; use crate::backend::SwapBuffersError;
@ -27,6 +32,8 @@ pub mod ffi {
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
} }
static RENDERER_COUNTER: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)] #[derive(Debug)]
struct Gles2Program { struct Gles2Program {
program: ffi::types::GLuint, program: ffi::types::GLuint,
@ -39,22 +46,32 @@ struct Gles2Program {
} }
/// A handle to a GLES2 texture /// A handle to a GLES2 texture
#[derive(Debug, Clone)]
pub struct Gles2Texture(Rc<Gles2TextureInternal>);
#[derive(Debug)] #[derive(Debug)]
pub struct Gles2Texture { struct Gles2TextureInternal {
texture: ffi::types::GLuint, texture: ffi::types::GLuint,
texture_kind: usize, texture_kind: usize,
is_external: bool, is_external: bool,
y_inverted: bool, y_inverted: bool,
width: u32, width: u32,
height: u32, height: u32,
destruction_callback_sender: Sender<ffi::types::GLuint>,
}
impl Drop for Gles2TextureInternal {
fn drop(&mut self) {
let _ = self.destruction_callback_sender.send(self.texture);
}
} }
impl Texture for Gles2Texture { impl Texture for Gles2Texture {
fn width(&self) -> u32 { fn width(&self) -> u32 {
self.width self.0.width
} }
fn height(&self) -> u32 { fn height(&self) -> u32 {
self.height self.0.height
} }
} }
@ -74,6 +91,7 @@ struct Gles2Buffer {
/// A renderer utilizing OpenGL ES 2 /// A renderer utilizing OpenGL ES 2
pub struct Gles2Renderer { pub struct Gles2Renderer {
id: usize,
buffers: Vec<WeakGles2Buffer>, buffers: Vec<WeakGles2Buffer>,
target_buffer: Option<Gles2Buffer>, target_buffer: Option<Gles2Buffer>,
target_surface: Option<Rc<EGLSurface>>, target_surface: Option<Rc<EGLSurface>>,
@ -82,6 +100,8 @@ pub struct Gles2Renderer {
programs: [Gles2Program; shaders::FRAGMENT_COUNT], programs: [Gles2Program; shaders::FRAGMENT_COUNT],
gl: ffi::Gles2, gl: ffi::Gles2,
egl: EGLContext, egl: EGLContext,
destruction_callback: Receiver<ffi::types::GLuint>,
destruction_callback_sender: Sender<ffi::types::GLuint>,
logger: Option<*mut ::slog::Logger>, logger: Option<*mut ::slog::Logger>,
_not_send: *mut (), _not_send: *mut (),
} }
@ -89,6 +109,7 @@ pub struct Gles2Renderer {
impl fmt::Debug for Gles2Renderer { impl fmt::Debug for Gles2Renderer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Gles2Renderer") f.debug_struct("Gles2Renderer")
.field("id", &self.id)
.field("buffers", &self.buffers) .field("buffers", &self.buffers)
.field("target_buffer", &self.target_buffer) .field("target_buffer", &self.target_buffer)
.field("target_surface", &self.target_surface) .field("target_surface", &self.target_surface)
@ -134,6 +155,10 @@ pub enum Gles2Error {
#[error("Error accessing the buffer ({0:?})")] #[error("Error accessing the buffer ({0:?})")]
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
BufferAccessError(crate::wayland::shm::BufferAccessError), BufferAccessError(crate::wayland::shm::BufferAccessError),
/// The given egl buffer was not accessible
#[error("Error accessing the buffer ({0:?})")]
#[cfg(feature = "wayland_frontend")]
EGLBufferAccessError(crate::backend::egl::BufferAccessError),
/// 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,
@ -151,7 +176,8 @@ 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::BufferAccessError(_) => SwapBuffersError::TemporaryFailure(Box::new(x)), | x @ Gles2Error::BufferAccessError(_)
| x @ Gles2Error::EGLBufferAccessError(_) => SwapBuffersError::TemporaryFailure(Box::new(x)),
} }
} }
} }
@ -336,16 +362,20 @@ impl Gles2Renderer {
texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?, texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?,
]; ];
let (tx, rx) = channel();
let renderer = Gles2Renderer { let renderer = Gles2Renderer {
id: RENDERER_COUNTER.fetch_add(1, Ordering::SeqCst),
gl, gl,
egl: context, egl: context,
extensions: exts, extensions: exts,
programs, programs,
logger,
target_buffer: None, target_buffer: None,
target_surface: None, target_surface: None,
buffers: Vec::new(), buffers: Vec::new(),
current_projection: None, current_projection: None,
destruction_callback: rx,
destruction_callback_sender: tx,
logger,
_not_send: std::ptr::null_mut(), _not_send: std::ptr::null_mut(),
}; };
renderer.egl.unbind()?; renderer.egl.unbind()?;
@ -362,6 +392,176 @@ impl Gles2Renderer {
} }
Ok(()) Ok(())
} }
fn cleanup(&mut self) -> Result<(), Gles2Error> {
self.make_current()?;
for texture in self.destruction_callback.try_iter() {
unsafe {
self.gl.DeleteTextures(1, &texture);
}
}
Ok(())
}
}
struct BufferCache {
cache: Vec<Option<BufferCacheVariant>>,
}
enum BufferCacheVariant {
Egl(Option<EglCache>),
Shm(Option<ShmCache>),
}
struct EglCache {
buf: Option<EGLBuffer>,
texture: Gles2Texture,
}
struct ShmCache {
texture: Gles2Texture,
}
impl Gles2Renderer {
#[cfg(feature = "wayland_frontend")]
fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer, cache: &mut Option<ShmCache>) -> Result<Gles2Texture, Gles2Error> {
use crate::wayland::shm::with_buffer_contents;
with_buffer_contents(&buffer, |slice, data| {
self.make_current()?;
let offset = data.offset as i32;
let width = data.width as i32;
let height = data.height as i32;
let stride = data.stride as i32;
// number of bytes per pixel
// TODO: compute from data.format
let pixelsize = 4i32;
// ensure consistency, the SHM handler of smithay should ensure this
assert!((offset + (height - 1) * stride + width * pixelsize) as usize <= slice.len());
let (gl_format, shader_idx) = match data.format {
wl_shm::Format::Abgr8888 => (ffi::RGBA, 0),
wl_shm::Format::Xbgr8888 => (ffi::RGBA, 1),
wl_shm::Format::Argb8888 => (ffi::BGRA_EXT, 2),
wl_shm::Format::Xrgb8888 => (ffi::BGRA_EXT, 3),
format => return Err(Gles2Error::UnsupportedPixelFormat(format)),
};
let texture = cache.as_ref().map(|x| x.texture.clone()).unwrap_or_else(|| {
let mut tex = 0;
unsafe { self.gl.GenTextures(1, &mut tex) };
Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex,
texture_kind: shader_idx,
is_external: false,
y_inverted: false,
width: width as u32,
height: height as u32,
destruction_callback_sender: self.destruction_callback_sender.clone(),
}))
});
unsafe {
self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture);
self.gl
.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32);
self.gl
.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.TexImage2D(
ffi::TEXTURE_2D,
0,
gl_format as i32,
width,
height,
0,
ffi::RGBA,
ffi::UNSIGNED_BYTE as u32,
slice.as_ptr() as *const _,
);
self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0);
self.gl.BindTexture(ffi::TEXTURE_2D, 0);
}
self.egl.unbind()?;
*cache = Some(ShmCache {
texture: texture.clone(),
});
Ok(texture)
})
.map_err(Gles2Error::BufferAccessError)?
}
#[cfg(feature = "wayland_frontend")]
fn import_egl(&mut self, buffer: &wl_buffer::WlBuffer, reader: &EGLBufferReader, cache: &mut Option<EglCache>) -> Result<Gles2Texture, Gles2Error> {
if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") {
return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"]));
}
self.make_current()?;
let egl_buffer = cache.as_mut().and_then(|x| x.buf.take()).map(Ok).unwrap_or_else(|| {
reader.egl_buffer_contents(&buffer)
}).map_err(Gles2Error::EGLBufferAccessError)?;
// we do not need to re-import external textures
if egl_buffer.format == EGLFormat::External && cache.is_some() {
return Ok(cache.as_ref().map(|x| x.texture.clone()).unwrap());
}
let tex = self.import_egl_image(egl_buffer.image(0).unwrap(), egl_buffer.format == EGLFormat::External, cache.as_ref().map(|x| x.texture.0.texture))?;
let texture = cache.as_ref().map(|x| x.texture.clone()).unwrap_or_else(|| {
Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex,
texture_kind: match egl_buffer.format {
EGLFormat::RGB => 3,
EGLFormat::RGBA => 2,
EGLFormat::External => 4,
_ => unreachable!("EGLBuffer currenly does not expose multi-planar buffers to us"),
},
is_external: egl_buffer.format == EGLFormat::External,
y_inverted: egl_buffer.y_inverted,
width: egl_buffer.width,
height: egl_buffer.height,
destruction_callback_sender: self.destruction_callback_sender.clone(),
}))
});
self.egl.unbind()?;
*cache = Some(EglCache {
buf: Some(egl_buffer),
texture: texture.clone(),
});
Ok(texture)
}
fn import_egl_image(&self, image: EGLImage, is_external: bool, tex: Option<u32>) -> Result<u32, Gles2Error> {
let tex = tex.unwrap_or_else(|| unsafe {
let mut tex = 0;
self.gl.GenTextures(1, &mut tex);
tex
});
let target = if is_external {
ffi::TEXTURE_EXTERNAL_OES
} else {
ffi::TEXTURE_2D
};
unsafe {
self.gl.BindTexture(target, tex);
self.gl
.EGLImageTargetTexture2DOES(target, image);
}
Ok(tex)
}
} }
impl Bind<Rc<EGLSurface>> for Gles2Renderer { impl Bind<Rc<EGLSurface>> for Gles2Renderer {
@ -393,7 +593,7 @@ impl Bind<Dmabuf> for Gles2Renderer {
let buffer = self let buffer = self
.buffers .buffers
.iter() .iter()
.find(|buffer| dmabuf == buffer.dmabuf) .find(|buffer| if let Some(dma) = buffer.dmabuf.upgrade() { dma == dmabuf } else { false })
.map(|buf| { .map(|buf| {
let dmabuf = buf let dmabuf = buf
.dmabuf .dmabuf
@ -559,140 +759,69 @@ impl Renderer for Gles2Renderer {
self.gl.BindTexture(ffi::TEXTURE_2D, 0); self.gl.BindTexture(ffi::TEXTURE_2D, 0);
} }
let texture = Gles2Texture { let texture = Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex, texture: tex,
texture_kind: 2, texture_kind: 2,
is_external: false, is_external: false,
y_inverted: false, y_inverted: false,
width: image.width(), width: image.width(),
height: image.height(), height: image.height(),
}; destruction_callback_sender: self.destruction_callback_sender.clone(),
}));
self.egl.unbind()?; self.egl.unbind()?;
Ok(texture) Ok(texture)
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::TextureId, Self::Error> { fn import_buffer(&mut self, buffer: &wl_buffer::WlBuffer, egl: Option<&EGLBufferReader>) -> Result<Self::TextureId, Self::Error> {
use crate::wayland::shm::with_buffer_contents; // init cache if not existing
let cache_cell = match buffer.as_ref().user_data().get::<Rc<RefCell<BufferCache>>>() {
Some(cache) => cache.clone(),
None => {
let cache = BufferCache {
cache: Vec::with_capacity(self.id+1),
};
with_buffer_contents(&buffer, |slice, data| { let data: Rc<RefCell<BufferCache>> = Rc::new(RefCell::new(cache));
self.make_current()?; let result = data.clone();
buffer.as_ref().user_data().set(|| data);
let offset = data.offset as i32; result
let width = data.width as i32;
let height = data.height as i32;
let stride = data.stride as i32;
// number of bytes per pixel
// TODO: compute from data.format
let pixelsize = 4i32;
// ensure consistency, the SHM handler of smithay should ensure this
assert!((offset + (height - 1) * stride + width * pixelsize) as usize <= slice.len());
let (gl_format, shader_idx) = match data.format {
wl_shm::Format::Abgr8888 => (ffi::RGBA, 0),
wl_shm::Format::Xbgr8888 => (ffi::RGBA, 1),
wl_shm::Format::Argb8888 => (ffi::BGRA_EXT, 2),
wl_shm::Format::Xrgb8888 => (ffi::BGRA_EXT, 3),
format => return Err(Gles2Error::UnsupportedPixelFormat(format)),
};
let mut tex = 0;
unsafe {
self.gl.GenTextures(1, &mut tex);
self.gl.BindTexture(ffi::TEXTURE_2D, tex);
self.gl
.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32);
self.gl
.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.TexImage2D(
ffi::TEXTURE_2D,
0,
gl_format as i32,
width,
height,
0,
gl_format,
ffi::UNSIGNED_BYTE as u32,
slice.as_ptr() as *const _,
);
self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0);
self.gl.BindTexture(ffi::TEXTURE_2D, 0);
} }
let texture = Gles2Texture {
texture: tex,
texture_kind: shader_idx,
is_external: false,
y_inverted: false,
width: width as u32,
height: height as u32,
};
self.egl.unbind()?;
Ok(texture)
})
.map_err(Gles2Error::BufferAccessError)?
}
#[cfg(feature = "wayland_frontend")]
fn import_egl(&mut self, buffer: &EGLBuffer) -> Result<Self::TextureId, Self::Error> {
if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") {
return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"]));
}
self.make_current()?;
let mut tex = 0;
let target = if buffer.format == EGLFormat::External {
ffi::TEXTURE_EXTERNAL_OES
} else {
ffi::TEXTURE_2D
}; };
unsafe {
self.gl.GenTextures(1, &mut tex);
self.gl.BindTexture(target, tex);
self.gl // init cache
.EGLImageTargetTexture2DOES(target, buffer.image(0).unwrap()); let mut cache = cache_cell.borrow_mut();
while cache.cache.len() < self.id {
cache.cache.push(None);
} }
let texture = Gles2Texture { if cache.cache.len() == self.id {
texture: tex, cache.cache.push(Some(
texture_kind: match buffer.format { if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() {
EGLFormat::RGB => 3, BufferCacheVariant::Egl(None)
EGLFormat::RGBA => 2, } else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
EGLFormat::External => 4, BufferCacheVariant::Shm(None)
_ => unreachable!("EGLBuffer currenly does not expose multi-planar buffers to us"), } else {
}, unreachable!("Completely unknown buffer format. How did we got here?");
is_external: buffer.format == EGLFormat::External, }
y_inverted: buffer.y_inverted, ));
width: buffer.width,
height: buffer.height,
};
self.egl.unbind()?;
Ok(texture)
}
fn destroy_texture(&mut self, texture: Self::TextureId) -> Result<(), Self::Error> {
self.make_current()?;
unsafe {
self.gl.DeleteTextures(1, &texture.texture);
} }
self.egl.unbind()?;
Ok(()) // delegate for different buffer types
match cache.cache[self.id].as_mut() {
Some(BufferCacheVariant::Egl(cache)) => self.import_egl(&buffer, egl.unwrap(), cache),
Some(BufferCacheVariant::Shm(cache)) => self.import_shm(&buffer, cache),
_ => unreachable!(),
}
} }
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), Gles2Error> { fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), Gles2Error> {
self.make_current()?; self.make_current()?;
// delayed destruction until the next frame rendering.
self.cleanup()?;
unsafe { unsafe {
self.gl.Viewport(0, 0, width as i32, height as i32); self.gl.Viewport(0, 0, width as i32, height as i32);
@ -746,7 +875,7 @@ impl Renderer for Gles2Renderer {
//apply output transformation //apply output transformation
matrix = self.current_projection.as_ref().unwrap() * matrix; matrix = self.current_projection.as_ref().unwrap() * matrix;
let target = if tex.is_external { let target = if tex.0.is_external {
ffi::TEXTURE_EXTERNAL_OES ffi::TEXTURE_EXTERNAL_OES
} else { } else {
ffi::TEXTURE_2D ffi::TEXTURE_2D
@ -755,27 +884,27 @@ impl Renderer for Gles2Renderer {
// render // render
unsafe { unsafe {
self.gl.ActiveTexture(ffi::TEXTURE0); self.gl.ActiveTexture(ffi::TEXTURE0);
self.gl.BindTexture(target, tex.texture); self.gl.BindTexture(target, tex.0.texture);
self.gl self.gl
.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); .TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32);
self.gl.UseProgram(self.programs[tex.texture_kind].program); self.gl.UseProgram(self.programs[tex.0.texture_kind].program);
self.gl.Uniform1i(self.programs[tex.texture_kind].uniform_tex, 0); self.gl.Uniform1i(self.programs[tex.0.texture_kind].uniform_tex, 0);
self.gl.UniformMatrix3fv( self.gl.UniformMatrix3fv(
self.programs[tex.texture_kind].uniform_matrix, self.programs[tex.0.texture_kind].uniform_matrix,
1, 1,
ffi::FALSE, ffi::FALSE,
matrix.as_ptr(), matrix.as_ptr(),
); );
self.gl.Uniform1i( self.gl.Uniform1i(
self.programs[tex.texture_kind].uniform_invert_y, self.programs[tex.0.texture_kind].uniform_invert_y,
if tex.y_inverted { 1 } else { 0 }, if tex.0.y_inverted { 1 } else { 0 },
); );
self.gl self.gl
.Uniform1f(self.programs[tex.texture_kind].uniform_alpha, alpha); .Uniform1f(self.programs[tex.0.texture_kind].uniform_alpha, alpha);
self.gl.VertexAttribPointer( self.gl.VertexAttribPointer(
self.programs[tex.texture_kind].attrib_position as u32, self.programs[tex.0.texture_kind].attrib_position as u32,
2, 2,
ffi::FLOAT, ffi::FLOAT,
ffi::FALSE, ffi::FALSE,
@ -783,7 +912,7 @@ impl Renderer for Gles2Renderer {
VERTS.as_ptr() as *const _, VERTS.as_ptr() as *const _,
); );
self.gl.VertexAttribPointer( self.gl.VertexAttribPointer(
self.programs[tex.texture_kind].attrib_tex_coords as u32, self.programs[tex.0.texture_kind].attrib_tex_coords as u32,
2, 2,
ffi::FLOAT, ffi::FLOAT,
ffi::FALSE, ffi::FALSE,
@ -792,16 +921,16 @@ impl Renderer for Gles2Renderer {
); );
self.gl self.gl
.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_position as u32); .EnableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_position as u32);
self.gl self.gl
.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_tex_coords as u32); .EnableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_tex_coords as u32);
self.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4); self.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4);
self.gl self.gl
.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_position as u32); .DisableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_position as u32);
self.gl self.gl
.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_tex_coords as u32); .DisableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_tex_coords as u32);
self.gl.BindTexture(target, 0); self.gl.BindTexture(target, 0);
} }

View File

@ -17,8 +17,8 @@ use wayland_server::protocol::{wl_buffer, wl_shm};
use crate::backend::SwapBuffersError; use crate::backend::SwapBuffersError;
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
pub mod gles2; pub mod gles2;
#[cfg(feature = "wayland_frontend")] #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::EGLBuffer; use crate::backend::egl::display::EGLBufferReader;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes /// Possible transformations to two-dimensional planes
@ -164,39 +164,19 @@ pub trait Renderer {
&[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888] &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
} }
/// Import a given shared memory buffer into the renderer.
/// Import a given buffer into the renderer.
/// ///
/// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions. /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
/// ///
/// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it, /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
/// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
/// ///
/// This operation needs no bound or default rendering target. /// This operation needs no bound or default rendering target.
/// ///
/// The implementation defines, if the id keeps being valid, if the buffer is released, /// The implementation defines, if the id keeps being valid, if the buffer is released,
/// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again. /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::TextureId, Self::Error>; fn import_buffer(&mut self, buffer: &wl_buffer::WlBuffer, egl: Option<&EGLBufferReader>) -> Result<Self::TextureId, Self::Error>;
/// Import a given egl-backed memory buffer into the renderer.
///
/// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
///
/// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
/// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
///
/// This operation needs no bound or default rendering target.
///
/// The implementation defines, if the id keeps being valid, if the buffer is released,
/// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
#[cfg(feature = "wayland_frontend")]
fn import_egl(&mut self, buffer: &EGLBuffer) -> Result<Self::TextureId, Self::Error>;
/// Deallocate the given texture.
///
/// In case the texture type of this renderer is cloneable or copyable, those handles will also become invalid
/// and destroy calls with one of these handles might error out as the texture is already freed.
fn destroy_texture(&mut self, texture: Self::TextureId) -> Result<(), Self::Error>;
/// Initialize a rendering context on the current rendering target with given dimensions and transformation. /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
/// ///
@ -265,3 +245,25 @@ pub trait Renderer {
self.render_texture(texture, mat, alpha) self.render_texture(texture, mat, alpha)
} }
} }
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer, egl_buffer_reader: Option<&EGLBufferReader>) -> Option<(i32, i32)> {
if let Some((w, h)) = egl_buffer_reader.as_ref().and_then(|x| x.egl_buffer_dimensions(&buffer)) {
Some((w, h))
} else if let Ok((w, h)) = crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height)) {
Some((w, h))
} else {
None
}
}
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
use crate::backend::allocator::Buffer;
if let Ok((w, h)) = crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height)) {
Some((w, h))
} else {
None
}
}

View File

@ -2,7 +2,7 @@
use crate::backend::egl::display::EGLDisplay; use crate::backend::egl::display::EGLDisplay;
use crate::backend::{ use crate::backend::{
egl::{context::GlAttributes, native, EGLBuffer, EGLContext, EGLSurface, Error as EGLError}, egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
input::{ input::{
Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent,
@ -291,17 +291,8 @@ impl Renderer for WinitGraphicsBackend {
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::TextureId, Self::Error> { fn import_buffer(&mut self, buffer: &wl_buffer::WlBuffer, egl: Option<&EGLBufferReader>) -> Result<Self::TextureId, Self::Error> {
self.renderer.import_shm(buffer) self.renderer.import_buffer(buffer, egl)
}
#[cfg(feature = "wayland_frontend")]
fn import_egl(&mut self, buffer: &EGLBuffer) -> Result<Self::TextureId, Self::Error> {
self.renderer.import_egl(buffer)
}
fn destroy_texture(&mut self, texture: Self::TextureId) -> Result<(), Self::Error> {
self.renderer.destroy_texture(texture)
} }
fn begin( fn begin(