use { crate::{ allocator::Allocator, cpu_worker::CpuWorker, format::{Format, XRGB8888}, gfx_api::{ AsyncShmGfxTexture, BufferResvUser, GfxApi, GfxBlendBuffer, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxInternalFramebuffer, ResetStatus, ShmGfxTexture, }, gfx_apis::gl::{ GfxGlState, RenderError, Texture, egl::{context::EglContext, display::EglDisplay, image::EglImage}, ext::GL_OES_EGL_IMAGE_EXTERNAL, gl::{ program::GlProgram, render_buffer::GlRenderBuffer, sys::GLint, texture::GlTexture, }, renderer::{framebuffer::Framebuffer, image::Image}, }, rect::Rect, video::{ dmabuf::DmaBuf, drm::{Drm, syncobj::SyncobjCtx}, gbm::GbmDevice, }, }, ahash::AHashMap, linearize::{Linearize, StaticMap, static_map}, std::{ cell::{Cell, RefCell}, ffi::CString, fmt::{Debug, Formatter}, rc::Rc, }, }; pub(crate) struct TexProg { pub(crate) prog: GlProgram, pub(crate) pos: GLint, pub(crate) texcoord: GLint, pub(crate) tex: GLint, pub(crate) alpha: GLint, } impl TexProg { unsafe fn from(prog: GlProgram, alpha_multiplier: bool) -> Self { unsafe { let alpha = match alpha_multiplier { true => prog.get_uniform_location(c"alpha"), false => 0, }; Self { pos: prog.get_attrib_location(c"pos"), texcoord: prog.get_attrib_location(c"texcoord"), tex: prog.get_uniform_location(c"tex"), alpha, prog, } } } } #[derive(Copy, Clone, PartialEq, Linearize)] pub(in crate::gfx_apis::gl) enum TexCopyType { Identity, Multiply, } #[derive(Copy, Clone, PartialEq, Linearize)] pub(in crate::gfx_apis::gl) enum TexSourceType { Opaque, HasAlpha, } pub(in crate::gfx_apis::gl) struct GlRenderContext { pub(crate) ctx: Rc, pub gbm: Rc, pub sync_ctx: Rc, pub(crate) render_node: Rc, pub(crate) tex_internal: StaticMap>, pub(crate) tex_external: Option>>, pub(crate) fill_prog: GlProgram, pub(crate) fill_prog_pos: GLint, pub(crate) fill_prog_color: GLint, pub(in crate::gfx_apis::gl) gl_state: RefCell, pub(in crate::gfx_apis::gl) buffer_resv_user: BufferResvUser, } impl Debug for GlRenderContext { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("RenderContext").finish_non_exhaustive() } } impl GlRenderContext { pub fn reset_status(&self) -> Option { self.ctx.reset_status() } pub(in crate::gfx_apis::gl) fn from_drm_device( drm: &Drm, software: bool, ) -> Result { let node = drm .get_render_node()? .ok_or(RenderError::NoRenderNode) .map(Rc::new)?; let dpy = EglDisplay::create(drm, software)?; if !dpy.formats.contains_key(&XRGB8888.drm) { return Err(RenderError::XRGB888); } let ctx = dpy.create_context()?; ctx.with_current(|| unsafe { Self::new(&ctx, &node) }) } unsafe fn new(ctx: &Rc, node: &Rc) -> Result { let tex_vert = include_str!("../shaders/tex.vert.glsl"); let tex_frag = include_str!("../shaders/tex.frag.glsl"); let create_programs = |external: bool| { let create_program = |alpha_multiplier: bool, alpha: bool| { let mut tex_frac_src = String::new(); if external { tex_frac_src.push_str("#define EXTERNAL\n"); } if alpha_multiplier { tex_frac_src.push_str("#define ALPHA_MULTIPLIER\n"); } if alpha { tex_frac_src.push_str("#define ALPHA\n"); } tex_frac_src.push_str(tex_frag); unsafe { let prog = GlProgram::from_shaders(ctx, tex_vert, &tex_frac_src)?; Ok::<_, RenderError>(TexProg::from(prog, alpha_multiplier)) } }; Ok::<_, RenderError>(static_map! { TexCopyType::Identity => static_map! { TexSourceType::Opaque => create_program(false, false)?, TexSourceType::HasAlpha => create_program(false, true)?, }, TexCopyType::Multiply => static_map! { TexSourceType::Opaque => create_program(true, false)?, TexSourceType::HasAlpha => create_program(true, true)?, }, }) }; let tex_internal = create_programs(false)?; let tex_external = if ctx.ext.contains(GL_OES_EGL_IMAGE_EXTERNAL) { Some(create_programs(true)?) } else { None }; let fill_prog = unsafe { GlProgram::from_shaders( ctx, include_str!("../shaders/fill.vert.glsl"), include_str!("../shaders/fill.frag.glsl"), )? }; Ok(Self { ctx: ctx.clone(), gbm: ctx.dpy.gbm.clone(), sync_ctx: Rc::new(SyncobjCtx::new(ctx.dpy.gbm.drm.fd())), render_node: node.clone(), tex_internal, tex_external, fill_prog_pos: unsafe { fill_prog.get_attrib_location(c"pos") }, fill_prog_color: unsafe { fill_prog.get_uniform_location(c"color") }, fill_prog, gl_state: Default::default(), buffer_resv_user: Default::default(), }) } pub fn render_node(&self) -> Rc { self.render_node.clone() } pub fn formats(&self) -> &Rc> { &self.ctx.formats } 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, })) }) } fn dmabuf_img(self: &Rc, buf: &DmaBuf) -> Result, RenderError> { self.ctx.with_current(|| { let img = self.ctx.dpy.import_dmabuf(buf)?; Ok(Rc::new(Image { ctx: self.clone(), gl: img, })) }) } fn shmem_texture( self: &Rc, data: &[Cell], format: &'static Format, width: i32, height: i32, stride: i32, ) -> Result, RenderError> { let gl = GlTexture::import_shm(&self.ctx, data, format, width, height, stride)?; Ok(Rc::new(Texture { ctx: self.clone(), gl, format, })) } pub fn image_to_fb( self: &Rc, img: &Rc, ) -> Result, RenderError> { self.ctx.with_current(|| unsafe { let rb = GlRenderBuffer::from_image(img, &self.ctx)?; let fb = rb.create_framebuffer()?; Ok(Rc::new(Framebuffer { ctx: self.clone(), gl: fb, })) }) } } impl GfxContext for GlRenderContext { fn reset_status(&self) -> Option { self.reset_status() } fn render_node(&self) -> Option> { Some(self.render_node()) } fn formats(&self) -> &Rc> { self.formats() } fn fast_ram_access(&self) -> bool { self.ctx.dpy.fast_ram_access } fn dmabuf_fb(self: Rc, buf: &DmaBuf) -> Result, GfxError> { (&self) .dmabuf_fb(buf) .map(|w| w as Rc) .map_err(|e| e.into()) } fn dmabuf_img(self: Rc, buf: &DmaBuf) -> Result, GfxError> { (&self) .dmabuf_img(buf) .map(|w| w as Rc) .map_err(|e| e.into()) } fn shmem_texture( self: Rc, _old: Option>, data: &[Cell], format: &'static Format, width: i32, height: i32, stride: i32, _damage: Option<&[Rect]>, ) -> Result, GfxError> { (&self) .shmem_texture(data, format, width, height, stride) .map(|w| w as Rc) .map_err(|e| e.into()) } fn async_shmem_texture( self: Rc, format: &'static Format, width: i32, height: i32, stride: i32, _cpu_worker: &Rc, ) -> Result, GfxError> { let tex = self.ctx.with_current(|| unsafe { let mut tex = 0; (self.ctx.dpy.gles.glGenTextures)(1, &mut tex); Ok(tex) })?; Ok(Rc::new(Texture { gl: GlTexture { ctx: self.ctx.clone(), img: None, tex, width, height, stride, external_only: false, format, contents_valid: Cell::new(false), }, ctx: self, format, })) } fn allocator(&self) -> Rc { self.gbm.clone() } fn gfx_api(&self) -> GfxApi { GfxApi::OpenGl } fn create_internal_fb( self: Rc, _cpu_worker: &Rc, width: i32, height: i32, stride: i32, format: &'static Format, ) -> Result, GfxError> { let fb = self.ctx.with_current(|| unsafe { GlRenderBuffer::new(&self.ctx, width, height, stride, format)?.create_framebuffer() })?; Ok(Rc::new(Framebuffer { ctx: self, gl: fb })) } fn syncobj_ctx(&self) -> Option<&Rc> { Some(&self.sync_ctx) } fn acquire_blend_buffer( &self, _width: i32, _height: i32, ) -> Result, GfxError> { Err(GfxError(Box::new(RenderError::NoBlendBuffer))) } fn supports_invalid_modifier(&self) -> bool { true } }