diff --git a/build.rs b/build.rs index 539db96c..389d107b 100644 --- a/build.rs +++ b/build.rs @@ -147,7 +147,8 @@ fn write_egl_procs(f: &mut W) -> anyhow::Result<()> { ]; writeln!(f, "use std::ptr;")?; - writeln!(f, "use super::sys::*;")?; + writeln!(f, "use super::gl::sys::*;")?; + writeln!(f, "use super::egl::sys::*;")?; writeln!(f)?; writeln!(f, "#[derive(Copy, Clone, Debug)]")?; writeln!(f, "pub struct ExtProc {{")?; @@ -156,6 +157,9 @@ fn write_egl_procs(f: &mut W) -> anyhow::Result<()> { } writeln!(f, "}}")?; writeln!(f)?; + writeln!(f, "unsafe impl Sync for ExtProc {{ }}")?; + writeln!(f, "unsafe impl Send for ExtProc {{ }}")?; + writeln!(f)?; writeln!(f, "impl ExtProc {{")?; writeln!(f, " pub fn load() -> Self {{")?; writeln!(f, " Self {{")?; diff --git a/src/backends/xorg/mod.rs b/src/backends/xorg/mod.rs index c6b27054..a6e4db1f 100644 --- a/src/backends/xorg/mod.rs +++ b/src/backends/xorg/mod.rs @@ -4,31 +4,21 @@ use crate::backend::{ 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}; -use crate::tree::Node; +use crate::render::{Framebuffer, RenderContext, RenderError}; +use crate::servermem::ServerMemError; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::ptr_ext::PtrExt; use crate::wheel::{WheelDispatcher, WheelId}; -use crate::{gles2, pixman, EventLoopError, NumCell, State, WheelError}; -use gles2::egl; +use crate::{EventLoopError, NumCell, State, WheelError}; use isnt::std_1::primitive::IsntConstPtrExt; use rand::Rng; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; use std::error::Error; -use std::ops::Deref; use std::ptr; use std::rc::Rc; use thiserror::Error; @@ -45,16 +35,12 @@ pub enum XorgBackendError { 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), - #[error(transparent)] - EglError(#[from] GlesError), + CreateEgl(#[source] RenderError), + #[error("Could not create a framebuffer from a dma-buf")] + CreateFramebuffer(#[source] RenderError), #[error("Could not select input events")] CannotSelectInputEvents(#[source] XcbError), #[error("Could not select present events")] @@ -69,10 +55,6 @@ pub enum XorgBackendError { WheelError(#[source] Box), #[error("Could not allocate and map image memory")] ServerMemError(#[source] Box), - #[error("Pixman returned an error")] - PixmanError(#[source] Box), - #[error("dupfd failed")] - DupfdFailed(#[source] std::io::Error), #[error("Could not create a window")] CreateWindow(#[source] XcbError), #[error("Could not set WM_CLASS")] @@ -86,7 +68,6 @@ pub enum XorgBackendError { } efrom!(XorgBackendError, EventLoopError, EventLoopError); efrom!(XorgBackendError, ServerMemError, ServerMemError); -efrom!(XorgBackendError, PixmanError, PixmanError); efrom!(XorgBackendError, WheelError, WheelError); struct XcbCon { @@ -221,8 +202,7 @@ pub struct XorgBackend { outputs: CopyHashMap>, seats: CopyHashMap>, mouse_seats: CopyHashMap>, - ctx: Rc, - renderer: GlesRenderer, + ctx: Rc, gbm: GbmDevice, r: Cell, g: Cell, @@ -251,24 +231,11 @@ impl XorgBackend { 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 ctx = match RenderContext::from_drm_device(&drm) { + Ok(r) => Rc::new(r), + Err(e) => return Err(XorgBackendError::CreateEgl(e)), + }; let fd = con.xcb.xcb_get_file_descriptor(con.c); @@ -283,7 +250,6 @@ impl XorgBackend { seats: Default::default(), mouse_seats: Default::default(), ctx: ctx.clone(), - renderer, gbm, r: Cell::new(0.0), g: Cell::new(0.0), @@ -311,7 +277,7 @@ impl XorgBackend { slf.query_devices(ffi::XCB_INPUT_DEVICE_ALL_MASTER as _)?; slf.handle_events()?; - state.egl.set(Some(ctx.clone())); + state.render_ctx.set(Some(ctx.clone())); Ok(slf) } @@ -337,12 +303,9 @@ impl XorgBackend { 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 fb = match self.ctx.dmabuf_fb(dma) { + Ok(f) => f, + Err(e) => return Err(XorgBackendError::CreateFramebuffer(e)), }; let pixmap = unsafe { let pixmap = self.con.xcb.xcb_generate_id(self.con.c); @@ -416,7 +379,6 @@ impl XorgBackend { serial: Default::default(), next_msc: Cell::new(0), next_image: Default::default(), - image: RefCell::new(None), cb: CloneCell::new(None), images, }); @@ -695,17 +657,7 @@ impl XorgBackend { 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(); + fb.render(&*node); } unsafe { @@ -890,7 +842,6 @@ impl XorgBackend { _ => return Ok(()), }; output.removed.set(true); - *output.image.borrow_mut() = None; output.changed(); Ok(()) } @@ -911,87 +862,12 @@ impl XorgBackend { unsafe { 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()); - } + 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()?; - } - Ok(()) - } - - fn render(&self) -> Result<(), XorgBackendError> { - return Ok(()); - let outputs = self.outputs.lock(); - for output in outputs.values() { - let image = output.image.borrow(); - let image = match image.deref() { - Some(i) => i, - None => continue, - }; - let _ = image.fill(0, 0, 0, 255); - let node = match self.state.root.outputs.get(&output.id) { - Some(n) => n, - _ => continue, - }; - let pos = node.position.get(); - let mut renderer = PixmanRenderer::new(image); - renderer.render_output(&node); - for floating in self.state.root.floaters.iter() { - let fpos = floating.position.get(); - if floating.visible.get() && fpos.intersects(&pos) { - let (x, y) = pos.translate(fpos.x1(), fpos.x2()); - floating.render(&mut renderer, x, y); - } - } - unsafe { - let cookie = - self.con - .xcb - .xcb_clear_area_checked(self.con.c, 0, output.window, 0, 0, 0, 0); - self.con.check_cookie(cookie)?; - } } Ok(()) } @@ -1009,8 +885,6 @@ impl EventLoopDispatcher for XorgBackend { impl WheelDispatcher for XorgBackend { fn dispatch(self: Rc) -> Result<(), Box> { - self.render()?; - self.handle_events()?; Ok(()) } } @@ -1033,13 +907,12 @@ struct XorgOutput { next_msc: Cell, next_image: NumCell, images: [XorgImage; 2], - image: RefCell>>>, cb: CloneCell>>, } struct XorgImage { pixmap: Cell, - fb: CloneCell>, + fb: CloneCell>, idle: Cell, render_on_idle: Cell, last_serial: Cell, diff --git a/src/clientmem.rs b/src/clientmem.rs index 8f4140bf..ffa8f415 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -1,4 +1,3 @@ -use crate::pixman::PixmanMemory; use std::cell::{Cell, UnsafeCell}; use std::mem::MaybeUninit; use std::ptr; @@ -165,14 +164,3 @@ pub fn init() -> Result<(), ClientMemError> { } } } - -unsafe impl PixmanMemory for ClientMemOffset { - type E = ClientMemError; - - fn access(&self, f: F) -> Result - where - F: FnOnce(&[Cell]) -> T, - { - ClientMemOffset::access(self, f) - } -} diff --git a/src/drm/drm.rs b/src/drm/drm.rs index 48bd1710..9322faab 100644 --- a/src/drm/drm.rs +++ b/src/drm/drm.rs @@ -11,8 +11,6 @@ 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")] diff --git a/src/format.rs b/src/format.rs index 4786e49b..f8c9a9f8 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,5 +1,5 @@ -use crate::gles2::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE}; use crate::pixman; +use crate::render::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE}; use ahash::AHashMap; use once_cell::sync::Lazy; diff --git a/src/gles2/egl.rs b/src/gles2/egl.rs deleted file mode 100644 index 3ed3e1a6..00000000 --- a/src/gles2/egl.rs +++ /dev/null @@ -1,487 +0,0 @@ -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 = Lazy::new(|| ExtProc::load()); - -#[thread_local] -pub(super) static EXTS: Lazy = 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, 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, 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, - dev: EglDevice, - dpy: EGLDisplay, -} - -#[derive(Debug, Clone)] -pub struct EglContext { - pub dpy: Rc, - 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) -> Result, 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, buf: &DmaBuf) -> Result, 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, - 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 Result>( - &self, - f: F, - ) -> Result { - unsafe { - if CURRENT == self.ctx { - return f(); - } - self.with_current_slow(f) - } - } - - #[cold] - unsafe fn with_current_slow Result>( - &self, - f: F, - ) -> Result { - 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, 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, 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, - } -} diff --git a/src/gles2/gl.rs b/src/gles2/gl.rs deleted file mode 100644 index 38431dc4..00000000 --- a/src/gles2/gl.rs +++ /dev/null @@ -1,363 +0,0 @@ -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, - pub tex: GLuint, - pub width: i32, - pub height: i32, -} - -impl GlTexture { - pub fn new( - ctx: &Rc, - format: &'static Format, - width: i32, - height: i32, - ) -> Result, 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) -> Result, 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, - data: &[Cell], - format: &'static Format, - width: i32, - height: i32, - stride: i32, - ) -> Result, 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>(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, - shader: GLuint, -} - -impl GlShader { - pub unsafe fn compile(ctx: &Rc, ty: GLenum, src: &str) -> Result { - 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, - prog: GLuint, -} - -impl GlProgram { - pub unsafe fn link(vert: &GlShader, frag: &GlShader) -> Result { - 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, - pub ctx: Rc, - rbo: GLuint, -} - -impl GlRenderBuffer { - pub fn from_image( - img: &Rc, - ctx: &Rc, - ) -> Result, 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) -> Result, 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>, - pub _tex: Option>, - pub ctx: Rc, - 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(()) - }); - } -} diff --git a/src/gles2/mod.rs b/src/gles2/mod.rs deleted file mode 100644 index 3bfdd783..00000000 --- a/src/gles2/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -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), -} diff --git a/src/ifs/wl_buffer/mod.rs b/src/ifs/wl_buffer/mod.rs index cc9cd70b..b9b16044 100644 --- a/src/ifs/wl_buffer/mod.rs +++ b/src/ifs/wl_buffer/mod.rs @@ -3,11 +3,10 @@ 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::render::Texture; use crate::utils::buffd::MsgParser; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; @@ -27,9 +26,8 @@ pub struct WlBuffer { pub rect: Rect, stride: i32, format: &'static Format, - pub image: Rc>, mem: ClientMemOffset, - pub texture: CloneCell>>, + pub texture: CloneCell>>, pub(super) surfaces: CopyHashMap>, width: i32, height: i32, @@ -57,13 +55,6 @@ impl WlBuffer { if (stride as u64) < min_row_size { return Err(WlBufferError::StrideTooSmall); } - let image = pixman::Image::new( - mem.clone(), - format.pixman.unwrap(), - width as u32, - height as u32, - stride as u32, - )?; Ok(Self { id, client: client.clone(), @@ -71,7 +62,6 @@ impl WlBuffer { rect: Rect::new_sized(0, 0, width, height).unwrap(), stride, format, - image: Rc::new(image), mem, width, height, @@ -82,9 +72,9 @@ impl WlBuffer { pub fn update_texture(&self) -> Result<(), WlBufferError> { self.texture.set(None); - let ctx = self.client.state.egl.get().unwrap(); + let ctx = self.client.state.render_ctx.get().unwrap(); let tex = self.mem.access(|mem| { - GlTexture::import_texture(&ctx, mem, self.format, self.width, self.height, self.stride) + ctx.shmem_texture(mem, self.format, self.width, self.height, self.stride) })??; self.texture.set(Some(tex)); Ok(()) diff --git a/src/ifs/wl_buffer/types.rs b/src/ifs/wl_buffer/types.rs index e8e48920..e84eac00 100644 --- a/src/ifs/wl_buffer/types.rs +++ b/src/ifs/wl_buffer/types.rs @@ -1,8 +1,7 @@ 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::render::RenderError; use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError}; use crate::ClientMemError; use std::fmt::{Debug, Formatter}; @@ -17,16 +16,13 @@ pub enum WlBufferError { StrideTooSmall, #[error("Could not handle a `destroy` request")] DestroyError(#[from] DestroyError), - #[error("Pixman returned an error")] - PixmanError(#[source] Box), #[error("Could not access the client memory")] ClientMemError(#[source] Box), #[error("GLES could not import the client image")] - GlesError(#[source] Box), + GlesError(#[source] Box), } -efrom!(WlBufferError, PixmanError, PixmanError); efrom!(WlBufferError, ClientMemError, ClientMemError); -efrom!(WlBufferError, GlesError, GlesError); +efrom!(WlBufferError, GlesError, RenderError); #[derive(Debug, Error)] pub enum DestroyError { diff --git a/src/ifs/wl_surface/mod.rs b/src/ifs/wl_surface/mod.rs index a32e11e6..9fd10269 100644 --- a/src/ifs/wl_surface/mod.rs +++ b/src/ifs/wl_surface/mod.rs @@ -5,7 +5,6 @@ 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; diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs index b2c1954a..9d1dd6b2 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/mod.rs @@ -367,7 +367,7 @@ impl Node for XdgToplevel { } } - fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) { + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { renderer.render_toplevel(self, x, y) } diff --git a/src/main.rs b/src/main.rs index 59e28360..94ccf89e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,6 @@ 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; @@ -27,6 +26,7 @@ use crate::ifs::wl_shm::WlShmGlobal; use crate::ifs::wl_subcompositor::WlSubcompositorGlobal; use crate::ifs::wl_surface::NoneSurfaceExt; use crate::ifs::xdg_wm_base::XdgWmBaseGlobal; +use crate::render::RenderError; use crate::sighand::SighandError; use crate::state::State; use crate::tree::{DisplayNode, NodeIds}; @@ -54,7 +54,6 @@ mod drm; mod event_loop; mod fixed; mod format; -mod gles2; mod globals; mod ifs; mod object; @@ -97,10 +96,12 @@ enum MainError { AsyncError(#[from] AsyncError), #[error("The xorg backend caused an error")] XorgBackendError(#[from] XorgBackendError), + #[error("The render backend caused an error")] + RenderError(#[from] RenderError), } fn main_() -> Result<(), MainError> { - egl::init(); + render::init()?; clientmem::init()?; let el = EventLoop::new()?; @@ -117,7 +118,7 @@ fn main_() -> Result<(), MainError> { let state = Rc::new(State { eng: engine.clone(), el: el.clone(), - egl: Default::default(), + render_ctx: Default::default(), wheel, clients: Clients::new(), next_name: NumCell::new(1), diff --git a/src/pixman/mod.rs b/src/pixman/mod.rs index a27c3348..0011b11b 100644 --- a/src/pixman/mod.rs +++ b/src/pixman/mod.rs @@ -2,13 +2,8 @@ mod consts; include!(concat!(env!("OUT_DIR"), "/pixman_tys.rs")); -use crate::rect::Rect; -use crate::render::Border; -use crate::ClientMemError; pub use consts::*; -use std::cell::Cell; use std::ptr; -use thiserror::Error; use uapi::c; #[link(name = "pixman-1")] @@ -26,37 +21,6 @@ extern "C" { fn pixman_region32_copy(dst: *mut Region, src: *const Region); fn pixman_region32_union(dst: *mut Region, a: *const Region, b: *const Region); fn pixman_region32_subtract(dst: *mut Region, a: *const Region, b: *const Region); - fn pixman_image_set_clip_region32(image: *mut PixmanImage, region: *const Region); - fn pixman_image_create_bits_no_clear( - format: PixmanFormat, - width: c::c_int, - height: c::c_int, - bits: *mut u32, - stride: c::c_int, - ) -> *mut PixmanImage; - fn pixman_image_unref(image: *mut PixmanImage) -> c::c_int; - // fn pixman_image_ref(image: *mut PixmanImage) -> *mut PixmanImage; - fn pixman_image_fill_boxes( - op: PixmanOp, - dest: *mut PixmanImage, - color: *const Color, - nboxes: c::c_int, - boxes: *const Box32, - ) -> c::c_int; - fn pixman_image_composite32( - op: PixmanOp, - src: *mut PixmanImage, - mask: *mut PixmanImage, - dest: *mut PixmanImage, - src_x: i32, - src_y: i32, - mask_x: i32, - mask_y: i32, - dest_x: i32, - dest_y: i32, - width: i32, - height: i32, - ); } #[repr(C)] @@ -144,244 +108,3 @@ impl Drop for Region { } } } - -pub unsafe trait PixmanMemory: Clone { - type E; - - fn access(&self, f: F) -> Result - where - F: FnOnce(&[Cell]) -> T; -} - -#[derive(Debug, Error)] -pub enum PixmanError { - #[error("The image size values cannot be represented in c_int")] - SizeOverflow, - #[error("The pixman memory does not contain enough memory to hold the image")] - InsufficientMemory, - #[error("The stride does not contain enough bytes to contain a row")] - RowOverflow, - #[error("Pixman images must be aligned at a 4 byte boundary")] - UnalignedMemory, - #[error("Internal pixman error")] - Internal, - #[error(transparent)] - ClientMemError(Box), -} -efrom!(PixmanError, ClientMemError, ClientMemError); - -impl From for PixmanError { - fn from(_: !) -> Self { - unreachable!() - } -} - -struct PixmanImage; - -pub struct Image { - data: *mut PixmanImage, - width: u32, - height: u32, - memory: T, - clip: Cell>, -} - -impl Image -where - PixmanError: From<::E>, -{ - pub fn new( - memory: T, - format: Format, - width: u32, - height: u32, - stride: u32, - ) -> Result { - let data = memory.access(|m| { - if format_bpp(format.raw()) as u64 * width as u64 > stride as u64 * 8 { - return Err(PixmanError::RowOverflow); - } - if (m.len() as u64) < height as u64 * stride as u64 { - return Err(PixmanError::InsufficientMemory); - } - let (width, height, stride) = - match (width.try_into(), height.try_into(), stride.try_into()) { - (Ok(w), Ok(h), Ok(s)) => (w, h, s), - _ => return Err(PixmanError::SizeOverflow), - }; - if m.as_ptr() as usize % 4 != 0 { - return Err(PixmanError::UnalignedMemory); - } - let data = unsafe { - pixman_image_create_bits_no_clear( - format.raw() as _, - width, - height, - m.as_ptr() as _, - stride, - ) - }; - if data.is_null() { - return Err(PixmanError::Internal); - } - Ok(data) - })??; - Ok(Self { - data, - width, - height, - memory, - clip: Cell::new(None), - }) - } - - pub fn with_clip(&self, clip: Rect, f: F) { - let region = Region::rect(clip.x1(), clip.y1(), clip.width(), clip.height()); - unsafe { - pixman_image_set_clip_region32(self.data, ®ion); - } - let region = self.clip.replace(Some(region)); - f(); - unsafe { - let region = region - .as_ref() - .map(|p| p as *const _) - .unwrap_or(ptr::null()); - pixman_image_set_clip_region32(self.data, region); - } - self.clip.set(region); - } - - pub fn fill(&self, r: u8, g: u8, b: u8, a: u8) -> Result<(), PixmanError> { - self.fill_rect(r, g, b, a, 0, 0, self.width as _, self.height as _) - } - - #[allow(clippy::too_many_arguments)] - pub fn fill_rect( - &self, - r: u8, - g: u8, - b: u8, - a: u8, - x1: i32, - y1: i32, - x2: i32, - y2: i32, - ) -> Result<(), PixmanError> { - self.memory.access(|_| { - let bx = Box32 { - x1: x1.max(0), - y1: y1.max(0), - x2: x2.min(self.width as i32), - y2: y2.min(self.height as i32), - }; - let color = Color { - red: (r as u16) << 8, - green: (g as u16) << 8, - blue: (b as u16) << 8, - alpha: (a as u16) << 8, - }; - unsafe { - pixman_image_fill_boxes(OP_SRC.raw() as PixmanOp, self.data, &color, 1, &bx); - } - })?; - Ok(()) - } - - #[allow(clippy::too_many_arguments)] - pub fn fill_inner_border( - &self, - r: u8, - g: u8, - b: u8, - a: u8, - x1: i32, - y1: i32, - x2: i32, - y2: i32, - width: i32, - borders: Border, - ) -> Result<(), PixmanError> { - self.memory.access(|_| { - let mut bx = [ - Box32 { x1, y1, x2, y2: y1 }, - Box32 { x1: x2, y1, x2, y2 }, - Box32 { x1, y1, x2: x1, y2 }, - Box32 { x1, y1: y2, x2, y2 }, - ]; - if borders.contains(Border::TOP) { - bx[0].y2 += width; - } - if borders.contains(Border::RIGHT) { - bx[1].x1 -= width; - } - if borders.contains(Border::LEFT) { - bx[2].x2 += width; - } - if borders.contains(Border::BOTTOM) { - bx[3].y1 -= width; - } - for bx in &mut bx { - bx.x1 = bx.x1.max(0).min(self.width as i32); - bx.x2 = bx.x2.max(0).min(self.width as i32); - bx.y1 = bx.y1.max(0).min(self.height as i32); - bx.y2 = bx.y2.max(0).min(self.height as i32); - } - let color = Color { - red: (r as u16) << 8, - green: (g as u16) << 8, - blue: (b as u16) << 8, - alpha: (a as u16) << 8, - }; - unsafe { - pixman_image_fill_boxes( - OP_SRC.raw() as PixmanOp, - self.data, - &color, - bx.len() as _, - bx.as_ptr(), - ); - } - })?; - Ok(()) - } - - pub fn add_image(&self, over: &Image, x: i32, y: i32) -> Result<(), PixmanError> - where - U: PixmanMemory, - PixmanError: From<::E>, - { - self.memory.access(|_| { - over.memory.access(|_| unsafe { - pixman_image_composite32( - OP_OVER.raw(), - over.data, - ptr::null_mut(), - self.data, - 0, - 0, - 0, - 0, - x, - y, - over.width as _, - over.height as _, - ); - }) - })??; - Ok(()) - } - - #[allow(dead_code)] - pub fn memory(&self) -> &T { - &self.memory - } -} - -impl Drop for Image { - fn drop(&mut self) { - unsafe { - pixman_image_unref(self.data); - } - } -} diff --git a/src/render/egl/context.rs b/src/render/egl/context.rs new file mode 100644 index 00000000..e1b0917c --- /dev/null +++ b/src/render/egl/context.rs @@ -0,0 +1,72 @@ +use crate::render::egl::display::EglDisplay; +use crate::render::egl::sys::{ + eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE, +}; +use crate::render::ext::GlExt; +use crate::render::gl::sys::{GLint, GLuint}; +use crate::render::RenderError; +use std::rc::Rc; + +#[derive(Debug, Clone)] +pub struct EglContext { + pub dpy: Rc, + pub ext: GlExt, + pub ctx: EGLContext, + + pub tex_prog: GLuint, + pub tex_tex: GLint, + pub tex_texcoord: GLint, + pub tex_pos: GLint, +} + +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 Result>( + &self, + f: F, + ) -> Result { + unsafe { + if CURRENT == self.ctx { + return f(); + } + self.with_current_slow(f) + } + } + + #[cold] + unsafe fn with_current_slow Result>( + &self, + f: F, + ) -> Result { + if eglMakeCurrent( + self.dpy.dpy, + EGLSurface::none(), + EGLSurface::none(), + self.ctx, + ) == EGL_FALSE + { + return Err(RenderError::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 + } +} diff --git a/src/render/egl/device.rs b/src/render/egl/device.rs new file mode 100644 index 00000000..0dabd04d --- /dev/null +++ b/src/render/egl/device.rs @@ -0,0 +1,99 @@ +use crate::format::{formats, Format}; +use crate::render::egl::display::EglDisplay; +use crate::render::egl::sys::{ + eglInitialize, EGLDeviceEXT, EGLDisplay, EGLint, EGL_PLATFORM_DEVICE_EXT, EGL_TRUE, +}; +use crate::render::egl::PROCS; +use crate::render::ext::{get_display_ext, DeviceExt, DisplayExt}; +use crate::render::RenderError; +use ahash::AHashMap; +use std::ffi::CStr; +use std::ptr; +use std::rc::Rc; + +#[derive(Debug, Copy, Clone)] +pub struct EglDevice { + pub exts: DeviceExt, + pub dev: EGLDeviceEXT, +} + +impl EglDevice { + pub fn query_string(&self, name: EGLint) -> Result<&'static CStr, RenderError> { + unsafe { + let res = PROCS.eglQueryDeviceStringEXT(self.dev, name); + if res.is_null() { + return Err(RenderError::DeviceQueryString); + } + Ok(CStr::from_ptr(res)) + } + } + + pub fn create_display(&self) -> Result, RenderError> { + unsafe { + let dpy = PROCS.eglGetPlatformDisplayEXT( + EGL_PLATFORM_DEVICE_EXT as _, + self.dev.0, + ptr::null(), + ); + if dpy.is_none() { + return Err(RenderError::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(RenderError::Initialize); + } + dpy.exts = get_display_ext(dpy.dpy); + if !dpy.exts.intersects(DisplayExt::KHR_IMAGE_BASE) { + return Err(RenderError::ImageBase); + } + if !dpy + .exts + .intersects(DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS) + { + return Err(RenderError::DmaBufImport); + } + if !dpy + .exts + .intersects(DisplayExt::KHR_NO_CONFIG_CONTEXT | DisplayExt::MESA_CONFIGLESS_CONTEXT) + { + return Err(RenderError::ConfiglessContext); + } + if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) { + return Err(RenderError::SurfacelessContext); + } + dpy.formats = query_formats(dpy.dpy)?; + + Ok(Rc::new(dpy)) + } + } +} + +unsafe fn query_formats(dpy: EGLDisplay) -> Result, RenderError> { + 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(RenderError::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(RenderError::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) +} diff --git a/src/render/egl/display.rs b/src/render/egl/display.rs new file mode 100644 index 00000000..ae4568fb --- /dev/null +++ b/src/render/egl/display.rs @@ -0,0 +1,147 @@ +use crate::drm::dma::DmaBuf; +use crate::drm::INVALID_MODIFIER; +use crate::format::Format; +use crate::render::egl::context::EglContext; +use crate::render::egl::device::EglDevice; +use crate::render::egl::image::EglImage; +use crate::render::egl::sys::{ + eglCreateContext, eglTerminate, EGLClientBuffer, EGLConfig, EGLContext, EGLDisplay, EGLint, + 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_HEIGHT, EGL_IMAGE_PRESERVED_KHR, EGL_LINUX_DMA_BUF_EXT, + EGL_LINUX_DRM_FOURCC_EXT, EGL_NONE, EGL_TRUE, EGL_WIDTH, +}; +use crate::render::egl::PROCS; +use crate::render::ext::{get_gl_ext, DisplayExt, GlExt}; +use crate::render::RenderError; +use ahash::AHashMap; +use std::rc::Rc; + +#[derive(Debug, Clone)] +pub struct EglDisplay { + pub exts: DisplayExt, + pub formats: AHashMap, + pub dev: EglDevice, + pub dpy: EGLDisplay, +} + +impl EglDisplay { + pub fn create_context(self: &Rc) -> Result, RenderError> { + 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(RenderError::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, buf: &DmaBuf) -> Result, RenderError> { + 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(RenderError::CreateImage); + } + Ok(Rc::new(EglImage { + dpy: self.clone(), + img, + width: buf.width, + height: buf.height, + })) + } +} + +impl Drop for EglDisplay { + fn drop(&mut self) { + unsafe { + if eglTerminate(self.dpy) != EGL_TRUE { + log::warn!("`eglTerminate` failed"); + } + } + } +} diff --git a/src/render/egl/image.rs b/src/render/egl/image.rs new file mode 100644 index 00000000..62324f02 --- /dev/null +++ b/src/render/egl/image.rs @@ -0,0 +1,21 @@ +use crate::render::egl::display::EglDisplay; +use crate::render::egl::sys::{EGLImageKHR, EGL_FALSE}; +use crate::render::egl::PROCS; +use std::rc::Rc; + +pub struct EglImage { + pub dpy: Rc, + pub 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"); + } + } + } +} diff --git a/src/render/egl/mod.rs b/src/render/egl/mod.rs new file mode 100644 index 00000000..7811456c --- /dev/null +++ b/src/render/egl/mod.rs @@ -0,0 +1,171 @@ +use crate::drm::drm::Drm; +use crate::render::egl::device::EglDevice; +use crate::render::egl::sys::{ + eglBindAPI, EGLAttrib, EGLLabelKHR, EGLenum, EGLint, EGL_DEBUG_MSG_CRITICAL_KHR, + EGL_DEBUG_MSG_ERROR_KHR, EGL_DEBUG_MSG_INFO_KHR, EGL_DEBUG_MSG_WARN_KHR, + EGL_DRM_DEVICE_FILE_EXT, EGL_NONE, EGL_OPENGL_ES_API, EGL_TRUE, +}; +use crate::render::ext::{get_client_ext, get_device_ext, ClientExt, DeviceExt}; +use crate::render::proc::ExtProc; +use crate::render::RenderError; +use bstr::ByteSlice; +use log::Level; +use once_cell::sync::Lazy; +use std::ffi::CStr; +use std::ptr; +use sys::{ + 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_NOT_INITIALIZED, EGL_SUCCESS, +}; +use uapi::c; + +pub mod context; +pub mod device; +pub mod display; +pub mod image; +pub mod sys; + +pub(super) static PROCS: Lazy = Lazy::new(|| ExtProc::load()); + +pub(super) static EXTS: Lazy = Lazy::new(|| get_client_ext()); + +pub fn init() -> Result<(), RenderError> { + if !EXTS.contains(ClientExt::EXT_PLATFORM_BASE) { + return Err(RenderError::ExtPlatformBase); + } + if !EXTS.device_query() { + return Err(RenderError::DeviceQuery); + } + if !EXTS.device_enumeration() { + return Err(RenderError::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(RenderError::BindFailed); + } + Ok(()) +} + +pub fn find_drm_device(drm: &Drm) -> Result, RenderError> { + 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, RenderError> { + if !EXTS.device_enumeration() { + return Err(RenderError::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(RenderError::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(RenderError::QueryDevices); + } + devices.set_len(num_devices as usize); + Ok(devices + .into_iter() + .map(|d| EglDevice { + exts: get_device_ext(d), + dev: d, + }) + .collect()) + } +} + +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, + } +} diff --git a/src/render/egl/sys.rs b/src/render/egl/sys.rs new file mode 100644 index 00000000..ee5e21bb --- /dev/null +++ b/src/render/egl/sys.rs @@ -0,0 +1,102 @@ +use uapi::c; + +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; + +#[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; +} diff --git a/src/gles2/ext.rs b/src/render/ext.rs similarity index 96% rename from src/gles2/ext.rs rename to src/render/ext.rs index b961dd9f..ff31ae0a 100644 --- a/src/gles2/ext.rs +++ b/src/render/ext.rs @@ -1,7 +1,6 @@ -use crate::gles2::egl::PROCS; -use crate::gles2::sys::{ - eglQueryString, glGetString, EGLDeviceEXT, EGLDisplay, EGL_EXTENSIONS, GL_EXTENSIONS, -}; +use crate::render::egl::sys::{eglQueryString, EGLDeviceEXT, EGLDisplay, EGL_EXTENSIONS}; +use crate::render::egl::PROCS; +use crate::render::gl::sys::{glGetString, GL_EXTENSIONS}; use ahash::AHashSet; use bstr::ByteSlice; use std::ffi::CStr; diff --git a/src/render/gl/frame_buffer.rs b/src/render/gl/frame_buffer.rs new file mode 100644 index 00000000..6e62cadc --- /dev/null +++ b/src/render/gl/frame_buffer.rs @@ -0,0 +1,55 @@ +use crate::rect::Rect; +use crate::render::egl::context::EglContext; +use crate::render::gl::render_buffer::GlRenderBuffer; +use crate::render::gl::sys::{glDeleteFramebuffers, GLuint}; +use crate::render::gl::texture::GlTexture; +use crate::render::sys::{glDisable, glEnable, glScissor, GL_SCISSOR_TEST}; +use crate::utils::ptr_ext::PtrExt; +use std::ptr; +use std::rc::Rc; + +pub struct GlFrameBuffer { + pub _rb: Option>, + pub _tex: Option>, + pub ctx: Rc, + pub width: i32, + pub height: i32, + pub fbo: GLuint, +} + +impl Drop for GlFrameBuffer { + fn drop(&mut self) { + let _ = self.ctx.with_current(|| { + unsafe { + glDeleteFramebuffers(1, &self.fbo); + } + Ok(()) + }); + } +} + +pub unsafe fn with_scissor T>(scissor: &Rect, f: F) -> T { + #[thread_local] + static mut SCISSOR: *const Rect = ptr::null(); + + let prev = SCISSOR; + if prev.is_null() { + glEnable(GL_SCISSOR_TEST); + } + glScissor( + scissor.x1(), + scissor.y1(), + 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.y1(), prev.width(), prev.height()); + } + SCISSOR = prev; + res +} diff --git a/src/render/gl/mod.rs b/src/render/gl/mod.rs new file mode 100644 index 00000000..25accd2d --- /dev/null +++ b/src/render/gl/mod.rs @@ -0,0 +1,6 @@ +pub mod frame_buffer; +pub mod program; +pub mod render_buffer; +pub mod shader; +pub mod sys; +pub mod texture; diff --git a/src/render/gl/program.rs b/src/render/gl/program.rs new file mode 100644 index 00000000..0e5a0d70 --- /dev/null +++ b/src/render/gl/program.rs @@ -0,0 +1,66 @@ +use crate::render::egl::context::EglContext; +use crate::render::gl::shader::GlShader; +use crate::render::gl::sys::{ + glAttachShader, glCreateProgram, glDeleteProgram, glDetachShader, glGetAttribLocation, + glGetProgramiv, glGetUniformLocation, glLinkProgram, GLint, GLuint, GL_FALSE, GL_LINK_STATUS, +}; +use crate::render::gl::sys::{GL_FRAGMENT_SHADER, GL_VERTEX_SHADER}; +use crate::render::RenderError; +use std::rc::Rc; +use uapi::Ustr; + +pub struct GlProgram { + pub _ctx: Rc, + pub prog: GLuint, +} + +impl GlProgram { + pub unsafe fn from_shaders( + ctx: &Rc, + vert: &str, + frag: &str, + ) -> Result { + let vert = GlShader::compile(ctx, GL_VERTEX_SHADER, vert)?; + let frag = GlShader::compile(ctx, GL_FRAGMENT_SHADER, frag)?; + Self::link(&vert, &frag) + } + + pub unsafe fn link(vert: &GlShader, frag: &GlShader) -> Result { + 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(RenderError::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 _) + } +} + +impl Drop for GlProgram { + fn drop(&mut self) { + unsafe { + let _ = self._ctx.with_current(|| { + glDeleteProgram(self.prog); + Ok(()) + }); + } + } +} diff --git a/src/render/gl/render_buffer.rs b/src/render/gl/render_buffer.rs new file mode 100644 index 00000000..af799de9 --- /dev/null +++ b/src/render/gl/render_buffer.rs @@ -0,0 +1,72 @@ +use crate::render::egl::context::EglContext; +use crate::render::egl::image::EglImage; +use crate::render::egl::PROCS; +use crate::render::gl::frame_buffer::GlFrameBuffer; +use crate::render::gl::sys::{ + glBindFramebuffer, glBindRenderbuffer, glCheckFramebufferStatus, glDeleteRenderbuffers, + glFramebufferRenderbuffer, glGenFramebuffers, glGenRenderbuffers, GLeglImageOES, GLuint, + GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER, +}; +use crate::render::RenderError; +use std::rc::Rc; + +pub struct GlRenderBuffer { + pub img: Rc, + pub ctx: Rc, + rbo: GLuint, +} + +impl GlRenderBuffer { + pub unsafe fn from_image( + img: &Rc, + ctx: &Rc, + ) -> Result, RenderError> { + 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 unsafe fn create_framebuffer(self: &Rc) -> Result { + 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(RenderError::CreateFramebuffer); + } + Ok(fb) + } +} + +impl Drop for GlRenderBuffer { + fn drop(&mut self) { + let _ = self.ctx.with_current(|| { + unsafe { + glDeleteRenderbuffers(1, &self.rbo); + } + Ok(()) + }); + } +} diff --git a/src/render/gl/shader.rs b/src/render/gl/shader.rs new file mode 100644 index 00000000..7e4c8344 --- /dev/null +++ b/src/render/gl/shader.rs @@ -0,0 +1,45 @@ +use crate::render::egl::context::EglContext; +use crate::render::gl::sys::{ + glCompileShader, glCreateShader, glDeleteShader, glGetShaderiv, glShaderSource, GLenum, GLuint, + GL_COMPILE_STATUS, GL_FALSE, +}; +use crate::render::RenderError; +use std::rc::Rc; + +pub struct GlShader { + pub ctx: Rc, + pub shader: GLuint, +} + +impl GlShader { + pub unsafe fn compile( + ctx: &Rc, + ty: GLenum, + src: &str, + ) -> Result { + 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(RenderError::ShaderCompileFailed); + } + Ok(res) + } +} + +impl Drop for GlShader { + fn drop(&mut self) { + let _ = self.ctx.with_current(|| unsafe { + glDeleteShader(self.shader); + Ok(()) + }); + } +} diff --git a/src/gles2/sys.rs b/src/render/gl/sys.rs similarity index 50% rename from src/gles2/sys.rs rename to src/render/gl/sys.rs index 41b4c88e..7c10f777 100644 --- a/src/gles2/sys.rs +++ b/src/render/gl/sys.rs @@ -1,147 +1,43 @@ 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; +pub type GLchar = c::c_char; +pub type GLenum = c::c_uint; +pub type GLfloat = f32; +pub type GLint = c::c_int; +pub type GLsizei = c::c_int; +pub type GLubyte = u8; +pub type GLuint = c::c_uint; egl_transparent!(GLeglImageOES); -pub const GL_EXTENSIONS: GLenum = 0x1F03; -pub const GL_RENDERBUFFER: GLenum = 0x8D41; -pub const GL_FRAMEBUFFER: GLenum = 0x8D40; +pub const GL_BGRA_EXT: GLint = 0x80E1; +pub const GL_CLAMP_TO_EDGE: GLint = 0x812F; 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_COMPILE_STATUS: GLenum = 0x8B81; +pub const GL_EXTENSIONS: GLenum = 0x1F03; +pub const GL_FALSE: GLboolean = 0; +pub const GL_FLOAT: GLenum = 0x1406; +pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30; +pub const GL_FRAMEBUFFER_COMPLETE: GLenum = 0x8CD5; +pub const GL_FRAMEBUFFER: GLenum = 0x8D40; +pub const GL_LINEAR: GLint = 0x2601; +pub const GL_LINK_STATUS: GLenum = 0x8B82; +pub const GL_RENDERBUFFER: GLenum = 0x8D41; +pub const GL_SCISSOR_TEST: GLenum = 0x0C11; +pub const GL_TEXTURE0: GLenum = 0x84C0; pub const GL_TEXTURE_2D: GLenum = 0x0DE1; +pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800; +pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801; 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; +pub const GL_TRIANGLES: GLenum = 0x0004; +pub const GL_UNPACK_ROW_LENGTH_EXT: GLenum = 0x0CF2; +pub const GL_UNSIGNED_BYTE: GLint = 0x1401; +pub const GL_VERTEX_SHADER: GLenum = 0x8B31; #[link(name = "GLESv2")] extern "C" { @@ -217,6 +113,7 @@ extern "C" { 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 glUniform4f(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat); pub fn glVertexAttribPointer( index: GLuint, size: GLint, @@ -232,25 +129,3 @@ extern "C" { 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; -} diff --git a/src/render/gl/texture.rs b/src/render/gl/texture.rs new file mode 100644 index 00000000..fb2830df --- /dev/null +++ b/src/render/gl/texture.rs @@ -0,0 +1,138 @@ +use crate::format::Format; +use crate::render::egl::context::EglContext; +use crate::render::gl::frame_buffer::GlFrameBuffer; +use crate::render::gl::sys::{ + glBindFramebuffer, glBindTexture, glCheckFramebufferStatus, glDeleteTextures, + glFramebufferTexture2D, glGenFramebuffers, glGenTextures, glPixelStorei, glTexImage2D, + glTexParameteri, GLint, GLuint, GL_CLAMP_TO_EDGE, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER, + GL_FRAMEBUFFER_COMPLETE, GL_LINEAR, 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::render::RenderError; +use std::cell::Cell; +use std::ptr; +use std::rc::Rc; + +pub struct GlTexture { + pub(super) ctx: Rc, + pub tex: GLuint, + pub width: i32, + pub height: i32, +} + +impl GlTexture { + pub fn new( + ctx: &Rc, + format: &'static Format, + width: i32, + height: i32, + ) -> Result, RenderError> { + 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) -> Result, RenderError> { + 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(RenderError::CreateFramebuffer); + } + Ok(Rc::new(fb)) + }) + } + + pub fn import_texture( + ctx: &Rc, + data: &[Cell], + format: &'static Format, + width: i32, + height: i32, + stride: i32, + ) -> Result { + if (stride * height) as usize > data.len() { + return Err(RenderError::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(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(()) + }); + } + } +} diff --git a/src/render/gles.rs b/src/render/gles.rs deleted file mode 100644 index e45b8743..00000000 --- a/src/render/gles.rs +++ /dev/null @@ -1,335 +0,0 @@ -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, - - renderdoc: Option>>, - - tex_prog: GlProgram, - tex_prog_pos: GLint, - tex_prog_texcoord: GLint, - tex_prog_tex: GLint, -} - -impl GlesRenderer { - pub unsafe fn new(ctx: &Rc) -> Result { - 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) - } - } -} diff --git a/src/render/mod.rs b/src/render/mod.rs index 70bb1c18..0a317250 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,29 +1,92 @@ -use crate::ifs::wl_buffer::WlBuffer; -use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; -use crate::ifs::wl_surface::WlSurface; -use crate::tree::ContainerNode; -use crate::tree::{FloatNode, OutputNode, WorkspaceNode}; +macro_rules! egl_transparent { + ($name:ident) => { + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + #[repr(transparent)] + pub struct $name(pub *mut u8); -pub mod gles; -pub mod pixman; + impl $name { + #[allow(dead_code)] + pub const fn none() -> Self { + Self(std::ptr::null_mut()) + } -bitflags::bitflags! { - pub struct Border: u32 { - const NONE = 0b0000; - const LEFT = 0b0001; - const TOP = 0b0010; - const RIGHT = 0b0100; - const BOTTOM = 0b1000; - const ALL = 0b1111; - } + #[allow(dead_code)] + pub fn is_none(self) -> bool { + self.0.is_null() + } + } + }; } -pub trait Renderer { - fn render_output(&mut self, output: &OutputNode); - fn render_workspace(&mut self, workspace: &WorkspaceNode); - fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32); - fn render_toplevel(&mut self, toplevel: &XdgToplevel, x: i32, y: i32); - fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32); - fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32); - fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32); +use crate::drm::drm::DrmError; +pub use renderer::*; +use thiserror::Error; + +mod egl; +mod ext; +mod gl; +mod proc; +mod renderer; + +pub mod sys { + pub use super::egl::sys::*; + pub use super::gl::sys::*; +} + +pub fn init() -> Result<(), RenderError> { + egl::init() +} + +#[derive(Debug, Error)] +pub enum RenderError { + #[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), + #[error("Could not find the requested DRM device")] + UnknownDrmDevice, + #[error("The GLES driver does not support the XRGB8888 format")] + XRGB888, } diff --git a/src/render/pixman.rs b/src/render/pixman.rs deleted file mode 100644 index f60fa183..00000000 --- a/src/render/pixman.rs +++ /dev/null @@ -1,223 +0,0 @@ -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 std::ops::Deref; -use std::rc::Rc; - -pub struct PixmanRenderer<'a> { - image: &'a Image>, -} - -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<'a> PixmanRenderer<'a> { - pub fn new(image: &'a Image>) -> Self { - Self { image } - } -} - -impl Renderer for PixmanRenderer<'_> { - fn render_output(&mut self, output: &OutputNode) { - 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; - } - self.image.with_clip(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, - ); - } - 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(), - ); - }); - } - } - } - - 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) { - if let Err(e) = self.image.add_image(&buffer.image, x, y) { - let client = &buffer.client; - log::error!("Could not access client {} memory: {:#}", client.id, e); - if let Ok(d) = client.display() { - client.fatal_event( - d.implementation_error(format!("Could not access memory: {:#}", e)), - ); - } else { - client.state.clients.kill(client.id); - } - } - } - - fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) { - if let Some(child) = floating.child.get() { - child.render(self, x, y) - } - } -} diff --git a/src/gles2/ext_proc.rs b/src/render/proc.rs similarity index 100% rename from src/gles2/ext_proc.rs rename to src/render/proc.rs diff --git a/src/render/renderer/context.rs b/src/render/renderer/context.rs new file mode 100644 index 00000000..94756211 --- /dev/null +++ b/src/render/renderer/context.rs @@ -0,0 +1,104 @@ +use crate::drm::dma::DmaBuf; +use crate::drm::drm::Drm; +use crate::format::{Format, XRGB8888}; +use crate::render::egl::context::EglContext; +use crate::render::egl::find_drm_device; +use crate::render::gl::program::GlProgram; +use crate::render::gl::render_buffer::GlRenderBuffer; +use crate::render::gl::sys::GLint; +use crate::render::gl::texture::GlTexture; +use crate::render::renderer::framebuffer::Framebuffer; +use crate::render::renderer::RENDERDOC; +use crate::render::{RenderError, Texture}; +use renderdoc::{RenderDoc, V100}; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use uapi::ustr; + +pub struct RenderContext { + pub(super) ctx: Rc, + + pub(super) renderdoc: Option>>, + + pub(super) tex_prog: GlProgram, + pub(super) tex_prog_pos: GLint, + pub(super) tex_prog_texcoord: GLint, + pub(super) tex_prog_tex: GLint, + + pub(super) fill_prog: GlProgram, + pub(super) fill_prog_pos: GLint, + pub(super) fill_prog_color: GLint, +} + +impl RenderContext { + pub fn from_drm_device(drm: &Drm) -> Result { + let egl_dev = match find_drm_device(&drm)? { + Some(d) => d, + None => return Err(RenderError::UnknownDrmDevice), + }; + let dpy = egl_dev.create_display()?; + if !dpy.formats.contains_key(&XRGB8888.drm) { + return Err(RenderError::XRGB888); + } + let ctx = dpy.create_context()?; + ctx.with_current(|| unsafe { Self::new(&ctx) }) + } + + unsafe fn new(ctx: &Rc) -> Result { + let tex_prog = GlProgram::from_shaders( + ctx, + include_str!("../shaders/tex.vert.glsl"), + include_str!("../shaders/tex.frag.glsl"), + )?; + let fill_prog = GlProgram::from_shaders( + ctx, + include_str!("../shaders/fill.vert.glsl"), + include_str!("../shaders/fill.frag.glsl"), + )?; + Ok(Self { + ctx: ctx.clone(), + + tex_prog_pos: tex_prog.get_attrib_location(ustr!("pos")), + tex_prog_texcoord: tex_prog.get_attrib_location(ustr!("texcoord")), + tex_prog_tex: tex_prog.get_uniform_location(ustr!("tex")), + tex_prog, + + fill_prog_pos: fill_prog.get_attrib_location(ustr!("pos")), + fill_prog_color: fill_prog.get_uniform_location(ustr!("color")), + fill_prog, + + renderdoc: if RENDERDOC { + Some(RefCell::new(RenderDoc::new().unwrap())) + } else { + None + }, + }) + } + + pub fn dmabuf_fb(self: &Rc, buf: &DmaBuf) -> Result, RenderError> { + self.ctx.with_current(|| unsafe { + let img = self.ctx.dpy.import_dmabuf(buf)?; + let rb = GlRenderBuffer::from_image(&img, &self.ctx)?; + let fb = rb.create_framebuffer()?; + Ok(Rc::new(Framebuffer { + ctx: self.clone(), + gl: fb, + })) + }) + } + + pub fn shmem_texture( + self: &Rc, + data: &[Cell], + format: &'static Format, + width: i32, + height: i32, + stride: i32, + ) -> Result, RenderError> { + let gl = GlTexture::import_texture(&self.ctx, data, format, width, height, stride)?; + Ok(Rc::new(Texture { + ctx: self.clone(), + gl, + })) + } +} diff --git a/src/render/renderer/framebuffer.rs b/src/render/renderer/framebuffer.rs new file mode 100644 index 00000000..bb01fafb --- /dev/null +++ b/src/render/renderer/framebuffer.rs @@ -0,0 +1,40 @@ +use crate::render::gl::frame_buffer::GlFrameBuffer; +use crate::render::gl::sys::{ + glBindFramebuffer, glClear, glClearColor, glViewport, GL_COLOR_BUFFER_BIT, GL_FRAMEBUFFER, +}; +use crate::render::renderer::context::RenderContext; +use crate::render::renderer::renderer::Renderer; +use crate::tree::Node; +use std::ptr; +use std::rc::Rc; + +pub struct Framebuffer { + pub(super) ctx: Rc, + pub(super) gl: GlFrameBuffer, +} + +impl Framebuffer { + pub fn render(&self, node: &dyn Node) { + let _ = self.ctx.ctx.with_current(|| { + if let Some(rd) = &self.ctx.renderdoc { + rd.borrow_mut() + .start_frame_capture(ptr::null(), ptr::null()); + } + unsafe { + glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo); + glViewport(0, 0, self.gl.width, self.gl.height); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } + let mut renderer = Renderer { + renderer: &self.ctx, + image: &self.gl, + }; + node.render(&mut renderer, 0, 0); + if let Some(rd) = &self.ctx.renderdoc { + rd.borrow_mut().end_frame_capture(ptr::null(), ptr::null()); + } + Ok(()) + }); + } +} diff --git a/src/render/renderer/mod.rs b/src/render/renderer/mod.rs new file mode 100644 index 00000000..b7fefd68 --- /dev/null +++ b/src/render/renderer/mod.rs @@ -0,0 +1,11 @@ +pub use context::*; +pub use framebuffer::*; +pub use renderer::*; +pub use texture::*; + +mod context; +mod framebuffer; +mod renderer; +mod texture; + +pub const RENDERDOC: bool = true; diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs new file mode 100644 index 00000000..2f76cf7b --- /dev/null +++ b/src/render/renderer/renderer.rs @@ -0,0 +1,280 @@ +use crate::ifs::wl_buffer::WlBuffer; +use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; +use crate::ifs::wl_surface::WlSurface; +use crate::rect::Rect; +use crate::render::gl::frame_buffer::{with_scissor, GlFrameBuffer}; +use crate::render::gl::sys::{ + glActiveTexture, glBindTexture, glDisableVertexAttribArray, glDrawArrays, + glEnableVertexAttribArray, glTexParameteri, glUniform1i, glUniform4f, glUseProgram, + glVertexAttribPointer, GL_FALSE, GL_FLOAT, GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP, +}; +use crate::render::renderer::context::RenderContext; +use crate::tree::{ + ContainerFocus, ContainerNode, ContainerSplit, FloatNode, OutputNode, WorkspaceNode, + CONTAINER_BORDER, CONTAINER_TITLE_HEIGHT, +}; +use std::ops::Deref; +use std::rc::Rc; +use std::slice; + +const NON_COLOR: (f32, f32, f32) = (0.2, 0.2, 0.2); +const CHILD_COLOR: (f32, f32, f32) = (0.8, 0.8, 0.8); +const YES_COLOR: (f32, f32, f32) = (0.0, 0.0, 1.0); + +fn focus_color(focus: ContainerFocus) -> (f32, f32, f32) { + match focus { + ContainerFocus::None => NON_COLOR, + ContainerFocus::Child => CHILD_COLOR, + ContainerFocus::Yes => YES_COLOR, + } +} + +pub struct Renderer<'a> { + pub(super) renderer: &'a RenderContext, + pub(super) image: &'a GlFrameBuffer, +} + +impl Renderer<'_> { + pub fn render_output(&mut self, output: &OutputNode, x: i32, y: i32) { + if let Some(ws) = output.workspace.get() { + self.render_workspace(&ws, x, y); + } + } + + pub fn render_workspace(&mut self, workspace: &WorkspaceNode, x: i32, y: i32) { + if let Some(node) = workspace.container.get() { + self.render_container(&node, x, y) + } + } + + fn x_to_f(&self, x: i32) -> f32 { + 2.0 * (x as f32 / self.image.width as f32) - 1.0 + } + + fn y_to_f(&self, y: i32) -> f32 { + 2.0 * (y as f32 / self.image.height as f32) - 1.0 + } + + fn fill_boxes(&self, boxes: &[Rect], r: f32, g: f32, b: f32, a: f32) { + let mut pos = Vec::with_capacity(boxes.len() * 12); + for bx in boxes { + let x1 = self.x_to_f(bx.x1()); + let y1 = self.y_to_f(bx.y1()); + let x2 = self.x_to_f(bx.x2()); + let y2 = self.y_to_f(bx.y2()); + pos.extend_from_slice(&[ + // triangle 1 + x2, y1, // top right + x1, y1, // top left + x1, y2, // bottom left + // triangle 2 + x2, y1, // top right + x1, y2, // bottom left + x2, y2, // bottom right + ]); + } + unsafe { + glUseProgram(self.renderer.fill_prog.prog); + glUniform4f(self.renderer.fill_prog_color, r, g, b, a); + glVertexAttribPointer( + self.renderer.fill_prog_pos as _, + 2, + GL_FLOAT, + GL_FALSE, + 0, + pos.as_ptr() as _, + ); + glEnableVertexAttribArray(self.renderer.fill_prog_pos as _); + glDrawArrays(GL_TRIANGLES, 0, (boxes.len() * 6) as _); + glDisableVertexAttribArray(self.renderer.fill_prog_pos as _); + } + } + + pub 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(); + let title_rect = + Rect::new_sized(x, y, container.width.get(), CONTAINER_TITLE_HEIGHT).unwrap(); + 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; + let (r, g, b) = focus_color(ContainerFocus::None); + self.fill_boxes(slice::from_ref(&title_rect), r, g, b, 1.0); + for child in container.children.iter() { + let focus = child.focus.get(); + let (r, g, b) = focus_color(focus); + let mut width = space_per_child; + if rem > 0 { + rem -= 1; + width += 1; + } + if focus != ContainerFocus::None { + let rect = Rect::new_sized(pos, y, width, CONTAINER_TITLE_HEIGHT).unwrap(); + self.fill_boxes(slice::from_ref(&rect), r, g, b, 1.0); + } + pos += width as i32; + } + unsafe { + 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(); + let mut rects = Vec::with_capacity(num_children); + rects.push(title_rect); + for (i, child) in container.children.iter().enumerate() { + let body = child.body.get(); + if i + 1 < num_children { + let rect = if split == ContainerSplit::Horizontal { + Rect::new_sized( + x + body.x2(), + y + body.y1() - CONTAINER_TITLE_HEIGHT, + CONTAINER_BORDER, + container.height.get(), + ) + .unwrap() + } else { + Rect::new_sized( + x + body.x1(), + y + body.y2(), + container.width.get(), + CONTAINER_BORDER, + ) + .unwrap() + }; + rects.push(rect); + } + } + let (r, g, b) = focus_color(ContainerFocus::None); + self.fill_boxes(&rects, r, g, b, 1.0); + for child in container.children.iter() { + let body = child.body.get(); + if body.x1() >= cwidth || body.y1() >= cheight { + break; + } + unsafe { + with_scissor(&body, || { + let content = child.content.get(); + child.node.render(self, x + content.x1(), y + content.y1()); + }); + } + } + } + } + + pub 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); + } + + pub 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); + } + } + + pub fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32) { + let texture = match buffer.texture.get() { + Some(t) => t, + _ => return, + }; + assert!(Rc::ptr_eq(&self.renderer.ctx, &texture.ctx.ctx)); + unsafe { + glActiveTexture(GL_TEXTURE0); + + glBindTexture(GL_TEXTURE_2D, texture.gl.tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glUseProgram(self.renderer.tex_prog.prog); + + 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.gl.width) as f32 / f_width) - 1.0; + let y2 = 2.0 * ((y + texture.gl.height) as f32 / f_height) - 1.0; + + let pos: [f32; 8] = [ + x2, y1, // top right + x1, y1, // top left + x2, y2, // bottom right + x1, y2, // bottom left + ]; + + 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); + } + } + + pub fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) { + if let Some(child) = floating.child.get() { + child.render(self, x, y) + } + } +} diff --git a/src/render/renderer/texture.rs b/src/render/renderer/texture.rs new file mode 100644 index 00000000..83a01f13 --- /dev/null +++ b/src/render/renderer/texture.rs @@ -0,0 +1,8 @@ +use crate::render::gl::texture::GlTexture; +use crate::render::renderer::context::RenderContext; +use std::rc::Rc; + +pub struct Texture { + pub(super) ctx: Rc, + pub(super) gl: GlTexture, +} diff --git a/src/render/shaders/fill.frag.glsl b/src/render/shaders/fill.frag.glsl new file mode 100644 index 00000000..f75c8c87 --- /dev/null +++ b/src/render/shaders/fill.frag.glsl @@ -0,0 +1,6 @@ +precision mediump float; +uniform vec4 color; + +void main() { + gl_FragColor = color; +} diff --git a/src/render/shaders/fill.vert.glsl b/src/render/shaders/fill.vert.glsl new file mode 100644 index 00000000..c7e263de --- /dev/null +++ b/src/render/shaders/fill.vert.glsl @@ -0,0 +1,5 @@ +attribute vec2 pos; + +void main() { + gl_Position = vec4(pos, 0.0, 1.0); +} diff --git a/src/render/util.rs b/src/render/util.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/servermem.rs b/src/servermem.rs index a631739d..3496b6be 100644 --- a/src/servermem.rs +++ b/src/servermem.rs @@ -1,7 +1,5 @@ -use crate::pixman::PixmanMemory; use std::cell::Cell; use std::ptr; -use std::rc::Rc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; use thiserror::Error; @@ -80,14 +78,3 @@ impl Drop for ServerMem { } } } - -unsafe impl PixmanMemory for Rc { - type E = !; - - fn access(&self, f: F) -> Result - where - F: FnOnce(&[Cell]) -> T, - { - Ok(ServerMem::access(self, f)) - } -} diff --git a/src/state.rs b/src/state.rs index 685e2f7c..2a655b71 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,12 +1,12 @@ 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::globals::{AddGlobal, Globals}; use crate::ifs::wl_output::WlOutputGlobal; use crate::ifs::wl_seat::WlSeatGlobal; use crate::ifs::wl_surface::NoneSurfaceExt; +use crate::render::RenderContext; use crate::tree::{DisplayNode, NodeIds}; use crate::utils::asyncevent::AsyncEvent; use crate::utils::clonecell::CloneCell; @@ -22,7 +22,7 @@ use std::rc::Rc; pub struct State { pub eng: Rc, pub el: Rc, - pub egl: CloneCell>>, + pub render_ctx: CloneCell>>, pub wheel: Rc, pub clients: Clients, pub next_name: NumCell, diff --git a/src/tree/container.rs b/src/tree/container.rs index c30fd40f..eb749a7b 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -1,5 +1,5 @@ use crate::rect::Rect; -use crate::render::{Border, Renderer}; +use crate::render::Renderer; use crate::tree::{FoundNode, Node, NodeId, WorkspaceNode}; use crate::utils::clonecell::CloneCell; use crate::utils::linkedlist::{LinkedList, LinkedNode, NodeRef}; @@ -39,7 +39,6 @@ pub struct ContainerNode { pub height: Cell, pub content_width: Cell, pub content_height: Cell, - pub borders: Cell, num_children: NumCell, pub children: LinkedList, child_nodes: RefCell>>, @@ -59,11 +58,13 @@ impl ContainerChild { let body = self.body.get(); let width = content.width(); let height = content.height(); - let x1 = body.x1() + (body.width() - width) / 2; - let y1 = body.y1() + (body.height() - height) / 2; + // let x1 = body.x1() + (body.width() - width) / 2; + // let y1 = body.y1() + (body.height() - height) / 2; + let x1 = body.x1(); + let y1 = body.y1(); content = Rect::new_sized(x1, y1, width, height).unwrap(); - log::debug!("body: {:?}", body); - log::debug!("content: {:?}", content); + // log::debug!("body: {:?}", body); + // log::debug!("content: {:?}", content); self.content.set(content); } } @@ -93,7 +94,6 @@ impl ContainerNode { height: Cell::new(0), content_width: Cell::new(0), content_height: Cell::new(0), - borders: Cell::new(Border::NONE), num_children: NumCell::new(1), children, child_nodes: RefCell::new(child_nodes), @@ -145,20 +145,8 @@ impl ContainerNode { }), ); } - match self.split.get() { - ContainerSplit::Horizontal => { - let new_content_size = self.content_width.get().saturating_sub(CONTAINER_BORDER); - self.content_width.set(new_content_size); - } - ContainerSplit::Vertical => { - let new_content_size = self - .content_height - .get() - .saturating_sub(CONTAINER_BORDER + CONTAINER_TITLE_HEIGHT); - self.content_height.set(new_content_size); - } - } let num_children = self.num_children.fetch_add(1) + 1; + self.update_content_size(); let new_child_factor = 1.0 / num_children as f64; let mut sum_factors = 0.0; for child in self.children.iter() { @@ -198,7 +186,7 @@ impl ContainerNode { child.body.set(body); pos += body_size + CONTAINER_BORDER; if split == ContainerSplit::Vertical { - pos += body_size + CONTAINER_BORDER + CONTAINER_TITLE_HEIGHT; + pos += CONTAINER_TITLE_HEIGHT; } } if remaining_content_size > 0 { @@ -242,6 +230,29 @@ impl ContainerNode { child.position_content(); } } + + fn update_content_size(&self) { + let nc = self.num_children.get(); + match self.split.get() { + ContainerSplit::Horizontal => { + let new_content_size = self + .width + .get() + .saturating_sub((nc - 1) as i32 * CONTAINER_BORDER); + self.content_width.set(new_content_size); + self.content_height + .set(self.height.get().saturating_sub(CONTAINER_TITLE_HEIGHT)); + } + ContainerSplit::Vertical => { + let new_content_size = self.height.get().saturating_sub( + CONTAINER_TITLE_HEIGHT + + (nc - 1) as i32 * (CONTAINER_BORDER + CONTAINER_TITLE_HEIGHT), + ); + self.content_height.set(new_content_size); + self.content_width.set(self.width.get()); + } + } + } } impl Node for ContainerNode { @@ -295,6 +306,7 @@ impl Node for ContainerNode { self.parent.get().remove_child(self); return; } + self.update_content_size(); let rem = 1.0 - node.factor.get(); let mut sum = 0.0; if rem <= 0.0 { @@ -323,7 +335,7 @@ impl Node for ContainerNode { } } - fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) { + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { renderer.render_container(self, x, y); } @@ -338,22 +350,7 @@ impl Node for ContainerNode { fn change_size(self: Rc, width: i32, height: i32) { self.width.set(width); self.height.set(height); - let num_children = self.num_children.get(); - match self.split.get() { - ContainerSplit::Horizontal => { - self.content_width - .set(width.saturating_sub((num_children - 1) as i32 * CONTAINER_BORDER)); - self.content_height - .set(height.saturating_sub(CONTAINER_TITLE_HEIGHT)); - } - ContainerSplit::Vertical => { - self.content_width.set(width); - self.content_height.set(height.saturating_sub( - (num_children - 1) as i32 * CONTAINER_BORDER - + num_children as i32 * CONTAINER_TITLE_HEIGHT, - )); - } - } + self.update_content_size(); self.apply_factors(1.0); } } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 25802ed1..e3fd84a0 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -107,7 +107,7 @@ pub trait Node { let _ = y; } - fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) { + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { let _ = renderer; let _ = x; let _ = y; @@ -229,8 +229,8 @@ impl Node for OutputNode { } } - fn render(&self, renderer: &mut dyn Renderer, _x: i32, _y: i32) { - renderer.render_output(self); + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { + renderer.render_output(self, x, y); } fn remove_child(&self, _child: &dyn Node) { @@ -277,7 +277,7 @@ impl Node for FloatNode { self.workspace_link.set(None); } - fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) { + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { renderer.render_floating(self, x, y) } diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index 63d29ae0..421a8552 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -47,8 +47,8 @@ impl Node for WorkspaceNode { } } - fn render(&self, renderer: &mut dyn Renderer, _x: i32, _y: i32) { - renderer.render_workspace(self); + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { + renderer.render_workspace(self, x, y); } fn get_workspace(self: Rc) -> Option> {