1
0
Fork 0
forked from wry/wry

autocommit 2022-01-28 03:35:35 CET

This commit is contained in:
Julian Orth 2022-01-28 03:35:35 +01:00
parent c340df0d08
commit a5573b8a3a
36 changed files with 3046 additions and 114 deletions

88
Cargo.lock generated
View file

@ -99,6 +99,15 @@ dependencies = [
"termcolor",
]
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]]
name = "futures"
version = "0.3.19"
@ -229,6 +238,9 @@ dependencies = [
"log",
"num-derive",
"num-traits",
"once_cell",
"rand",
"renderdoc",
"repc",
"thiserror",
"uapi",
@ -317,6 +329,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro2"
version = "1.0.36"
@ -335,6 +353,46 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.5.4"
@ -358,6 +416,27 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "renderdoc"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42e14087d51efd3b42eb341e37b6f320af2b0750519ea849cb68bb7289643ed"
dependencies = [
"bitflags",
"float-cmp",
"libloading",
"once_cell",
"renderdoc-sys",
"winapi",
"wio",
]
[[package]]
name = "renderdoc-sys"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
[[package]]
name = "repc"
version = "0.1.1"
@ -500,6 +579,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wio"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
dependencies = [
"winapi",
]
[[package]]
name = "xcb-dl"
version = "0.2.0"

View file

@ -24,6 +24,9 @@ xcb-dl-util = { version = "0.2.0", features = ["xcb_shm", "xcb_xinput", "xcb_xkb
libloading = "0.7.2"
bstr = "0.2.17"
isnt = "0.1.0"
once_cell = "1.9.0"
rand = "0.8.4"
renderdoc = "0.10.1"
[build-dependencies]
repc = "0.1.1"

142
build.rs
View file

@ -1,4 +1,5 @@
use repc::layout::{Type, TypeVariant};
use std::fmt::Write as FmtWrite;
use std::fs::{File, OpenOptions};
use std::io::BufWriter;
use std::io::Write;
@ -58,6 +59,144 @@ fn write_ty<W: Write>(f: &mut W, vals: &[u32], ty: &str) -> anyhow::Result<()> {
Ok(())
}
fn write_egl_procs<W: Write>(f: &mut W) -> anyhow::Result<()> {
let map = [
(
"eglGetPlatformDisplayEXT",
"EGLDisplay",
&[
("platform", "EGLenum"),
("native_display", "*mut u8"),
("attrib_list", "*const EGLint"),
][..],
),
(
"eglCreateImageKHR",
"EGLImageKHR",
&[
("dpy", "EGLDisplay"),
("ctx", "EGLContext"),
("target", "EGLenum"),
("buffer", "EGLClientBuffer"),
("attrib_list", "*const EGLint"),
][..],
),
(
"eglDestroyImageKHR",
"EGLBoolean",
&[("dpy", "EGLDisplay"), ("image", "EGLImageKHR")][..],
),
(
"eglQueryDmaBufFormatsEXT",
"EGLBoolean",
&[
("dpy", "EGLDisplay"),
("max_formats", "EGLint"),
("formats", "*mut EGLint"),
("num_formats", "*mut EGLint"),
][..],
),
(
"eglQueryDmaBufModifiersEXT",
"EGLBoolean",
&[
("dpy", "EGLDisplay"),
("format", "EGLint"),
("max_modifiers", "EGLint"),
("modifiers", "*mut EGLuint64KHR"),
("external_only", "*mut EGLBoolean"),
("num_modifiers", "*mut EGLint"),
][..],
),
(
"eglDebugMessageControlKHR",
"EGLint",
&[
("callback", "EGLDEBUGPROCKHR"),
("attrib_list", "*const EGLAttrib"),
][..],
),
(
"eglQueryDisplayAttribEXT",
"EGLBoolean",
&[
("dpy", "EGLDisplay"),
("attribute", "EGLint"),
("value", "*mut EGLAttrib"),
][..],
),
(
"eglQueryDeviceStringEXT",
"*const c::c_char",
&[("device", "EGLDeviceEXT"), ("name", "EGLint")][..],
),
(
"eglQueryDevicesEXT",
"EGLBoolean",
&[
("max_devices", "EGLint"),
("devices", "*mut EGLDeviceEXT"),
("num_devices", "*mut EGLint"),
][..],
),
(
"glEGLImageTargetRenderbufferStorageOES",
"()",
&[("target", "GLenum"), ("image", "GLeglImageOES")][..],
),
];
writeln!(f, "use std::ptr;")?;
writeln!(f, "use super::sys::*;")?;
writeln!(f)?;
writeln!(f, "#[derive(Copy, Clone, Debug)]")?;
writeln!(f, "pub struct ExtProc {{")?;
for (name, _, _) in map.iter().copied() {
writeln!(f, " {}: *mut u8,", name)?;
}
writeln!(f, "}}")?;
writeln!(f)?;
writeln!(f, "impl ExtProc {{")?;
writeln!(f, " pub fn load() -> Self {{")?;
writeln!(f, " Self {{")?;
for (name, _, _) in map.iter().copied() {
writeln!(
f,
" {}: unsafe {{ eglGetProcAddress(\"{}\\0\".as_ptr() as _) }},",
name, name
)?;
}
writeln!(f, " }}")?;
writeln!(f, " }}")?;
for (name, ret, args) in map.iter().copied() {
let mut args_names = String::new();
let mut args_full = String::new();
let mut args_tys = String::new();
for (name, ty) in args.iter().copied() {
write!(args_full, "{}: {}, ", name, ty)?;
write!(args_names, "{}, ", name)?;
write!(args_tys, "{}, ", ty)?;
}
writeln!(f)?;
writeln!(
f,
" pub(super) unsafe fn {}(&self, {}) -> {} {{",
name, args_full, ret
)?;
writeln!(f, " if self.{}.is_null() {{", name)?;
writeln!(f, " panic!(\"Could not load `{}`\");", name)?;
writeln!(f, " }}")?;
writeln!(
f,
" ptr::read(&self.{} as *const *mut u8 as *const unsafe extern fn({}) -> {})({})",
name, args_tys, ret, args_names
)?;
writeln!(f, " }}")?;
}
writeln!(f, "}}")?;
Ok(())
}
fn main() -> anyhow::Result<()> {
let mut f = open("pixman_tys.rs")?;
write_ty(&mut f, pixman::FORMATS, "PixmanFormat")?;
@ -79,6 +218,9 @@ fn main() -> anyhow::Result<()> {
)?;
write_ty(&mut f, xkbcommon::XKB_KEY_DIRECTION, "xkb_key_direction")?;
let mut f = open("egl_procs.rs")?;
write_egl_procs(&mut f)?;
println!("cargo:rerun-if-changed=build.rs");
Ok(())
}

View file

@ -1,9 +1,18 @@
use crate::backend::{
BackendEvent, KeyState, Output, OutputId, ScrollAxis, Seat, SeatEvent, SeatId,
};
use crate::drm::drm::{Drm, DrmError};
use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING};
use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
use crate::egl::{EglContext, EglImage};
use crate::event_loop::{EventLoopDispatcher, EventLoopId};
use crate::fixed::Fixed;
use crate::format::XRGB8888;
use crate::gles2::gl::{GlFrameBuffer, GlRenderBuffer, GlTexture};
use crate::gles2::sys::{glBindFramebuffer, glFlush, glViewport, GL_FRAMEBUFFER};
use crate::gles2::GlesError;
use crate::pixman::{Image, PixmanError};
use crate::render::gles::{GlesRenderer, RENDERDOC};
use crate::render::pixman::PixmanRenderer;
use crate::render::Renderer;
use crate::servermem::{ServerMem, ServerMemError};
@ -12,8 +21,10 @@ use crate::utils::clonecell::CloneCell;
use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::ptr_ext::PtrExt;
use crate::wheel::{WheelDispatcher, WheelId};
use crate::{pixman, EventLoopError, State, WheelError};
use crate::{gles2, pixman, EventLoopError, NumCell, State, WheelError};
use gles2::egl;
use isnt::std_1::primitive::IsntConstPtrExt;
use rand::Rng;
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::error::Error;
@ -21,7 +32,7 @@ use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
use thiserror::Error;
use uapi::c;
use uapi::{c, OwnedFd};
use xcb_dl::{ffi, Xcb, XcbDri3, XcbPresent, XcbShm, XcbXinput, XcbXkb};
use xcb_dl_util::error::{XcbError, XcbErrorParser};
use xcb_dl_util::xcb_box::XcbBox;
@ -30,6 +41,20 @@ use xcb_dl_util::xcb_box::XcbBox;
pub enum XorgBackendError {
#[error("The xcb connection is in an error state")]
ErrorEvent,
#[error("The drm subsystem returned an error")]
DrmError(#[from] DrmError),
#[error("The gbm subsystem returned an error")]
GbmError(#[from] GbmError),
#[error("Could not find an EGL device that matches the display's DRM device")]
MissingEglDevice,
#[error("Could not import a dma-buf")]
ImportBuffer(#[source] XcbError),
#[error("The EGL device does not support the XRGB8888 format")]
XRGB8888,
#[error("Could not create an EGL context")]
CreateEgl(#[source] Box<Self>),
#[error(transparent)]
EglError(#[from] GlesError),
#[error("Could not select input events")]
CannotSelectInputEvents(#[source] XcbError),
#[error("Could not select present events")]
@ -73,6 +98,7 @@ struct XcbCon {
input_opcode: u8,
present_opcode: u8,
xkb: Box<XcbXkb>,
screen: ffi::xcb_screen_t,
c: *mut ffi::xcb_connection_t,
errors: XcbErrorParser,
}
@ -85,15 +111,18 @@ impl XcbCon {
let input = Box::new(XcbXinput::load_loose()?);
let xkb = Box::new(XcbXkb::load_loose()?);
let dri = Box::new(XcbDri3::load_loose()?);
let present = Box::new(XcbPresent::load_loose()?);
let c = xcb.xcb_connect(ptr::null(), ptr::null_mut());
let errors = XcbErrorParser::new(&xcb, c);
let mut con = Self {
screen: *xcb.xcb_setup_roots_iterator(xcb.xcb_get_setup(c)).data,
xcb,
shm,
input,
dri,
present,
input_opcode: 0,
present_opcode: 0,
xkb,
@ -192,6 +221,28 @@ pub struct XorgBackend {
outputs: CopyHashMap<ffi::xcb_window_t, Rc<XorgOutput>>,
seats: CopyHashMap<ffi::xcb_input_device_id_t, Rc<XorgSeat>>,
mouse_seats: CopyHashMap<ffi::xcb_input_device_id_t, Rc<XorgSeat>>,
ctx: Rc<EglContext>,
renderer: GlesRenderer,
gbm: GbmDevice,
r: Cell<f32>,
g: Cell<f32>,
b: Cell<f32>,
}
fn get_drm(con: &XcbCon) -> Result<Drm, XorgBackendError> {
unsafe {
let mut err = ptr::null_mut();
let res = con.dri.xcb_dri3_open_reply(
con.c,
con.dri.xcb_dri3_open(con.c, con.screen.root, 0),
&mut err,
);
let mut res = con.check(res, err)?;
assert!(res.nfd == 1);
let fd = *con.dri.xcb_dri3_open_reply_fds(con.c, &mut *res);
let fd = OwnedFd::new(fd);
Ok(Drm::new(fd.raw(), true)?)
}
}
impl XorgBackend {
@ -199,6 +250,26 @@ impl XorgBackend {
unsafe {
let con = XcbCon::new()?;
let drm = get_drm(&con)?;
let res = (|| {
let egl_dev = match egl::find_drm_device(&drm)? {
Some(d) => d,
None => return Err(XorgBackendError::MissingEglDevice),
};
let dpy = egl_dev.create_display()?;
if !dpy.formats.contains_key(&XRGB8888.drm) {
return Err(XorgBackendError::XRGB8888);
}
let ctx = dpy.create_context()?;
Ok(ctx)
})();
let ctx = match res {
Ok(r) => r,
Err(e) => return Err(XorgBackendError::CreateEgl(Box::new(e))),
};
let renderer = ctx.with_current(|| GlesRenderer::new(&ctx))?;
let gbm = GbmDevice::new(&drm)?;
let fd = con.xcb.xcb_get_file_descriptor(con.c);
let wheel_id = state.wheel.id();
@ -211,6 +282,12 @@ impl XorgBackend {
outputs: Default::default(),
seats: Default::default(),
mouse_seats: Default::default(),
ctx: ctx.clone(),
renderer,
gbm,
r: Cell::new(0.0),
g: Cell::new(0.0),
b: Cell::new(0.0),
});
{
@ -226,7 +303,7 @@ impl XorgBackend {
}
}
state.wheel.periodic(wheel_id, 16_667, slf.clone())?;
// state.wheel.periodic(wheel_id, 16_667, slf.clone())?;
// state.wheel.periodic(wheel_id, 1000_000, slf.clone())?;
state.el.insert(slf.id, Some(fd), c::EPOLLIN, slf.clone())?;
@ -234,10 +311,69 @@ impl XorgBackend {
slf.query_devices(ffi::XCB_INPUT_DEVICE_ALL_MASTER as _)?;
slf.handle_events()?;
state.egl.set(Some(ctx.clone()));
Ok(slf)
}
}
fn create_images(
&self,
window: ffi::xcb_window_t,
width: i32,
height: i32,
) -> Result<[XorgImage; 2], XorgBackendError> {
let format = ModifiedFormat {
format: XRGB8888,
modifier: INVALID_MODIFIER,
};
let mut images = [None, None];
for i in 0..2 {
let bo = self
.gbm
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)?;
let dma = bo.dma();
assert!(dma.planes.len() == 1);
let plane = dma.planes.first().unwrap();
let size = plane.stride * dma.height as u32;
let fd = uapi::fcntl_dupfd_cloexec(plane.fd.raw(), 0).unwrap();
let fb = if RENDERDOC {
unsafe { GlTexture::new(&self.ctx, XRGB8888, width, height)?.to_framebuffer()? }
} else {
let egl_img = self.ctx.dpy.import_dmabuf(dma)?;
let rb = GlRenderBuffer::from_image(&egl_img, &self.ctx)?;
rb.create_framebuffer()?
};
let pixmap = unsafe {
let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
let cookie = self.con.dri.xcb_dri3_pixmap_from_buffer_checked(
self.con.c,
pixmap,
window,
size,
dma.width as _,
dma.height as _,
plane.stride as _,
24,
32,
fd.unwrap(),
);
if let Err(e) = self.con.check_cookie(cookie) {
return Err(XorgBackendError::ImportBuffer(e));
}
pixmap
};
images[i] = Some(XorgImage {
pixmap: Cell::new(pixmap),
fb: CloneCell::new(fb),
idle: Cell::new(true),
render_on_idle: Cell::new(false),
last_serial: Cell::new(0),
});
}
Ok([images[0].take().unwrap(), images[1].take().unwrap()])
}
fn add_output(self: &Rc<Self>) -> Result<(), XorgBackendError> {
unsafe {
let con = &self.con;
@ -247,6 +383,8 @@ impl XorgBackend {
.data
.deref();
let window_id = con.xcb.xcb_generate_id(con.c);
const WIDTH: i32 = 800;
const HEIGHT: i32 = 600;
{
let cookie = con.xcb.xcb_create_window_checked(
con.c,
@ -255,8 +393,8 @@ impl XorgBackend {
screen.root,
0,
0,
800,
600,
WIDTH as _,
HEIGHT as _,
0,
ffi::XCB_WINDOW_CLASS_INPUT_OUTPUT as _,
0,
@ -267,6 +405,7 @@ impl XorgBackend {
return Err(XorgBackendError::CreateWindow(e));
}
}
let images = self.create_images(window_id, WIDTH, HEIGHT).unwrap();
let output = Rc::new(XorgOutput {
id: self.state.output_ids.next(),
backend: self.clone(),
@ -274,8 +413,12 @@ impl XorgBackend {
removed: Cell::new(false),
width: Cell::new(0),
height: Cell::new(0),
serial: Default::default(),
next_msc: Cell::new(0),
next_image: Default::default(),
image: RefCell::new(None),
cb: CloneCell::new(None),
images,
});
{
let class = "i4\0i4\0";
@ -356,6 +499,7 @@ impl XorgBackend {
self.state
.backend_events
.push(BackendEvent::NewOutput(output.clone()));
self.present(&output);
}
Ok(())
}
@ -495,14 +639,104 @@ impl XorgBackend {
let event = unsafe {
(event as *const _ as *const ffi::xcb_present_complete_notify_event_t).deref()
};
let output = match self.outputs.get(&event.window) {
let window = event.window;
let output = match self.outputs.get(&window) {
Some(o) => o,
_ => return Ok(()),
};
output.next_msc.set(event.msc + 1);
let image = &output.images[output.next_image.get() % output.images.len()];
if image.idle.get() {
self.present(&output);
} else {
image.render_on_idle.set(true);
}
Ok(())
}
fn handle_present_idle(
self: &Rc<Self>,
event: &ffi::xcb_ge_generic_event_t,
) -> Result<(), XorgBackendError> {
let event =
unsafe { (event as *const _ as *const ffi::xcb_present_idle_notify_event_t).deref() };
let output = match self.outputs.get(&event.window) {
Some(o) => o,
_ => return Ok(()),
};
for image in &output.images {
if image.last_serial.get() == event.serial {
image.idle.set(true);
if image.render_on_idle.replace(false) {
self.present(&output);
}
}
}
Ok(())
}
fn present(&self, output: &Rc<XorgOutput>) {
// {
// let clients = self.state.clients.clients.borrow();
// for client in clients.values() {
// let s = client.data.objects.surfaces.lock();
// for s in s.values() {
// let mut fr = s.frame_requests.borrow_mut();
// for cb in fr.drain(..) {
// s.client.dispatch_frame_requests.push(cb);
// }
// }
// }
// return;
// }
let image = &output.images[output.next_image.fetch_add(1) % output.images.len()];
let serial = output.serial.fetch_add(1);
if let Some(node) = self.state.root.outputs.get(&output.id) {
let fb = image.fb.get();
let mut renderer = self.renderer.render_fb(&fb);
self.ctx
.with_current(|| unsafe {
fb.bind();
glViewport(0, 0, fb.width, fb.height);
// fb.clear(0.0, 0.0, 0.0, 1.0);
renderer.render_output(&node);
glFlush();
Ok(())
})
.unwrap();
}
unsafe {
let cookie = self.con.present.xcb_present_pixmap_checked(
self.con.c,
output.window,
image.pixmap.get(),
serial,
0,
0,
0,
0,
0,
0,
0,
0,
output.next_msc.get(),
1,
0,
0,
ptr::null(),
);
if let Err(e) = self.con.check_cookie(cookie) {
log::error!("Could not present image: {:?}", e);
return;
}
}
image.idle.set(false);
image.last_serial.set(serial);
}
fn handle_input_event(
self: &Rc<Self>,
event: &ffi::xcb_ge_generic_event_t,
@ -567,6 +801,12 @@ impl XorgBackend {
event: &ffi::xcb_ge_generic_event_t,
state: KeyState,
) -> Result<(), XorgBackendError> {
if state == KeyState::Pressed {
let mut rng = rand::thread_rng();
self.r.set(rng.gen_range(0.0..1.0));
self.g.set(rng.gen_range(0.0..1.0));
self.b.set(rng.gen_range(0.0..1.0));
}
let event =
unsafe { (event as *const _ as *const ffi::xcb_input_key_press_event_t).deref() };
if let Some(seat) = self.seats.get(&event.deviceid) {
@ -626,7 +866,10 @@ impl XorgBackend {
event: &ffi::xcb_ge_generic_event_t,
) -> Result<(), XorgBackendError> {
let event = unsafe { (event as *const _ as *const ffi::xcb_input_motion_event_t).deref() };
let (win, seat) = match (self.outputs.get(&event.event), self.mouse_seats.get(&event.deviceid)) {
let (win, seat) = match (
self.outputs.get(&event.event),
self.mouse_seats.get(&event.deviceid),
) {
(Some(a), Some(b)) => (a, b),
_ => return Ok(()),
};
@ -639,6 +882,7 @@ impl XorgBackend {
}
fn handle_destroy(&self, event: &ffi::xcb_generic_event_t) -> Result<(), XorgBackendError> {
self.state.el.stop();
let event =
unsafe { (event as *const _ as *const ffi::xcb_destroy_notify_event_t).deref() };
let output = match self.outputs.remove(&event.event) {
@ -658,56 +902,67 @@ impl XorgBackend {
Some(o) => o,
_ => return Ok(()),
};
let width = event.width as u32;
let height = event.height as u32;
let width = event.width as i32;
let height = event.height as i32;
let mut changed = false;
changed |= output.width.replace(width as i32) != width as i32;
changed |= output.height.replace(height as i32) != height as i32;
changed |= output.width.replace(width) != width;
changed |= output.height.replace(height) != height;
if changed {
let shm = Rc::new(ServerMem::new((width * height * 4) as usize)?);
let fd = shm.fd();
let image = Image::new(shm, pixman::X8R8G8B8, width, height, width * 4)?;
*output.image.borrow_mut() = Some(image);
unsafe {
let fd = match uapi::fcntl_dupfd_cloexec(fd, 0) {
Ok(fd) => fd,
Err(e) => return Err(XorgBackendError::DupfdFailed(e.into())),
};
let shmseg = self.con.xcb.xcb_generate_id(self.con.c);
let cookie =
self.con
.shm
.xcb_shm_attach_fd_checked(self.con.c, shmseg, fd.unwrap(), 0);
self.con.check_cookie(cookie)?;
let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
let cookie = self.con.shm.xcb_shm_create_pixmap(
self.con.c,
pixmap,
output.window,
width as _,
height as _,
24,
shmseg,
0,
);
self.con.check_cookie(cookie)?;
let cookie = self.con.xcb.xcb_change_window_attributes_checked(
self.con.c,
output.window,
ffi::XCB_CW_BACK_PIXMAP,
&pixmap as *const _ as _,
);
self.con.check_cookie(cookie)?;
self.con.xcb.xcb_free_pixmap(self.con.c, pixmap);
self.con.shm.xcb_shm_detach(self.con.c, shmseg);
let images = self.create_images(output.window, width, height).unwrap();
for (new, old) in images.iter().zip(output.images.iter()) {
unsafe {
self.con.xcb.xcb_free_pixmap(self.con.c, old.pixmap.get());
}
old.fb.set(new.fb.get());
old.pixmap.set(new.pixmap.get());
}
}
// let shm = Rc::new(ServerMem::new((width * height * 4) as usize)?);
// let fd = shm.fd();
// let image = Image::new(shm, pixman::X8R8G8B8, width, height, width * 4)?;
// *output.image.borrow_mut() = Some(image);
// unsafe {
// let fd = match uapi::fcntl_dupfd_cloexec(fd, 0) {
// Ok(fd) => fd,
// Err(e) => return Err(XorgBackendError::DupfdFailed(e.into())),
// };
// let shmseg = self.con.xcb.xcb_generate_id(self.con.c);
// let cookie =
// self.con
// .shm
// .xcb_shm_attach_fd_checked(self.con.c, shmseg, fd.unwrap(), 0);
// self.con.check_cookie(cookie)?;
// let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
// let cookie = self.con.shm.xcb_shm_create_pixmap(
// self.con.c,
// pixmap,
// output.window,
// width as _,
// height as _,
// 24,
// shmseg,
// 0,
// );
// self.con.check_cookie(cookie)?;
// let cookie = self.con.xcb.xcb_change_window_attributes_checked(
// self.con.c,
// output.window,
// ffi::XCB_CW_BACK_PIXMAP,
// &pixmap as *const _ as _,
// );
// self.con.check_cookie(cookie)?;
// self.con.xcb.xcb_free_pixmap(self.con.c, pixmap);
// self.con.shm.xcb_shm_detach(self.con.c, shmseg);
// }
output.changed();
self.render()?;
// self.render()?;
}
Ok(())
}
fn render(&self) -> Result<(), XorgBackendError> {
return Ok(());
let outputs = self.outputs.lock();
for output in outputs.values() {
let image = output.image.borrow();
@ -774,15 +1029,20 @@ struct XorgOutput {
removed: Cell<bool>,
width: Cell<i32>,
height: Cell<i32>,
serial: NumCell<u32>,
next_msc: Cell<u64>,
next_image: Cell<usize>,
// images: [XorgImage; 2],
next_image: NumCell<usize>,
images: [XorgImage; 2],
image: RefCell<Option<Image<Rc<ServerMem>>>>,
cb: CloneCell<Option<Rc<dyn Fn()>>>,
}
struct XorgImage {
pixmap: Cell<ffi::xcb_pixmap_t>,
fb: CloneCell<Rc<GlFrameBuffer>>,
idle: Cell<bool>,
render_on_idle: Cell<bool>,
last_serial: Cell<u32>,
}
impl Drop for XorgOutput {

16
src/drm/dma.rs Normal file
View file

@ -0,0 +1,16 @@
use crate::format::Format;
use uapi::OwnedFd;
pub struct DmaBufPlane {
pub offset: u32,
pub stride: u32,
pub fd: OwnedFd,
}
pub struct DmaBuf {
pub width: i32,
pub height: i32,
pub format: &'static Format,
pub modifier: u64,
pub planes: Vec<DmaBufPlane>,
}

395
src/drm/drm.rs Normal file
View file

@ -0,0 +1,395 @@
use crate::utils::bitflags::BitflagsExt;
use crate::utils::debug_fn::debug_fn;
use crate::utils::ptr_ext::PtrExt;
use bstr::ByteSlice;
use std::ffi::{CStr, CString};
use std::fmt::{Debug, Formatter};
use std::ptr;
use thiserror::Error;
use uapi::c::c_char;
use uapi::{c, Errno, OwnedFd, Ustring};
#[derive(Debug, Error)]
pub enum DrmError {
#[error("Could not create a lease")]
CreateLeaseError(#[source] std::io::Error),
#[error("Could not reopen a node")]
ReopenNode(#[source] std::io::Error),
#[error("Could not retrieve the render node name")]
RenderNodeName,
#[error("Could not retrieve the device node name")]
DeviceNodeName,
#[error("Could not retrieve device")]
GetDevice(#[source] std::io::Error),
}
const DRM_NODE_PRIMARY: c::c_int = 0;
const DRM_NODE_CONTROL: c::c_int = 1;
const DRM_NODE_RENDER: c::c_int = 2;
const DRM_NODE_MAX: c::c_int = 3;
const DRM_BUS_PCI: c::c_int = 0;
const DRM_BUS_USB: c::c_int = 1;
const DRM_BUS_PLATFORM: c::c_int = 2;
const DRM_BUS_HOST1X: c::c_int = 3;
#[link(name = "drm")]
extern "C" {
fn drmIsMaster(fd: c::c_int) -> c::c_int;
fn drmModeCreateLease(
fd: c::c_int,
o: *const u32,
num_objects: c::c_int,
flags: c::c_int,
lessee_id: *mut u32,
) -> c::c_int;
fn drmGetNodeTypeFromFd(fd: c::c_int) -> c::c_int;
fn drmGetRenderDeviceNameFromFd(fd: c::c_int) -> *mut c::c_char;
fn drmGetDeviceNameFromFd2(fd: c::c_int) -> *mut c::c_char;
fn drmFreeDevice(device: *mut *mut drmDevice);
fn drmGetDevice(fd: c::c_int, device: *mut *mut drmDevice) -> c::c_int;
}
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
unsafe {
let name = drmGetRenderDeviceNameFromFd(fd);
if name.is_null() {
Err(DrmError::RenderNodeName)
} else {
Ok(CString::from_raw(name).into())
}
}
}
fn device_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
unsafe {
let name = drmGetDeviceNameFromFd2(fd);
if name.is_null() {
Err(DrmError::DeviceNodeName)
} else {
Ok(CString::from_raw(name).into())
}
}
}
fn reopen(fd: c::c_int, allow_downgrade: bool) -> Result<OwnedFd, DrmError> {
unsafe {
if drmIsMaster(fd) != 0 {
let mut lessee = 0;
let lease_fd = drmModeCreateLease(fd, ptr::null(), 0, c::O_CLOEXEC, &mut lessee);
if lease_fd >= 0 {
return Ok(OwnedFd::new(lease_fd));
}
}
let path = if drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER {
uapi::format_ustr!("/proc/self/fd/{}", fd)
} else if allow_downgrade {
render_node_name(fd)?
} else {
device_node_name(fd)?
};
match uapi::open(&path, c::O_RDWR | c::O_CLOEXEC, 0) {
Ok(f) => Ok(f),
Err(e) => Err(DrmError::ReopenNode(e.into())),
}
}
}
pub struct Drm {
fd: OwnedFd,
}
impl Drm {
pub fn new(fd: c::c_int, allow_downgrade: bool) -> Result<Self, DrmError> {
Ok(Self {
fd: reopen(fd, allow_downgrade)?,
})
}
pub fn raw(&self) -> c::c_int {
self.fd.raw()
}
pub fn dup_unprivileged(&self) -> Result<Self, DrmError> {
Self::new(self.fd.raw(), true)
}
pub fn get_device(&self) -> Result<DrmDevice, DrmError> {
unsafe {
let mut dev = ptr::null_mut();
if drmGetDevice(self.fd.raw(), &mut dev) < 0 {
return Err(DrmError::GetDevice(Errno::default().into()));
}
Ok(DrmDevice { dev })
}
}
}
#[repr(C)]
struct drmPciBusInfo {
domain: u16,
bus: u8,
dev: u8,
func: u8,
}
#[repr(C)]
struct drmUsbBusInfo {
bus: u8,
dev: u8,
}
const DRM_PLATFORM_DEVICE_NAME_LEN: usize = 512;
#[repr(C)]
struct drmPlatformBusInfo {
fullname: [c::c_char; DRM_PLATFORM_DEVICE_NAME_LEN],
}
const DRM_HOST1X_DEVICE_NAME_LEN: usize = 512;
#[repr(C)]
struct drmHost1xBusInfo {
fullname: [c::c_char; DRM_HOST1X_DEVICE_NAME_LEN],
}
#[repr(C)]
union businfo {
pci: *mut drmPciBusInfo,
usb: *mut drmUsbBusInfo,
platform: *mut drmPlatformBusInfo,
host1x: *mut drmHost1xBusInfo,
}
#[repr(C)]
struct drmPciDeviceInfo {
vendor_id: u16,
device_id: u16,
subvendor_id: u16,
subdevice_id: u16,
revision_id: u8,
}
#[repr(C)]
struct drmUsbDeviceInfo {
vendor: u16,
product: u16,
}
#[repr(C)]
struct drmPlatformDeviceInfo {
compatible: *mut *mut c::c_char,
}
#[repr(C)]
struct drmHost1xDeviceInfo {
compatible: *mut *mut c::c_char,
}
#[repr(C)]
union deviceinfo {
pci: *mut drmPciDeviceInfo,
usb: *mut drmUsbDeviceInfo,
platform: *mut drmPlatformDeviceInfo,
host1x: *mut drmHost1xDeviceInfo,
}
#[repr(C)]
struct drmDevice {
nodes: *mut *mut c::c_char,
available_nodes: c::c_int,
bustype: c::c_int,
businfo: businfo,
deviceinfo: deviceinfo,
}
pub struct DrmDevice {
dev: *mut drmDevice,
}
impl Drop for DrmDevice {
fn drop(&mut self) {
unsafe {
drmFreeDevice(&mut self.dev);
}
}
}
impl DrmDevice {
pub fn nodes<'a>(&'a self) -> impl Iterator<Item = (c::c_int, &'a CStr)> + 'a {
struct Iter<'a> {
next: usize,
dev: &'a DrmDevice,
}
impl<'a> Iterator for Iter<'a> {
type Item = (c::c_int, &'a CStr);
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let dev = self.dev.dev.deref();
while self.next < DRM_NODE_MAX as _ {
let idx = self.next;
self.next += 1;
if dev.available_nodes.contains(1 << idx) {
return Some((idx as _, CStr::from_ptr(*dev.nodes.add(idx))));
}
}
None
}
}
}
Iter { next: 0, dev: self }
}
}
impl Debug for DrmDevice {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
struct StrStr<'a> {
v: &'a [*mut c::c_char],
}
impl Debug for StrStr<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut list = f.debug_list();
for &v in self.v {
if v.is_null() {
list.entry(&v);
} else {
unsafe {
list.entry(&CStr::from_ptr(v));
}
}
}
list.finish()
}
}
impl<'a> StrStr<'a> {
fn from_nt(nt: *const *mut c_char) -> Self {
unsafe {
let mut num = 0;
let mut tmp = nt;
while !tmp.deref().is_null() {
num += 1;
tmp = tmp.add(1);
}
Self {
v: std::slice::from_raw_parts(nt, num),
}
}
}
}
let mut ds = f.debug_struct("DrmDevice");
unsafe {
let dev = self.dev.deref();
let nodes = std::slice::from_raw_parts(dev.nodes, DRM_NODE_MAX as _);
ds.field(
"available_nodes",
&debug_fn(|f| write!(f, "0b{:b}", dev.available_nodes)),
);
ds.field("nodes", &StrStr { v: nodes });
ds.field("bustype", &dev.bustype);
match dev.bustype {
DRM_BUS_PCI => {
ds.field(
"businfo",
&debug_fn(|f| {
let pci = dev.businfo.pci.deref();
f.debug_struct("drmPciBusInfo")
.field("domain", &pci.domain)
.field("bus", &pci.bus)
.field("dev", &pci.dev)
.field("func", &pci.func)
.finish()
}),
);
ds.field(
"deviceinfo",
&debug_fn(|f| {
let pci = dev.deviceinfo.pci.deref();
f.debug_struct("drmPciDeviceInfo")
.field("vendor_id", &pci.vendor_id)
.field("device_id", &pci.device_id)
.field("subvendor_id", &pci.subvendor_id)
.field("subdevice_id", &pci.subdevice_id)
.field("revision_id", &pci.revision_id)
.finish()
}),
);
}
DRM_BUS_USB => {
ds.field(
"businfo",
&debug_fn(|f| {
let usb = dev.businfo.usb.deref();
f.debug_struct("drmUsbBusInfo")
.field("bus", &usb.bus)
.field("dev", &usb.dev)
.finish()
}),
);
ds.field(
"deviceinfo",
&debug_fn(|f| {
let usb = dev.deviceinfo.usb.deref();
f.debug_struct("drmUsbDeviceInfo")
.field("vendor", &usb.vendor)
.field("product", &usb.product)
.finish()
}),
);
}
DRM_BUS_PLATFORM => {
ds.field(
"businfo",
&debug_fn(|f| {
let platform = dev.businfo.platform.deref();
f.debug_struct("drmPlatformBusInfo")
.field(
"fullname",
&CStr::from_ptr(platform.fullname.as_ptr())
.to_bytes()
.as_bstr(),
)
.finish()
}),
);
ds.field(
"deviceinfo",
&debug_fn(|f| {
let platform = dev.deviceinfo.platform.deref();
f.debug_struct("drmPlatformDeviceInfo")
.field("compatible", &StrStr::from_nt(platform.compatible))
.finish()
}),
);
}
DRM_BUS_HOST1X => {
ds.field(
"businfo",
&debug_fn(|f| {
let host1x = dev.businfo.host1x.deref();
f.debug_struct("drmHost1xBusInfo")
.field(
"fullname",
&CStr::from_ptr(host1x.fullname.as_ptr())
.to_bytes()
.as_bstr(),
)
.finish()
}),
);
ds.field(
"deviceinfo",
&debug_fn(|f| {
let host1x = dev.deviceinfo.host1x.deref();
f.debug_struct("drmHost1xDeviceInfo")
.field("compatible", &StrStr::from_nt(host1x.compatible))
.finish()
}),
);
}
_ => {}
}
ds.finish()
}
}
}

169
src/drm/gbm.rs Normal file
View file

@ -0,0 +1,169 @@
use crate::drm::dma::{DmaBuf, DmaBufPlane};
use crate::drm::drm::{Drm, DrmError};
use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
use crate::format::formats;
use std::ptr;
use thiserror::Error;
use uapi::{c, OwnedFd};
#[derive(Debug, Error)]
pub enum GbmError {
#[error("The drm subsystem returned an error")]
Drm(#[from] DrmError),
#[error("Cloud not create a gbm device")]
CreateDevice,
#[error("Cloud not create a gbm buffer")]
CreateBo,
#[error("gbm buffer has an unknown format")]
UnknownFormat,
#[error("Could not retrieve a drm-buf fd")]
DrmFd,
}
type Device = u8;
type Bo = u8;
pub const GBM_BO_USE_SCANOUT: u32 = 1 << 0;
pub const GBM_BO_USE_CURSOR: u32 = 1 << 1;
pub const GBM_BO_USE_RENDERING: u32 = 1 << 2;
pub const GBM_BO_USE_WRITE: u32 = 1 << 3;
pub const GBM_BO_USE_LINEAR: u32 = 1 << 4;
pub const GBM_BO_USE_PROTECTED: u32 = 1 << 5;
#[link(name = "gbm")]
extern "C" {
fn gbm_create_device(fd: c::c_int) -> *mut Device;
fn gbm_device_destroy(dev: *mut Device);
fn gbm_bo_create_with_modifiers2(
dev: *mut Device,
width: u32,
height: u32,
format: u32,
modifiers: *const u64,
count: c::c_uint,
flags: u32,
) -> *mut Bo;
fn gbm_bo_destroy(bo: *mut Bo);
fn gbm_bo_get_plane_count(bo: *mut Bo) -> c::c_int;
fn gbm_bo_get_width(bo: *mut Bo) -> u32;
fn gbm_bo_get_height(bo: *mut Bo) -> u32;
fn gbm_bo_get_stride(bo: *mut Bo) -> u32;
fn gbm_bo_get_modifier(bo: *mut Bo) -> u64;
fn gbm_bo_get_stride_for_plane(bo: *mut Bo, plane: c::c_int) -> u32;
fn gbm_bo_get_fd_for_plane(bo: *mut Bo, plane: c::c_int) -> c::c_int;
fn gbm_bo_get_offset(bo: *mut Bo, plane: c::c_int) -> u32;
fn gbm_bo_get_format(bo: *mut Bo) -> u32;
fn gbm_bo_get_bpp(bo: *mut Bo) -> u32;
}
pub struct GbmDevice {
drm: Drm,
dev: *mut Device,
}
struct BoHolder {
bo: *mut Bo,
}
pub struct GbmBo {
bo: BoHolder,
dma: DmaBuf,
}
unsafe fn export_bo(bo: *mut Bo) -> Result<DmaBuf, GbmError> {
Ok(DmaBuf {
width: gbm_bo_get_width(bo) as _,
height: gbm_bo_get_height(bo) as _,
modifier: gbm_bo_get_modifier(bo),
format: {
let format = gbm_bo_get_format(bo);
match formats().get(&format).copied() {
Some(f) => f,
_ => return Err(GbmError::UnknownFormat),
}
},
planes: {
let mut planes = vec![];
for plane in 0..gbm_bo_get_plane_count(bo) {
let offset = gbm_bo_get_offset(bo, plane);
let stride = gbm_bo_get_stride_for_plane(bo, plane);
let fd = gbm_bo_get_fd_for_plane(bo, plane);
if fd < 0 {
return Err(GbmError::DrmFd);
}
planes.push(DmaBufPlane {
offset,
stride,
fd: OwnedFd::new(fd),
})
}
planes
},
})
}
impl GbmDevice {
pub fn new(drm: &Drm) -> Result<Self, GbmError> {
let drm = drm.dup_unprivileged()?;
let dev = unsafe { gbm_create_device(drm.raw()) };
if dev.is_null() {
Err(GbmError::CreateDevice)
} else {
Ok(Self { drm, dev })
}
}
pub fn create_bo(
&self,
width: i32,
height: i32,
format: &ModifiedFormat,
usage: u32,
) -> Result<GbmBo, GbmError> {
unsafe {
let (modifiers, n_modifiers) = if format.modifier == INVALID_MODIFIER {
(ptr::null(), 0)
} else {
(&format.modifier as _, 1)
};
let bo = gbm_bo_create_with_modifiers2(
self.dev,
width as _,
height as _,
format.format.drm,
modifiers,
n_modifiers,
usage,
);
if bo.is_null() {
return Err(GbmError::CreateBo);
}
let bo = BoHolder { bo };
let dma = export_bo(bo.bo)?;
Ok(GbmBo { bo, dma })
}
}
}
impl Drop for GbmDevice {
fn drop(&mut self) {
unsafe {
gbm_device_destroy(self.dev);
}
}
}
impl GbmBo {
pub fn dma(&self) -> &DmaBuf {
&self.dma
}
}
impl Drop for BoHolder {
fn drop(&mut self) {
unsafe {
gbm_bo_destroy(self.bo);
}
}
}

14
src/drm/mod.rs Normal file
View file

@ -0,0 +1,14 @@
use crate::format::Format;
pub mod dma;
pub mod drm;
pub mod gbm;
pub type Modifier = u64;
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
pub struct ModifiedFormat {
pub format: &'static Format,
pub modifier: Modifier,
}

View file

@ -1,20 +1,29 @@
use crate::gles2::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE};
use crate::pixman;
use ahash::AHashMap;
use once_cell::sync::Lazy;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Format {
pub id: u32,
pub name: &'static str,
pub bpp: u32,
pub pixman: pixman::Format,
pub pixman: Option<pixman::Format>,
pub gl_format: GLint,
pub gl_type: GLint,
pub drm: u32,
pub wl_id: Option<u32>,
}
pub fn formats() -> AHashMap<u32, &'static Format> {
static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.id, format).is_none());
assert!(map.insert(format.drm, format).is_none());
}
map
});
pub fn formats() -> &'static AHashMap<u32, &'static Format> {
&*FORMATS_MAP
}
#[allow(dead_code)]
@ -22,18 +31,41 @@ const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
}
static FORMATS: &[Format] = &[
const ARGB8888_ID: u32 = 0;
const ARGB8888_DRM: u32 = fourcc_code('A', 'R', '2', '4');
const XRGB8888_ID: u32 = 1;
const XRGB8888_DRM: u32 = fourcc_code('X', 'R', '2', '4');
pub fn map_wayland_format_id(id: u32) -> u32 {
match id {
ARGB8888_ID => ARGB8888_DRM,
XRGB8888_ID => XRGB8888_DRM,
_ => id,
}
}
pub static ARGB8888: &Format = &FORMATS[0];
pub static XRGB8888: &Format = &FORMATS[1];
pub static FORMATS: &[Format] = &[
Format {
id: 0,
name: "argb8888",
bpp: 4,
pixman: pixman::A8R8G8B8,
pixman: Some(pixman::A8R8G8B8),
gl_format: GL_BGRA_EXT,
gl_type: GL_UNSIGNED_BYTE,
drm: ARGB8888_DRM,
wl_id: Some(ARGB8888_ID),
},
Format {
id: 1,
name: "xrgb8888",
bpp: 4,
pixman: pixman::X8R8G8B8,
pixman: Some(pixman::X8R8G8B8),
gl_format: GL_BGRA_EXT,
gl_type: GL_UNSIGNED_BYTE,
drm: XRGB8888_DRM,
wl_id: Some(XRGB8888_ID),
},
// Format {
// id: fourcc_code('C', '8', ' ', ' '),

487
src/gles2/egl.rs Normal file
View file

@ -0,0 +1,487 @@
use super::ext::{get_client_ext, ClientExt};
use super::ext_proc::ExtProc;
use super::sys::{
eglBindAPI, EGLAttrib, EGLLabelKHR, EGLenum, EGLint, EGL_BAD_ACCESS, EGL_BAD_ALLOC,
EGL_BAD_ATTRIBUTE, EGL_BAD_CONFIG, EGL_BAD_CONTEXT, EGL_BAD_CURRENT_SURFACE,
EGL_BAD_DEVICE_EXT, EGL_BAD_DISPLAY, EGL_BAD_MATCH, EGL_BAD_NATIVE_PIXMAP,
EGL_BAD_NATIVE_WINDOW, EGL_BAD_PARAMETER, EGL_BAD_SURFACE, EGL_CONTEXT_LOST,
EGL_DEBUG_MSG_CRITICAL_KHR, EGL_DEBUG_MSG_ERROR_KHR, EGL_DEBUG_MSG_INFO_KHR,
EGL_DEBUG_MSG_WARN_KHR, EGL_NONE, EGL_NOT_INITIALIZED, EGL_OPENGL_ES_API, EGL_SUCCESS,
EGL_TRUE,
};
use super::GlesError;
use crate::drm::dma::DmaBuf;
use crate::drm::drm::Drm;
use crate::drm::INVALID_MODIFIER;
use crate::format::{formats, Format};
use crate::gles2::ext::{
get_device_ext, get_display_ext, get_gl_ext, DeviceExt, DisplayExt, GlExt,
};
use crate::gles2::gl::GlTexture;
use crate::gles2::sys::{
eglCreateContext, eglDestroyContext, eglInitialize, eglMakeCurrent, eglTerminate,
glBindFramebuffer, glBindRenderbuffer, glBindTexture, glCheckFramebufferStatus, glClear,
glClearColor, glDeleteFramebuffers, glDeleteRenderbuffers, glFlush, glFramebufferRenderbuffer,
glGenFramebuffers, glGenRenderbuffers, glGenTextures, glPixelStorei, glTexImage2D,
glTexParameteri, EGLClientBuffer, EGLConfig, EGLContext, EGLDeviceEXT, EGLDisplay, EGLImageKHR,
EGLSurface, GLeglImageOES, GLint, GLuint, EGL_CONTEXT_CLIENT_VERSION,
EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT,
EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT,
EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT,
EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT,
EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DRM_DEVICE_FILE_EXT, EGL_FALSE, EGL_HEIGHT,
EGL_IMAGE_PRESERVED_KHR, EGL_LINUX_DMA_BUF_EXT, EGL_LINUX_DRM_FOURCC_EXT,
EGL_PLATFORM_DEVICE_EXT, EGL_WIDTH, GL_CLAMP_TO_EDGE, GL_COLOR_ATTACHMENT0,
GL_COLOR_BUFFER_BIT, GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER, GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT,
};
use crate::rect::Rect;
use ahash::AHashMap;
use bstr::ByteSlice;
use log::Level;
use once_cell::sync::Lazy;
use std::cell::Cell;
use std::ffi::CStr;
use std::ptr;
use std::rc::Rc;
use uapi::c;
#[thread_local]
pub(super) static PROCS: Lazy<ExtProc> = Lazy::new(|| ExtProc::load());
#[thread_local]
pub(super) static EXTS: Lazy<ClientExt> = Lazy::new(|| get_client_ext());
pub fn init() -> Result<(), GlesError> {
if !EXTS.contains(ClientExt::EXT_PLATFORM_BASE) {
return Err(GlesError::ExtPlatformBase);
}
if !EXTS.device_query() {
return Err(GlesError::DeviceQuery);
}
if !EXTS.device_enumeration() {
return Err(GlesError::DeviceEnumeration);
}
if EXTS.contains(ClientExt::KHR_DEBUG) {
let attrib: &[EGLAttrib] = &[
EGL_DEBUG_MSG_CRITICAL_KHR as _,
EGL_TRUE as _,
EGL_DEBUG_MSG_ERROR_KHR as _,
EGL_TRUE as _,
EGL_DEBUG_MSG_WARN_KHR as _,
EGL_TRUE as _,
EGL_DEBUG_MSG_INFO_KHR as _,
EGL_TRUE as _,
EGL_NONE as _,
];
unsafe {
PROCS.eglDebugMessageControlKHR(egl_log, attrib.as_ptr());
}
}
if unsafe { eglBindAPI(EGL_OPENGL_ES_API) } != EGL_TRUE {
return Err(GlesError::BindFailed);
}
Ok(())
}
pub fn find_drm_device(drm: &Drm) -> Result<Option<EglDevice>, GlesError> {
let drm_dev = drm.get_device()?;
for device in query_devices()? {
if device.exts.contains(DeviceExt::EXT_DEVICE_DRM) {
let device_file = device.query_string(EGL_DRM_DEVICE_FILE_EXT)?;
for (_, name) in drm_dev.nodes() {
if device_file == name {
return Ok(Some(device));
}
}
}
}
Ok(None)
}
pub fn query_devices() -> Result<Vec<EglDevice>, GlesError> {
if !EXTS.device_enumeration() {
return Err(GlesError::DeviceEnumeration);
}
unsafe {
let mut devices = vec![];
let mut num_devices = 0;
let res = PROCS.eglQueryDevicesEXT(num_devices, ptr::null_mut(), &mut num_devices);
if res != EGL_TRUE {
return Err(GlesError::QueryDevices);
}
devices.reserve_exact(num_devices as usize);
let res = PROCS.eglQueryDevicesEXT(num_devices, devices.as_mut_ptr(), &mut num_devices);
if res != EGL_TRUE {
return Err(GlesError::QueryDevices);
}
devices.set_len(num_devices as usize);
Ok(devices
.into_iter()
.map(|d| EglDevice {
exts: get_device_ext(d),
dev: d,
})
.collect())
}
}
#[derive(Debug, Copy, Clone)]
pub struct EglDevice {
pub exts: DeviceExt,
dev: EGLDeviceEXT,
}
#[derive(Debug, Clone)]
pub struct EglDisplay {
pub exts: DisplayExt,
pub formats: AHashMap<u32, &'static Format>,
dev: EglDevice,
dpy: EGLDisplay,
}
#[derive(Debug, Clone)]
pub struct EglContext {
pub dpy: Rc<EglDisplay>,
pub ext: GlExt,
ctx: EGLContext,
pub tex_prog: GLuint,
pub tex_tex: GLint,
pub tex_texcoord: GLint,
pub tex_pos: GLint,
}
impl EglDisplay {
pub fn create_context(self: &Rc<Self>) -> Result<Rc<EglContext>, GlesError> {
let attrib = [EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE];
unsafe {
let ctx = eglCreateContext(
self.dpy,
EGLConfig::none(),
EGLContext::none(),
attrib.as_ptr(),
);
if ctx.is_none() {
return Err(GlesError::CreateContext);
}
let mut ctx = EglContext {
dpy: self.clone(),
ext: GlExt::empty(),
ctx,
tex_prog: 0,
tex_tex: 0,
tex_texcoord: 0,
tex_pos: 0,
};
ctx.ext = ctx.with_current(|| Ok(get_gl_ext()))?;
// if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) {
// return Err(GlesError::OesEglImage);
// }
Ok(Rc::new(ctx))
}
}
pub fn import_dmabuf(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<EglImage>, GlesError> {
struct PlaneKey {
fd: EGLint,
offset: EGLint,
pitch: EGLint,
mod_lo: EGLint,
mod_hi: EGLint,
}
const PLANE_KEYS: [PlaneKey; 4] = [
PlaneKey {
fd: EGL_DMA_BUF_PLANE0_FD_EXT,
offset: EGL_DMA_BUF_PLANE0_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE0_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
},
PlaneKey {
fd: EGL_DMA_BUF_PLANE1_FD_EXT,
offset: EGL_DMA_BUF_PLANE1_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE1_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
},
PlaneKey {
fd: EGL_DMA_BUF_PLANE2_FD_EXT,
offset: EGL_DMA_BUF_PLANE2_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE2_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
},
PlaneKey {
fd: EGL_DMA_BUF_PLANE3_FD_EXT,
offset: EGL_DMA_BUF_PLANE3_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE3_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
},
];
let mut attribs = vec![];
attribs.extend_from_slice(&[EGL_WIDTH, buf.width]);
attribs.extend_from_slice(&[EGL_HEIGHT, buf.height]);
attribs.extend_from_slice(&[EGL_LINUX_DRM_FOURCC_EXT, buf.format.drm as _]);
attribs.extend_from_slice(&[EGL_IMAGE_PRESERVED_KHR, EGL_TRUE as _]);
for (key, plane) in PLANE_KEYS.iter().zip(buf.planes.iter()) {
attribs.extend_from_slice(&[key.fd, plane.fd.raw()]);
attribs.extend_from_slice(&[key.pitch, plane.stride as _]);
attribs.extend_from_slice(&[key.offset, plane.offset as _]);
if buf.modifier != INVALID_MODIFIER {
attribs.extend_from_slice(&[key.mod_lo, buf.modifier as i32]);
attribs.extend_from_slice(&[key.mod_hi, (buf.modifier >> 32) as i32]);
}
}
attribs.push(EGL_NONE);
let img = unsafe {
PROCS.eglCreateImageKHR(
self.dpy,
EGLContext::none(),
EGL_LINUX_DMA_BUF_EXT as _,
EGLClientBuffer::none(),
attribs.as_ptr(),
)
};
if img.is_none() {
return Err(GlesError::CreateImage);
}
Ok(Rc::new(EglImage {
dpy: self.clone(),
img,
width: buf.width,
height: buf.height,
}))
}
}
pub struct EglImage {
dpy: Rc<EglDisplay>,
pub(super) img: EGLImageKHR,
pub width: i32,
pub height: i32,
}
impl Drop for EglImage {
fn drop(&mut self) {
unsafe {
if PROCS.eglDestroyImageKHR(self.dpy.dpy, self.img) == EGL_FALSE {
log::warn!("`eglDestroyImageKHR` failed");
}
}
}
}
impl Drop for EglContext {
fn drop(&mut self) {
unsafe {
if eglDestroyContext(self.dpy.dpy, self.ctx) != EGL_TRUE {
log::warn!("`eglDestroyContext` failed");
}
}
}
}
#[thread_local]
static mut CURRENT: EGLContext = EGLContext::none();
impl EglContext {
#[inline]
pub fn with_current<T, F: FnOnce() -> Result<T, GlesError>>(
&self,
f: F,
) -> Result<T, GlesError> {
unsafe {
if CURRENT == self.ctx {
return f();
}
self.with_current_slow(f)
}
}
#[cold]
unsafe fn with_current_slow<T, F: FnOnce() -> Result<T, GlesError>>(
&self,
f: F,
) -> Result<T, GlesError> {
if eglMakeCurrent(
self.dpy.dpy,
EGLSurface::none(),
EGLSurface::none(),
self.ctx,
) == EGL_FALSE
{
return Err(GlesError::MakeCurrent);
}
let prev = CURRENT;
CURRENT = self.ctx;
let res = f();
if eglMakeCurrent(self.dpy.dpy, EGLSurface::none(), EGLSurface::none(), prev) == EGL_FALSE {
panic!("Could not restore EGLContext");
}
CURRENT = prev;
res
}
}
impl Drop for EglDisplay {
fn drop(&mut self) {
unsafe {
if eglTerminate(self.dpy) != EGL_TRUE {
log::warn!("`eglTerminate` failed");
}
}
}
}
impl EglDevice {
pub fn query_string(&self, name: EGLint) -> Result<&'static CStr, GlesError> {
unsafe {
let res = PROCS.eglQueryDeviceStringEXT(self.dev, name);
if res.is_null() {
return Err(GlesError::DeviceQueryString);
}
Ok(CStr::from_ptr(res))
}
}
pub fn create_display(&self) -> Result<Rc<EglDisplay>, GlesError> {
unsafe {
let dpy = PROCS.eglGetPlatformDisplayEXT(
EGL_PLATFORM_DEVICE_EXT as _,
self.dev.0,
ptr::null(),
);
if dpy.is_none() {
return Err(GlesError::GetDisplay);
}
let mut dpy = EglDisplay {
exts: DisplayExt::empty(),
formats: AHashMap::new(),
dev: *self,
dpy,
};
let mut major = 0;
let mut minor = 0;
if eglInitialize(dpy.dpy, &mut major, &mut minor) != EGL_TRUE {
return Err(GlesError::Initialize);
}
dpy.exts = get_display_ext(dpy.dpy);
if !dpy.exts.intersects(DisplayExt::KHR_IMAGE_BASE) {
return Err(GlesError::ImageBase);
}
if !dpy
.exts
.intersects(DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS)
{
return Err(GlesError::DmaBufImport);
}
if !dpy
.exts
.intersects(DisplayExt::KHR_NO_CONFIG_CONTEXT | DisplayExt::MESA_CONFIGLESS_CONTEXT)
{
return Err(GlesError::ConfiglessContext);
}
if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) {
return Err(GlesError::SurfacelessContext);
}
dpy.formats = query_formats(dpy.dpy)?;
Ok(Rc::new(dpy))
}
}
}
unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, &'static Format>, GlesError> {
let mut vec = vec![];
let mut num = 0;
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, ptr::null_mut(), &mut num);
if res != EGL_TRUE {
return Err(GlesError::QueryDmaBufFormats);
}
vec.reserve_exact(num as usize);
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, vec.as_mut_ptr(), &mut num);
if res != EGL_TRUE {
return Err(GlesError::QueryDmaBufFormats);
}
vec.set_len(num as usize);
let mut res = AHashMap::new();
let formats = formats();
for fmt in vec {
if let Some(format) = formats.get(&(fmt as u32)) {
res.insert(format.drm, *format);
}
}
Ok(res)
}
unsafe extern "C" fn egl_log(
error: EGLenum,
command: *const c::c_char,
message_type: EGLint,
_thread_label: EGLLabelKHR,
_object_label: EGLLabelKHR,
message: *const c::c_char,
) {
let level = match message_type {
EGL_DEBUG_MSG_CRITICAL_KHR => Level::Error,
EGL_DEBUG_MSG_ERROR_KHR => Level::Error,
EGL_DEBUG_MSG_WARN_KHR => Level::Warn,
EGL_DEBUG_MSG_INFO_KHR => Level::Info,
_ => Level::Warn,
};
let command = if !command.is_null() {
CStr::from_ptr(command).to_bytes()
} else {
b"none"
};
let message = if !message.is_null() {
CStr::from_ptr(message).to_bytes()
} else {
b"none"
};
let err_name = error_name(error);
log::log!(
level,
"EGL: command: {}, error: {} (0x{:x}), message: {}",
command.as_bstr(),
err_name,
error,
message.as_bstr()
);
}
fn error_name(error: EGLenum) -> &'static str {
macro_rules! en {
($($name:ident,)*) => {
match error as _ {
$($name => stringify!($name),)*
_ => "unknown",
}
}
}
en! {
EGL_SUCCESS,
EGL_NOT_INITIALIZED,
EGL_BAD_ACCESS,
EGL_BAD_ALLOC,
EGL_BAD_ATTRIBUTE,
EGL_BAD_CONTEXT,
EGL_BAD_CONFIG,
EGL_BAD_CURRENT_SURFACE,
EGL_BAD_DISPLAY,
EGL_BAD_DEVICE_EXT,
EGL_BAD_SURFACE,
EGL_BAD_MATCH,
EGL_BAD_PARAMETER,
EGL_BAD_NATIVE_PIXMAP,
EGL_BAD_NATIVE_WINDOW,
EGL_CONTEXT_LOST,
}
}

172
src/gles2/ext.rs Normal file
View file

@ -0,0 +1,172 @@
use crate::gles2::egl::PROCS;
use crate::gles2::sys::{
eglQueryString, glGetString, EGLDeviceEXT, EGLDisplay, EGL_EXTENSIONS, GL_EXTENSIONS,
};
use ahash::AHashSet;
use bstr::ByteSlice;
use std::ffi::CStr;
use std::ops::BitOrAssign;
use std::str;
use uapi::c;
unsafe fn get_extensions(ext: *const c::c_char) -> Option<AHashSet<String>> {
if ext.is_null() {
return None;
}
let mut res = AHashSet::new();
let ext = CStr::from_ptr(ext).to_bytes();
for part in ext.split_str(" ") {
let name = part.trim();
if name.len() > 0 {
if let Ok(s) = str::from_utf8(name) {
res.insert(s.to_string());
}
}
}
Some(res)
}
unsafe fn get_dpy_extensions(dpy: EGLDisplay) -> Option<AHashSet<String>> {
let ext = eglQueryString(dpy, EGL_EXTENSIONS);
get_extensions(ext)
}
fn get_typed_ext<T>(exts: &AHashSet<String>, mut base: T, map: &[(&str, T)]) -> T
where
T: BitOrAssign + Copy,
{
for (name, ext) in map.iter().copied() {
if exts.contains(name) {
base |= ext;
}
}
base
}
bitflags::bitflags! {
pub struct ClientExt: u32 {
const EXT_CLIENT_EXTENSION = 1 << 0;
const EXT_PLATFORM_BASE = 1 << 1;
const KHR_PLATFORM_GBM = 1 << 2;
const EXT_PLATFORM_DEVICE = 1 << 3;
const EXT_DEVICE_BASE = 1 << 4;
const EXT_DEVICE_ENUMERATION = 1 << 5;
const EXT_DEVICE_QUERY = 1 << 6;
const KHR_DEBUG = 1 << 7;
}
}
impl ClientExt {
pub fn device_enumeration(self) -> bool {
self.intersects(Self::EXT_DEVICE_BASE | Self::EXT_DEVICE_ENUMERATION)
}
pub fn device_query(self) -> bool {
self.intersects(Self::EXT_DEVICE_BASE | Self::EXT_DEVICE_QUERY)
}
}
pub fn get_client_ext() -> ClientExt {
let map = [
("EGL_EXT_platform_base", ClientExt::EXT_PLATFORM_BASE),
("EGL_KHR_platform_gbm", ClientExt::KHR_PLATFORM_GBM),
("EGL_EXT_platform_device", ClientExt::EXT_PLATFORM_DEVICE),
("EGL_EXT_device_base", ClientExt::EXT_DEVICE_BASE),
(
"EGL_EXT_device_enumeration",
ClientExt::EXT_DEVICE_ENUMERATION,
),
("EGL_EXT_device_query", ClientExt::EXT_DEVICE_QUERY),
("EGL_KHR_debug", ClientExt::KHR_DEBUG),
];
match unsafe { get_dpy_extensions(EGLDisplay::none()) } {
Some(exts) => get_typed_ext(&exts, ClientExt::EXT_CLIENT_EXTENSION, &map),
_ => ClientExt::empty(),
}
}
bitflags::bitflags! {
pub struct DisplayExt: u32 {
const KHR_IMAGE_BASE = 1 << 0;
const EXT_IMAGE_DMA_BUF_IMPORT = 1 << 1;
const EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS = 1 << 2;
const KHR_NO_CONFIG_CONTEXT = 1 << 3;
const MESA_CONFIGLESS_CONTEXT = 1 << 4;
const KHR_SURFACELESS_CONTEXT = 1 << 5;
const IMG_CONTEXT_PRIORITY = 1 << 6;
}
}
pub(super) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt {
let map = [
("EGL_KHR_image_base", DisplayExt::KHR_IMAGE_BASE),
(
"EGL_EXT_image_dma_buf_import",
DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT,
),
(
"EGL_EXT_image_dma_buf_import_modifiers",
DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS,
),
(
"EGL_KHR_no_config_context",
DisplayExt::KHR_NO_CONFIG_CONTEXT,
),
(
"EGL_MESA_configless_context",
DisplayExt::MESA_CONFIGLESS_CONTEXT,
),
(
"EGL_KHR_surfaceless_context",
DisplayExt::KHR_SURFACELESS_CONTEXT,
),
("EGL_IMG_context_priority", DisplayExt::IMG_CONTEXT_PRIORITY),
];
match get_dpy_extensions(dpy) {
Some(exts) => get_typed_ext(&exts, DisplayExt::empty(), &map),
_ => DisplayExt::empty(),
}
}
bitflags::bitflags! {
pub struct DeviceExt: u32 {
const MESA_DEVICE_SOFTWARE = 1 << 0;
const EXT_DEVICE_PERSISTENT_ID = 1 << 1;
const EXT_DEVICE_DRM = 1 << 2;
const EXT_DEVICE_DRM_RENDER_NODE = 1 << 3;
}
}
pub(super) unsafe fn get_device_ext(dev: EGLDeviceEXT) -> DeviceExt {
let map = [
("EGL_MESA_device_software", DeviceExt::MESA_DEVICE_SOFTWARE),
(
"EGL_EXT_device_persistent_id",
DeviceExt::EXT_DEVICE_PERSISTENT_ID,
),
("EGL_EXT_device_drm", DeviceExt::EXT_DEVICE_DRM),
(
"EGL_EXT_device_drm_render_node",
DeviceExt::EXT_DEVICE_DRM_RENDER_NODE,
),
];
let ext = PROCS.eglQueryDeviceStringEXT(dev, EGL_EXTENSIONS);
match get_extensions(ext) {
Some(exts) => get_typed_ext(&exts, DeviceExt::empty(), &map),
_ => DeviceExt::empty(),
}
}
bitflags::bitflags! {
pub struct GlExt: u32 {
const GL_OES_EGL_IMAGE = 1 << 0;
}
}
pub fn get_gl_ext() -> GlExt {
let map = [("GL_OES_EGL_image", GlExt::GL_OES_EGL_IMAGE)];
match unsafe { get_extensions(glGetString(GL_EXTENSIONS) as _) } {
Some(exts) => get_typed_ext(&exts, GlExt::empty(), &map),
_ => GlExt::empty(),
}
}

3
src/gles2/ext_proc.rs Normal file
View file

@ -0,0 +1,3 @@
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/egl_procs.rs"));

363
src/gles2/gl.rs Normal file
View file

@ -0,0 +1,363 @@
use crate::egl::{EglContext, EglImage, PROCS};
use crate::format::Format;
use crate::gles2::sys::{
glAttachShader, glBindFramebuffer, glBindRenderbuffer, glBindTexture, glCheckFramebufferStatus,
glClear, glClearColor, glCompileShader, glCreateProgram, glCreateShader, glDeleteFramebuffers,
glDeleteProgram, glDeleteRenderbuffers, glDeleteShader, glDeleteTextures, glDetachShader,
glDisable, glEnable, glFramebufferRenderbuffer, glFramebufferTexture2D, glGenFramebuffers,
glGenRenderbuffers, glGenTextures, glGetAttribLocation, glGetProgramiv, glGetShaderiv,
glGetUniformLocation, glLinkProgram, glPixelStorei, glScissor, glShaderSource, glTexImage2D,
glTexParameteri, glUseProgram, EGLContext, GLeglImageOES, GLenum, GLint, GLuint,
GL_CLAMP_TO_EDGE, GL_COLOR_ATTACHMENT0, GL_COLOR_BUFFER_BIT, GL_COMPILE_STATUS, GL_FALSE,
GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_LINEAR, GL_LINK_STATUS, GL_RENDERBUFFER,
GL_SCISSOR_TEST, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER,
GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT,
};
use crate::gles2::GlesError;
use crate::rect::Rect;
use crate::utils::ptr_ext::PtrExt;
use std::cell::Cell;
use std::ptr;
use std::rc::Rc;
use uapi::{ustr, Ustr};
pub struct GlTexture {
pub(super) ctx: Rc<EglContext>,
pub tex: GLuint,
pub width: i32,
pub height: i32,
}
impl GlTexture {
pub fn new(
ctx: &Rc<EglContext>,
format: &'static Format,
width: i32,
height: i32,
) -> Result<Rc<GlTexture>, GlesError> {
let tex = ctx.with_current(|| unsafe {
let mut tex = 0;
glGenTextures(1, &mut tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(
GL_TEXTURE_2D,
0,
format.gl_format,
width,
height,
0,
format.gl_format as _,
format.gl_type as _,
ptr::null(),
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
Ok(tex)
})?;
Ok(Rc::new(GlTexture {
ctx: ctx.clone(),
tex,
width,
height,
}))
}
pub unsafe fn to_framebuffer(self: &Rc<Self>) -> Result<Rc<GlFrameBuffer>, GlesError> {
self.ctx.with_current(|| unsafe {
let mut fbo = 0;
glGenFramebuffers(1, &mut fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
self.tex,
0,
);
let fb = GlFrameBuffer {
_rb: None,
_tex: Some(self.clone()),
ctx: self.ctx.clone(),
fbo,
width: self.width,
height: self.height,
};
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if status != GL_FRAMEBUFFER_COMPLETE {
return Err(GlesError::CreateFramebuffer);
}
Ok(Rc::new(fb))
})
}
pub fn import_texture(
ctx: &Rc<EglContext>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
height: i32,
stride: i32,
) -> Result<Rc<GlTexture>, GlesError> {
if (stride * height) as usize > data.len() {
return Err(GlesError::SmallImageBuffer);
}
let tex = ctx.with_current(|| unsafe {
let mut tex = 0;
glGenTextures(1, &mut tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format.bpp as GLint);
glTexImage2D(
GL_TEXTURE_2D,
0,
format.gl_format,
width,
height,
0,
format.gl_format as _,
format.gl_type as _,
data.as_ptr() as _,
);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
Ok(tex)
})?;
Ok(Rc::new(GlTexture {
ctx: ctx.clone(),
tex,
width,
height,
}))
}
}
impl Drop for GlTexture {
fn drop(&mut self) {
unsafe {
self.ctx.with_current(|| {
glDeleteTextures(1, &self.tex);
Ok(())
});
}
}
}
pub fn with_scissor<T, F: FnOnce() -> T>(scissor: &Rect, f: F) -> T {
return f();
#[thread_local]
static mut SCISSOR: *const Rect = ptr::null();
unsafe {
let prev = SCISSOR;
if prev.is_null() {
glEnable(GL_SCISSOR_TEST);
}
glScissor(
scissor.x1(),
scissor.x2(),
scissor.width(),
scissor.height(),
);
SCISSOR = scissor;
let res = f();
if prev.is_null() {
glDisable(GL_SCISSOR_TEST);
} else {
let prev = prev.deref();
glScissor(prev.x1(), prev.x2(), prev.width(), prev.height());
}
SCISSOR = prev;
res
}
}
pub struct GlShader {
_ctx: Rc<EglContext>,
shader: GLuint,
}
impl GlShader {
pub unsafe fn compile(ctx: &Rc<EglContext>, ty: GLenum, src: &str) -> Result<Self, GlesError> {
let shader = glCreateShader(ty);
let res = GlShader {
_ctx: ctx.clone(),
shader,
};
let len = src.len() as _;
glShaderSource(shader, 1, &(src.as_ptr() as _), &len);
glCompileShader(shader);
let mut ok = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &mut ok);
if ok == GL_FALSE as _ {
return Err(GlesError::ShaderCompileFailed);
}
Ok(res)
}
}
impl Drop for GlShader {
fn drop(&mut self) {
unsafe {
self._ctx.with_current(|| {
glDeleteShader(self.shader);
Ok(())
});
}
}
}
pub struct GlProgram {
_ctx: Rc<EglContext>,
prog: GLuint,
}
impl GlProgram {
pub unsafe fn link(vert: &GlShader, frag: &GlShader) -> Result<Self, GlesError> {
let res = GlProgram {
_ctx: vert._ctx.clone(),
prog: glCreateProgram(),
};
glAttachShader(res.prog, vert.shader);
glAttachShader(res.prog, frag.shader);
glLinkProgram(res.prog);
glDetachShader(res.prog, vert.shader);
glDetachShader(res.prog, frag.shader);
let mut ok = 0;
glGetProgramiv(res.prog, GL_LINK_STATUS, &mut ok);
if ok == GL_FALSE as _ {
return Err(GlesError::ProgramLink);
}
Ok(res)
}
pub unsafe fn get_uniform_location(&self, name: &Ustr) -> GLint {
glGetUniformLocation(self.prog, name.as_ptr() as _)
}
pub unsafe fn get_attrib_location(&self, name: &Ustr) -> GLint {
glGetAttribLocation(self.prog, name.as_ptr() as _)
}
pub unsafe fn use_(&self) {
glUseProgram(self.prog);
}
}
impl Drop for GlProgram {
fn drop(&mut self) {
unsafe {
self._ctx.with_current(|| {
glDeleteProgram(self.prog);
Ok(())
});
}
}
}
pub struct GlRenderBuffer {
pub img: Rc<EglImage>,
pub ctx: Rc<EglContext>,
rbo: GLuint,
}
impl GlRenderBuffer {
pub fn from_image(
img: &Rc<EglImage>,
ctx: &Rc<EglContext>,
) -> Result<Rc<GlRenderBuffer>, GlesError> {
ctx.with_current(|| unsafe {
let mut rbo = 0;
glGenRenderbuffers(1, &mut rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
PROCS.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, GLeglImageOES(img.img.0));
glBindRenderbuffer(GL_RENDERBUFFER, 0);
Ok(Rc::new(GlRenderBuffer {
img: img.clone(),
ctx: ctx.clone(),
rbo,
}))
})
}
pub fn create_framebuffer(self: &Rc<Self>) -> Result<Rc<GlFrameBuffer>, GlesError> {
self.ctx.with_current(|| unsafe {
let mut fbo = 0;
glGenFramebuffers(1, &mut fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
self.rbo,
);
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
let fb = GlFrameBuffer {
_rb: Some(self.clone()),
_tex: None,
ctx: self.ctx.clone(),
fbo,
width: self.img.width,
height: self.img.height,
};
if status != GL_FRAMEBUFFER_COMPLETE {
return Err(GlesError::CreateFramebuffer);
}
Ok(Rc::new(fb))
})
}
}
impl Drop for GlRenderBuffer {
fn drop(&mut self) {
self.ctx.with_current(|| {
unsafe {
glDeleteRenderbuffers(1, &self.rbo);
}
Ok(())
});
}
}
pub struct GlFrameBuffer {
pub _rb: Option<Rc<GlRenderBuffer>>,
pub _tex: Option<Rc<GlTexture>>,
pub ctx: Rc<EglContext>,
pub width: i32,
pub height: i32,
fbo: GLuint,
}
impl GlFrameBuffer {
pub unsafe fn bind(&self) {
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo);
}
pub fn clear(&self, r: f32, g: f32, b: f32, a: f32) -> Result<(), GlesError> {
self.ctx.with_current(|| unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo);
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT);
// glFlush();
Ok(())
})
}
}
impl Drop for GlFrameBuffer {
fn drop(&mut self) {
self.ctx.with_current(|| {
unsafe {
glDeleteFramebuffers(1, &self.fbo);
}
Ok(())
});
}
}

58
src/gles2/mod.rs Normal file
View file

@ -0,0 +1,58 @@
pub mod egl;
pub mod ext;
mod ext_proc;
pub mod gl;
pub mod sys;
use crate::drm::drm::DrmError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum GlesError {
#[error("EGL library does not support `EGL_EXT_platform_base`")]
ExtPlatformBase,
#[error("Could not compile a shader")]
ShaderCompileFailed,
#[error("Could not link a program")]
ProgramLink,
#[error("Could not bind to `EGL_OPENGL_ES_API`")]
BindFailed,
#[error("EGL library does not support device enumeration")]
DeviceEnumeration,
#[error("EGL library does not support device querying")]
DeviceQuery,
#[error("`eglQueryDeviceStringEXT` failed")]
DeviceQueryString,
#[error("`eglCreateContext` failed")]
CreateContext,
#[error("`eglMakeCurrent` failed")]
MakeCurrent,
#[error("`eglCreateImageKHR` failed")]
CreateImage,
#[error("Image buffer is too small")]
SmallImageBuffer,
#[error("Binding a renderbuffer to a framebuffer failed")]
CreateFramebuffer,
#[error("`eglQueryDevicesEXT` failed")]
QueryDevices,
#[error("`eglGetPlatformDisplayEXT` failed")]
GetDisplay,
#[error("`eglInitialize` failed")]
Initialize,
#[error("EGL display does not support `EGL_EXT_image_dma_buf_import_modifiers`")]
DmaBufImport,
#[error("GLES driver does not support `GL_OES_EGL_image`")]
OesEglImage,
#[error("EGL display does not support `EGL_KHR_image_base`")]
ImageBase,
#[error(
"EGL display does not support `EGL_KHR_no_config_context` or `EGL_MESA_configless_context`"
)]
ConfiglessContext,
#[error("EGL display does not support `EGL_KHR_surfaceless_context`")]
SurfacelessContext,
#[error("`eglQueryDmaBufFormatsEXT` failed")]
QueryDmaBufFormats,
#[error(transparent)]
DrmError(#[from] DrmError),
}

256
src/gles2/sys.rs Normal file
View file

@ -0,0 +1,256 @@
pub use uapi::c;
macro_rules! egl_transparent {
($name:ident) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct $name(pub *mut u8);
impl $name {
#[allow(dead_code)]
pub const fn none() -> Self {
Self(std::ptr::null_mut())
}
#[allow(dead_code)]
pub fn is_none(self) -> bool {
self.0.is_null()
}
}
};
}
pub type EGLint = i32;
pub type EGLenum = c::c_uint;
pub type EGLBoolean = c::c_uint;
pub type EGLuint64KHR = u64;
pub type EGLAttrib = isize;
egl_transparent!(EGLDisplay);
egl_transparent!(EGLSurface);
egl_transparent!(EGLConfig);
egl_transparent!(EGLImageKHR);
egl_transparent!(EGLContext);
egl_transparent!(EGLClientBuffer);
egl_transparent!(EGLLabelKHR);
egl_transparent!(EGLDeviceEXT);
pub type EGLDEBUGPROCKHR = unsafe extern "C" fn(
error: EGLenum,
command: *const c::c_char,
message_type: EGLint,
thread_label: EGLLabelKHR,
object_label: EGLLabelKHR,
message: *const c::c_char,
);
pub const EGL_EXTENSIONS: EGLint = 0x3055;
pub const EGL_DEBUG_MSG_CRITICAL_KHR: EGLint = 0x33B9;
pub const EGL_DEBUG_MSG_ERROR_KHR: EGLint = 0x33BA;
pub const EGL_DEBUG_MSG_WARN_KHR: EGLint = 0x33BB;
pub const EGL_DEBUG_MSG_INFO_KHR: EGLint = 0x33BC;
pub const EGL_TRUE: EGLBoolean = 1;
pub const EGL_FALSE: EGLBoolean = 0;
pub const EGL_NONE: EGLint = 0x3038;
pub const EGL_SUCCESS: EGLint = 0x3000;
pub const EGL_NOT_INITIALIZED: EGLint = 0x3001;
pub const EGL_BAD_ACCESS: EGLint = 0x3002;
pub const EGL_BAD_ALLOC: EGLint = 0x3003;
pub const EGL_BAD_ATTRIBUTE: EGLint = 0x3004;
pub const EGL_BAD_CONFIG: EGLint = 0x3005;
pub const EGL_BAD_CONTEXT: EGLint = 0x3006;
pub const EGL_BAD_CURRENT_SURFACE: EGLint = 0x3007;
pub const EGL_BAD_DISPLAY: EGLint = 0x3008;
pub const EGL_BAD_MATCH: EGLint = 0x3009;
pub const EGL_BAD_NATIVE_PIXMAP: EGLint = 0x300A;
pub const EGL_BAD_NATIVE_WINDOW: EGLint = 0x300B;
pub const EGL_BAD_PARAMETER: EGLint = 0x300C;
pub const EGL_BAD_SURFACE: EGLint = 0x300D;
pub const EGL_CONTEXT_LOST: EGLint = 0x300E;
pub const EGL_BAD_DEVICE_EXT: EGLint = 0x322B;
pub const EGL_OPENGL_ES_API: EGLenum = 0x30A0;
pub const EGL_DRM_DEVICE_FILE_EXT: EGLint = 0x3233;
pub const EGL_PLATFORM_DEVICE_EXT: EGLint = 0x313F;
pub const EGL_CONTEXT_CLIENT_VERSION: EGLint = 0x3098;
pub const EGL_WIDTH: EGLint = 0x3057;
pub const EGL_HEIGHT: EGLint = 0x3056;
pub const EGL_LINUX_DRM_FOURCC_EXT: EGLint = 0x3271;
pub const EGL_DMA_BUF_PLANE0_FD_EXT: EGLint = 0x3272;
pub const EGL_DMA_BUF_PLANE0_OFFSET_EXT: EGLint = 0x3273;
pub const EGL_DMA_BUF_PLANE0_PITCH_EXT: EGLint = 0x3274;
pub const EGL_DMA_BUF_PLANE1_FD_EXT: EGLint = 0x3275;
pub const EGL_DMA_BUF_PLANE1_OFFSET_EXT: EGLint = 0x3276;
pub const EGL_DMA_BUF_PLANE1_PITCH_EXT: EGLint = 0x3277;
pub const EGL_DMA_BUF_PLANE2_FD_EXT: EGLint = 0x3278;
pub const EGL_DMA_BUF_PLANE2_OFFSET_EXT: EGLint = 0x3279;
pub const EGL_DMA_BUF_PLANE2_PITCH_EXT: EGLint = 0x327A;
pub const EGL_DMA_BUF_PLANE3_FD_EXT: EGLint = 0x3440;
pub const EGL_DMA_BUF_PLANE3_OFFSET_EXT: EGLint = 0x3441;
pub const EGL_DMA_BUF_PLANE3_PITCH_EXT: EGLint = 0x3442;
pub const EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT: EGLint = 0x3443;
pub const EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT: EGLint = 0x3444;
pub const EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT: EGLint = 0x3445;
pub const EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT: EGLint = 0x3446;
pub const EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT: EGLint = 0x3447;
pub const EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT: EGLint = 0x3448;
pub const EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT: EGLint = 0x3449;
pub const EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT: EGLint = 0x344A;
pub const EGL_IMAGE_PRESERVED_KHR: EGLint = 0x30D2;
pub const EGL_LINUX_DMA_BUF_EXT: EGLint = 0x3270;
pub type GLenum = c::c_uint;
pub type GLbitfield = c::c_uint;
pub type GLfloat = f32;
pub type GLubyte = u8;
pub type GLsizei = c::c_int;
pub type GLuint = c::c_uint;
pub type GLint = c::c_int;
pub type GLchar = c::c_char;
pub type GLboolean = c::c_uchar;
egl_transparent!(GLeglImageOES);
pub const GL_EXTENSIONS: GLenum = 0x1F03;
pub const GL_RENDERBUFFER: GLenum = 0x8D41;
pub const GL_FRAMEBUFFER: GLenum = 0x8D40;
pub const GL_COLOR_ATTACHMENT0: GLenum = 0x8CE0;
pub const GL_FRAMEBUFFER_COMPLETE: GLenum = 0x8CD5;
pub const GL_COLOR_BUFFER_BIT: GLbitfield = 0x00004000;
pub const GL_TEXTURE_2D: GLenum = 0x0DE1;
pub const GL_TEXTURE_WRAP_S: GLenum = 0x2802;
pub const GL_TEXTURE_WRAP_T: GLenum = 0x2803;
pub const GL_CLAMP_TO_EDGE: GLint = 0x812F;
pub const GL_UNPACK_ROW_LENGTH_EXT: GLenum = 0x0CF2;
pub const GL_BGRA_EXT: GLint = 0x80E1;
pub const GL_UNSIGNED_BYTE: GLint = 0x1401;
pub const GL_SCISSOR_TEST: GLenum = 0x0C11;
pub const GL_COMPILE_STATUS: GLenum = 0x8B81;
pub const GL_LINK_STATUS: GLenum = 0x8B82;
pub const GL_FALSE: GLboolean = 0;
pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30;
pub const GL_VERTEX_SHADER: GLenum = 0x8B31;
pub const GL_TEXTURE0: GLenum = 0x84C0;
pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801;
pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800;
pub const GL_LINEAR: GLint = 0x2601;
pub const GL_FLOAT: GLenum = 0x1406;
pub const GL_TRIANGLE_STRIP: GLenum = 0x0005;
#[link(name = "GLESv2")]
extern "C" {
pub fn glGetString(name: GLenum) -> *const u8;
pub fn glGenRenderbuffers(n: GLsizei, renderbuffers: *mut GLuint);
pub fn glDeleteRenderbuffers(n: GLsizei, renderbuffers: *const GLuint);
pub fn glBindRenderbuffer(target: GLenum, renderbuffer: GLuint);
pub fn glGenFramebuffers(n: GLsizei, framebuffers: *mut GLuint);
pub fn glDeleteFramebuffers(n: GLsizei, framebuffers: *const GLuint);
pub fn glBindFramebuffer(target: GLenum, framebuffer: GLuint);
pub fn glFramebufferRenderbuffer(
target: GLenum,
attachment: GLenum,
renderbuffertarget: GLenum,
renderbuffer: GLuint,
);
pub fn glFramebufferTexture2D(
target: GLenum,
attachment: GLenum,
textarget: GLenum,
texture: GLenum,
level: GLint,
);
pub fn glCheckFramebufferStatus(target: GLenum) -> GLenum;
pub fn glClear(mask: GLbitfield);
pub fn glClearColor(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat);
pub fn glFlush();
pub fn glGenTextures(n: GLsizei, textures: *mut GLuint);
pub fn glDeleteTextures(n: GLsizei, textures: *const GLuint);
pub fn glBindTexture(target: GLenum, texture: GLuint);
pub fn glTexParameteri(target: GLenum, pname: GLenum, param: GLint);
pub fn glPixelStorei(pname: GLenum, param: GLint);
pub fn glTexImage2D(
target: GLenum,
level: GLint,
internalformat: GLint,
width: GLsizei,
height: GLsizei,
border: GLint,
format: GLenum,
ty: GLenum,
pixels: *const c::c_void,
);
pub fn glScissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
pub fn glEnable(cap: GLenum);
pub fn glDisable(cap: GLenum);
pub fn glViewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
pub fn glCreateShader(ty: GLenum) -> GLuint;
pub fn glDeleteShader(shader: GLuint);
pub fn glShaderSource(
shader: GLuint,
count: GLsizei,
string: *const *const GLchar,
length: *const GLint,
);
pub fn glCompileShader(shader: GLuint);
pub fn glGetShaderiv(shader: GLuint, pname: GLenum, params: *mut GLint);
pub fn glCreateProgram() -> GLuint;
pub fn glDeleteProgram(prog: GLuint);
pub fn glAttachShader(prog: GLuint, shader: GLuint);
pub fn glDetachShader(prog: GLuint, shader: GLuint);
pub fn glLinkProgram(prog: GLuint);
pub fn glGetProgramiv(program: GLuint, pname: GLenum, params: *mut GLint);
pub fn glUseProgram(program: GLuint);
pub fn glGetUniformLocation(prog: GLuint, name: *const GLchar) -> GLint;
pub fn glGetAttribLocation(prog: GLuint, name: *const GLchar) -> GLint;
pub fn glUniform1i(location: GLint, v0: GLint);
pub fn glUniform1f(location: GLint, v0: GLfloat);
pub fn glVertexAttribPointer(
index: GLuint,
size: GLint,
ty: GLenum,
normalized: GLboolean,
stride: GLsizei,
pointer: *const u8,
);
pub fn glActiveTexture(texture: GLuint);
pub fn glEnableVertexAttribArray(idx: GLuint);
pub fn glDisableVertexAttribArray(idx: GLuint);
pub fn glDrawArrays(mode: GLenum, first: GLint, count: GLsizei);
}
#[link(name = "EGL")]
extern "C" {
pub fn eglQueryString(dpy: EGLDisplay, name: EGLint) -> *const c::c_char;
pub fn eglGetProcAddress(procname: *const c::c_char) -> *mut u8;
pub fn eglBindAPI(api: EGLenum) -> EGLBoolean;
pub fn eglTerminate(dpy: EGLDisplay) -> EGLBoolean;
pub fn eglInitialize(dpy: EGLDisplay, major: *mut EGLint, minor: *mut EGLint) -> EGLBoolean;
pub fn eglCreateContext(
dpy: EGLDisplay,
config: EGLConfig,
share_context: EGLContext,
attrib_list: *const EGLint,
) -> EGLContext;
pub fn eglDestroyContext(dpy: EGLDisplay, ctx: EGLContext) -> EGLBoolean;
pub fn eglMakeCurrent(
dpy: EGLDisplay,
draw: EGLSurface,
read: EGLSurface,
ctx: EGLContext,
) -> EGLBoolean;
}

View file

@ -3,11 +3,13 @@ mod types;
use crate::client::{Client, DynEventFormatter};
use crate::clientmem::{ClientMem, ClientMemOffset};
use crate::format::Format;
use crate::gles2::gl::GlTexture;
use crate::ifs::wl_surface::{WlSurface, WlSurfaceId};
use crate::object::{Interface, Object, ObjectId};
use crate::pixman;
use crate::rect::Rect;
use crate::utils::buffd::MsgParser;
use crate::utils::clonecell::CloneCell;
use crate::utils::copyhashmap::CopyHashMap;
use std::rc::Rc;
pub use types::*;
@ -23,10 +25,14 @@ pub struct WlBuffer {
pub client: Rc<Client>,
_offset: usize,
pub rect: Rect,
_stride: i32,
_format: &'static Format,
stride: i32,
format: &'static Format,
pub image: Rc<pixman::Image<ClientMemOffset>>,
mem: ClientMemOffset,
pub texture: CloneCell<Option<Rc<GlTexture>>>,
pub(super) surfaces: CopyHashMap<WlSurfaceId, Rc<WlSurface>>,
width: i32,
height: i32,
}
impl WlBuffer {
@ -52,8 +58,8 @@ impl WlBuffer {
return Err(WlBufferError::StrideTooSmall);
}
let image = pixman::Image::new(
mem,
format.pixman,
mem.clone(),
format.pixman.unwrap(),
width as u32,
height as u32,
stride as u32,
@ -63,13 +69,27 @@ impl WlBuffer {
client: client.clone(),
_offset: offset,
rect: Rect::new_sized(0, 0, width, height).unwrap(),
_stride: stride,
_format: format,
stride,
format,
image: Rc::new(image),
mem,
width,
height,
texture: CloneCell::new(None),
surfaces: Default::default(),
})
}
pub fn update_texture(&self) -> Result<(), WlBufferError> {
self.texture.set(None);
let ctx = self.client.state.egl.get().unwrap();
let tex = self.mem.access(|mem| {
GlTexture::import_texture(&ctx, mem, self.format, self.width, self.height, self.stride)
})??;
self.texture.set(Some(tex));
Ok(())
}
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> {
let _req: Destroy = self.client.parse(self, parser)?;
{

View file

@ -1,8 +1,10 @@
use crate::client::{ClientError, EventFormatter, RequestParser};
use crate::gles2::GlesError;
use crate::ifs::wl_buffer::{WlBuffer, RELEASE};
use crate::object::Object;
use crate::pixman::PixmanError;
use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError};
use crate::ClientMemError;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use thiserror::Error;
@ -17,8 +19,14 @@ pub enum WlBufferError {
DestroyError(#[from] DestroyError),
#[error("Pixman returned an error")]
PixmanError(#[source] Box<PixmanError>),
#[error("Could not access the client memory")]
ClientMemError(#[source] Box<ClientMemError>),
#[error("GLES could not import the client image")]
GlesError(#[source] Box<GlesError>),
}
efrom!(WlBufferError, PixmanError, PixmanError);
efrom!(WlBufferError, ClientMemError, ClientMemError);
efrom!(WlBufferError, GlesError, GlesError);
#[derive(Debug, Error)]
pub enum DestroyError {

View file

@ -8,7 +8,7 @@ use crate::client::{Client, ClientId, DynEventFormatter};
use crate::fixed::Fixed;
use crate::globals::{Global, GlobalName};
use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardId};
use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerId};
use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerId, POINTER_FRAME_SINCE_VERSION};
use crate::ifs::wl_seat::wl_touch::WlTouch;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::{XdgToplevel, XdgToplevelId};
use crate::ifs::wl_surface::WlSurface;
@ -139,12 +139,12 @@ impl WlSeatGlobal {
KeyState::Released => wl_pointer::RELEASED,
KeyState::Pressed => wl_pointer::PRESSED,
};
self.surface_pointer_event(surface, |p| p.button(0, 0, button, state));
self.surface_pointer_event(0, surface, |p| p.button(0, 0, button, state));
}
pub fn focus_surface(&self, surface: &Rc<WlSurface>) {
let pressed_keys: Vec<_> = self.pressed_keys.borrow().iter().copied().collect();
self.surface_kb_event(&surface, |k| {
self.surface_kb_event(0, &surface, |k| {
k.enter(0, surface.id, pressed_keys.clone())
});
let ModifierState {
@ -153,15 +153,13 @@ impl WlSeatGlobal {
mods_locked,
group,
} = self.kb_state.borrow().mods();
self.surface_kb_event(surface, |k| {
self.surface_kb_event(0, surface, |k| {
k.modifiers(0, mods_depressed, mods_latched, mods_locked, group)
});
}
pub fn unfocus_surface(&self, surface: &Rc<WlSurface>) {
self.surface_kb_event(surface, |k| {
k.leave(0, surface.id)
})
self.surface_kb_event(0, surface, |k| k.leave(0, surface.id))
}
fn focus_toplevel(&self, toplevel: &Rc<XdgToplevel>) {
@ -191,23 +189,25 @@ impl WlSeatGlobal {
self.set_new_position(x, y);
}
fn for_each_seat<C>(&self, client: ClientId, mut f: C)
fn for_each_seat<C>(&self, ver: u32, client: ClientId, mut f: C)
where
C: FnMut(&Rc<WlSeatObj>),
{
let bindings = self.bindings.borrow();
if let Some(hm) = bindings.get(&client) {
for seat in hm.values() {
f(seat);
if seat.version >= ver {
f(seat);
}
}
}
}
fn for_each_pointer<C>(&self, client: ClientId, mut f: C)
fn for_each_pointer<C>(&self, ver: u32, client: ClientId, mut f: C)
where
C: FnMut(&Rc<WlPointer>),
{
self.for_each_seat(client, |seat| {
self.for_each_seat(ver, client, |seat| {
let pointers = seat.pointers.lock();
for pointer in pointers.values() {
f(pointer);
@ -215,11 +215,11 @@ impl WlSeatGlobal {
})
}
fn for_each_kb<C>(&self, client: ClientId, mut f: C)
fn for_each_kb<C>(&self, ver: u32, client: ClientId, mut f: C)
where
C: FnMut(&Rc<WlKeyboard>),
{
self.for_each_seat(client, |seat| {
self.for_each_seat(ver, client, |seat| {
let keyboards = seat.keyboards.lock();
for keyboard in keyboards.values() {
f(keyboard);
@ -227,23 +227,23 @@ impl WlSeatGlobal {
})
}
fn surface_pointer_event<F>(&self, surface: &WlSurface, mut f: F)
fn surface_pointer_event<F>(&self, ver: u32, surface: &WlSurface, mut f: F)
where
F: FnMut(&Rc<WlPointer>) -> DynEventFormatter,
{
let client = &surface.client;
self.for_each_pointer(client.id, |p| {
self.for_each_pointer(ver, client.id, |p| {
client.event(f(p));
});
client.flush();
}
fn surface_kb_event<F>(&self, surface: &WlSurface, mut f: F)
fn surface_kb_event<F>(&self, ver: u32, surface: &WlSurface, mut f: F)
where
F: FnMut(&Rc<WlKeyboard>) -> DynEventFormatter,
{
let client = &surface.client;
self.for_each_kb(client.id, |p| {
self.for_each_kb(ver, client.id, |p| {
client.event(f(p));
});
client.flush();
@ -321,7 +321,7 @@ impl WlSeatGlobal {
}
pub fn leave_surface(&self, n: &WlSurface) {
self.surface_pointer_event(n, |p| p.leave(0, n.id));
self.surface_pointer_event(0, n, |p| p.leave(0, n.id));
}
pub fn enter_toplevel(&self, n: &Rc<XdgToplevel>) {
@ -329,12 +329,12 @@ impl WlSeatGlobal {
}
pub fn enter_surface(&self, n: &WlSurface, x: Fixed, y: Fixed) {
self.surface_pointer_event(n, |p| p.enter(0, n.id, x, y));
self.surface_pointer_event(0, n, |p| p.enter(0, n.id, x, y));
}
pub fn motion_surface(&self, n: &WlSurface, x: Fixed, y: Fixed) {
self.surface_pointer_event(n, |p| p.motion(0, x, y));
self.surface_pointer_event(n, |p| p.frame());
self.surface_pointer_event(0, n, |p| p.motion(0, x, y));
self.surface_pointer_event(POINTER_FRAME_SINCE_VERSION, n, |p| p.frame());
}
fn motion_event(&self, dx: Fixed, dy: Fixed) {
@ -370,8 +370,8 @@ impl WlSeatGlobal {
ScrollAxis::Horizontal => wl_pointer::HORIZONTAL_SCROLL,
ScrollAxis::Vertical => wl_pointer::VERTICAL_SCROLL,
};
self.surface_pointer_event(surface, |p| p.axis(0, axis, Fixed::from_int(delta)));
self.surface_pointer_event(surface, |p| p.frame());
self.surface_pointer_event(0, surface, |p| p.axis(0, axis, Fixed::from_int(delta)));
self.surface_pointer_event(POINTER_FRAME_SINCE_VERSION, surface, |p| p.frame());
}
fn scroll_event(&self, delta: i32, axis: ScrollAxis) {

View file

@ -40,6 +40,8 @@ const CONTINUOUS: u32 = 2;
#[allow(dead_code)]
const WHEEL_TILT: u32 = 3;
pub const POINTER_FRAME_SINCE_VERSION: u32 = 5;
id!(WlPointerId);
pub struct WlPointer {

View file

@ -1,6 +1,7 @@
mod types;
use crate::client::Client;
use crate::format::FORMATS;
use crate::globals::{Global, GlobalName};
use crate::ifs::wl_shm_pool::WlShmPool;
use crate::object::{Interface, Object, ObjectId};
@ -41,7 +42,7 @@ impl WlShmGlobal {
client: client.clone(),
});
client.add_client_obj(&obj)?;
for &format in client.state.formats.values() {
for format in FORMATS {
client.event(Box::new(FormatE {
obj: obj.clone(),
format,

View file

@ -65,7 +65,8 @@ pub(super) struct FormatE {
}
impl EventFormatter for FormatE {
fn format(self: Box<Self>, fmt: &mut MsgFormatter<'_>) {
fmt.header(self.obj.id, FORMAT).uint(self.format.id);
fmt.header(self.obj.id, FORMAT)
.uint(self.format.wl_id.unwrap_or(self.format.drm));
}
fn obj(&self) -> &dyn Object {
&*self.obj
@ -76,7 +77,8 @@ impl Debug for FormatE {
write!(
f,
"format(format: \"{}\" (0x{:x}))",
self.format.name, self.format.id
self.format.name,
self.format.wl_id.unwrap_or(self.format.drm),
)
}
}

View file

@ -2,6 +2,7 @@ mod types;
use crate::client::Client;
use crate::clientmem::ClientMem;
use crate::format::{formats, map_wayland_format_id};
use crate::ifs::wl_buffer::WlBuffer;
use crate::object::{Interface, Object, ObjectId};
use crate::utils::buffd::MsgParser;
@ -40,7 +41,8 @@ impl WlShmPool {
fn create_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), CreateBufferError> {
let req: CreateBuffer = self.client.parse(self, parser)?;
let format = match self.client.state.formats.get(&req.format) {
let drm_format = map_wayland_format_id(req.format);
let format = match formats().get(&drm_format) {
Some(f) => *f,
_ => return Err(CreateBufferError::InvalidFormat(req.format)),
};

View file

@ -2,9 +2,13 @@ mod types;
pub mod wl_subsurface;
pub mod xdg_surface;
use crate::backend::{KeyState, ScrollAxis};
use crate::client::{Client, RequestParser};
use crate::fixed::Fixed;
use crate::gles2::gl::GlTexture;
use crate::ifs::wl_buffer::WlBuffer;
use crate::ifs::wl_callback::WlCallback;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_surface::wl_subsurface::WlSubsurface;
use crate::object::{Interface, Object, ObjectId};
use crate::pixman::Region;
@ -20,9 +24,6 @@ use std::mem;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
pub use types::*;
use crate::backend::{KeyState, ScrollAxis};
use crate::fixed::Fixed;
use crate::ifs::wl_seat::WlSeatGlobal;
const DESTROY: u32 = 0;
const ATTACH: u32 = 1;
@ -342,6 +343,7 @@ impl WlSurface {
buffer.surfaces.remove(&self.id);
}
if let Some((dx, dy, buffer)) = buffer_change {
let _ = buffer.update_texture();
new_size = Some(buffer.rect);
self.buffer.set(Some(buffer));
self.buf_x.fetch_add(dx);

View file

@ -1,5 +1,8 @@
mod types;
use crate::backend::{KeyState, ScrollAxis};
use crate::fixed::Fixed;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_surface::{
CommitAction, CommitContext, StackElement, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceId,
};
@ -13,9 +16,6 @@ use std::cell::{Cell, RefCell};
use std::ops::Deref;
use std::rc::Rc;
pub use types::*;
use crate::backend::{KeyState, ScrollAxis};
use crate::fixed::Fixed;
use crate::ifs::wl_seat::WlSeatGlobal;
const DESTROY: u32 = 0;
const SET_POSITION: u32 = 1;

View file

@ -2,7 +2,8 @@
generic_associated_types,
type_alias_impl_trait,
never_type,
c_variadic
c_variadic,
thread_local
)]
#![allow(
clippy::len_zero,
@ -18,6 +19,7 @@ use crate::backends::xorg::{XorgBackend, XorgBackendError};
use crate::client::Clients;
use crate::clientmem::ClientMemError;
use crate::event_loop::EventLoopError;
use crate::gles2::egl;
use crate::globals::{AddGlobal, Globals};
use crate::ifs::wl_compositor::WlCompositorGlobal;
use crate::ifs::wl_data_device_manager::WlDataDeviceManagerGlobal;
@ -48,9 +50,11 @@ mod backend;
mod backends;
mod client;
mod clientmem;
mod drm;
mod event_loop;
mod fixed;
mod format;
mod gles2;
mod globals;
mod ifs;
mod object;
@ -96,6 +100,8 @@ enum MainError {
}
fn main_() -> Result<(), MainError> {
egl::init();
clientmem::init()?;
let el = EventLoop::new()?;
sighand::install(&el)?;
@ -111,11 +117,11 @@ fn main_() -> Result<(), MainError> {
let state = Rc::new(State {
eng: engine.clone(),
el: el.clone(),
egl: Default::default(),
wheel,
clients: Clients::new(),
next_name: NumCell::new(1),
globals,
formats: format::formats(),
output_ids: Default::default(),
root: Rc::new(DisplayNode::new(node_ids.next())),
node_ids,

335
src/render/gles.rs Normal file
View file

@ -0,0 +1,335 @@
use crate::egl::EglContext;
use crate::gles2::gl::{with_scissor, GlFrameBuffer, GlProgram, GlShader};
use crate::gles2::sys::{
glActiveTexture, glBindTexture, glClear, glClearColor, glDisableVertexAttribArray,
glDrawArrays, glEnableVertexAttribArray, glTexParameteri, glUniform1f, glUniform1i,
glVertexAttribPointer, GLint, GL_COLOR_BUFFER_BIT, GL_FALSE, GL_FLOAT, GL_FRAGMENT_SHADER,
GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_TRIANGLE_STRIP,
GL_VERTEX_SHADER,
};
use crate::gles2::GlesError;
use crate::ifs::wl_buffer::WlBuffer;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::ifs::wl_surface::WlSurface;
use crate::pixman::Image;
use crate::render::{Border, Renderer};
use crate::servermem::ServerMem;
use crate::tree::{
ContainerFocus, ContainerNode, ContainerSplit, CONTAINER_BORDER, CONTAINER_TITLE_HEIGHT,
};
use crate::tree::{FloatNode, OutputNode, WorkspaceNode};
use renderdoc::{RenderDoc, V100};
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
use uapi::ustr;
pub const RENDERDOC: bool = false;
pub struct GlesRenderer {
ctx: Rc<EglContext>,
renderdoc: Option<RefCell<RenderDoc<V100>>>,
tex_prog: GlProgram,
tex_prog_pos: GLint,
tex_prog_texcoord: GLint,
tex_prog_tex: GLint,
}
impl GlesRenderer {
pub unsafe fn new(ctx: &Rc<EglContext>) -> Result<Self, GlesError> {
let vert = GlShader::compile(ctx, GL_VERTEX_SHADER, include_str!("shaders/tex.vert.glsl"))?;
let frag = GlShader::compile(
ctx,
GL_FRAGMENT_SHADER,
include_str!("shaders/tex.frag.glsl"),
)?;
let prog = GlProgram::link(&vert, &frag)?;
Ok(Self {
ctx: ctx.clone(),
tex_prog_pos: prog.get_attrib_location(ustr!("pos")),
tex_prog_texcoord: prog.get_attrib_location(ustr!("texcoord")),
tex_prog_tex: prog.get_uniform_location(ustr!("tex")),
tex_prog: prog,
renderdoc: if RENDERDOC {
Some(RefCell::new(RenderDoc::new().unwrap()))
} else {
None
},
})
}
pub fn render_fb<'a>(&'a self, fb: &'a GlFrameBuffer) -> GlesImageRenderer<'a> {
if let Some(rd) = &self.renderdoc {
rd.borrow_mut()
.start_frame_capture(ptr::null(), ptr::null());
}
GlesImageRenderer {
renderer: self,
image: fb,
}
}
}
pub struct GlesImageRenderer<'a> {
renderer: &'a GlesRenderer,
image: &'a GlFrameBuffer,
}
impl Drop for GlesImageRenderer<'_> {
fn drop(&mut self) {
if let Some(rd) = &self.renderer.renderdoc {
rd.borrow_mut().end_frame_capture(ptr::null(), ptr::null());
}
}
}
const NON_COLOR: (u8, u8, u8) = (100, 100, 100);
const CHILD_COLOR: (u8, u8, u8) = (200, 200, 200);
const YES_COLOR: (u8, u8, u8) = (0, 0, 255);
fn focus_color(focus: ContainerFocus) -> (u8, u8, u8) {
match focus {
ContainerFocus::None => NON_COLOR,
ContainerFocus::Child => CHILD_COLOR,
ContainerFocus::Yes => YES_COLOR,
}
}
impl Renderer for GlesImageRenderer<'_> {
fn render_output(&mut self, output: &OutputNode) {
unsafe {
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
if let Some(ws) = output.workspace.get() {
self.render_workspace(&ws);
}
}
fn render_workspace(&mut self, workspace: &WorkspaceNode) {
if let Some(node) = workspace.container.get() {
self.render_container(&node, 0, 0)
}
}
fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
let cwidth = container.width.get();
let cheight = container.height.get();
let num_children = container.num_children();
if let Some(child) = container.mono_child.get() {
let space_per_child = cwidth / num_children as i32;
let mut rem = cwidth % num_children as i32;
let mut pos = x;
for child in container.children.iter() {
let (r, g, b) = focus_color(child.focus.get());
let mut width = space_per_child;
if rem > 0 {
rem -= 1;
width += 1;
}
// let _ = self.image.fill_rect(
// r,
// g,
// b,
// 255,
// pos,
// y,
// pos + width as i32,
// y + CONTAINER_TITLE_HEIGHT as i32,
// );
pos += width as i32;
}
with_scissor(&container.mono_body.get(), || {
let content = container.mono_content.get();
child.node.render(self, x + content.x1(), y + content.y1());
});
} else {
let split = container.split.get();
for (i, child) in container.children.iter().enumerate() {
let body = child.body.get();
if body.x1() >= cwidth || body.y1() >= cheight {
break;
}
let (r, g, b) = focus_color(child.focus.get());
// let _ = self.image.fill_rect(
// r,
// g,
// b,
// 255,
// x + body.x1(),
// y + body.y1() - CONTAINER_TITLE_HEIGHT,
// x + body.x2(),
// y + body.y1(),
// );
{
let mut x1 = x + body.x1();
let mut x2 = x + body.x2();
let mut y2 = y + body.y2();
let mut border = Border::empty();
if i < num_children {
if split == ContainerSplit::Horizontal {
border |= Border::RIGHT;
x2 += CONTAINER_BORDER;
} else if split == ContainerSplit::Vertical {
border |= Border::BOTTOM;
y2 += CONTAINER_BORDER;
}
}
if i > 0 && split == ContainerSplit::Horizontal {
border |= Border::LEFT;
x1 -= CONTAINER_BORDER;
}
// let _ = self.image.fill_inner_border(
// r,
// g,
// b,
// 255,
// x1,
// y + body.y1() - CONTAINER_TITLE_HEIGHT,
// x2,
// y2,
// CONTAINER_BORDER as i32,
// border,
// );
}
with_scissor(&body, || {
let content = child.content.get();
child.node.render(self, x + content.x1(), y + content.y1());
// self.image.fill_inner_border(
// 0,
// 0,
// 255,
// 255,
// x + body.x1(),
// y + body.y1(),
// x + body.x1() + body.width(),
// y + body.y1() + body.height(),
// 2,
// Border::all(),
// );
// self.image.fill_inner_border(
// 255,
// 0,
// 0,
// 255,
// x + content.x1(),
// y + content.y1(),
// x + content.x1() + content.width(),
// y + content.y1() + content.height(),
// 2,
// Border::all(),
// );
});
}
}
}
fn render_toplevel(&mut self, tl: &XdgToplevel, mut x: i32, mut y: i32) {
let surface = &tl.xdg.surface;
if let Some(geo) = tl.xdg.geometry() {
let (xt, yt) = geo.translate(x, y);
x = xt;
y = yt;
}
self.render_surface(surface, x, y);
}
fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32) {
let children = surface.children.borrow();
let buffer = match surface.buffer.get() {
Some(b) => b,
_ => {
log::warn!("surface has no buffer attached");
return;
}
};
if let Some(children) = children.deref() {
macro_rules! render {
($children:expr) => {
for child in $children.rev_iter() {
if child.pending.get() {
continue;
}
let pos = child.sub_surface.position.get();
self.render_surface(&child.sub_surface.surface, x + pos.x1(), y + pos.y1());
}
};
}
render!(&children.above);
self.render_buffer(&buffer, x, y);
render!(&children.below);
} else {
self.render_buffer(&buffer, x, y);
}
let mut fr = surface.frame_requests.borrow_mut();
for cb in fr.drain(..) {
surface.client.dispatch_frame_requests.push(cb);
}
}
fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32) {
let texture = match buffer.texture.get() {
Some(t) => t,
_ => return,
};
unsafe {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
self.renderer.tex_prog.use_();
glUniform1i(self.renderer.tex_prog_tex, 0);
let texcoord: [f32; 8] = [1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0];
let f_width = self.image.width as f32;
let f_height = self.image.height as f32;
let x1 = 2.0 * (x as f32 / f_width) - 1.0;
let y1 = 2.0 * (y as f32 / f_height) - 1.0;
let x2 = 2.0 * ((x + texture.width) as f32 / f_width) - 1.0;
let y2 = 2.0 * ((y + texture.height) as f32 / f_height) - 1.0;
let pos: [f32; 8] = [x2, y1, x1, y1, x2, y2, x1, y2];
glVertexAttribPointer(
self.renderer.tex_prog_texcoord as _,
2,
GL_FLOAT,
GL_FALSE,
0,
texcoord.as_ptr() as _,
);
glVertexAttribPointer(
self.renderer.tex_prog_pos as _,
2,
GL_FLOAT,
GL_FALSE,
0,
pos.as_ptr() as _,
);
glEnableVertexAttribArray(self.renderer.tex_prog_texcoord as _);
glEnableVertexAttribArray(self.renderer.tex_prog_pos as _);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(self.renderer.tex_prog_texcoord as _);
glDisableVertexAttribArray(self.renderer.tex_prog_pos as _);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) {
if let Some(child) = floating.child.get() {
child.render(self, x, y)
}
}
}

View file

@ -4,6 +4,7 @@ use crate::ifs::wl_surface::WlSurface;
use crate::tree::ContainerNode;
use crate::tree::{FloatNode, OutputNode, WorkspaceNode};
pub mod gles;
pub mod pixman;
bitflags::bitflags! {

View file

@ -75,9 +75,7 @@ impl Renderer for PixmanRenderer<'_> {
}
self.image.with_clip(container.mono_body.get(), || {
let content = container.mono_content.get();
child
.node
.render(self, x + content.x1(), y + content.y1());
child.node.render(self, x + content.x1(), y + content.y1());
});
} else {
let split = container.split.get();
@ -131,8 +129,30 @@ impl Renderer for PixmanRenderer<'_> {
self.image.with_clip(body, || {
let content = child.content.get();
child.node.render(self, x + content.x1(), y + content.y1());
self.image.fill_inner_border(0, 0, 255, 255, x + body.x1(), y + body.y1(), x + body.x1() + body.width(), y + body.y1() + body.height(), 2, Border::all());
self.image.fill_inner_border(255, 0, 0, 255, x + content.x1(), y + content.y1(), x + content.x1() + content.width(), y + content.y1() + content.height(), 2, Border::all());
self.image.fill_inner_border(
0,
0,
255,
255,
x + body.x1(),
y + body.y1(),
x + body.x1() + body.width(),
y + body.y1() + body.height(),
2,
Border::all(),
);
self.image.fill_inner_border(
255,
0,
0,
255,
x + content.x1(),
y + content.y1(),
x + content.x1() + content.width(),
y + content.y1() + content.height(),
2,
Border::all(),
);
});
}
}
@ -154,8 +174,8 @@ impl Renderer for PixmanRenderer<'_> {
Some(b) => b,
_ => {
log::warn!("surface has no buffer attached");
return
},
return;
}
};
if let Some(children) = children.deref() {
macro_rules! render {

View file

@ -0,0 +1,7 @@
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0);
}

View file

@ -0,0 +1,8 @@
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
gl_Position = vec4(pos, 0.0, 1.0);
v_texcoord = texcoord;
}

View file

@ -1,14 +1,15 @@
use crate::async_engine::{AsyncEngine, SpawnedFuture};
use crate::backend::{BackendEvent, OutputId, OutputIds, SeatId, SeatIds};
use crate::client::{Client, Clients};
use crate::egl::EglContext;
use crate::event_loop::EventLoop;
use crate::format::Format;
use crate::globals::{AddGlobal, Globals};
use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_surface::NoneSurfaceExt;
use crate::tree::{DisplayNode, NodeIds};
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::clonecell::CloneCell;
use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::linkedlist::LinkedList;
use crate::utils::numcell::NumCell;
@ -21,11 +22,11 @@ use std::rc::Rc;
pub struct State {
pub eng: Rc<AsyncEngine>,
pub el: Rc<EventLoop>,
pub egl: CloneCell<Option<Rc<EglContext>>>,
pub wheel: Rc<Wheel>,
pub clients: Clients,
pub next_name: NumCell<u32>,
pub globals: Globals,
pub formats: AHashMap<u32, &'static Format>,
pub output_ids: OutputIds,
pub seat_ids: SeatIds,
pub node_ids: NodeIds,

View file

@ -113,7 +113,11 @@ impl ContainerNode {
}
pub fn add_child_after(self: &Rc<Self>, prev: &dyn Node, new: Rc<dyn Node>) {
let node = self.child_nodes.borrow().get(&prev.id()).map(|n| n.to_ref());
let node = self
.child_nodes
.borrow()
.get(&prev.id())
.map(|n| n.to_ref());
if let Some(node) = node {
self.add_child_after_(&node, new);
return;
@ -185,7 +189,9 @@ impl ContainerNode {
body_size = body_size.min(remaining_content_size);
remaining_content_size -= body_size;
let (x1, y1, width, height) = match split {
ContainerSplit::Horizontal => (pos, CONTAINER_TITLE_HEIGHT, body_size, other_content_size),
ContainerSplit::Horizontal => {
(pos, CONTAINER_TITLE_HEIGHT, body_size, other_content_size)
}
_ => (0, pos, other_content_size, body_size),
};
let body = Rect::new_sized(x1, y1, width, height).unwrap();
@ -209,7 +215,13 @@ impl ContainerNode {
let (x1, y1, width, height, size) = match split {
ContainerSplit::Horizontal => {
let width = body.width() + add;
(pos, CONTAINER_TITLE_HEIGHT, width, other_content_size, width)
(
pos,
CONTAINER_TITLE_HEIGHT,
width,
other_content_size,
width,
)
}
_ => {
let height = body.height() + add;

View file

@ -238,7 +238,8 @@ impl Node for OutputNode {
}
fn change_size(self: Rc<Self>, width: i32, height: i32) {
self.position.set(Rect::new_sized(0, 0, width, height).unwrap());
self.position
.set(Rect::new_sized(0, 0, width, height).unwrap());
if let Some(c) = self.workspace.get() {
c.change_size(width, height);
}
@ -294,6 +295,7 @@ impl Node for FloatNode {
fn child_size_changed(&self, _child: &dyn Node, width: i32, height: i32) {
let pos = self.position.get();
self.position.set(Rect::new_sized(pos.x1(), pos.x2(), width, height).unwrap());
self.position
.set(Rect::new_sized(pos.x1(), pos.x2(), width, height).unwrap());
}
}

22
src/utils/bitflags.rs Normal file
View file

@ -0,0 +1,22 @@
pub trait BitflagsExt {
fn contains(self, other: Self) -> bool;
}
macro_rules! num {
($ty:ident) => {
impl BitflagsExt for $ty {
fn contains(self, other: Self) -> bool {
self & other != 0
}
}
};
}
num!(u8);
num!(u16);
num!(u32);
num!(u64);
num!(i8);
num!(i16);
num!(i32);
num!(i64);

21
src/utils/debug_fn.rs Normal file
View file

@ -0,0 +1,21 @@
use std::fmt::{Debug, Formatter};
pub fn debug_fn<F>(f: F) -> impl Debug
where
F: Fn(&mut Formatter<'_>) -> std::fmt::Result,
{
DebugFn { f }
}
struct DebugFn<F> {
f: F,
}
impl<F> Debug for DebugFn<F>
where
F: Fn(&mut Formatter<'_>) -> std::fmt::Result,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
(self.f)(f)
}
}

View file

@ -1,7 +1,9 @@
pub mod asyncevent;
pub mod bitflags;
pub mod buffd;
pub mod clonecell;
pub mod copyhashmap;
pub mod debug_fn;
pub mod errorfmt;
pub mod linkedlist;
pub mod numcell;