use { crate::{ format::Format, gfx_api::{ AsyncShmGfxTexture, AsyncShmGfxTextureCallback, GfxError, GfxStagingBuffer, GfxTexture, PendingShmTransfer, ShmGfxTexture, ShmMemory, }, gfx_apis::gl::{ gl::texture::GlTexture, renderer::{context::GlRenderContext, framebuffer::Framebuffer}, sys::{ GLint, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT, }, RenderError, }, rect::Region, video::dmabuf::DmaBuf, }, std::{ any::Any, cell::Cell, fmt::{Debug, Formatter}, rc::Rc, }, }; pub struct Texture { pub(in crate::gfx_apis::gl) ctx: Rc, pub(in crate::gfx_apis::gl) gl: GlTexture, pub(in crate::gfx_apis::gl) format: &'static Format, } impl Debug for Texture { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("Texture").finish_non_exhaustive() } } impl Texture { pub fn width(&self) -> i32 { self.gl.width } pub fn height(&self) -> i32 { self.gl.height } pub fn to_framebuffer(&self) -> Result, RenderError> { match &self.gl.img { Some(img) => self.ctx.image_to_fb(img), _ => Err(RenderError::ShmTextureToFb), } } } impl GfxTexture for Texture { fn size(&self) -> (i32, i32) { (self.width(), self.height()) } fn as_any(&self) -> &dyn Any { self } fn into_any(self: Rc) -> Rc { self } fn read_pixels( self: Rc, x: i32, y: i32, width: i32, height: i32, _stride: i32, format: &Format, shm: &[Cell], ) -> Result<(), GfxError> { self.to_framebuffer()? .copy_to_shm(x, y, width, height, format, shm) .map_err(|e| e.into()) } fn dmabuf(&self) -> Option<&DmaBuf> { self.gl.img.as_ref().map(|i| &i.dmabuf) } fn format(&self) -> &'static Format { self.format } } impl ShmGfxTexture for Texture { fn into_texture(self: Rc) -> Rc { self } } impl AsyncShmGfxTexture for Texture { fn async_upload( self: Rc, _staging: &Rc, _callback: Rc, mem: Rc, _damage: Region, ) -> Result, GfxError> { let mut res = Ok(()); mem.access(&mut |data| { res = self.clone().sync_upload(data, Region::default()); }) .map_err(RenderError::AccessFailed)?; res.map(|_| None) } fn sync_upload(self: Rc, data: &[Cell], _damage: Region) -> Result<(), GfxError> { let shm_info = self.format.shm_info.as_ref().unwrap(); if (self.gl.stride * self.gl.height) as usize > data.len() { return Err(RenderError::SmallImageBuffer.into()); } let gles = self.ctx.ctx.dpy.gles; self.ctx.ctx.with_current(|| unsafe { (gles.glBindTexture)(GL_TEXTURE_2D, self.gl.tex); (gles.glTexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); (gles.glTexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); (gles.glPixelStorei)( GL_UNPACK_ROW_LENGTH_EXT, self.gl.stride / shm_info.bpp as GLint, ); (gles.glTexImage2D)( GL_TEXTURE_2D, 0, shm_info.gl_format, self.gl.width, self.gl.height, 0, shm_info.gl_format as _, shm_info.gl_type as _, data.as_ptr() as _, ); (gles.glPixelStorei)(GL_UNPACK_ROW_LENGTH_EXT, 0); (gles.glBindTexture)(GL_TEXTURE_2D, 0); Ok(()) })?; self.gl.contents_valid.set(true); Ok(()) } fn compatible_with( &self, format: &'static Format, width: i32, height: i32, stride: i32, ) -> bool { format == self.gl.format && width == self.gl.width && height == self.gl.height && stride == self.gl.stride } fn into_texture(self: Rc) -> Rc { self } }