Merge pull request #334 from Smithay/features/renderer

gles2: add and use cropping to the renderer
This commit is contained in:
Victor Brekenfeld 2021-07-13 23:56:36 +02:00 committed by GitHub
commit af2aa017bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 478 additions and 194 deletions

View File

@ -22,7 +22,6 @@ drm = { version = "0.4.0", optional = true }
drm-ffi = { version = "0.1.0", optional = true }
gbm = { version = "0.6.0", optional = true, default-features = false, features = ["drm-support"] }
input = { version = "0.6", default-features = false, features=["libinput_1_14"], optional = true }
image = { version = "0.23.14", default-features = false, optional = true }
lazy_static = "1"
libc = "0.2.70"
libseat= { version = "0.1.1", optional = true }
@ -50,7 +49,7 @@ gl_generator = { version = "0.14", optional = true }
pkg-config = { version = "0.3.17", optional = true }
[features]
default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_logind", "backend_winit", "image", "renderer_gl", "xwayland", "wayland_frontend", "slog-stdlog"]
default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_logind", "backend_winit", "renderer_gl", "xwayland", "wayland_frontend", "slog-stdlog"]
backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl"]
backend_drm = ["drm", "drm-ffi"]
backend_gbm = ["gbm"]

View File

@ -9,7 +9,10 @@ edition = "2018"
[dependencies]
bitflags = "1.2.1"
input = { version = "0.5.0", features = ["udev"], optional = true }
image = { version = "0.23.0", optional = true, default-features = false }
thiserror = "1"
xcursor = { version = "0.3.3", optional = true }
image = { version = "0.23.14", default-features = false, optional = true }
fps_ticker = { version = "1.0.0", optional = true }
rand = "0.7"
slog = { version = "2.1.1" }
slog-term = "2.8"
@ -36,9 +39,10 @@ gl_generator = "0.14"
default = [ "winit", "udev", "logind", "egl", "xwayland" ]
egl = [ "smithay/use_system_lib", "smithay/backend_egl" ]
winit = [ "smithay/backend_winit" ]
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image", "smithay/image"]
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image", "xcursor" ]
logind = [ "smithay/backend_session_logind" ]
elogind = ["logind", "smithay/backend_session_elogind" ]
libseat = ["smithay/backend_session_libseat" ]
xwayland = [ "smithay/xwayland", "x11rb" ]
test_all_features = ["default"]
debug = [ "fps_ticker", "image/png" ]
test_all_features = ["default", "debug"]

Binary file not shown.

Binary file not shown.

BIN
anvil/resources/numbers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

92
anvil/src/cursor.rs Normal file
View File

@ -0,0 +1,92 @@
use std::io::Read;
use xcursor::{
parser::{parse_xcursor, Image},
CursorTheme,
};
static FALLBACK_CURSOR_DATA: &[u8] = include_bytes!("../resources/cursor.rgba");
pub struct Cursor {
icons: Vec<Image>,
size: u32,
}
impl Cursor {
pub fn load(log: &::slog::Logger) -> Cursor {
let name = std::env::var("XCURSOR_THEME")
.ok()
.unwrap_or_else(|| "default".into());
let size = std::env::var("XCURSOR_SIZE")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(24);
let theme = CursorTheme::load(&name);
let icons = load_icon(&theme)
.map_err(|err| slog::warn!(log, "Unable to load xcursor: {}, using fallback cursor", err))
.unwrap_or_else(|_| {
vec![Image {
size: 32,
width: 64,
height: 64,
xhot: 1,
yhot: 1,
delay: 1,
pixels_rgba: Vec::from(FALLBACK_CURSOR_DATA),
pixels_argb: vec![], //unused
}]
});
Cursor { icons, size }
}
pub fn get_image(&self, scale: u32, millis: u32) -> Image {
let size = self.size * scale;
frame(millis, size, &self.icons)
}
}
fn nearest_images(size: u32, images: &[Image]) -> impl Iterator<Item = &Image> {
// Follow the nominal size of the cursor to choose the nearest
let nearest_image = images
.iter()
.min_by_key(|image| (size as i32 - image.size as i32).abs())
.unwrap();
images
.iter()
.filter(move |image| image.width == nearest_image.width && image.height == nearest_image.height)
}
fn frame(mut millis: u32, size: u32, images: &[Image]) -> Image {
let total = nearest_images(size, images).fold(0, |acc, image| acc + image.delay);
millis %= total;
for img in nearest_images(size, images) {
if millis < img.delay {
return img.clone();
}
millis -= img.delay;
}
unreachable!()
}
#[derive(thiserror::Error, Debug)]
enum Error {
#[error("Theme has no default cursor")]
NoDefaultCursor,
#[error("Error opening xcursor file: {0}")]
File(#[from] std::io::Error),
#[error("Failed to parse XCursor file")]
Parse,
}
fn load_icon(theme: &CursorTheme) -> Result<Vec<Image>, Error> {
let icon_path = theme.load_icon("default").ok_or(Error::NoDefaultCursor)?;
let mut cursor_file = std::fs::File::open(&icon_path)?;
let mut cursor_data = Vec::new();
cursor_file.read_to_end(&mut cursor_data)?;
parse_xcursor(&cursor_data).ok_or(Error::Parse)
}

View File

@ -2,7 +2,11 @@
use std::{cell::RefCell, sync::Mutex};
#[cfg(feature = "image")]
use image::{ImageBuffer, Rgba};
use slog::Logger;
#[cfg(feature = "image")]
use smithay::backend::renderer::gles2::{Gles2Error, Gles2Renderer, Gles2Texture};
use smithay::{
backend::{
renderer::{buffer_type, BufferType, Frame, ImportAll, Renderer, Texture, Transform},
@ -155,7 +159,7 @@ where
let mut location = *location;
if let Some(ref data) = states.data_map.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
let buffer_scale = data.buffer_scale as f32;
let buffer_scale = data.buffer_scale;
if let Some(texture) = data
.texture
.as_mut()
@ -167,12 +171,12 @@ where
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
let render_scale = output_scale as f32 / buffer_scale;
if let Err(err) = frame.render_texture_at(
&texture.texture,
location.to_f64().to_physical(output_scale as f64).to_i32_round(),
buffer_scale,
output_scale as f64,
Transform::Normal, /* TODO */
render_scale,
1.0,
) {
result = Err(err.into());
@ -260,3 +264,84 @@ where
}
draw_surface_tree(renderer, frame, surface, location, output_scale, log)
}
#[cfg(feature = "debug")]
pub static FPS_NUMBERS_PNG: &[u8] = include_bytes!("../resources/numbers.png");
#[cfg(feature = "debug")]
pub fn draw_fps<R, E, F, T>(
_renderer: &mut R,
frame: &mut F,
texture: &T,
output_scale: f64,
value: u32,
) -> Result<(), SwapBuffersError>
where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
F: Frame<Error = E, TextureId = T>,
E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static,
{
let value_str = value.to_string();
let mut offset_x = 0f64;
for digit in value_str.chars().map(|d| d.to_digit(10).unwrap()) {
frame
.render_texture_from_to(
texture,
match digit {
9 => Rectangle::from_loc_and_size((0, 0), (22, 35)),
6 => Rectangle::from_loc_and_size((22, 0), (22, 35)),
3 => Rectangle::from_loc_and_size((44, 0), (22, 35)),
1 => Rectangle::from_loc_and_size((66, 0), (22, 35)),
8 => Rectangle::from_loc_and_size((0, 35), (22, 35)),
0 => Rectangle::from_loc_and_size((22, 35), (22, 35)),
2 => Rectangle::from_loc_and_size((44, 35), (22, 35)),
7 => Rectangle::from_loc_and_size((0, 70), (22, 35)),
4 => Rectangle::from_loc_and_size((22, 70), (22, 35)),
5 => Rectangle::from_loc_and_size((44, 70), (22, 35)),
_ => unreachable!(),
},
Rectangle::from_loc_and_size((offset_x, 0.0), (22.0 * output_scale, 35.0 * output_scale)),
Transform::Normal,
1.0,
)
.map_err(Into::into)?;
offset_x += 24.0 * output_scale;
}
Ok(())
}
#[cfg(feature = "image")]
pub fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
renderer: &mut Gles2Renderer,
image: &ImageBuffer<Rgba<u8>, C>,
) -> Result<Gles2Texture, Gles2Error> {
use smithay::backend::renderer::gles2::ffi;
renderer.with_context(|renderer, gl| unsafe {
let mut tex = 0;
gl.GenTextures(1, &mut tex);
gl.BindTexture(ffi::TEXTURE_2D, tex);
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32);
gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32);
gl.TexImage2D(
ffi::TEXTURE_2D,
0,
ffi::RGBA as i32,
image.width() as i32,
image.height() as i32,
0,
ffi::RGBA,
ffi::UNSIGNED_BYTE as u32,
image.as_ptr() as *const _,
);
gl.BindTexture(ffi::TEXTURE_2D, 0);
Gles2Texture::from_raw(
renderer,
tex,
(image.width() as i32, image.height() as i32).into(),
)
})
}

View File

@ -14,6 +14,8 @@ use std::{cell::RefCell, rc::Rc};
use slog::Drain;
use smithay::reexports::{calloop::EventLoop, wayland_server::Display};
#[cfg(feature = "udev")]
mod cursor;
mod drawing;
mod input_handler;
mod shell;

View File

@ -9,7 +9,7 @@ use std::{
time::Duration,
};
use image::{ImageBuffer, Rgba};
use image::ImageBuffer;
use slog::Logger;
use smithay::{
@ -90,7 +90,7 @@ pub struct UdevData {
primary_gpu: Option<PathBuf>,
backends: HashMap<dev_t, BackendData>,
signaler: Signaler<SessionSignal>,
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
pointer_image: crate::cursor::Cursor,
render_timer: TimerHandle<(u64, crtc::Handle)>,
}
@ -122,7 +122,6 @@ pub fn run_udev(
/*
* Initialize the compositor
*/
let pointer_bytes = include_bytes!("../resources/cursor2.rgba");
#[cfg(feature = "egl")]
let primary_gpu = primary_gpu(&session.seat()).unwrap_or_default();
@ -135,7 +134,7 @@ pub fn run_udev(
primary_gpu,
backends: HashMap::new(),
signaler: session_signal.clone(),
pointer_image: ImageBuffer::from_raw(64, 64, pointer_bytes.to_vec()).unwrap(),
pointer_image: crate::cursor::Cursor::load(&log),
render_timer: timer.handle(),
};
let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone());
@ -255,10 +254,18 @@ pub fn run_udev(
pub type RenderSurface = GbmBufferedSurface<SessionFd>;
struct SurfaceData {
surface: RenderSurface,
#[cfg(feature = "debug")]
fps: fps_ticker::Fps,
}
struct BackendData {
_restart_token: SignalToken,
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
pointer_image: Gles2Texture,
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>>>>,
pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>,
#[cfg(feature = "debug")]
fps_texture: Gles2Texture,
renderer: Rc<RefCell<Gles2Renderer>>,
gbm: GbmDevice<SessionFd>,
registration_token: RegistrationToken,
@ -273,7 +280,7 @@ fn scan_connectors(
output_map: &mut crate::output_map::OutputMap,
signaler: &Signaler<SessionSignal>,
logger: &::slog::Logger,
) -> HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>> {
) -> HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>> {
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap();
@ -322,7 +329,7 @@ fn scan_connectors(
let renderer_formats =
Bind::<Dmabuf>::supported_formats(renderer).expect("Dmabuf renderer without formats");
let renderer =
let gbm_surface =
match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone())
{
Ok(renderer) => renderer,
@ -374,7 +381,11 @@ fn scan_connectors(
device_id: device.device_id(),
});
entry.insert(Rc::new(RefCell::new(renderer)));
entry.insert(Rc::new(RefCell::new(SurfaceData {
surface: gbm_surface,
#[cfg(feature = "debug")]
fps: fps_ticker::Fps::default(),
})));
break 'outer;
}
}
@ -469,11 +480,6 @@ impl AnvilState<UdevData> {
&self.log,
)));
let pointer_image = renderer
.borrow_mut()
.import_bitmap(&self.backend_data.pointer_image)
.expect("Failed to load pointer");
let dev_id = device.device_id();
let handle = self.handle.clone();
let restart_token = self.backend_data.signaler.register(move |signal| match signal {
@ -502,6 +508,19 @@ impl AnvilState<UdevData> {
schedule_initial_render(backend.clone(), renderer.clone(), &self.handle, self.log.clone());
}
#[cfg(feature = "debug")]
let fps_texture = import_bitmap(
&mut renderer.borrow_mut(),
&image::io::Reader::with_format(
std::io::Cursor::new(FPS_NUMBERS_PNG),
image::ImageFormat::Png,
)
.decode()
.unwrap()
.to_rgba8(),
)
.expect("Unable to upload FPS texture");
self.backend_data.backends.insert(
dev_id,
BackendData {
@ -511,7 +530,9 @@ impl AnvilState<UdevData> {
surfaces: backends,
renderer,
gbm,
pointer_image,
pointer_images: Vec::new(),
#[cfg(feature = "debug")]
fps_texture,
dev_id,
},
);
@ -602,7 +623,7 @@ impl AnvilState<UdevData> {
.iter()
.flat_map(|crtc| surfaces.get(&crtc).map(|surface| (crtc, surface)));
let to_render_iter: &mut dyn Iterator<Item = (&crtc::Handle, &Rc<RefCell<RenderSurface>>)> =
let to_render_iter: &mut dyn Iterator<Item = (&crtc::Handle, &Rc<RefCell<SurfaceData>>)> =
if crtc.is_some() {
&mut option_iter
} else {
@ -610,15 +631,36 @@ impl AnvilState<UdevData> {
};
for (&crtc, surface) in to_render_iter {
// TODO get scale from the rendersurface when supporting HiDPI
let frame = self
.backend_data
.pointer_image
.get_image(1 /*scale*/, self.start_time.elapsed().as_millis() as u32);
let renderer = &mut *device_backend.renderer.borrow_mut();
let pointer_images = &mut device_backend.pointer_images;
let pointer_image = pointer_images
.iter()
.find_map(|(image, texture)| if image == &frame { Some(texture) } else { None })
.cloned()
.unwrap_or_else(|| {
let image =
ImageBuffer::from_raw(frame.width, frame.height, &*frame.pixels_rgba).unwrap();
let texture = import_bitmap(renderer, &image).expect("Failed to import cursor bitmap");
pointer_images.push((frame, texture.clone()));
texture
});
let result = render_surface(
&mut *surface.borrow_mut(),
&mut *device_backend.renderer.borrow_mut(),
renderer,
device_backend.dev_id,
crtc,
&mut *self.window_map.borrow_mut(),
&*self.output_map.borrow(),
self.pointer_location,
&device_backend.pointer_image,
&pointer_image,
#[cfg(feature = "debug")]
&device_backend.fps_texture,
&*self.dnd_icon.lock().unwrap(),
&mut *self.cursor_status.lock().unwrap(),
&self.log,
@ -658,7 +700,7 @@ impl AnvilState<UdevData> {
#[allow(clippy::too_many_arguments)]
fn render_surface(
surface: &mut RenderSurface,
surface: &mut SurfaceData,
renderer: &mut Gles2Renderer,
device_id: dev_t,
crtc: crtc::Handle,
@ -666,11 +708,12 @@ fn render_surface(
output_map: &crate::output_map::OutputMap,
pointer_location: Point<f64, Logical>,
pointer_image: &Gles2Texture,
#[cfg(feature = "debug")] fps_texture: &Gles2Texture,
dnd_icon: &Option<wl_surface::WlSurface>,
cursor_status: &mut CursorImageStatus,
logger: &slog::Logger,
) -> Result<(), SwapBuffersError> {
surface.frame_submitted()?;
surface.surface.frame_submitted()?;
let output = output_map
.find(|o| o.userdata().get::<UdevOutputId>() == Some(&UdevOutputId { device_id, crtc }))
@ -683,7 +726,7 @@ fn render_surface(
return Ok(());
};
let dmabuf = surface.next_buffer()?;
let dmabuf = surface.surface.next_buffer()?;
renderer.bind(dmabuf)?;
// and draw to our buffer
match renderer
@ -742,13 +785,26 @@ fn render_surface(
.to_f64()
.to_physical(output_scale as f64)
.to_i32_round(),
1,
output_scale as f64,
Transform::Normal,
output_scale,
1.0,
)?;
}
}
}
#[cfg(feature = "debug")]
{
draw_fps(
renderer,
frame,
fps_texture,
output_scale as f64,
surface.fps.avg().round() as u32,
)?;
surface.fps.tick();
}
Ok(())
},
)
@ -756,13 +812,16 @@ fn render_surface(
.and_then(|x| x)
.map_err(Into::<SwapBuffersError>::into)
{
Ok(()) => surface.queue_buffer().map_err(Into::<SwapBuffersError>::into),
Ok(()) => surface
.surface
.queue_buffer()
.map_err(Into::<SwapBuffersError>::into),
Err(err) => Err(err),
}
}
fn schedule_initial_render<Data: 'static>(
surface: Rc<RefCell<RenderSurface>>,
surface: Rc<RefCell<SurfaceData>>,
renderer: Rc<RefCell<Gles2Renderer>>,
evt_handle: &LoopHandle<'static, Data>,
logger: ::slog::Logger,
@ -770,7 +829,7 @@ fn schedule_initial_render<Data: 'static>(
let result = {
let mut surface = surface.borrow_mut();
let mut renderer = renderer.borrow_mut();
initial_render(&mut *surface, &mut *renderer)
initial_render(&mut surface.surface, &mut *renderer)
};
if let Err(err) = result {
match err {

View File

@ -1,5 +1,7 @@
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
#[cfg(feature = "debug")]
use smithay::backend::renderer::gles2::Gles2Texture;
#[cfg(feature = "egl")]
use smithay::{
backend::renderer::{ImportDma, ImportEgl},
@ -24,7 +26,12 @@ use crate::state::{AnvilState, Backend};
pub const OUTPUT_NAME: &str = "winit";
pub struct WinitData;
pub struct WinitData {
#[cfg(feature = "debug")]
fps_texture: Gles2Texture,
#[cfg(feature = "debug")]
pub fps: fps_ticker::Fps,
}
impl Backend for WinitData {
fn seat_name(&self) -> String {
@ -71,7 +78,20 @@ pub fn run_winit(
* Initialize the globals
*/
let mut state = AnvilState::init(display.clone(), event_loop.handle(), WinitData, log.clone());
let data = WinitData {
#[cfg(feature = "debug")]
fps_texture: import_bitmap(
&mut renderer.borrow_mut().renderer(),
&image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png)
.decode()
.unwrap()
.to_rgba8(),
)
.expect("Unable to upload FPS texture"),
#[cfg(feature = "debug")]
fps: fps_ticker::Fps::default(),
};
let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone());
let mode = Mode {
size,
@ -176,6 +196,18 @@ pub fn run_winit(
}
}
#[cfg(feature = "debug")]
{
let fps = state.backend_data.fps.avg().round() as u32;
draw_fps(
renderer,
frame,
&state.backend_data.fps_texture,
output_scale as f64,
fps,
)?;
}
Ok(())
})
.map_err(Into::<SwapBuffersError>::into)
@ -206,6 +238,9 @@ pub fn run_winit(
state.window_map.borrow_mut().refresh();
state.output_map.borrow_mut().refresh();
}
#[cfg(feature = "debug")]
state.backend_data.fps.tick();
}
// Cleanup stuff

View File

@ -11,6 +11,7 @@
//! the lifetime of the buffer. E.g. when you are only caching associated resources for a dmabuf.
use super::{Buffer, Format, Fourcc, Modifier};
use crate::utils::{Buffer as BufferCoords, Size};
use std::hash::{Hash, Hasher};
use std::os::unix::io::{IntoRawFd, RawFd};
use std::sync::{Arc, Weak};
@ -22,10 +23,8 @@ pub const MAX_PLANES: usize = 4;
pub(crate) struct DmabufInternal {
/// The submitted planes
pub planes: Vec<Plane>,
/// The width of this buffer
pub width: i32,
/// The height of this buffer
pub height: i32,
/// The size of this buffer
pub size: Size<i32, BufferCoords>,
/// The format in use
pub format: Fourcc,
/// The flags applied to it
@ -107,12 +106,8 @@ impl Hash for WeakDmabuf {
}
impl Buffer for Dmabuf {
fn width(&self) -> u32 {
self.0.width as u32
}
fn height(&self) -> u32 {
self.0.height as u32
fn size(&self) -> Size<i32, BufferCoords> {
self.0.size
}
fn format(&self) -> Format {
@ -172,8 +167,7 @@ impl Dmabuf {
DmabufBuilder {
internal: DmabufInternal {
planes: Vec::with_capacity(MAX_PLANES),
width: src.width() as i32,
height: src.height() as i32,
size: src.size(),
format: src.format().code,
flags,
},
@ -181,12 +175,15 @@ impl Dmabuf {
}
/// Create a new Dmabuf builder
pub fn builder(width: u32, height: u32, format: Fourcc, flags: DmabufFlags) -> DmabufBuilder {
pub fn builder(
size: impl Into<Size<i32, BufferCoords>>,
format: Fourcc,
flags: DmabufFlags,
) -> DmabufBuilder {
DmabufBuilder {
internal: DmabufInternal {
planes: Vec::with_capacity(MAX_PLANES),
width: width as i32,
height: height as i32,
size: size.into(),
format,
flags,
},

View File

@ -9,6 +9,7 @@ use drm::control::{dumbbuffer::DumbBuffer as Handle, Device as ControlDevice};
use super::{Allocator, Buffer, Format, Fourcc, Modifier};
use crate::backend::drm::device::{DrmDevice, DrmDeviceInternal, FdWrapper};
use crate::utils::{Buffer as BufferCoords, Size};
/// Wrapper around raw DumbBuffer handles.
pub struct DumbBuffer<A: AsRawFd + 'static> {
@ -61,12 +62,9 @@ impl<A: AsRawFd + 'static> Allocator<DumbBuffer<A>> for DrmDevice<A> {
}
impl<A: AsRawFd + 'static> Buffer for DumbBuffer<A> {
fn width(&self) -> u32 {
self.handle.size().0
}
fn height(&self) -> u32 {
self.handle.size().1
fn size(&self) -> Size<i32, BufferCoords> {
let (w, h) = self.handle.size();
(w as i32, h as i32).into()
}
fn format(&self) -> Format {

View File

@ -8,6 +8,7 @@ use super::{
dmabuf::{AsDmabuf, Dmabuf, DmabufFlags, MAX_PLANES},
Allocator, Buffer, Format, Fourcc, Modifier,
};
use crate::utils::{Buffer as BufferCoords, Size};
pub use gbm::{BufferObject as GbmBuffer, BufferObjectFlags as GbmBufferFlags, Device as GbmDevice};
use std::os::unix::io::AsRawFd;
@ -39,12 +40,12 @@ impl<A: AsRawFd + 'static, T> Allocator<GbmBuffer<T>> for GbmDevice<A> {
}
impl<T> Buffer for GbmBuffer<T> {
fn width(&self) -> u32 {
self.width().unwrap_or(0)
}
fn height(&self) -> u32 {
self.height().unwrap_or(0)
fn size(&self) -> Size<i32, BufferCoords> {
(
self.width().unwrap_or(0) as i32,
self.height().unwrap_or(0) as i32,
)
.into()
}
fn format(&self) -> Format {

View File

@ -22,6 +22,7 @@ pub mod dumb;
pub mod gbm;
mod swapchain;
use crate::utils::{Buffer as BufferCoords, Size};
pub use swapchain::{Slot, Swapchain};
pub use drm_fourcc::{
@ -32,13 +33,15 @@ pub use drm_fourcc::{
/// Common trait describing common properties of most types of buffers.
pub trait Buffer {
/// Width of the two-dimensional buffer
fn width(&self) -> u32;
/// Height of the two-dimensional buffer
fn height(&self) -> u32;
/// Size (w x h) of the two-dimensional buffer
fn size(&self) -> (u32, u32) {
(self.width(), self.height())
fn width(&self) -> u32 {
self.size().w as u32
}
/// Height of the two-dimensional buffer
fn height(&self) -> u32 {
self.size().h as u32
}
/// Size of the two-dimensional buffer
fn size(&self) -> Size<i32, BufferCoords>;
/// Pixel format of the buffer
fn format(&self) -> Format;
}

View File

@ -808,8 +808,7 @@ impl EGLBufferReader {
Ok(EGLBuffer {
display: self.display.clone(),
width: width as u32,
height: height as u32,
size: (width, height).into(),
// y_inverted is negated here because the gles2 renderer
// already inverts the buffer during rendering.
y_inverted: !y_inverted,

View File

@ -32,6 +32,8 @@ pub use self::context::EGLContext;
mod error;
pub use self::error::*;
use crate::backend::SwapBuffersError as GraphicsSwapBuffersError;
#[cfg(feature = "wayland_frontend")]
use crate::utils::{Buffer, Size};
use nix::libc::c_void;
@ -248,10 +250,8 @@ impl Format {
#[derive(Debug)]
pub struct EGLBuffer {
display: Arc<EGLDisplayHandle>,
/// Width in pixels
pub width: u32,
/// Height in pixels
pub height: u32,
/// Size of the buffer
pub size: Size<i32, Buffer>,
/// If the y-axis is inverted or not
pub y_inverted: bool,
/// Format of these images

View File

@ -11,7 +11,7 @@ use std::sync::{
};
use std::{collections::HashSet, os::raw::c_char};
use cgmath::{prelude::*, Matrix3};
use cgmath::{prelude::*, Matrix3, Vector2};
mod shaders;
mod version;
@ -34,7 +34,6 @@ use super::ImportEgl;
use super::{ImportDma, ImportShm};
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use crate::backend::egl::{display::EGLBufferReader, Format as EGLFormat};
#[cfg(feature = "wayland_frontend")]
use crate::utils::{Buffer, Rectangle};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};
@ -67,14 +66,42 @@ struct Gles2Program {
#[derive(Debug, Clone)]
pub struct Gles2Texture(Rc<Gles2TextureInternal>);
impl Gles2Texture {
/// Create a Gles2Texture from a raw gl texture id.
///
/// This expects the texture to be in RGBA format to be rendered
/// correctly by the `render_texture*`-functions of [`Frame`](super::Frame).
/// It is also expected to not be external or y_inverted.
///
/// Ownership over the texture is taken by the renderer, you should not free the texture yourself.
///
/// # Safety
///
/// The renderer cannot make sure `tex` is a valid texture id.
pub unsafe fn from_raw(
renderer: &Gles2Renderer,
tex: ffi::types::GLuint,
size: Size<i32, Buffer>,
) -> Gles2Texture {
Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex,
texture_kind: 0,
is_external: false,
y_inverted: false,
size,
egl_images: None,
destruction_callback_sender: renderer.destruction_callback_sender.clone(),
}))
}
}
#[derive(Debug)]
struct Gles2TextureInternal {
texture: ffi::types::GLuint,
texture_kind: usize,
is_external: bool,
y_inverted: bool,
width: u32,
height: u32,
size: Size<i32, Buffer>,
egl_images: Option<Vec<EGLImage>>,
destruction_callback_sender: Sender<CleanupResource>,
}
@ -101,10 +128,13 @@ enum CleanupResource {
impl Texture for Gles2Texture {
fn width(&self) -> u32 {
self.0.width
self.0.size.w as u32
}
fn height(&self) -> u32 {
self.0.height
self.0.size.h as u32
}
fn size(&self) -> Size<i32, Buffer> {
self.0.size
}
}
@ -559,8 +589,7 @@ impl ImportShm for Gles2Renderer {
texture_kind: shader_idx,
is_external: false,
y_inverted: false,
width: width as u32,
height: height as u32,
size: (width, height).into(),
egl_images: None,
destruction_callback_sender: self.destruction_callback_sender.clone(),
})
@ -689,8 +718,7 @@ impl ImportEgl for Gles2Renderer {
},
is_external: egl.format == EGLFormat::External,
y_inverted: egl.y_inverted,
width: egl.width,
height: egl.height,
size: egl.size,
egl_images: Some(egl.into_images()),
destruction_callback_sender: self.destruction_callback_sender.clone(),
}));
@ -723,8 +751,7 @@ impl ImportDma for Gles2Renderer {
texture_kind: if is_external { 2 } else { 0 },
is_external,
y_inverted: buffer.y_inverted(),
width: buffer.width(),
height: buffer.height(),
size: buffer.size(),
egl_images: Some(vec![image]),
destruction_callback_sender: self.destruction_callback_sender.clone(),
}));
@ -950,69 +977,28 @@ impl Drop for Gles2Renderer {
}
}
static VERTS: [ffi::types::GLfloat; 8] = [
1.0, 0.0, // top right
0.0, 0.0, // top left
1.0, 1.0, // bottom right
0.0, 1.0, // bottom left
];
static TEX_COORDS: [ffi::types::GLfloat; 8] = [
1.0, 0.0, // top right
0.0, 0.0, // top left
1.0, 1.0, // bottom right
0.0, 1.0, // bottom left
];
impl Gles2Renderer {
/// Run custom code in the GL context owned by this renderer.
///
/// *Note*: Any changes to the GL state should be restored at the end of this function.
/// Otherwise this can lead to rendering errors while using functions of this renderer.
/// Relying on any state set by the renderer may break on any smithay update as the
/// details about how this renderer works are considered an implementation detail.
pub fn with_context<F, R>(&mut self, func: F) -> Result<R, Gles2Error>
where
F: FnOnce(&mut Self, &ffi::Gles2) -> R,
{
self.make_current()?;
let gl = self.gl.clone();
Ok(func(self, &gl))
}
}
impl Renderer for Gles2Renderer {
type Error = Gles2Error;
type TextureId = Gles2Texture;
type Frame = Gles2Frame;
#[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.make_current()?;
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.TexImage2D(
ffi::TEXTURE_2D,
0,
ffi::RGBA as i32,
image.width() as i32,
image.height() as i32,
0,
ffi::RGBA,
ffi::UNSIGNED_BYTE as u32,
image.as_ptr() as *const _,
);
self.gl.BindTexture(ffi::TEXTURE_2D, 0);
}
let texture = Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex,
texture_kind: 0,
is_external: false,
y_inverted: false,
width: image.width(),
height: image.height(),
egl_images: None,
destruction_callback_sender: self.destruction_callback_sender.clone(),
}));
self.egl.unbind()?;
Ok(texture)
}
fn render<F, R>(
&mut self,
size: Size<i32, Physical>,
@ -1029,6 +1015,9 @@ impl Renderer for Gles2Renderer {
unsafe {
self.gl.Viewport(0, 0, size.w, size.h);
self.gl.Scissor(0, 0, size.w, size.h);
self.gl.Enable(ffi::SCISSOR_TEST);
self.gl.Enable(ffi::BLEND);
self.gl.BlendFunc(ffi::ONE, ffi::ONE_MINUS_SRC_ALPHA);
}
@ -1083,6 +1072,13 @@ impl Renderer for Gles2Renderer {
}
}
static VERTS: [ffi::types::GLfloat; 8] = [
1.0, 0.0, // top right
0.0, 0.0, // top left
1.0, 1.0, // bottom right
0.0, 1.0, // bottom left
];
impl Frame for Gles2Frame {
type Error = Gles2Error;
type TextureId = Gles2Texture;
@ -1100,6 +1096,7 @@ impl Frame for Gles2Frame {
&mut self,
tex: &Self::TextureId,
mut matrix: Matrix3<f32>,
tex_coords: [Vector2<f32>; 4],
alpha: f32,
) -> Result<(), Self::Error> {
//apply output transformation
@ -1148,7 +1145,7 @@ impl Frame for Gles2Frame {
ffi::FLOAT,
ffi::FALSE,
0,
TEX_COORDS.as_ptr() as *const _,
tex_coords.as_ptr() as *const _, // cgmath::Vector2 is marked as repr(C), this cast should be safe
);
self.gl

View File

@ -10,16 +10,18 @@
use std::collections::HashSet;
use std::error::Error;
use crate::utils::{Physical, Point, Size};
use crate::utils::{Buffer, Physical, Point, Rectangle, Size};
#[cfg(feature = "wayland_frontend")]
use crate::{utils::Rectangle, wayland::compositor::SurfaceData};
use cgmath::{prelude::*, Matrix3, Vector2};
use crate::wayland::compositor::SurfaceData;
use cgmath::{prelude::*, Matrix3, Vector2, Vector3};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};
#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(feature = "wayland_frontend")]
use crate::backend::allocator::{dmabuf::Dmabuf, Format};
#[cfg(all(
feature = "wayland_frontend",
feature = "backend_egl",
@ -29,11 +31,6 @@ use crate::backend::egl::{
display::{EGLBufferReader, BUFFER_READER},
Error as EglError,
};
#[cfg(feature = "wayland_frontend")]
use crate::{
backend::allocator::{dmabuf::Dmabuf, Format},
utils::Buffer,
};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
@ -142,9 +139,9 @@ pub trait Unbind: Renderer {
/// A two dimensional texture
pub trait Texture {
/// Size of the texture plane (w x h)
fn size(&self) -> (u32, u32) {
(self.width(), self.height())
/// Size of the texture plane
fn size(&self) -> Size<i32, Buffer> {
Size::from((self.width() as i32, self.height() as i32))
}
/// Width of the texture plane
@ -166,34 +163,57 @@ pub trait Frame {
/// 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.
/// The given verticies are used to source the texture. This is mostly useful for cropping the texture.
fn render_texture(
&mut self,
texture: &Self::TextureId,
matrix: Matrix3<f32>,
tex_coords: [Vector2<f32>; 4],
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.
/// position and applying the given transformation with the given alpha value.
fn render_texture_at(
&mut self,
texture: &Self::TextureId,
pos: Point<i32, Physical>,
pos: Point<f64, Physical>,
texture_scale: i32,
output_scale: f64,
transform: Transform,
alpha: f32,
) -> Result<(), Self::Error> {
self.render_texture_from_to(
texture,
Rectangle::from_loc_and_size(Point::<i32, Buffer>::from((0, 0)), texture.size()),
Rectangle::from_loc_and_size(
pos,
texture
.size()
.to_logical(texture_scale)
.to_f64()
.to_physical(output_scale),
),
transform,
alpha,
)
}
/// Render part of a texture as given by src to the current target into the rectangle described by dest
/// as a flat 2d-plane after applying the given transformations.
fn render_texture_from_to(
&mut self,
texture: &Self::TextureId,
src: Rectangle<i32, Buffer>,
dest: Rectangle<f64, Physical>,
transform: Transform,
scale: f32,
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.x as f32, pos.y as f32));
mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32 * scale, size.1 as f32 * scale);
mat = mat * Matrix3::from_translation(Vector2::new(dest.loc.x as f32, dest.loc.y as f32));
mat = mat * Matrix3::from_nonuniform_scale(dest.size.w as f32, dest.size.h as f32);
//apply surface transformation
mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
@ -204,7 +224,24 @@ pub trait Frame {
mat = mat * transform.invert().matrix();
mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));
self.render_texture(texture, mat, alpha)
// this matrix should be regular, we can expect invert to succeed
let tex_size = texture.size();
let texture_mat = Matrix3::from_nonuniform_scale(tex_size.w as f32, tex_size.h as f32)
.invert()
.unwrap();
let verts = [
(texture_mat * Vector3::new((src.loc.x + src.size.w) as f32, src.loc.y as f32, 0.0)).truncate(), // top-right
(texture_mat * Vector3::new(src.loc.x as f32, src.loc.y as f32, 0.0)).truncate(), // top-left
(texture_mat
* Vector3::new(
(src.loc.x + src.size.w) as f32,
(src.loc.y + src.size.h) as f32,
0.0,
))
.truncate(), // bottom-right
(texture_mat * Vector3::new(src.loc.x as f32, (src.loc.y + src.size.h) as f32, 0.0)).truncate(), // bottom-left
];
self.render_texture(texture, mat, verts, alpha)
}
}
@ -217,20 +254,6 @@ pub trait Renderer {
/// Type representing a currently in-progress frame during the [`Renderer::render`]-call
type Frame: Frame<Error = Self::Error, TextureId = Self::TextureId>;
/// Import a given bitmap into the renderer.
///
/// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
///
/// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
/// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
///
/// This operation needs no bound or default rendering target.
#[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>;
/// Initialize a rendering context on the current rendering target with given dimensions and transformation.
///
/// This function *may* error, if:

View File

@ -260,12 +260,7 @@ where
return;
}
let mut buf = Dmabuf::builder(
width as u32,
height as u32,
format,
DmabufFlags::from_bits_truncate(flags),
);
let mut buf = Dmabuf::builder((width, height), format, DmabufFlags::from_bits_truncate(flags));
let planes = std::mem::take(&mut self.pending_planes);
for (i, plane) in planes.into_iter().enumerate() {
let offset = plane.offset;
@ -350,12 +345,7 @@ where
return;
}
let mut buf = Dmabuf::builder(
width as u32,
height as u32,
format,
DmabufFlags::from_bits_truncate(flags),
);
let mut buf = Dmabuf::builder((width, height), format, DmabufFlags::from_bits_truncate(flags));
let planes = ::std::mem::take(&mut self.pending_planes);
for (i, plane) in planes.into_iter().enumerate() {
let offset = plane.offset;