renderer: Closure-based api
This commit is contained in:
parent
73420b75bc
commit
978ef1b393
|
@ -6,7 +6,7 @@ use slog::Logger;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
egl::display::EGLBufferReader,
|
egl::display::EGLBufferReader,
|
||||||
renderer::{Renderer, Texture, Transform},
|
renderer::{Frame, Renderer, Texture, Transform},
|
||||||
SwapBuffersError,
|
SwapBuffersError,
|
||||||
},
|
},
|
||||||
reexports::{
|
reexports::{
|
||||||
|
@ -36,8 +36,9 @@ impl<T> Drop for BufferTextures<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_cursor<R, E, T>(
|
pub fn draw_cursor<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
egl_buffer_reader: Option<&EGLBufferReader>,
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
||||||
(x, y): (i32, i32),
|
(x, y): (i32, i32),
|
||||||
|
@ -45,7 +46,8 @@ pub fn draw_cursor<R, E, T>(
|
||||||
log: &Logger,
|
log: &Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
R: Renderer<Error = E, TextureId = T>,
|
R: Renderer<Error = E, TextureId = T, Frame=F>,
|
||||||
|
F: Frame<Error = E, TextureId = T>,
|
||||||
E: std::error::Error + Into<SwapBuffersError>,
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
|
@ -59,11 +61,12 @@ where
|
||||||
(0, 0)
|
(0, 0)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
draw_surface_tree(renderer, surface, egl_buffer_reader, (x - dx, y - dy), token, log)
|
draw_surface_tree(renderer, frame, surface, egl_buffer_reader, (x - dx, y - dy), token, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_surface_tree<R, E, T>(
|
fn draw_surface_tree<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
root: &wl_surface::WlSurface,
|
root: &wl_surface::WlSurface,
|
||||||
egl_buffer_reader: Option<&EGLBufferReader>,
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
||||||
location: (i32, i32),
|
location: (i32, i32),
|
||||||
|
@ -71,7 +74,8 @@ fn draw_surface_tree<R, E, T>(
|
||||||
log: &Logger,
|
log: &Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
R: Renderer<Error = E, TextureId = T>,
|
R: Renderer<Error = E, TextureId = T, Frame=F>,
|
||||||
|
F: Frame<Error = E, TextureId = T>,
|
||||||
E: std::error::Error + Into<SwapBuffersError>,
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
|
@ -79,8 +83,8 @@ where
|
||||||
|
|
||||||
compositor_token.with_surface_tree_upward(
|
compositor_token.with_surface_tree_upward(
|
||||||
root,
|
root,
|
||||||
(),
|
location,
|
||||||
|_surface, attributes, _, _| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
// 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();
|
||||||
|
@ -107,31 +111,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now, should we be drawn ?
|
// Now, should we be drawn ?
|
||||||
if data.texture.is_some() {
|
if data.texture.is_some() {// if yes, also process the children
|
||||||
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 data = data.borrow();
|
|
||||||
// Now, should we be drawn ?
|
|
||||||
if data.texture.is_some() {
|
|
||||||
// if yes, also process the children
|
|
||||||
if Role::<SubsurfaceRole>::has(role) {
|
if Role::<SubsurfaceRole>::has(role) {
|
||||||
x += data.current_state.sub_location.0;
|
x += data.current_state.sub_location.0;
|
||||||
y += data.current_state.sub_location.1;
|
y += data.current_state.sub_location.1;
|
||||||
|
@ -161,7 +141,7 @@ where
|
||||||
x += sub_x;
|
x += sub_x;
|
||||||
y += sub_y;
|
y += sub_y;
|
||||||
}
|
}
|
||||||
if let Err(err) = renderer.render_texture_at(
|
if let Err(err) = frame.render_texture_at(
|
||||||
&texture.texture,
|
&texture.texture,
|
||||||
(x, y),
|
(x, y),
|
||||||
Transform::Normal, /* TODO */
|
Transform::Normal, /* TODO */
|
||||||
|
@ -178,8 +158,9 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_windows<R, E, T>(
|
pub fn draw_windows<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
egl_buffer_reader: Option<&EGLBufferReader>,
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
||||||
window_map: &MyWindowMap,
|
window_map: &MyWindowMap,
|
||||||
output_rect: Option<Rectangle>,
|
output_rect: Option<Rectangle>,
|
||||||
|
@ -187,7 +168,8 @@ pub fn draw_windows<R, E, T>(
|
||||||
log: &::slog::Logger,
|
log: &::slog::Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
R: Renderer<Error = E, TextureId = T>,
|
R: Renderer<Error = E, TextureId = T, Frame=F>,
|
||||||
|
F: Frame<Error = E, TextureId = T>,
|
||||||
E: std::error::Error + Into<SwapBuffersError>,
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
|
@ -206,6 +188,7 @@ where
|
||||||
// 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
|
||||||
if let Err(err) = draw_surface_tree(
|
if let Err(err) = draw_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
|
frame,
|
||||||
&wl_surface,
|
&wl_surface,
|
||||||
egl_buffer_reader,
|
egl_buffer_reader,
|
||||||
initial_place,
|
initial_place,
|
||||||
|
@ -220,8 +203,9 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_dnd_icon<R, E, T>(
|
pub fn draw_dnd_icon<R, E, F, T>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
egl_buffer_reader: Option<&EGLBufferReader>,
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
||||||
(x, y): (i32, i32),
|
(x, y): (i32, i32),
|
||||||
|
@ -229,7 +213,8 @@ pub fn draw_dnd_icon<R, E, T>(
|
||||||
log: &::slog::Logger,
|
log: &::slog::Logger,
|
||||||
) -> Result<(), SwapBuffersError>
|
) -> Result<(), SwapBuffersError>
|
||||||
where
|
where
|
||||||
R: Renderer<Error = E, TextureId = T>,
|
R: Renderer<Error = E, TextureId = T, Frame=F>,
|
||||||
|
F: Frame<Error = E, TextureId = T>,
|
||||||
E: std::error::Error + Into<SwapBuffersError>,
|
E: std::error::Error + Into<SwapBuffersError>,
|
||||||
T: Texture + 'static,
|
T: Texture + 'static,
|
||||||
{
|
{
|
||||||
|
@ -239,39 +224,5 @@ where
|
||||||
"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, surface, egl_buffer_reader, (x, y), token, log)
|
draw_surface_tree(renderer, frame, surface, egl_buffer_reader, (x, y), token, log)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn schedule_initial_render<R: Renderer + 'static, Data: 'static>(
|
|
||||||
renderer: Rc<RefCell<R>>,
|
|
||||||
evt_handle: &LoopHandle<Data>,
|
|
||||||
logger: ::slog::Logger,
|
|
||||||
) where
|
|
||||||
<R as Renderer>::Error: Into<SwapBuffersError>,
|
|
||||||
{
|
|
||||||
let result = {
|
|
||||||
let mut renderer = renderer.borrow_mut();
|
|
||||||
// Does not matter if we render an empty frame
|
|
||||||
renderer
|
|
||||||
.begin(1, 1, Transform::Normal)
|
|
||||||
.map_err(Into::<SwapBuffersError>::into)
|
|
||||||
.and_then(|_| {
|
|
||||||
renderer
|
|
||||||
.clear([0.8, 0.8, 0.9, 1.0])
|
|
||||||
.map_err(Into::<SwapBuffersError>::into)
|
|
||||||
})
|
|
||||||
.and_then(|_| renderer.finish())
|
|
||||||
};
|
|
||||||
if let Err(err) = result {
|
|
||||||
match err {
|
|
||||||
SwapBuffersError::AlreadySwapped => {}
|
|
||||||
SwapBuffersError::TemporaryFailure(err) => {
|
|
||||||
// TODO dont reschedule after 3(?) retries
|
|
||||||
warn!(logger, "Failed to submit page_flip: {}", err);
|
|
||||||
let handle = evt_handle.clone();
|
|
||||||
evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle, logger));
|
|
||||||
}
|
|
||||||
SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use smithay::{
|
||||||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||||
renderer::{
|
renderer::{
|
||||||
gles2::{Gles2Renderer, Gles2Texture},
|
gles2::{Gles2Renderer, Gles2Texture},
|
||||||
Renderer, Transform,
|
Frame, Renderer, Transform,
|
||||||
},
|
},
|
||||||
session::{auto::AutoSession, Session, Signal as SessionSignal},
|
session::{auto::AutoSession, Session, Signal as SessionSignal},
|
||||||
udev::{UdevBackend, UdevEvent},
|
udev::{UdevBackend, UdevEvent},
|
||||||
|
@ -528,10 +528,10 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
trace!(self.logger, "Backends: {:?}", backends.borrow().keys());
|
trace!(self.logger, "Backends: {:?}", backends.borrow().keys());
|
||||||
for renderer in backends.borrow_mut().values() {
|
for backend in backends.borrow_mut().values() {
|
||||||
// render first frame
|
// render first frame
|
||||||
trace!(self.logger, "Scheduling frame");
|
trace!(self.logger, "Scheduling frame");
|
||||||
schedule_initial_render(renderer.clone(), &self.loop_handle, self.logger.clone());
|
schedule_initial_render(backend.clone(), &self.loop_handle, self.logger.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backends.insert(
|
self.backends.insert(
|
||||||
|
@ -767,70 +767,108 @@ impl DrmRenderer {
|
||||||
.unwrap_or((0, 0)); // in this case the output will be removed.
|
.unwrap_or((0, 0)); // in this case the output will be removed.
|
||||||
|
|
||||||
// and draw in sync with our monitor
|
// and draw in sync with our monitor
|
||||||
surface.queue_frame()?;
|
surface.render(|renderer, frame| {
|
||||||
surface.clear([0.8, 0.8, 0.9, 1.0])?;
|
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||||
// draw the surfaces
|
// draw the surfaces
|
||||||
draw_windows(
|
draw_windows(
|
||||||
surface,
|
renderer,
|
||||||
egl_buffer_reader,
|
frame,
|
||||||
window_map,
|
egl_buffer_reader,
|
||||||
Some(Rectangle {
|
window_map,
|
||||||
x: x as i32,
|
Some(Rectangle {
|
||||||
y: y as i32,
|
x: x as i32,
|
||||||
width: width as i32,
|
y: y as i32,
|
||||||
height: height as i32,
|
width: width as i32,
|
||||||
}),
|
height: height as i32,
|
||||||
*compositor_token,
|
}),
|
||||||
logger,
|
*compositor_token,
|
||||||
)?;
|
logger,
|
||||||
|
)?;
|
||||||
|
|
||||||
// get pointer coordinates
|
// get pointer coordinates
|
||||||
let (ptr_x, ptr_y) = *pointer_location;
|
let (ptr_x, ptr_y) = *pointer_location;
|
||||||
let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
|
let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
|
||||||
let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;
|
let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;
|
||||||
|
|
||||||
// set cursor
|
// set cursor
|
||||||
if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
|
if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
|
||||||
// draw the dnd icon if applicable
|
// draw the dnd icon if applicable
|
||||||
{
|
{
|
||||||
if let Some(ref wl_surface) = dnd_icon.as_ref() {
|
if let Some(ref wl_surface) = dnd_icon.as_ref() {
|
||||||
if wl_surface.as_ref().is_alive() {
|
if wl_surface.as_ref().is_alive() {
|
||||||
draw_dnd_icon(
|
draw_dnd_icon(
|
||||||
surface,
|
renderer,
|
||||||
|
frame,
|
||||||
|
wl_surface,
|
||||||
|
egl_buffer_reader,
|
||||||
|
(ptr_x, ptr_y),
|
||||||
|
*compositor_token,
|
||||||
|
logger,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// draw the cursor as relevant
|
||||||
|
{
|
||||||
|
// reset the cursor if the surface is no longer alive
|
||||||
|
let mut reset = false;
|
||||||
|
if let CursorImageStatus::Image(ref surface) = *cursor_status {
|
||||||
|
reset = !surface.as_ref().is_alive();
|
||||||
|
}
|
||||||
|
if reset {
|
||||||
|
*cursor_status = CursorImageStatus::Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
|
||||||
|
draw_cursor(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
wl_surface,
|
wl_surface,
|
||||||
egl_buffer_reader,
|
egl_buffer_reader,
|
||||||
(ptr_x, ptr_y),
|
(ptr_x, ptr_y),
|
||||||
*compositor_token,
|
*compositor_token,
|
||||||
logger,
|
logger,
|
||||||
)?;
|
)?;
|
||||||
|
} else {
|
||||||
|
frame.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// draw the cursor as relevant
|
|
||||||
{
|
|
||||||
// reset the cursor if the surface is no longer alive
|
|
||||||
let mut reset = false;
|
|
||||||
if let CursorImageStatus::Image(ref surface) = *cursor_status {
|
|
||||||
reset = !surface.as_ref().is_alive();
|
|
||||||
}
|
|
||||||
if reset {
|
|
||||||
*cursor_status = CursorImageStatus::Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
|
Ok(())
|
||||||
draw_cursor(
|
}).map_err(Into::<SwapBuffersError>::into)
|
||||||
surface,
|
.and_then(|x| x)
|
||||||
wl_surface,
|
.map_err(Into::<SwapBuffersError>::into)
|
||||||
egl_buffer_reader,
|
}
|
||||||
(ptr_x, ptr_y),
|
}
|
||||||
*compositor_token,
|
|
||||||
logger,
|
fn schedule_initial_render<Data: 'static>(
|
||||||
)?;
|
renderer: Rc<RefCell<RenderSurface>>,
|
||||||
} else {
|
evt_handle: &LoopHandle<Data>,
|
||||||
surface.render_texture_at(pointer_image, (ptr_x, ptr_y), Transform::Normal, 1.0)?;
|
logger: ::slog::Logger,
|
||||||
}
|
) {
|
||||||
}
|
let result = {
|
||||||
}
|
let mut renderer = renderer.borrow_mut();
|
||||||
surface.finish()
|
// Does not matter if we render an empty frame
|
||||||
|
renderer
|
||||||
|
.render(|_, frame| {
|
||||||
|
frame
|
||||||
|
.clear([0.8, 0.8, 0.9, 1.0])
|
||||||
|
.map_err(Into::<SwapBuffersError>::into)
|
||||||
|
})
|
||||||
|
.map_err(Into::<SwapBuffersError>::into)
|
||||||
|
.and_then(|x| x.map_err(Into::<SwapBuffersError>::into))
|
||||||
|
};
|
||||||
|
if let Err(err) = result {
|
||||||
|
match err {
|
||||||
|
SwapBuffersError::AlreadySwapped => {}
|
||||||
|
SwapBuffersError::TemporaryFailure(err) => {
|
||||||
|
// TODO dont reschedule after 3(?) retries
|
||||||
|
warn!(logger, "Failed to submit page_flip: {}", err);
|
||||||
|
let handle = evt_handle.clone();
|
||||||
|
evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle, logger));
|
||||||
|
}
|
||||||
|
SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{input::InputBackend, renderer::Renderer, winit, SwapBuffersError},
|
backend::{input::InputBackend, renderer::Frame, winit, SwapBuffersError},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::EventLoop,
|
calloop::EventLoop,
|
||||||
wayland_server::{protocol::wl_output, Display},
|
wayland_server::{protocol::wl_output, Display},
|
||||||
|
@ -82,6 +82,7 @@ pub fn run_winit(
|
||||||
});
|
});
|
||||||
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
|
let mut cursor_visible = true;
|
||||||
|
|
||||||
info!(log, "Initialization completed, starting the main loop.");
|
info!(log, "Initialization completed, starting the main loop.");
|
||||||
|
|
||||||
|
@ -98,70 +99,75 @@ pub fn run_winit(
|
||||||
{
|
{
|
||||||
let mut renderer = renderer.borrow_mut();
|
let mut renderer = renderer.borrow_mut();
|
||||||
|
|
||||||
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
|
let result = renderer.render(|renderer, frame| {
|
||||||
draw_windows(
|
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||||
&mut *renderer,
|
|
||||||
reader.as_ref(),
|
|
||||||
&*state.window_map.borrow(),
|
|
||||||
None,
|
|
||||||
state.ctoken,
|
|
||||||
&log,
|
|
||||||
)
|
|
||||||
.expect("Failed to renderer windows");
|
|
||||||
|
|
||||||
let (x, y) = *state.pointer_location.borrow();
|
// draw the windows
|
||||||
// draw the dnd icon if any
|
draw_windows(
|
||||||
{
|
renderer,
|
||||||
let guard = state.dnd_icon.lock().unwrap();
|
frame,
|
||||||
if let Some(ref surface) = *guard {
|
reader.as_ref(),
|
||||||
if surface.as_ref().is_alive() {
|
&*state.window_map.borrow(),
|
||||||
draw_dnd_icon(
|
None,
|
||||||
&mut *renderer,
|
state.ctoken,
|
||||||
|
&log,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (x, y) = *state.pointer_location.borrow();
|
||||||
|
// draw the dnd icon if any
|
||||||
|
{
|
||||||
|
let guard = state.dnd_icon.lock().unwrap();
|
||||||
|
if let Some(ref surface) = *guard {
|
||||||
|
if surface.as_ref().is_alive() {
|
||||||
|
draw_dnd_icon(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
surface,
|
||||||
|
reader.as_ref(),
|
||||||
|
(x as i32, y as i32),
|
||||||
|
state.ctoken,
|
||||||
|
&log,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// draw the cursor as relevant
|
||||||
|
{
|
||||||
|
let mut guard = state.cursor_status.lock().unwrap();
|
||||||
|
// reset the cursor if the surface is no longer alive
|
||||||
|
let mut reset = false;
|
||||||
|
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||||
|
reset = !surface.as_ref().is_alive();
|
||||||
|
}
|
||||||
|
if reset {
|
||||||
|
*guard = CursorImageStatus::Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw as relevant
|
||||||
|
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||||
|
cursor_visible = false;
|
||||||
|
draw_cursor(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
surface,
|
surface,
|
||||||
reader.as_ref(),
|
reader.as_ref(),
|
||||||
(x as i32, y as i32),
|
(x as i32, y as i32),
|
||||||
state.ctoken,
|
state.ctoken,
|
||||||
&log,
|
&log,
|
||||||
)
|
)?;
|
||||||
.expect("Failed to render dnd icon");
|
} else {
|
||||||
|
cursor_visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// draw the cursor as relevant
|
|
||||||
{
|
|
||||||
let mut guard = state.cursor_status.lock().unwrap();
|
|
||||||
// reset the cursor if the surface is no longer alive
|
|
||||||
let mut reset = false;
|
|
||||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
|
||||||
reset = !surface.as_ref().is_alive();
|
|
||||||
}
|
|
||||||
if reset {
|
|
||||||
*guard = CursorImageStatus::Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw as relevant
|
Ok(())
|
||||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
}).map_err(Into::<SwapBuffersError>::into)
|
||||||
renderer.window().set_cursor_visible(false);
|
.and_then(|x| x.into());
|
||||||
draw_cursor(
|
|
||||||
&mut *renderer,
|
|
||||||
surface,
|
|
||||||
reader.as_ref(),
|
|
||||||
(x as i32, y as i32),
|
|
||||||
state.ctoken,
|
|
||||||
&log,
|
|
||||||
)
|
|
||||||
.expect("Failed to render cursor");
|
|
||||||
} else {
|
|
||||||
renderer.window().set_cursor_visible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(SwapBuffersError::ContextLost(err)) = renderer.finish() {
|
renderer.window().set_cursor_visible(cursor_visible);
|
||||||
|
|
||||||
|
if let Err(SwapBuffersError::ContextLost(err)) = result {
|
||||||
error!(log, "Critical Rendering Error: {}", err);
|
error!(log, "Critical Rendering Error: {}", err);
|
||||||
state.running.store(false, Ordering::SeqCst);
|
state.running.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,12 @@ use std::collections::HashSet;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
use crate::wayland::compositor::Damage;
|
|
||||||
use cgmath::Matrix3;
|
|
||||||
use drm::buffer::PlanarBuffer;
|
use drm::buffer::PlanarBuffer;
|
||||||
use drm::control::{connector, crtc, framebuffer, plane, Device, Mode};
|
use drm::control::{connector, crtc, framebuffer, plane, Device, Mode};
|
||||||
use gbm::{BufferObject, BufferObjectFlags, Device as GbmDevice};
|
use gbm::{BufferObject, BufferObjectFlags, Device as GbmDevice};
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
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::renderer::{Bind, Renderer, Texture, Transform};
|
use crate::backend::renderer::{Bind, Frame, Renderer, Texture, Transform};
|
||||||
use crate::backend::SwapBuffersError;
|
use crate::backend::SwapBuffersError;
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
allocator::{
|
allocator::{
|
||||||
|
@ -20,8 +15,6 @@ use crate::backend::{
|
||||||
Allocator, Buffer, Format, Fourcc, Modifier, Slot, Swapchain,
|
Allocator, Buffer, Format, Fourcc, Modifier, Slot, Swapchain,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
#[cfg(all(feature = "backend_egl", feature = "wayland_frontend"))]
|
|
||||||
use crate::backend::egl::display::EGLBufferReader;
|
|
||||||
|
|
||||||
/// Simplified by limited abstraction to link single [`DrmSurface`]s to renderers.
|
/// Simplified by limited abstraction to link single [`DrmSurface`]s to renderers.
|
||||||
///
|
///
|
||||||
|
@ -33,7 +26,6 @@ use crate::backend::egl::display::EGLBufferReader;
|
||||||
pub struct DrmRenderSurface<D: AsRawFd + 'static, A: Allocator<B>, R: Bind<Dmabuf>, B: Buffer> {
|
pub struct DrmRenderSurface<D: AsRawFd + 'static, A: Allocator<B>, R: Bind<Dmabuf>, B: Buffer> {
|
||||||
_format: Format,
|
_format: Format,
|
||||||
buffers: Buffers<D, B>,
|
buffers: Buffers<D, B>,
|
||||||
current_buffer: Option<(Slot<B, (Dmabuf, BufferObject<FbHandle<D>>)>, Dmabuf)>,
|
|
||||||
swapchain: Swapchain<A, B, (Dmabuf, BufferObject<FbHandle<D>>)>,
|
swapchain: Swapchain<A, B, (Dmabuf, BufferObject<FbHandle<D>>)>,
|
||||||
renderer: R,
|
renderer: R,
|
||||||
drm: Arc<DrmSurface<D>>,
|
drm: Arc<DrmSurface<D>>,
|
||||||
|
@ -169,11 +161,11 @@ where
|
||||||
.map_err(Error::<E1, E2, E3>::RenderError)
|
.map_err(Error::<E1, E2, E3>::RenderError)
|
||||||
.and_then(|_| {
|
.and_then(|_| {
|
||||||
renderer
|
renderer
|
||||||
.begin(mode.size().0 as u32, mode.size().1 as u32, Transform::Normal)
|
.render(mode.size().0 as u32, mode.size().1 as u32, Transform::Normal, |_, frame| {
|
||||||
|
frame.clear([0.0, 0.0, 0.0, 1.0])
|
||||||
|
})
|
||||||
.map_err(Error::RenderError)
|
.map_err(Error::RenderError)
|
||||||
})
|
})
|
||||||
.and_then(|_| renderer.clear([0.0, 0.0, 0.0, 1.0]).map_err(Error::RenderError))
|
|
||||||
.and_then(|_| renderer.finish().map_err(|_| Error::InitialRenderingError))
|
|
||||||
.and_then(|_| renderer.unbind().map_err(Error::RenderError))
|
.and_then(|_| renderer.unbind().map_err(Error::RenderError))
|
||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -204,7 +196,6 @@ where
|
||||||
renderer,
|
renderer,
|
||||||
swapchain,
|
swapchain,
|
||||||
buffers,
|
buffers,
|
||||||
current_buffer: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -217,34 +208,53 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut to [`Renderer::begin`] with the pending mode as dimensions.
|
/// Access the underlying renderer
|
||||||
pub fn queue_frame(&mut self) -> Result<(), Error<E1, E2, E3>> {
|
pub fn renderer(&mut self) -> &mut R {
|
||||||
let mode = self.drm.pending_mode();
|
&mut self.renderer
|
||||||
let (width, height) = (mode.size().0 as u32, mode.size().1 as u32);
|
|
||||||
self.begin(width, height, Transform::Normal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut to abort the current frame.
|
/// Shortcut to [`Renderer::render`] with the pending mode as dimensions
|
||||||
///
|
/// and this surface set a the rendering target.
|
||||||
/// Allows [`DrmRenderSurface::queue_frame`] or [`Renderer::begin`] to be called again
|
pub fn render<F, S>(&mut self, rendering: F) -> Result<S, Error<E1, E2, E3>>
|
||||||
/// without displaying the current rendering context to the user.
|
where
|
||||||
pub fn drop_frame(&mut self) -> Result<(), SwapBuffersError> {
|
F: FnOnce(&mut R, &mut <R as Renderer>::Frame) -> S
|
||||||
if self.current_buffer.is_none() {
|
{
|
||||||
return Ok(());
|
let mode = self.drm.pending_mode();
|
||||||
|
let (width, height) = (mode.size().0 as u32, mode.size().1 as u32);
|
||||||
|
let slot = self
|
||||||
|
.swapchain
|
||||||
|
.acquire()
|
||||||
|
.map_err(Error::SwapchainError)?
|
||||||
|
.ok_or(Error::NoFreeSlotsError)?;
|
||||||
|
let dmabuf = match &*slot.userdata() {
|
||||||
|
Some((buf, _)) => buf.clone(),
|
||||||
|
None => (*slot).export().map_err(Error::AsDmabufError)?,
|
||||||
|
};
|
||||||
|
self.renderer.bind(dmabuf.clone()).map_err(Error::RenderError)?;
|
||||||
|
|
||||||
|
let result = self.renderer
|
||||||
|
.render(
|
||||||
|
width, height,
|
||||||
|
Transform::Flipped180 /* TODO: add Add<Transform> implementation to add and correct _transform here */,
|
||||||
|
rendering,
|
||||||
|
)
|
||||||
|
.map_err(Error::RenderError)?;
|
||||||
|
|
||||||
|
match self.buffers.queue::<E1, E2, E3>(slot, dmabuf) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(Error::DrmError(drm)) => return Err(drm.into()),
|
||||||
|
Err(Error::GbmError(err)) => return Err(err.into()),
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// finish the renderer in case it needs it
|
Ok(result)
|
||||||
let result = self.renderer.finish();
|
|
||||||
// but do not queue the buffer, drop it in any case
|
|
||||||
let _ = self.current_buffer.take();
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the current frame as submitted.
|
/// Marks the current frame as submitted.
|
||||||
///
|
///
|
||||||
/// Needs to be called, after the vblank event of the matching [`DrmDevice`](super::DrmDevice)
|
/// Needs to be called, after the vblank event of the matching [`DrmDevice`](super::DrmDevice)
|
||||||
/// was received after calling [`Renderer::finish`] on this surface. Otherwise the rendering
|
/// was received after calling [`DrmRenderSurface::render`] on this surface.
|
||||||
/// will run out of buffers eventually.
|
/// Otherwise the rendering will run out of buffers eventually.
|
||||||
pub fn frame_submitted(&mut self) -> Result<(), Error<E1, E2, E3>> {
|
pub fn frame_submitted(&mut self) -> Result<(), Error<E1, E2, E3>> {
|
||||||
self.buffers.submitted()
|
self.buffers.submitted()
|
||||||
}
|
}
|
||||||
|
@ -325,100 +335,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, A, B, T, R, E1, E2, E3> Renderer for DrmRenderSurface<D, A, R, B>
|
|
||||||
where
|
|
||||||
D: AsRawFd + 'static,
|
|
||||||
A: Allocator<B, Error = E1>,
|
|
||||||
B: Buffer + AsDmabuf<Error = E2>,
|
|
||||||
R: Bind<Dmabuf> + Renderer<Error = E3, TextureId = T>,
|
|
||||||
T: Texture,
|
|
||||||
E1: std::error::Error + 'static,
|
|
||||||
E2: std::error::Error + 'static,
|
|
||||||
E3: std::error::Error + 'static,
|
|
||||||
{
|
|
||||||
type Error = Error<E1, E2, E3>;
|
|
||||||
type TextureId = T;
|
|
||||||
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
|
|
||||||
&mut self,
|
|
||||||
image: &image::ImageBuffer<image::Rgba<u8>, C>,
|
|
||||||
) -> Result<Self::TextureId, Self::Error> {
|
|
||||||
self.renderer.import_bitmap(image).map_err(Error::RenderError)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
fn shm_formats(&self) -> &[wl_shm::Format] {
|
|
||||||
self.renderer.shm_formats()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "backend_egl", feature = "wayland_frontend"))]
|
|
||||||
fn import_buffer(
|
|
||||||
&mut self,
|
|
||||||
buffer: &wl_buffer::WlBuffer,
|
|
||||||
damage: Option<&Damage>,
|
|
||||||
egl: Option<&EGLBufferReader>,
|
|
||||||
) -> Result<Self::TextureId, Self::Error> {
|
|
||||||
self.renderer
|
|
||||||
.import_buffer(buffer, damage, egl)
|
|
||||||
.map_err(Error::RenderError)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin(&mut self, width: u32, height: u32, _transform: Transform) -> Result<(), Error<E1, E2, E3>> {
|
|
||||||
if self.current_buffer.is_some() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let slot = self
|
|
||||||
.swapchain
|
|
||||||
.acquire()
|
|
||||||
.map_err(Error::SwapchainError)?
|
|
||||||
.ok_or(Error::NoFreeSlotsError)?;
|
|
||||||
let dmabuf = match &*slot.userdata() {
|
|
||||||
Some((buf, _)) => buf.clone(),
|
|
||||||
None => (*slot).export().map_err(Error::AsDmabufError)?,
|
|
||||||
};
|
|
||||||
self.renderer.bind(dmabuf.clone()).map_err(Error::RenderError)?;
|
|
||||||
self.current_buffer = Some((slot, dmabuf));
|
|
||||||
self.renderer
|
|
||||||
.begin(width, height, Transform::Flipped180 /* TODO: add Add<Transform> implementation to add and correct _transform here */)
|
|
||||||
.map_err(Error::RenderError)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> {
|
|
||||||
self.renderer.clear(color).map_err(Error::RenderError)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_texture(
|
|
||||||
&mut self,
|
|
||||||
texture: &Self::TextureId,
|
|
||||||
matrix: Matrix3<f32>,
|
|
||||||
alpha: f32,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.renderer
|
|
||||||
.render_texture(texture, matrix, alpha)
|
|
||||||
.map_err(Error::RenderError)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) -> Result<(), SwapBuffersError> {
|
|
||||||
if self.current_buffer.is_none() {
|
|
||||||
return Err(SwapBuffersError::AlreadySwapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = self.renderer.finish();
|
|
||||||
if result.is_ok() {
|
|
||||||
let (slot, dmabuf) = self.current_buffer.take().unwrap();
|
|
||||||
match self.buffers.queue::<E1, E2, E3>(slot, dmabuf) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(Error::DrmError(drm)) => return Err(drm.into()),
|
|
||||||
Err(Error::GbmError(err)) => return Err(SwapBuffersError::ContextLost(Box::new(err))),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FbHandle<D: AsRawFd + 'static> {
|
struct FbHandle<D: AsRawFd + 'static> {
|
||||||
drm: Arc<DrmSurface<D>>,
|
drm: Arc<DrmSurface<D>>,
|
||||||
fb: framebuffer::Handle,
|
fb: framebuffer::Handle,
|
||||||
|
|
|
@ -19,7 +19,7 @@ use cgmath::{prelude::*, Matrix3};
|
||||||
mod shaders;
|
mod shaders;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
use super::{Bind, Renderer, Texture, Transform, Unbind};
|
use super::{Bind, Frame, Renderer, Texture, Transform, Unbind};
|
||||||
use crate::backend::allocator::{
|
use crate::backend::allocator::{
|
||||||
dmabuf::{Dmabuf, WeakDmabuf},
|
dmabuf::{Dmabuf, WeakDmabuf},
|
||||||
Format,
|
Format,
|
||||||
|
@ -48,7 +48,7 @@ pub mod ffi {
|
||||||
// cannot assume, that resources between two renderers are (and even can be) shared.
|
// cannot assume, that resources between two renderers are (and even can be) shared.
|
||||||
static RENDERER_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
static RENDERER_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
struct Gles2Program {
|
struct Gles2Program {
|
||||||
program: ffi::types::GLuint,
|
program: ffi::types::GLuint,
|
||||||
uniform_tex: ffi::types::GLint,
|
uniform_tex: ffi::types::GLint,
|
||||||
|
@ -147,13 +147,12 @@ pub struct Gles2Renderer {
|
||||||
buffers: Vec<WeakGles2Buffer>,
|
buffers: Vec<WeakGles2Buffer>,
|
||||||
target_buffer: Option<Gles2Buffer>,
|
target_buffer: Option<Gles2Buffer>,
|
||||||
target_surface: Option<Rc<EGLSurface>>,
|
target_surface: Option<Rc<EGLSurface>>,
|
||||||
current_projection: Option<Matrix3<f32>>,
|
|
||||||
extensions: Vec<String>,
|
extensions: Vec<String>,
|
||||||
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
|
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
textures: HashMap<BufferEntry, Gles2Texture>,
|
textures: HashMap<BufferEntry, Gles2Texture>,
|
||||||
gl: ffi::Gles2,
|
|
||||||
egl: EGLContext,
|
egl: EGLContext,
|
||||||
|
gl: ffi::Gles2,
|
||||||
destruction_callback: Receiver<CleanupResource>,
|
destruction_callback: Receiver<CleanupResource>,
|
||||||
destruction_callback_sender: Sender<CleanupResource>,
|
destruction_callback_sender: Sender<CleanupResource>,
|
||||||
logger_ptr: Option<*mut ::slog::Logger>,
|
logger_ptr: Option<*mut ::slog::Logger>,
|
||||||
|
@ -161,6 +160,13 @@ pub struct Gles2Renderer {
|
||||||
_not_send: *mut (),
|
_not_send: *mut (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle to the currently rendered frame during [`Gles2Renderer::render`](Renderer::render)
|
||||||
|
pub struct Gles2Frame {
|
||||||
|
current_projection: Matrix3<f32>,
|
||||||
|
gl: ffi::Gles2,
|
||||||
|
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
|
||||||
|
}
|
||||||
|
|
||||||
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")
|
||||||
|
@ -168,7 +174,6 @@ impl fmt::Debug for Gles2Renderer {
|
||||||
.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)
|
||||||
.field("current_projection", &self.current_projection)
|
|
||||||
.field("extensions", &self.extensions)
|
.field("extensions", &self.extensions)
|
||||||
.field("programs", &self.programs)
|
.field("programs", &self.programs)
|
||||||
// ffi::Gles2 does not implement Debug
|
// ffi::Gles2 does not implement Debug
|
||||||
|
@ -453,7 +458,6 @@ impl Gles2Renderer {
|
||||||
buffers: Vec::new(),
|
buffers: Vec::new(),
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
textures: HashMap::new(),
|
textures: HashMap::new(),
|
||||||
current_projection: None,
|
|
||||||
destruction_callback: rx,
|
destruction_callback: rx,
|
||||||
destruction_callback_sender: tx,
|
destruction_callback_sender: tx,
|
||||||
logger_ptr,
|
logger_ptr,
|
||||||
|
@ -596,7 +600,6 @@ impl Gles2Renderer {
|
||||||
self.gl.BindTexture(ffi::TEXTURE_2D, 0);
|
self.gl.BindTexture(ffi::TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.egl.unbind()?;
|
|
||||||
Ok(texture)
|
Ok(texture)
|
||||||
})
|
})
|
||||||
.map_err(Gles2Error::BufferAccessError)?
|
.map_err(Gles2Error::BufferAccessError)?
|
||||||
|
@ -671,7 +674,6 @@ impl Gles2Renderer {
|
||||||
self.make_current()?;
|
self.make_current()?;
|
||||||
let tex = Some(texture.0.texture);
|
let tex = Some(texture.0.texture);
|
||||||
self.import_egl_image(egl_images[0], false, tex)?;
|
self.import_egl_image(egl_images[0], false, tex)?;
|
||||||
self.egl.unbind()?;
|
|
||||||
}
|
}
|
||||||
Ok(Some(texture))
|
Ok(Some(texture))
|
||||||
}
|
}
|
||||||
|
@ -867,6 +869,7 @@ static TEX_COORDS: [ffi::types::GLfloat; 8] = [
|
||||||
impl Renderer for Gles2Renderer {
|
impl Renderer for Gles2Renderer {
|
||||||
type Error = Gles2Error;
|
type Error = Gles2Error;
|
||||||
type TextureId = Gles2Texture;
|
type TextureId = Gles2Texture;
|
||||||
|
type Frame = Gles2Frame;
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
fn shm_formats(&self) -> &[wl_shm::Format] {
|
fn shm_formats(&self) -> &[wl_shm::Format] {
|
||||||
|
@ -962,7 +965,15 @@ impl Renderer for Gles2Renderer {
|
||||||
Ok(texture)
|
Ok(texture)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), Gles2Error> {
|
fn render<F, R>(
|
||||||
|
&mut self,
|
||||||
|
width: u32, height: u32,
|
||||||
|
transform: Transform,
|
||||||
|
rendering: F,
|
||||||
|
) -> Result<R, Self::Error>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self, &mut Self::Frame) -> R
|
||||||
|
{
|
||||||
self.make_current()?;
|
self.make_current()?;
|
||||||
// delayed destruction until the next frame rendering.
|
// delayed destruction until the next frame rendering.
|
||||||
self.cleanup()?;
|
self.cleanup()?;
|
||||||
|
@ -991,13 +1002,44 @@ impl Renderer for Gles2Renderer {
|
||||||
renderer[2][0] = -(1.0f32.copysign(renderer[0][0] + renderer[1][0]));
|
renderer[2][0] = -(1.0f32.copysign(renderer[0][0] + renderer[1][0]));
|
||||||
renderer[2][1] = -(1.0f32.copysign(renderer[0][1] + renderer[1][1]));
|
renderer[2][1] = -(1.0f32.copysign(renderer[0][1] + renderer[1][1]));
|
||||||
|
|
||||||
// output transformation passed in by the user
|
let mut frame = Gles2Frame {
|
||||||
self.current_projection = Some(transform.matrix() * renderer);
|
gl: self.gl.clone(),
|
||||||
Ok(())
|
programs: self.programs.clone(),
|
||||||
|
// output transformation passed in by the user
|
||||||
|
current_projection: transform.matrix() * renderer,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = rendering(self, &mut frame);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.gl.Flush();
|
||||||
|
// We need to wait for the previously submitted GL commands to complete
|
||||||
|
// or otherwise the buffer could be submitted to the drm surface while
|
||||||
|
// still writing to the buffer which results in flickering on the screen.
|
||||||
|
// The proper solution would be to create a fence just before calling
|
||||||
|
// glFlush that the backend can use to wait for the commands to be finished.
|
||||||
|
// In case of a drm atomic backend the fence could be supplied by using the
|
||||||
|
// IN_FENCE_FD property.
|
||||||
|
// See https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html#explicit-fencing-properties for
|
||||||
|
// the topic on submitting a IN_FENCE_FD and the mesa kmskube example
|
||||||
|
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/9f63f359fab1b5d8e862508e4e51c9dfe339ccb0/drm-atomic.c
|
||||||
|
// especially here
|
||||||
|
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/9f63f359fab1b5d8e862508e4e51c9dfe339ccb0/drm-atomic.c#L147
|
||||||
|
// and here
|
||||||
|
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/9f63f359fab1b5d8e862508e4e51c9dfe339ccb0/drm-atomic.c#L235
|
||||||
|
self.gl.Finish();
|
||||||
|
self.gl.Disable(ffi::BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame for Gles2Frame {
|
||||||
|
type Error = Gles2Error;
|
||||||
|
type TextureId = Gles2Texture;
|
||||||
|
|
||||||
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> {
|
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> {
|
||||||
self.make_current()?;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.gl.ClearColor(color[0], color[1], color[2], color[3]);
|
self.gl.ClearColor(color[0], color[1], color[2], color[3]);
|
||||||
self.gl.Clear(ffi::COLOR_BUFFER_BIT);
|
self.gl.Clear(ffi::COLOR_BUFFER_BIT);
|
||||||
|
@ -1012,13 +1054,8 @@ impl Renderer for Gles2Renderer {
|
||||||
mut matrix: Matrix3<f32>,
|
mut matrix: Matrix3<f32>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.make_current()?;
|
|
||||||
if self.current_projection.is_none() {
|
|
||||||
return Err(Gles2Error::UnconstraintRenderingOperation);
|
|
||||||
}
|
|
||||||
|
|
||||||
//apply output transformation
|
//apply output transformation
|
||||||
matrix = self.current_projection.as_ref().unwrap() * matrix;
|
matrix = self.current_projection * matrix;
|
||||||
|
|
||||||
let target = if tex.0.is_external {
|
let target = if tex.0.is_external {
|
||||||
ffi::TEXTURE_EXTERNAL_OES
|
ffi::TEXTURE_EXTERNAL_OES
|
||||||
|
@ -1083,31 +1120,4 @@ impl Renderer for Gles2Renderer {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> {
|
|
||||||
self.make_current()?;
|
|
||||||
unsafe {
|
|
||||||
self.gl.Flush();
|
|
||||||
// We need to wait for the previously submitted GL commands to complete
|
|
||||||
// or otherwise the buffer could be submitted to the drm surface while
|
|
||||||
// still writing to the buffer which results in flickering on the screen.
|
|
||||||
// The proper solution would be to create a fence just before calling
|
|
||||||
// glFlush that the backend can use to wait for the commands to be finished.
|
|
||||||
// In case of a drm atomic backend the fence could be supplied by using the
|
|
||||||
// IN_FENCE_FD property.
|
|
||||||
// See https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html#explicit-fencing-properties for
|
|
||||||
// the topic on submitting a IN_FENCE_FD and the mesa kmskube example
|
|
||||||
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/9f63f359fab1b5d8e862508e4e51c9dfe339ccb0/drm-atomic.c
|
|
||||||
// especially here
|
|
||||||
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/9f63f359fab1b5d8e862508e4e51c9dfe339ccb0/drm-atomic.c#L147
|
|
||||||
// and here
|
|
||||||
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/9f63f359fab1b5d8e862508e4e51c9dfe339ccb0/drm-atomic.c#L235
|
|
||||||
self.gl.Finish();
|
|
||||||
self.gl.Disable(ffi::BLEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current_projection = None;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,59 @@ pub trait Texture {
|
||||||
fn height(&self) -> u32;
|
fn height(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Frame {
|
||||||
|
/// Error type returned by the rendering operations of this renderer.
|
||||||
|
type Error: Error;
|
||||||
|
/// Texture Handle type used by this renderer.
|
||||||
|
type TextureId: Texture;
|
||||||
|
|
||||||
|
/// Clear the complete current target with a single given color.
|
||||||
|
///
|
||||||
|
/// This operation is only valid in between a `begin` and `finish`-call.
|
||||||
|
/// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
|
||||||
|
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
|
||||||
|
/// Render a texture to the current target using given projection matrix and alpha.
|
||||||
|
///
|
||||||
|
/// This operation is only valid in between a `begin` and `finish`-call.
|
||||||
|
/// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
|
||||||
|
fn render_texture(
|
||||||
|
&mut self,
|
||||||
|
texture: &Self::TextureId,
|
||||||
|
matrix: Matrix3<f32>,
|
||||||
|
alpha: f32,
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
|
/// Render a texture to the current target as a flat 2d-plane at a given
|
||||||
|
/// position, applying the given transformation with the given alpha value.
|
||||||
|
///
|
||||||
|
/// This operation is only valid in between a `begin` and `finish`-call.
|
||||||
|
/// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
|
||||||
|
fn render_texture_at(
|
||||||
|
&mut self,
|
||||||
|
texture: &Self::TextureId,
|
||||||
|
pos: (i32, i32),
|
||||||
|
transform: Transform,
|
||||||
|
alpha: f32,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let mut mat = Matrix3::<f32>::identity();
|
||||||
|
|
||||||
|
// position and scale
|
||||||
|
let size = texture.size();
|
||||||
|
mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
|
||||||
|
mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);
|
||||||
|
|
||||||
|
//apply surface transformation
|
||||||
|
mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
|
||||||
|
if transform == Transform::Normal {
|
||||||
|
assert_eq!(mat, mat * transform.invert().matrix());
|
||||||
|
assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
|
||||||
|
}
|
||||||
|
mat = mat * transform.invert().matrix();
|
||||||
|
mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));
|
||||||
|
|
||||||
|
self.render_texture(texture, mat, alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Abstraction of commonly used rendering operations for compositors.
|
/// Abstraction of commonly used rendering operations for compositors.
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
/// Error type returned by the rendering operations of this renderer.
|
/// Error type returned by the rendering operations of this renderer.
|
||||||
|
@ -147,6 +200,8 @@ pub trait Renderer {
|
||||||
/// Texture Handle type used by this renderer.
|
/// Texture Handle type used by this renderer.
|
||||||
type TextureId: Texture;
|
type TextureId: Texture;
|
||||||
|
|
||||||
|
type Frame: Frame<Error=Self::Error, TextureId=Self::TextureId>;
|
||||||
|
|
||||||
/// Import a given bitmap into the renderer.
|
/// Import a given bitmap 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.
|
||||||
|
@ -196,64 +251,16 @@ pub trait Renderer {
|
||||||
/// - There was a previous `begin`-call, which was not terminated by `finish`.
|
/// - There was a previous `begin`-call, which was not terminated by `finish`.
|
||||||
/// - This renderer implements `Bind`, no target was bound *and* has no default target.
|
/// - This renderer implements `Bind`, no target was bound *and* has no default target.
|
||||||
/// - (Renderers not implementing `Bind` always have a default target.)
|
/// - (Renderers not implementing `Bind` always have a default target.)
|
||||||
fn begin(
|
fn render<F, R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
) -> Result<(), <Self as Renderer>::Error>;
|
rendering: F,
|
||||||
|
) -> Result<R, Self::Error>
|
||||||
/// Finish a renderering context, previously started by `begin`.
|
where
|
||||||
///
|
F: FnOnce(&mut Self, &mut Self::Frame) -> R
|
||||||
/// After this operation is finished the current rendering target contains a sucessfully rendered image.
|
;
|
||||||
/// If the image is immediently shown to the user depends on the target.
|
|
||||||
fn finish(&mut self) -> Result<(), SwapBuffersError>;
|
|
||||||
|
|
||||||
/// Clear the complete current target with a single given color.
|
|
||||||
///
|
|
||||||
/// This operation is only valid in between a `begin` and `finish`-call.
|
|
||||||
/// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
|
|
||||||
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
|
|
||||||
/// Render a texture to the current target using given projection matrix and alpha.
|
|
||||||
///
|
|
||||||
/// This operation is only valid in between a `begin` and `finish`-call.
|
|
||||||
/// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
|
|
||||||
fn render_texture(
|
|
||||||
&mut self,
|
|
||||||
texture: &Self::TextureId,
|
|
||||||
matrix: Matrix3<f32>,
|
|
||||||
alpha: f32,
|
|
||||||
) -> Result<(), Self::Error>;
|
|
||||||
/// Render a texture to the current target as a flat 2d-plane at a given
|
|
||||||
/// position, applying the given transformation with the given alpha value.
|
|
||||||
///
|
|
||||||
/// This operation is only valid in between a `begin` and `finish`-call.
|
|
||||||
/// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
|
|
||||||
fn render_texture_at(
|
|
||||||
&mut self,
|
|
||||||
texture: &Self::TextureId,
|
|
||||||
pos: (i32, i32),
|
|
||||||
transform: Transform,
|
|
||||||
alpha: f32,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let mut mat = Matrix3::<f32>::identity();
|
|
||||||
|
|
||||||
// position and scale
|
|
||||||
let size = texture.size();
|
|
||||||
mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
|
|
||||||
mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);
|
|
||||||
|
|
||||||
//apply surface transformation
|
|
||||||
mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
|
|
||||||
if transform == Transform::Normal {
|
|
||||||
assert_eq!(mat, mat * transform.invert().matrix());
|
|
||||||
assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
|
|
||||||
}
|
|
||||||
mat = mat * transform.invert().matrix();
|
|
||||||
mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));
|
|
||||||
|
|
||||||
self.render_texture(texture, mat, alpha)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the dimensions of a wl_buffer
|
/// Returns the dimensions of a wl_buffer
|
||||||
|
|
|
@ -10,17 +10,12 @@ use crate::backend::{
|
||||||
UnusedEvent,
|
UnusedEvent,
|
||||||
},
|
},
|
||||||
renderer::{
|
renderer::{
|
||||||
gles2::{Gles2Error, Gles2Renderer, Gles2Texture},
|
gles2::{Gles2Error, Gles2Renderer, Gles2Frame, Gles2Texture},
|
||||||
Bind, Renderer, Transform,
|
Bind, Unbind, Frame, Renderer, Transform,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
use crate::wayland::compositor::Damage;
|
|
||||||
use cgmath::Matrix3;
|
|
||||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||||
use wayland_egl as wegl;
|
use wayland_egl as wegl;
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
use wayland_server::protocol::{wl_buffer, wl_shm};
|
|
||||||
use wayland_server::Display;
|
use wayland_server::Display;
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
||||||
|
@ -259,72 +254,27 @@ impl WinitGraphicsBackend {
|
||||||
&*self.window
|
&*self.window
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut to `Renderer::begin` with the current window dimensions.
|
/// Access the underlying renderer
|
||||||
pub fn begin(&mut self) -> Result<(), Gles2Error> {
|
pub fn renderer(&mut self) -> &mut Gles2Renderer {
|
||||||
|
&mut self.renderer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to `Renderer::render` with the current window dimensions
|
||||||
|
/// and this window set as the rendering target.
|
||||||
|
pub fn render<F, R>(&mut self, rendering: F) -> Result<R, crate::backend::SwapBuffersError>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Gles2Renderer, &mut Gles2Frame) -> R
|
||||||
|
{
|
||||||
let (width, height) = {
|
let (width, height) = {
|
||||||
let size = self.size.borrow();
|
let size = self.size.borrow();
|
||||||
size.physical_size.into()
|
size.physical_size.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.renderer.bind(self.egl.clone())?;
|
self.renderer.bind(self.egl.clone())?;
|
||||||
self.renderer.begin(width, height, Transform::Normal)
|
let result = self.renderer.render(width, height, Transform::Normal, rendering)?;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Renderer for WinitGraphicsBackend {
|
|
||||||
type Error = Gles2Error;
|
|
||||||
type TextureId = Gles2Texture;
|
|
||||||
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
|
|
||||||
&mut self,
|
|
||||||
image: &image::ImageBuffer<image::Rgba<u8>, C>,
|
|
||||||
) -> Result<Self::TextureId, Self::Error> {
|
|
||||||
self.renderer.import_bitmap(image)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
fn shm_formats(&self) -> &[wl_shm::Format] {
|
|
||||||
Renderer::shm_formats(&self.renderer)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
|
||||||
fn import_buffer(
|
|
||||||
&mut self,
|
|
||||||
buffer: &wl_buffer::WlBuffer,
|
|
||||||
damage: Option<&Damage>,
|
|
||||||
egl: Option<&EGLBufferReader>,
|
|
||||||
) -> Result<Self::TextureId, Self::Error> {
|
|
||||||
self.renderer.import_buffer(buffer, damage, egl)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.renderer.clear(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_texture(
|
|
||||||
&mut self,
|
|
||||||
texture: &Self::TextureId,
|
|
||||||
matrix: Matrix3<f32>,
|
|
||||||
alpha: f32,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.renderer.render_texture(texture, matrix, alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> {
|
|
||||||
self.renderer.finish()?;
|
|
||||||
self.egl.swap_buffers()?;
|
self.egl.swap_buffers()?;
|
||||||
Ok(())
|
self.renderer.unbind()?;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue