renderer: Remove `Frame` trait and merge into `Renderer`

Tracking of Frames, so that only one unique one can exist at a time
(gles does not allow multiple frames being rendered in parallel)
lead to very unfriendly lifetime-heavy code. A renderer is already
*unique*, just move the code there and add an error variant to catch
misuses.
This commit is contained in:
Victor Brekenfeld 2021-04-25 23:38:48 +02:00
parent 66fbb3eb06
commit 2c9c150e5e
5 changed files with 274 additions and 275 deletions

View File

@ -14,17 +14,16 @@ use smithay::{
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
pub fn draw_cursor<R, E, T, F>(
pub fn draw_cursor<R, E, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
(x, y): (i32, i32), (x, y): (i32, i32),
token: MyCompositorToken, token: MyCompositorToken,
log: &Logger, log: &Logger,
) )
where where
R: Renderer<Error=E, Texture=T, Frame=F>, R: Renderer<Error=E, Texture=T>,
F: Frame<Error=E, Texture=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -38,27 +37,25 @@ pub fn draw_cursor<R, E, T, F>(
(0, 0) (0, 0)
} }
}; };
draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), token, log); draw_surface_tree(renderer, surface, (x - dx, y - dy), token, log);
} }
fn draw_surface_tree<R, E, T, F>( fn draw_surface_tree<R, E, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F,
root: &wl_surface::WlSurface, root: &wl_surface::WlSurface,
location: (i32, i32), location: (i32, i32),
compositor_token: MyCompositorToken, compositor_token: MyCompositorToken,
log: &Logger, log: &Logger,
) )
where where
R: Renderer<Error=E, Texture=T, Frame=F>, R: Renderer<Error=E, Texture=T>,
F: Frame<Error=E, Texture=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
compositor_token.with_surface_tree_upward( compositor_token.with_surface_tree_upward(
root, root,
location, (),
|_surface, attributes, role, &(mut x, mut y)| { |_surface, attributes, role, _| {
// Pull a new buffer if available // Pull a new buffer if available
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() { if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
@ -76,6 +73,29 @@ fn draw_surface_tree<R, E, T, F>(
} }
} }
// Now, should we be drawn ? // Now, should we be drawn ?
if data.texture.is_some() {
TraversalAction::DoChildren(())
} else {
// we are not displayed, so our children are neither
TraversalAction::SkipChildren
}
} else {
// we are not displayed, so our children are neither
TraversalAction::SkipChildren
}
},
|_, _, _, _| {},
|_, _, _, _| true,
);
compositor_token.with_surface_tree_upward(
root,
location,
|_surface, attributes, role, &(mut x, mut y)| {
// Pull a new buffer if available
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
// Now, should we be drawn ?
if data.texture.is_some() { if data.texture.is_some() {
// if yes, also process the children // if yes, also process the children
if Role::<SubsurfaceRole>::has(role) { if Role::<SubsurfaceRole>::has(role) {
@ -103,7 +123,7 @@ fn draw_surface_tree<R, E, T, F>(
x += sub_x; x += sub_x;
y += sub_y; y += sub_y;
} }
frame.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0); renderer.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0);
} }
} }
}, },
@ -111,17 +131,15 @@ fn draw_surface_tree<R, E, T, F>(
); );
} }
pub fn draw_windows<R, E, T, F>( pub fn draw_windows<R, E, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F,
window_map: &MyWindowMap, window_map: &MyWindowMap,
output_rect: Option<Rectangle>, output_rect: Option<Rectangle>,
compositor_token: MyCompositorToken, compositor_token: MyCompositorToken,
log: &::slog::Logger, log: &::slog::Logger,
) )
where where
R: Renderer<Error=E, Texture=T, Frame=F>, R: Renderer<Error=E, Texture=T>,
F: Frame<Error=E, Texture=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -140,7 +158,6 @@ pub fn draw_windows<R, E, T, F>(
// this surface is a root of a subsurface tree that needs to be drawn // this surface is a root of a subsurface tree that needs to be drawn
draw_surface_tree( draw_surface_tree(
renderer, renderer,
frame,
&wl_surface, &wl_surface,
initial_place, initial_place,
compositor_token, compositor_token,
@ -152,17 +169,15 @@ pub fn draw_windows<R, E, T, F>(
} }
} }
pub fn draw_dnd_icon<R, E, T, F>( pub fn draw_dnd_icon<R, E, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
(x, y): (i32, i32), (x, y): (i32, i32),
token: MyCompositorToken, token: MyCompositorToken,
log: &::slog::Logger, log: &::slog::Logger,
) )
where where
R: Renderer<Error=E, Texture=T, Frame=F>, R: Renderer<Error=E, Texture=T>,
F: Frame<Error=E, Texture=T>,
E: std::error::Error, E: std::error::Error,
T: Texture + 'static, T: Texture + 'static,
{ {
@ -172,7 +187,7 @@ pub fn draw_dnd_icon<R, E, T, F>(
"Trying to display as a dnd icon a surface that does not have the DndIcon role." "Trying to display as a dnd icon a surface that does not have the DndIcon role."
); );
} }
draw_surface_tree(renderer, frame, surface, (x, y), token, log); draw_surface_tree(renderer, surface, (x, y), token, log);
} }
/* /*

View File

@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
//#[cfg(feature = "egl")] //#[cfg(feature = "egl")]
//use smithay::backend::egl::EGLGraphicsBackend; //use smithay::backend::egl::EGLGraphicsBackend;
use smithay::{ use smithay::{
backend::{renderer::Frame, input::InputBackend, winit, SwapBuffersError}, backend::{renderer::Renderer, input::InputBackend, winit, SwapBuffersError},
reexports::{ reexports::{
calloop::EventLoop, calloop::EventLoop,
wayland_server::{protocol::wl_output, Display}, wayland_server::{protocol::wl_output, Display},
@ -112,11 +112,11 @@ pub fn run_winit(
// drawing logic // drawing logic
{ {
let mut frame = renderer.begin().expect("Failed to render frame"); renderer.begin().expect("Failed to render frame");
frame.clear([0.8, 0.8, 0.9, 1.0]); renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame");
// draw the windows // draw the windows
draw_windows(&mut renderer, &mut frame, &*state.window_map.borrow(), None, state.ctoken, &log); draw_windows(&mut renderer, &*state.window_map.borrow(), None, state.ctoken, &log);
let (x, y) = *state.pointer_location.borrow(); let (x, y) = *state.pointer_location.borrow();
// draw the dnd icon if any // draw the dnd icon if any
@ -124,7 +124,7 @@ pub fn run_winit(
let guard = state.dnd_icon.lock().unwrap(); let guard = state.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard { if let Some(ref surface) = *guard {
if surface.as_ref().is_alive() { if surface.as_ref().is_alive() {
draw_dnd_icon(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log); draw_dnd_icon(&mut renderer, surface, (x as i32, y as i32), state.ctoken, &log);
} }
} }
} }
@ -143,13 +143,13 @@ pub fn run_winit(
// draw as relevant // draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard { if let CursorImageStatus::Image(ref surface) = *guard {
renderer.window().set_cursor_visible(false); renderer.window().set_cursor_visible(false);
draw_cursor(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log); draw_cursor(&mut renderer, surface, (x as i32, y as i32), state.ctoken, &log);
} else { } else {
renderer.window().set_cursor_visible(true); renderer.window().set_cursor_visible(true);
} }
} }
if let Err(SwapBuffersError::ContextLost(err)) = frame.finish() { if let Err(SwapBuffersError::ContextLost(err)) = renderer.finish() {
error!(log, "Critical Rendering Error: {}", err); error!(log, "Critical Rendering Error: {}", err);
state.running.store(false, Ordering::SeqCst); state.running.store(false, Ordering::SeqCst);
} }

View File

@ -1,15 +1,15 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::ffi::CStr; use std::ffi::CStr;
use std::ptr; use std::ptr;
use std::sync::Arc; use std::rc::Rc;
use cgmath::{prelude::*, Matrix3, Vector2}; use cgmath::{prelude::*, Matrix3, Vector2};
mod shaders; mod shaders;
use crate::backend::SwapBuffersError; use crate::backend::SwapBuffersError;
use crate::backend::allocator::{dmabuf::{Dmabuf, WeakDmabuf}, Format}; use crate::backend::allocator::{dmabuf::{Dmabuf, WeakDmabuf}, Format};
use crate::backend::egl::{EGLContext, EGLSurface, ffi::egl::types::EGLImage}; use crate::backend::egl::{EGLContext, EGLSurface, ffi::egl::types::EGLImage, MakeCurrentError};
use super::{Renderer, Frame, Bind, Unbind, Transform, Texture}; use super::{Renderer, Bind, Unbind, Transform, Texture};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_shm, wl_buffer}; use wayland_server::protocol::{wl_shm, wl_buffer};
@ -30,6 +30,7 @@ struct Gles2Program {
attrib_tex_coords: ffi::types::GLint, attrib_tex_coords: ffi::types::GLint,
} }
// TODO: drop?
pub struct Gles2Texture { pub struct Gles2Texture {
texture: ffi::types::GLuint, texture: ffi::types::GLuint,
texture_kind: usize, texture_kind: usize,
@ -62,22 +63,16 @@ struct Gles2Buffer {
} }
pub struct Gles2Renderer { pub struct Gles2Renderer {
internal: Arc<Gles2RendererInternal>,
buffers: Vec<WeakGles2Buffer>, buffers: Vec<WeakGles2Buffer>,
current_buffer: Option<Gles2Buffer>, target_buffer: Option<Gles2Buffer>,
} target_surface: Option<Rc<EGLSurface>>,
current_projection: Option<Matrix3<f32>>,
struct Gles2RendererInternal {
gl: ffi::Gles2,
egl: EGLContext,
extensions: Vec<String>, extensions: Vec<String>,
programs: [Gles2Program; shaders::FRAGMENT_COUNT], programs: [Gles2Program; shaders::FRAGMENT_COUNT],
gl: ffi::Gles2,
egl: EGLContext,
logger: Option<*mut ::slog::Logger>, logger: Option<*mut ::slog::Logger>,
} _not_send: *mut (),
pub struct Gles2Frame {
internal: Arc<Gles2RendererInternal>,
projection: Matrix3<f32>,
} }
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -103,6 +98,8 @@ 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),
#[error("Call begin before doing any rendering operations")]
UnconstraintRenderingOperation,
} }
impl From<Gles2Error> for SwapBuffersError { impl From<Gles2Error> for SwapBuffersError {
@ -112,6 +109,7 @@ impl From<Gles2Error> for SwapBuffersError {
| x @ Gles2Error::ProgramLinkError | x @ Gles2Error::ProgramLinkError
| x @ Gles2Error::GLFunctionLoaderError | x @ Gles2Error::GLFunctionLoaderError
| x @ Gles2Error::GLExtensionNotSupported(_) | x @ Gles2Error::GLExtensionNotSupported(_)
| x @ Gles2Error::UnconstraintRenderingOperation
=> SwapBuffersError::ContextLost(Box::new(x)), => SwapBuffersError::ContextLost(Box::new(x)),
Gles2Error::ContextActivationError(err) => err.into(), Gles2Error::ContextActivationError(err) => err.into(),
x @ Gles2Error::FramebufferBindingError x @ Gles2Error::FramebufferBindingError
@ -205,15 +203,15 @@ unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result<Gles2Pr
} }
impl Gles2Renderer { impl Gles2Renderer {
pub fn new<L>(context: EGLContext, logger: L) -> Result<Gles2Renderer, Gles2Error> pub unsafe fn new<L>(context: EGLContext, logger: L) -> Result<Gles2Renderer, Gles2Error>
where where
L: Into<Option<::slog::Logger>> L: Into<Option<::slog::Logger>>
{ {
let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "renderer_gles2")); let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "renderer_gles2"));
unsafe { context.make_current()? }; context.make_current()?;
let (gl, exts, logger) = unsafe { let (gl, exts, logger) = {
let gl = ffi::Gles2::load_with(|s| crate::backend::egl::get_proc_address(s) as *const _); let gl = ffi::Gles2::load_with(|s| crate::backend::egl::get_proc_address(s) as *const _);
let ext_ptr = gl.GetString(ffi::EXTENSIONS) as *const i8; let ext_ptr = gl.GetString(ffi::EXTENSIONS) as *const i8;
if ext_ptr.is_null() { if ext_ptr.is_null() {
@ -252,52 +250,55 @@ impl Gles2Renderer {
(gl, exts, logger) (gl, exts, logger)
}; };
let programs = { let programs = [
unsafe { [
texture_program(&gl, shaders::FRAGMENT_SHADER_ABGR)?, texture_program(&gl, shaders::FRAGMENT_SHADER_ABGR)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_XBGR)?, texture_program(&gl, shaders::FRAGMENT_SHADER_XBGR)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_BGRA)?, texture_program(&gl, shaders::FRAGMENT_SHADER_BGRA)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_BGRX)?, texture_program(&gl, shaders::FRAGMENT_SHADER_BGRX)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?, texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?,
] } ];
};
Ok(Gles2Renderer { let renderer = Gles2Renderer {
internal: Arc::new(Gles2RendererInternal { gl,
gl, egl: context,
egl: context, extensions: exts,
extensions: exts, programs,
programs, logger,
logger, target_buffer: None,
}), target_surface: None,
buffers: Vec::new(), buffers: Vec::new(),
current_buffer: None, current_projection: None,
}) _not_send: std::ptr::null_mut(),
};
renderer.egl.unbind()?;
Ok(renderer)
}
fn make_current(&self) -> Result<(), MakeCurrentError> {
unsafe {
if let Some(surface) = self.target_surface.as_ref() {
self.egl.make_current_with_surface(surface)?;
} else {
self.egl.make_current()?;
}
}
Ok(())
} }
} }
impl Bind<&EGLSurface> for Gles2Renderer { impl Bind<Rc<EGLSurface>> for Gles2Renderer {
fn bind(&mut self, surface: &EGLSurface) -> Result<(), Gles2Error> { fn bind(&mut self, surface: Rc<EGLSurface>) -> Result<(), Gles2Error> {
if self.current_buffer.is_some() { self.unbind()?;
self.unbind()?; self.target_surface = Some(surface);
}
unsafe {
self.internal.egl.make_current_with_surface(&surface)?;
}
Ok(()) Ok(())
} }
} }
impl Bind<Dmabuf> for Gles2Renderer { impl Bind<Dmabuf> for Gles2Renderer {
fn bind(&mut self, dmabuf: Dmabuf) -> Result<(), Gles2Error> { fn bind(&mut self, dmabuf: Dmabuf) -> Result<(), Gles2Error> {
if self.current_buffer.is_some() { self.unbind()?;
self.unbind()?;
}
unsafe { unsafe {
self.internal.egl.make_current()?; self.egl.make_current()?;
} }
// Free outdated buffer resources // Free outdated buffer resources
@ -323,21 +324,21 @@ impl Bind<Dmabuf> for Gles2Renderer {
}) })
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
let image = self.internal.egl.display.create_image_from_dmabuf(&dmabuf).map_err(Gles2Error::BindBufferEGLError)?; let image = self.egl.display.create_image_from_dmabuf(&dmabuf).map_err(Gles2Error::BindBufferEGLError)?;
unsafe { unsafe {
let mut rbo = 0; let mut rbo = 0;
self.internal.gl.GenRenderbuffers(1, &mut rbo as *mut _); self.gl.GenRenderbuffers(1, &mut rbo as *mut _);
self.internal.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo); self.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo);
self.internal.gl.EGLImageTargetRenderbufferStorageOES(ffi::RENDERBUFFER, image); self.gl.EGLImageTargetRenderbufferStorageOES(ffi::RENDERBUFFER, image);
self.internal.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0); self.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0);
let mut fbo = 0; let mut fbo = 0;
self.internal.gl.GenFramebuffers(1, &mut fbo as *mut _); self.gl.GenFramebuffers(1, &mut fbo as *mut _);
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo); self.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo);
self.internal.gl.FramebufferRenderbuffer(ffi::FRAMEBUFFER, ffi::COLOR_ATTACHMENT0, ffi::RENDERBUFFER, rbo); self.gl.FramebufferRenderbuffer(ffi::FRAMEBUFFER, ffi::COLOR_ATTACHMENT0, ffi::RENDERBUFFER, rbo);
let status = self.internal.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER); let status = self.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER);
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0); self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
if status != ffi::FRAMEBUFFER_COMPLETE { if status != ffi::FRAMEBUFFER_COMPLETE {
//TODO wrap image and drop here //TODO wrap image and drop here
@ -361,146 +362,36 @@ impl Bind<Dmabuf> for Gles2Renderer {
})?; })?;
unsafe { unsafe {
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, buffer.internal.fbo); self.gl.BindFramebuffer(ffi::FRAMEBUFFER, buffer.internal.fbo);
} }
self.current_buffer = Some(buffer);
self.target_buffer = Some(buffer);
Ok(()) Ok(())
} }
fn supported_formats(&self) -> Option<HashSet<Format>> { fn supported_formats(&self) -> Option<HashSet<Format>> {
Some(self.internal.egl.display.dmabuf_render_formats.clone()) Some(self.egl.display.dmabuf_render_formats.clone())
} }
} }
impl Unbind for Gles2Renderer { impl Unbind for Gles2Renderer {
fn unbind(&mut self) -> Result<(), Gles2Error> { fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> {
unsafe { unsafe {
self.internal.egl.make_current()?; self.egl.make_current()?;
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
} }
self.current_buffer = None; unsafe { self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0) };
let _ = self.internal.egl.unbind(); self.target_buffer = None;
self.target_surface = None;
self.egl.unbind()?;
Ok(()) Ok(())
} }
} }
impl Renderer for Gles2Renderer { impl Drop for Gles2Renderer {
type Error = Gles2Error;
type Texture = Gles2Texture;
type Frame = Gles2Frame;
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<Gles2Frame, Gles2Error> {
if !self.internal.egl.is_current() {
// Do not call this unconditionally.
// If surfaces are in use (e.g. for winit) this would unbind them.
unsafe { self.internal.egl.make_current()?; }
}
unsafe {
self.internal.gl.Viewport(0, 0, width as i32, height as i32);
self.internal.gl.Enable(ffi::BLEND);
self.internal.gl.BlendFunc(ffi::ONE, ffi::ONE_MINUS_SRC_ALPHA);
}
// output transformation passed in by the user
let mut projection = Matrix3::<f32>::identity();
projection = projection * Matrix3::from_translation(Vector2::new(width as f32 / 2.0, height as f32 / 2.0));
projection = projection * transform.matrix();
let (transformed_width, transformed_height) = transform.transform_size(width, height);
projection = projection * Matrix3::from_translation(Vector2::new(-(transformed_width as f32) / 2.0, -(transformed_height as f32) / 2.0));
// replicate https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml
// glOrtho(0, width, 0, height, 1, 1);
let mut renderer = Matrix3::<f32>::identity();
let t = Matrix3::<f32>::identity();
let x = 2.0 / (width as f32);
let y = 2.0 / (height as f32);
// Rotation & Reflection
renderer[0][0] = x * t[0][0];
renderer[1][0] = x * t[0][1];
renderer[0][1] = y * -t[1][0];
renderer[1][1] = y * -t[1][1];
//Translation
renderer[2][0] = -(1.0f32.copysign(renderer[0][0] + renderer[1][0]));
renderer[2][1] = -(1.0f32.copysign(renderer[0][1] + renderer[1][1]));
Ok(Gles2Frame {
internal: self.internal.clone(),
projection: projection * renderer,
})
}
#[cfg(feature = "wayland_frontend")]
fn shm_formats(&self) -> &[wl_shm::Format] {
&[
wl_shm::Format::Abgr8888,
wl_shm::Format::Xbgr8888,
wl_shm::Format::Argb8888,
wl_shm::Format::Xrgb8888,
]
}
#[cfg(feature = "wayland_frontend")]
fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error> {
use crate::wayland::shm::with_buffer_contents;
with_buffer_contents(&buffer, |slice, data| {
if !self.internal.egl.is_current() {
unsafe { self.internal.egl.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 mut tex = 0;
unsafe {
self.internal.gl.GenTextures(1, &mut tex);
self.internal.gl.BindTexture(ffi::TEXTURE_2D, tex);
self.internal.gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32);
self.internal.gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32);
self.internal.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize);
self.internal.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.internal.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0);
self.internal.gl.BindTexture(ffi::TEXTURE_2D, 0);
}
Ok(Gles2Texture {
texture: tex,
texture_kind: shader_idx,
is_external: false,
y_inverted: false,
width: width as u32,
height: height as u32,
})
}).map_err(Gles2Error::BufferAccessError)?
}
}
impl Drop for Gles2RendererInternal {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
if self.egl.make_current().is_ok() { if self.egl.make_current().is_ok() {
self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
for program in &self.programs { for program in &self.programs {
self.gl.DeleteProgram(program.program); self.gl.DeleteProgram(program.program);
} }
@ -533,60 +424,171 @@ static TEX_COORDS: [ffi::types::GLfloat; 8] = [
0.0, 1.0, // bottom left 0.0, 1.0, // bottom left
]; ];
impl Frame for Gles2Frame { impl Renderer for Gles2Renderer {
type Error = Gles2Error; type Error = Gles2Error;
type Texture = Gles2Texture; type Texture = Gles2Texture;
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> { #[cfg(feature = "wayland_frontend")]
fn shm_formats(&self) -> &[wl_shm::Format] {
&[
wl_shm::Format::Abgr8888,
wl_shm::Format::Xbgr8888,
wl_shm::Format::Argb8888,
wl_shm::Format::Xrgb8888,
]
}
#[cfg(feature = "wayland_frontend")]
fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error> {
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 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)?
}
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), Gles2Error> {
self.make_current()?;
unsafe { unsafe {
self.internal.gl.ClearColor(color[0], color[1], color[2], color[3]); self.gl.Viewport(0, 0, width as i32, height as i32);
self.internal.gl.Clear(ffi::COLOR_BUFFER_BIT);
self.gl.Enable(ffi::BLEND);
self.gl.BlendFunc(ffi::ONE, ffi::ONE_MINUS_SRC_ALPHA);
}
// output transformation passed in by the user
let mut projection = Matrix3::<f32>::identity();
//projection = projection * Matrix3::from_translation(Vector2::new(width as f32 / 2.0, height as f32 / 2.0));
projection = projection * transform.matrix();
//let (transformed_width, transformed_height) = transform.transform_size(width, height);
//projection = projection * Matrix3::from_translation(Vector2::new(-(transformed_width as f32) / 2.0, -(transformed_height as f32) / 2.0));
// replicate https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml
// glOrtho(0, width, 0, height, 1, 1);
let mut renderer = Matrix3::<f32>::identity();
let t = Matrix3::<f32>::identity();
let x = 2.0 / (width as f32);
let y = 2.0 / (height as f32);
// Rotation & Reflection
renderer[0][0] = x * t[0][0];
renderer[1][0] = x * t[0][1];
renderer[0][1] = y * -t[1][0];
renderer[1][1] = y * -t[1][1];
//Translation
renderer[2][0] = -(1.0f32.copysign(renderer[0][0] + renderer[1][0]));
renderer[2][1] = -(1.0f32.copysign(renderer[0][1] + renderer[1][1]));
self.current_projection = Some(projection * renderer);
Ok(())
}
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> {
self.make_current()?;
unsafe {
self.gl.ClearColor(color[0], color[1], color[2], color[3]);
self.gl.Clear(ffi::COLOR_BUFFER_BIT);
} }
Ok(()) Ok(())
} }
fn render_texture(&mut self, tex: &Self::Texture, mut matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error> { fn render_texture(&mut self, tex: &Self::Texture, mut matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error> {
self.make_current()?;
if self.current_projection.is_none() {
return Err(Gles2Error::UnconstraintRenderingOperation);
}
//apply output transformation //apply output transformation
matrix = self.projection * matrix; matrix = self.current_projection.as_ref().unwrap() * matrix;
let target = if tex.is_external { ffi::TEXTURE_EXTERNAL_OES } else { ffi::TEXTURE_2D }; let target = if tex.is_external { ffi::TEXTURE_EXTERNAL_OES } else { ffi::TEXTURE_2D };
// render // render
unsafe { unsafe {
self.internal.gl.ActiveTexture(ffi::TEXTURE0); self.gl.ActiveTexture(ffi::TEXTURE0);
self.internal.gl.BindTexture(target, tex.texture); self.gl.BindTexture(target, tex.texture);
self.internal.gl.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); self.gl.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32);
self.internal.gl.UseProgram(self.internal.programs[tex.texture_kind].program); self.gl.UseProgram(self.programs[tex.texture_kind].program);
self.internal.gl.Uniform1i(self.internal.programs[tex.texture_kind].uniform_tex, 0); self.gl.Uniform1i(self.programs[tex.texture_kind].uniform_tex, 0);
self.internal.gl.UniformMatrix3fv(self.internal.programs[tex.texture_kind].uniform_matrix, 1, ffi::FALSE, matrix.as_ptr()); self.gl.UniformMatrix3fv(self.programs[tex.texture_kind].uniform_matrix, 1, ffi::FALSE, matrix.as_ptr());
self.internal.gl.Uniform1i(self.internal.programs[tex.texture_kind].uniform_invert_y, if tex.y_inverted { 1 } else { 0 }); self.gl.Uniform1i(self.programs[tex.texture_kind].uniform_invert_y, if tex.y_inverted { 1 } else { 0 });
self.internal.gl.Uniform1f(self.internal.programs[tex.texture_kind].uniform_alpha, alpha); self.gl.Uniform1f(self.programs[tex.texture_kind].uniform_alpha, alpha);
self.internal.gl.VertexAttribPointer(self.internal.programs[tex.texture_kind].attrib_position as u32, 2, ffi::FLOAT, ffi::FALSE, 0, VERTS.as_ptr() as *const _); self.gl.VertexAttribPointer(self.programs[tex.texture_kind].attrib_position as u32, 2, ffi::FLOAT, ffi::FALSE, 0, VERTS.as_ptr() as *const _);
self.internal.gl.VertexAttribPointer(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32, 2, ffi::FLOAT, ffi::FALSE, 0, TEX_COORDS.as_ptr() as *const _); self.gl.VertexAttribPointer(self.programs[tex.texture_kind].attrib_tex_coords as u32, 2, ffi::FLOAT, ffi::FALSE, 0, TEX_COORDS.as_ptr() as *const _);
self.internal.gl.EnableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_position as u32); self.gl.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_position as u32);
self.internal.gl.EnableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32); self.gl.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_tex_coords as u32);
self.internal.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4); self.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4);
self.internal.gl.DisableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_position as u32); self.gl.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_position as u32);
self.internal.gl.DisableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32); self.gl.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_tex_coords as u32);
self.internal.gl.BindTexture(target, 0); self.gl.BindTexture(target, 0);
} }
Ok(()) Ok(())
} }
fn finish(self) -> Result<(), crate::backend::SwapBuffersError> { fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> {
self.make_current()?;
unsafe { unsafe {
self.internal.gl.Flush(); self.gl.Flush();
self.internal.gl.Disable(ffi::BLEND); self.gl.Disable(ffi::BLEND);
} }
self.current_projection = None;
Ok(()) Ok(())
} }
} }

View File

@ -133,10 +133,6 @@ pub trait Texture {
pub trait Renderer { pub trait Renderer {
type Error: Error; type Error: Error;
type Texture: Texture; type Texture: Texture;
type Frame: Frame<Error=Self::Error, Texture=Self::Texture>;
#[must_use]
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<Self::Frame, Self::Error>;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn shm_formats(&self) -> &[wl_shm::Format] { fn shm_formats(&self) -> &[wl_shm::Format] {
@ -144,13 +140,11 @@ pub trait Renderer {
&[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888] &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error>; fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error>;
}
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), <Self as Renderer>::Error>;
pub trait Frame { fn finish(&mut self) -> Result<(), SwapBuffersError>;
type Error: Error;
type Texture: Texture;
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>; fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error>; fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error>;
fn render_texture_at(&mut self, texture: &Self::Texture, pos: (i32, i32), transform: Transform, alpha: f32) -> Result<(), Self::Error> { fn render_texture_at(&mut self, texture: &Self::Texture, pos: (i32, i32), transform: Transform, alpha: f32) -> Result<(), Self::Error> {
@ -173,5 +167,4 @@ pub trait Frame {
self.render_texture(texture, mat, alpha) self.render_texture(texture, mat, alpha)
} }
fn finish(self) -> Result<(), SwapBuffersError>;
} }

View File

@ -4,8 +4,8 @@ use crate::backend::egl::display::EGLDisplay;
use crate::backend::{ use crate::backend::{
egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError}, egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
renderer::{ renderer::{
Renderer, Bind, Transform, Frame, Renderer, Bind, Transform,
gles2::{Gles2Renderer, Gles2Error, Gles2Texture, Gles2Frame}, gles2::{Gles2Renderer, Gles2Error, Gles2Texture},
}, },
input::{ input::{
Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
@ -73,11 +73,6 @@ pub struct WinitGraphicsBackend {
size: Rc<RefCell<WindowSize>>, size: Rc<RefCell<WindowSize>>,
} }
pub struct WinitFrame {
frame: Gles2Frame,
egl: Rc<EGLSurface>,
}
/// Abstracted event loop of a [`WinitWindow`] implementing the [`InputBackend`] trait /// Abstracted event loop of a [`WinitWindow`] implementing the [`InputBackend`] trait
/// ///
/// You need to call [`dispatch_new_events`](InputBackend::dispatch_new_events) /// You need to call [`dispatch_new_events`](InputBackend::dispatch_new_events)
@ -172,6 +167,8 @@ where
} else { } else {
unreachable!("No backends for winit other then Wayland and X11 are supported") unreachable!("No backends for winit other then Wayland and X11 are supported")
}; };
context.unbind();
( (
display, display,
@ -187,13 +184,14 @@ where
let window = Rc::new(winit_window); let window = Rc::new(winit_window);
let egl = Rc::new(surface); let egl = Rc::new(surface);
let mut renderer = unsafe { Gles2Renderer::new(context, log.clone())? };
Ok(( Ok((
WinitGraphicsBackend { WinitGraphicsBackend {
window: window.clone(), window: window.clone(),
display, display,
egl: egl.clone(), egl: egl.clone(),
renderer: Gles2Renderer::new(context, log.clone())?, renderer,
size: size.clone(), size: size.clone(),
}, },
WinitInputBackend { WinitInputBackend {
@ -247,55 +245,46 @@ impl WinitGraphicsBackend {
&*self.window &*self.window
} }
pub fn begin(&mut self) -> Result<WinitFrame, Gles2Error> { pub fn begin(&mut self) -> Result<(), Gles2Error> {
let (width, height) = { let (width, height) = {
let size = self.size.borrow(); let size = self.size.borrow();
size.physical_size.into() size.physical_size.into()
}; };
Renderer::begin(self, width, height, Transform::Normal)
self.renderer.bind(self.egl.clone())?;
self.renderer.begin(width, height, Transform::Normal)
} }
} }
impl Renderer for WinitGraphicsBackend { impl Renderer for WinitGraphicsBackend {
type Error = Gles2Error; type Error = Gles2Error;
type Texture = Gles2Texture; type Texture = Gles2Texture;
type Frame = WinitFrame;
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<Self::Frame, Self::Error> {
self.renderer.bind(&*self.egl)?;
let frame = self.renderer.begin(width, height, transform)?;
Ok(WinitFrame {
frame,
egl: self.egl.clone(),
})
}
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn shm_formats(&self) -> &[wl_shm::Format] { fn shm_formats(&self) -> &[wl_shm::Format] {
self.renderer.shm_formats() Renderer::shm_formats(&self.renderer)
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error> { fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error> {
self.renderer.import_shm(buffer) self.renderer.import_shm(buffer)
} }
}
impl Frame for WinitFrame { fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), <Self as Renderer>::Error> {
type Error = Gles2Error; self.renderer.bind(self.egl.clone())?;
type Texture = Gles2Texture; self.renderer.begin(width, height, transform)
}
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> { fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> {
self.frame.clear(color) self.renderer.clear(color)
} }
fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error> { fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error> {
self.frame.render_texture(texture, matrix, alpha) self.renderer.render_texture(texture, matrix, alpha)
} }
fn finish(self) -> Result<(), crate::backend::SwapBuffersError> { fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> {
self.frame.finish()?; self.renderer.finish()?;
self.egl.swap_buffers()?; self.egl.swap_buffers()?;
Ok(()) Ok(())
} }