Merge pull request #334 from Smithay/features/renderer
gles2: add and use cropping to the renderer
This commit is contained in:
commit
af2aa017bf
|
@ -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"]
|
||||
|
|
|
@ -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.
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -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)
|
||||
}
|
|
@ -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(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue