1
0
Fork 0
forked from wry/wry

render: split module into gfx_apis and renderer

This commit is contained in:
Julian Orth 2023-10-22 17:35:31 +02:00
parent 5e8a6eb86f
commit d650b3375d
68 changed files with 219 additions and 222 deletions

View file

@ -0,0 +1,210 @@
use {
crate::{
format::{Format, XRGB8888},
gfx_api::GfxApiOpt,
gfx_apis::gl::{
egl::{
context::EglContext,
display::{EglDisplay, EglFormat},
},
ext::GlExt,
gl::{
program::GlProgram, render_buffer::GlRenderBuffer, sys::GLint, texture::GlTexture,
},
renderer::{framebuffer::Framebuffer, image::Image},
GfxGlState, RenderError, Texture,
},
video::{
dmabuf::DmaBuf,
drm::{Drm, NodeType},
gbm::GbmDevice,
},
},
ahash::AHashMap,
std::{
cell::{Cell, RefCell},
ffi::CString,
fmt::{Debug, Formatter},
rc::Rc,
},
uapi::ustr,
};
pub(crate) struct TexProg {
pub(crate) prog: GlProgram,
pub(crate) pos: GLint,
pub(crate) texcoord: GLint,
pub(crate) tex: GLint,
}
impl TexProg {
unsafe fn from(prog: GlProgram) -> Self {
Self {
pos: prog.get_attrib_location(ustr!("pos")),
texcoord: prog.get_attrib_location(ustr!("texcoord")),
tex: prog.get_uniform_location(ustr!("tex")),
prog,
}
}
}
pub(crate) struct TexProgs {
pub alpha: TexProg,
pub solid: TexProg,
}
pub struct RenderContext {
pub(crate) ctx: Rc<EglContext>,
pub gbm: Rc<GbmDevice>,
pub(crate) render_node: Rc<CString>,
pub(crate) tex_internal: TexProgs,
pub(crate) tex_external: Option<TexProgs>,
pub(crate) fill_prog: GlProgram,
pub(crate) fill_prog_pos: GLint,
pub(crate) fill_prog_color: GLint,
pub(crate) gfx_ops: RefCell<Vec<GfxApiOpt>>,
pub(crate) gl_state: RefCell<GfxGlState>,
}
impl Debug for RenderContext {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RenderContext").finish_non_exhaustive()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ResetStatus {
Guilty,
Innocent,
Unknown,
Other(u32),
}
impl RenderContext {
pub fn reset_status(&self) -> Option<ResetStatus> {
self.ctx.reset_status()
}
pub fn supports_external_texture(&self) -> bool {
self.ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE_EXTERNAL)
}
pub fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
let nodes = drm.get_nodes()?;
let node = match nodes
.get(&NodeType::Render)
.or_else(|| nodes.get(&NodeType::Primary))
{
None => return Err(RenderError::NoRenderNode),
Some(path) => Rc::new(path.to_owned()),
};
let dpy = EglDisplay::create(drm)?;
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<EglContext>, node: &Rc<CString>) -> Result<Self, RenderError> {
let tex_vert = include_str!("../shaders/tex.vert.glsl");
let tex_prog =
GlProgram::from_shaders(ctx, tex_vert, include_str!("../shaders/tex.frag.glsl"))?;
let tex_alpha_prog = GlProgram::from_shaders(
ctx,
tex_vert,
include_str!("../shaders/tex-alpha.frag.glsl"),
)?;
let tex_external = if ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE_EXTERNAL) {
let solid = GlProgram::from_shaders(
ctx,
tex_vert,
include_str!("../shaders/tex-external.frag.glsl"),
)?;
let alpha = GlProgram::from_shaders(
ctx,
tex_vert,
include_str!("../shaders/tex-external-alpha.frag.glsl"),
)?;
Some(TexProgs {
alpha: TexProg::from(alpha),
solid: TexProg::from(solid),
})
} else {
None
};
let fill_prog = 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(),
render_node: node.clone(),
tex_internal: TexProgs {
solid: TexProg::from(tex_prog),
alpha: TexProg::from(tex_alpha_prog),
},
tex_external,
fill_prog_pos: fill_prog.get_attrib_location(ustr!("pos")),
fill_prog_color: fill_prog.get_uniform_location(ustr!("color")),
fill_prog,
gfx_ops: Default::default(),
gl_state: Default::default(),
})
}
pub fn render_node(&self) -> Rc<CString> {
self.render_node.clone()
}
pub fn formats(&self) -> Rc<AHashMap<u32, EglFormat>> {
self.ctx.dpy.formats.clone()
}
pub fn dmabuf_fb(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<Framebuffer>, 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 dmabuf_img(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<Image>, RenderError> {
self.ctx.with_current(|| {
let img = self.ctx.dpy.import_dmabuf(buf)?;
Ok(Rc::new(Image {
ctx: self.clone(),
gl: img,
}))
})
}
pub fn shmem_texture(
self: &Rc<Self>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
height: i32,
stride: i32,
) -> Result<Rc<Texture>, RenderError> {
let gl = GlTexture::import_shm(&self.ctx, data, format, width, height, stride)?;
Ok(Rc::new(Texture {
ctx: self.clone(),
gl,
}))
}
}

View file

@ -0,0 +1,257 @@
use {
crate::{
cursor::Cursor,
fixed::Fixed,
format::{Format, ARGB8888, XRGB8888},
gfx_apis::gl::{
gl::{
frame_buffer::GlFrameBuffer,
sys::{
glBindFramebuffer, glClear, glClearColor, glViewport, GL_COLOR_BUFFER_BIT,
GL_FRAMEBUFFER,
},
},
renderer::context::RenderContext,
run_ops,
sys::{glBlendFunc, glFlush, glReadnPixels, GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
Texture,
},
rect::Rect,
renderer::{renderer_base::RendererBase, RenderResult, Renderer},
scale::Scale,
state::State,
tree::Node,
},
std::{
cell::Cell,
fmt::{Debug, Formatter},
rc::Rc,
},
};
pub struct Framebuffer {
pub(crate) ctx: Rc<RenderContext>,
pub(crate) gl: GlFrameBuffer,
}
impl Debug for Framebuffer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Framebuffer").finish_non_exhaustive()
}
}
impl Framebuffer {
pub fn clear(&self) {
self.clear_with(0.0, 0.0, 0.0, 0.0);
}
pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) {
let _ = self.ctx.ctx.with_current(|| {
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT);
}
Ok(())
});
}
pub fn copy_texture(&self, state: &State, texture: &Rc<Texture>, x: i32, y: i32, alpha: bool) {
let mut ops = self.ctx.gfx_ops.borrow_mut();
ops.clear();
let scale = Scale::from_int(1);
let extents = Rect::new_sized(0, 0, self.gl.width, self.gl.height).unwrap();
let mut renderer = Renderer {
base: RendererBase {
ops: &mut ops,
scaled: false,
scale,
scalef: 1.0,
},
state,
on_output: false,
result: &mut RenderResult::default(),
logical_extents: extents,
physical_extents: extents,
};
let format = match alpha {
true => ARGB8888,
false => XRGB8888,
};
renderer
.base
.render_texture(texture, x, y, format, None, None, scale, i32::MAX, i32::MAX);
let _ = self.ctx.ctx.with_current(|| {
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
if alpha {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
run_ops(self, &ops);
unsafe {
glFlush();
}
Ok(())
});
}
pub fn copy_to_shm(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
format: &Format,
shm: &[Cell<u8>],
) {
let y = self.gl.height - y - height;
let _ = self.ctx.ctx.with_current(|| {
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glReadnPixels(
x,
y,
width,
height,
format.gl_format as _,
format.gl_type as _,
shm.len() as _,
shm.as_ptr() as _,
);
}
Ok(())
});
}
pub fn render_custom(&self, scale: Scale, f: impl FnOnce(&mut RendererBase)) {
let mut ops = self.ctx.gfx_ops.borrow_mut();
ops.clear();
let mut renderer = RendererBase {
ops: &mut ops,
scaled: scale != 1,
scale,
scalef: scale.to_f64(),
};
f(&mut renderer);
let _ = self.ctx.ctx.with_current(|| {
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
run_ops(self, &ops);
unsafe {
glFlush();
}
Ok(())
});
}
pub fn render(
&self,
node: &dyn Node,
state: &State,
cursor_rect: Option<Rect>,
on_output: bool,
result: &mut RenderResult,
scale: Scale,
render_hardware_cursor: bool,
) {
let mut ops = self.ctx.gfx_ops.borrow_mut();
ops.clear();
let mut renderer = Renderer {
base: RendererBase {
ops: &mut ops,
scaled: scale != 1,
scale,
scalef: scale.to_f64(),
},
state,
on_output,
result,
logical_extents: node.node_absolute_position().at_point(0, 0),
physical_extents: Rect::new(0, 0, self.gl.width, self.gl.height).unwrap(),
};
node.node_render(&mut renderer, 0, 0, i32::MAX, i32::MAX);
if let Some(rect) = cursor_rect {
let seats = state.globals.lock_seats();
for seat in seats.values() {
if !render_hardware_cursor && seat.hardware_cursor() {
continue;
}
if let Some(cursor) = seat.get_cursor() {
let (mut x, mut y) = seat.get_position();
if let Some(dnd_icon) = seat.dnd_icon() {
let extents = dnd_icon.extents.get().move_(
x.round_down() + dnd_icon.buf_x.get(),
y.round_down() + dnd_icon.buf_y.get(),
);
if extents.intersects(&rect) {
let (x, y) = rect.translate(extents.x1(), extents.y1());
renderer.render_surface(&dnd_icon, x, y, i32::MAX, i32::MAX);
}
}
cursor.tick();
x -= Fixed::from_int(rect.x1());
y -= Fixed::from_int(rect.y1());
cursor.render(&mut renderer, x, y);
}
}
}
let _ = self.ctx.ctx.with_current(|| {
let c = state.theme.colors.background.get();
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glClearColor(c.r, c.g, c.b, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
run_ops(self, &ops);
unsafe {
glFlush();
}
Ok(())
});
}
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
let mut ops = self.ctx.gfx_ops.borrow_mut();
ops.clear();
let mut res = RenderResult::default();
let mut renderer = Renderer {
base: RendererBase {
ops: &mut ops,
scaled: scale != 1,
scale,
scalef: scale.to_f64(),
},
state,
on_output: false,
result: &mut res,
logical_extents: Rect::new_empty(0, 0),
physical_extents: Rect::new(0, 0, self.gl.width, self.gl.height).unwrap(),
};
cursor.render_hardware_cursor(&mut renderer);
let _ = self.ctx.ctx.with_current(|| {
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
run_ops(self, &ops);
unsafe {
glFlush();
}
Ok(())
});
}
}

View file

@ -0,0 +1,41 @@
use {
crate::gfx_apis::gl::{
egl::image::EglImage,
gl::{render_buffer::GlRenderBuffer, texture::GlTexture},
Framebuffer, RenderContext, RenderError, Texture,
},
std::rc::Rc,
};
pub struct Image {
pub(crate) ctx: Rc<RenderContext>,
pub(crate) gl: Rc<EglImage>,
}
impl Image {
pub fn width(&self) -> i32 {
self.gl.width
}
pub fn height(&self) -> i32 {
self.gl.height
}
pub fn to_texture(self: &Rc<Self>) -> Result<Rc<Texture>, RenderError> {
Ok(Rc::new(Texture {
ctx: self.ctx.clone(),
gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?,
}))
}
pub fn to_framebuffer(&self) -> Result<Rc<Framebuffer>, RenderError> {
self.ctx.ctx.with_current(|| unsafe {
let rb = GlRenderBuffer::from_image(&self.gl, &self.ctx.ctx)?;
let fb = rb.create_framebuffer()?;
Ok(Rc::new(Framebuffer {
ctx: self.ctx.clone(),
gl: fb,
}))
})
}
}

View file

@ -0,0 +1,28 @@
use {
crate::gfx_apis::gl::{gl::texture::GlTexture, renderer::context::RenderContext},
std::{
fmt::{Debug, Formatter},
rc::Rc,
},
};
pub struct Texture {
pub(crate) ctx: Rc<RenderContext>,
pub(crate) gl: GlTexture,
}
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
}
}