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};
pub fn draw_cursor<R, E, T, F>(
pub fn draw_cursor<R, E, T>(
renderer: &mut R,
frame: &mut F,
surface: &wl_surface::WlSurface,
(x, y): (i32, i32),
token: MyCompositorToken,
log: &Logger,
)
where
R: Renderer<Error=E, Texture=T, Frame=F>,
F: Frame<Error=E, Texture=T>,
R: Renderer<Error=E, Texture=T>,
E: std::error::Error,
T: Texture + 'static,
{
@ -38,27 +37,25 @@ pub fn draw_cursor<R, E, T, F>(
(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,
frame: &mut F,
root: &wl_surface::WlSurface,
location: (i32, i32),
compositor_token: MyCompositorToken,
log: &Logger,
)
where
R: Renderer<Error=E, Texture=T, Frame=F>,
F: Frame<Error=E, Texture=T>,
R: Renderer<Error=E, Texture=T>,
E: std::error::Error,
T: Texture + 'static,
{
compositor_token.with_surface_tree_upward(
root,
location,
|_surface, attributes, role, &(mut x, mut y)| {
(),
|_surface, attributes, role, _| {
// Pull a new buffer if available
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
@ -76,6 +73,29 @@ fn draw_surface_tree<R, E, T, F>(
}
}
// 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 yes, also process the children
if Role::<SubsurfaceRole>::has(role) {
@ -103,7 +123,7 @@ fn draw_surface_tree<R, E, T, F>(
x += sub_x;
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,
frame: &mut F,
window_map: &MyWindowMap,
output_rect: Option<Rectangle>,
compositor_token: MyCompositorToken,
log: &::slog::Logger,
)
where
R: Renderer<Error=E, Texture=T, Frame=F>,
F: Frame<Error=E, Texture=T>,
R: Renderer<Error=E, Texture=T>,
E: std::error::Error,
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
draw_surface_tree(
renderer,
frame,
&wl_surface,
initial_place,
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,
frame: &mut F,
surface: &wl_surface::WlSurface,
(x, y): (i32, i32),
token: MyCompositorToken,
log: &::slog::Logger,
)
where
R: Renderer<Error=E, Texture=T, Frame=F>,
F: Frame<Error=E, Texture=T>,
R: Renderer<Error=E, Texture=T>,
E: std::error::Error,
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."
);
}
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")]
//use smithay::backend::egl::EGLGraphicsBackend;
use smithay::{
backend::{renderer::Frame, input::InputBackend, winit, SwapBuffersError},
backend::{renderer::Renderer, input::InputBackend, winit, SwapBuffersError},
reexports::{
calloop::EventLoop,
wayland_server::{protocol::wl_output, Display},
@ -112,11 +112,11 @@ pub fn run_winit(
// drawing logic
{
let mut frame = renderer.begin().expect("Failed to render frame");
frame.clear([0.8, 0.8, 0.9, 1.0]);
renderer.begin().expect("Failed to render frame");
renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame");
// 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();
// draw the dnd icon if any
@ -124,7 +124,7 @@ pub fn run_winit(
let guard = state.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard {
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
if let CursorImageStatus::Image(ref surface) = *guard {
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 {
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);
state.running.store(false, Ordering::SeqCst);
}

View File

@ -1,15 +1,15 @@
use std::collections::HashSet;
use std::ffi::CStr;
use std::ptr;
use std::sync::Arc;
use std::rc::Rc;
use cgmath::{prelude::*, Matrix3, Vector2};
mod shaders;
use crate::backend::SwapBuffersError;
use crate::backend::allocator::{dmabuf::{Dmabuf, WeakDmabuf}, Format};
use crate::backend::egl::{EGLContext, EGLSurface, ffi::egl::types::EGLImage};
use super::{Renderer, Frame, Bind, Unbind, Transform, Texture};
use crate::backend::egl::{EGLContext, EGLSurface, ffi::egl::types::EGLImage, MakeCurrentError};
use super::{Renderer, Bind, Unbind, Transform, Texture};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_shm, wl_buffer};
@ -30,6 +30,7 @@ struct Gles2Program {
attrib_tex_coords: ffi::types::GLint,
}
// TODO: drop?
pub struct Gles2Texture {
texture: ffi::types::GLuint,
texture_kind: usize,
@ -62,22 +63,16 @@ struct Gles2Buffer {
}
pub struct Gles2Renderer {
internal: Arc<Gles2RendererInternal>,
buffers: Vec<WeakGles2Buffer>,
current_buffer: Option<Gles2Buffer>,
}
struct Gles2RendererInternal {
gl: ffi::Gles2,
egl: EGLContext,
target_buffer: Option<Gles2Buffer>,
target_surface: Option<Rc<EGLSurface>>,
current_projection: Option<Matrix3<f32>>,
extensions: Vec<String>,
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
gl: ffi::Gles2,
egl: EGLContext,
logger: Option<*mut ::slog::Logger>,
}
pub struct Gles2Frame {
internal: Arc<Gles2RendererInternal>,
projection: Matrix3<f32>,
_not_send: *mut (),
}
#[derive(thiserror::Error, Debug)]
@ -103,6 +98,8 @@ pub enum Gles2Error {
#[error("Error accessing the buffer ({0:?})")]
#[cfg(feature = "wayland_frontend")]
BufferAccessError(crate::wayland::shm::BufferAccessError),
#[error("Call begin before doing any rendering operations")]
UnconstraintRenderingOperation,
}
impl From<Gles2Error> for SwapBuffersError {
@ -112,6 +109,7 @@ impl From<Gles2Error> for SwapBuffersError {
| x @ Gles2Error::ProgramLinkError
| x @ Gles2Error::GLFunctionLoaderError
| x @ Gles2Error::GLExtensionNotSupported(_)
| x @ Gles2Error::UnconstraintRenderingOperation
=> SwapBuffersError::ContextLost(Box::new(x)),
Gles2Error::ContextActivationError(err) => err.into(),
x @ Gles2Error::FramebufferBindingError
@ -205,15 +203,15 @@ unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result<Gles2Pr
}
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
L: Into<Option<::slog::Logger>>
{
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 ext_ptr = gl.GetString(ffi::EXTENSIONS) as *const i8;
if ext_ptr.is_null() {
@ -252,52 +250,55 @@ impl Gles2Renderer {
(gl, exts, logger)
};
let programs = {
unsafe { [
let programs = [
texture_program(&gl, shaders::FRAGMENT_SHADER_ABGR)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_XBGR)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_BGRA)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_BGRX)?,
texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?,
] }
};
];
Ok(Gles2Renderer {
internal: Arc::new(Gles2RendererInternal {
let renderer = Gles2Renderer {
gl,
egl: context,
extensions: exts,
programs,
logger,
}),
target_buffer: None,
target_surface: None,
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 {
fn bind(&mut self, surface: &EGLSurface) -> Result<(), Gles2Error> {
if self.current_buffer.is_some() {
impl Bind<Rc<EGLSurface>> for Gles2Renderer {
fn bind(&mut self, surface: Rc<EGLSurface>) -> Result<(), Gles2Error> {
self.unbind()?;
}
unsafe {
self.internal.egl.make_current_with_surface(&surface)?;
}
self.target_surface = Some(surface);
Ok(())
}
}
impl Bind<Dmabuf> for Gles2Renderer {
fn bind(&mut self, dmabuf: Dmabuf) -> Result<(), Gles2Error> {
if self.current_buffer.is_some() {
self.unbind()?;
}
unsafe {
self.internal.egl.make_current()?;
self.egl.make_current()?;
}
// Free outdated buffer resources
@ -323,21 +324,21 @@ impl Bind<Dmabuf> for Gles2Renderer {
})
})
.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 {
let mut rbo = 0;
self.internal.gl.GenRenderbuffers(1, &mut rbo as *mut _);
self.internal.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo);
self.internal.gl.EGLImageTargetRenderbufferStorageOES(ffi::RENDERBUFFER, image);
self.internal.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0);
self.gl.GenRenderbuffers(1, &mut rbo as *mut _);
self.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo);
self.gl.EGLImageTargetRenderbufferStorageOES(ffi::RENDERBUFFER, image);
self.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0);
let mut fbo = 0;
self.internal.gl.GenFramebuffers(1, &mut fbo as *mut _);
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo);
self.internal.gl.FramebufferRenderbuffer(ffi::FRAMEBUFFER, ffi::COLOR_ATTACHMENT0, ffi::RENDERBUFFER, rbo);
let status = self.internal.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER);
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
self.gl.GenFramebuffers(1, &mut fbo as *mut _);
self.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo);
self.gl.FramebufferRenderbuffer(ffi::FRAMEBUFFER, ffi::COLOR_ATTACHMENT0, ffi::RENDERBUFFER, rbo);
let status = self.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER);
self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
if status != ffi::FRAMEBUFFER_COMPLETE {
//TODO wrap image and drop here
@ -361,146 +362,36 @@ impl Bind<Dmabuf> for Gles2Renderer {
})?;
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(())
}
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 {
fn unbind(&mut self) -> Result<(), Gles2Error> {
fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error> {
unsafe {
self.internal.egl.make_current()?;
self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
self.egl.make_current()?;
}
self.current_buffer = None;
let _ = self.internal.egl.unbind();
unsafe { self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0) };
self.target_buffer = None;
self.target_surface = None;
self.egl.unbind()?;
Ok(())
}
}
impl Renderer 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 {
impl Drop for Gles2Renderer {
fn drop(&mut self) {
unsafe {
if self.egl.make_current().is_ok() {
self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0);
for program in &self.programs {
self.gl.DeleteProgram(program.program);
}
@ -533,60 +424,171 @@ static TEX_COORDS: [ffi::types::GLfloat; 8] = [
0.0, 1.0, // bottom left
];
impl Frame for Gles2Frame {
impl Renderer for Gles2Renderer {
type Error = Gles2Error;
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.internal.gl.ClearColor(color[0], color[1], color[2], color[3]);
self.internal.gl.Clear(ffi::COLOR_BUFFER_BIT);
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 {
self.gl.Viewport(0, 0, width as i32, height as i32);
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(())
}
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
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 };
// render
unsafe {
self.internal.gl.ActiveTexture(ffi::TEXTURE0);
self.internal.gl.BindTexture(target, tex.texture);
self.internal.gl.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32);
self.internal.gl.UseProgram(self.internal.programs[tex.texture_kind].program);
self.gl.ActiveTexture(ffi::TEXTURE0);
self.gl.BindTexture(target, tex.texture);
self.gl.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32);
self.gl.UseProgram(self.programs[tex.texture_kind].program);
self.internal.gl.Uniform1i(self.internal.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.internal.gl.Uniform1i(self.internal.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.Uniform1i(self.programs[tex.texture_kind].uniform_tex, 0);
self.gl.UniformMatrix3fv(self.programs[tex.texture_kind].uniform_matrix, 1, ffi::FALSE, matrix.as_ptr());
self.gl.Uniform1i(self.programs[tex.texture_kind].uniform_invert_y, if tex.y_inverted { 1 } else { 0 });
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.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_position as u32, 2, ffi::FLOAT, ffi::FALSE, 0, VERTS.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.internal.gl.EnableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32);
self.gl.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_position 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.internal.gl.DisableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32);
self.gl.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_position 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(())
}
fn finish(self) -> Result<(), crate::backend::SwapBuffersError> {
fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> {
self.make_current()?;
unsafe {
self.internal.gl.Flush();
self.internal.gl.Disable(ffi::BLEND);
self.gl.Flush();
self.gl.Disable(ffi::BLEND);
}
self.current_projection = None;
Ok(())
}
}

View File

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