render: split module into gfx_apis and renderer
This commit is contained in:
parent
5e8a6eb86f
commit
d650b3375d
68 changed files with 219 additions and 222 deletions
210
src/gfx_apis/gl/renderer/context.rs
Normal file
210
src/gfx_apis/gl/renderer/context.rs
Normal 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,
|
||||
}))
|
||||
}
|
||||
}
|
||||
257
src/gfx_apis/gl/renderer/framebuffer.rs
Normal file
257
src/gfx_apis/gl/renderer/framebuffer.rs
Normal 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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
41
src/gfx_apis/gl/renderer/image.rs
Normal file
41
src/gfx_apis/gl/renderer/image.rs
Normal 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,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
28
src/gfx_apis/gl/renderer/texture.rs
Normal file
28
src/gfx_apis/gl/renderer/texture.rs
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue