Skip to content

Commit

Permalink
Merge pull request #104 from mahkoh/jorth/dynamic-egl
Browse files Browse the repository at this point in the history
render: load libEGL and libGLESv2 at runtime
  • Loading branch information
mahkoh authored Feb 22, 2024
2 parents aa9ca29 + d015428 commit c992722
Show file tree
Hide file tree
Showing 16 changed files with 341 additions and 260 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ The following features have been implemented and should work:
- Hardware cursors
- Pointer constraints
- Selecting the primary device in multi-GPU systems
- An OpenGL backend
- A Vulkan backend

### Missing Features

Expand All @@ -56,7 +58,6 @@ automatically. It is however unavoidable that Jay depends on a number of native
libraries:

* **libinput.so**: For input event processing.
* **libEGL.so**, **libGLESv2.so**: For OpenGL rendering.
* **libgbm.so**: For graphics buffer allocation.
* **libxkbcommon.so**: For keymap handling.
* **libudev.so**: For device enumeration and hotplug support.
Expand All @@ -76,6 +77,8 @@ At runtime, Jay depends on the following services being available on the system:
Jay as an X client.)
* **Logind**: For the metal backend. (Only required if you want to run Jay from
a TTY.)
* **libEGL.so**, **libGLESv2.so**: For the OpenGL backend.
* **libvulkan.so**: For the Vulkan backend.

## Building and Installing

Expand Down
9 changes: 5 additions & 4 deletions build/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,17 @@ fn write_egl_procs<W: Write>(f: &mut W) -> anyhow::Result<()> {
writeln!(f, "unsafe impl Send for ExtProc {{ }}")?;
writeln!(f)?;
writeln!(f, "impl ExtProc {{")?;
writeln!(f, " pub fn load() -> Self {{")?;
writeln!(f, " Self {{")?;
writeln!(f, " pub fn load() -> Option<Self> {{")?;
writeln!(f, " let egl = EGL.as_ref()?;")?;
writeln!(f, " Some(Self {{")?;
for (name, _, _) in map.iter().copied() {
writeln!(
f,
" {}: unsafe {{ eglGetProcAddress(\"{}\\0\".as_ptr() as _) }},",
" {}: unsafe {{ (egl.eglGetProcAddress)(\"{}\\0\".as_ptr() as _) }},",
name, name
)?;
}
writeln!(f, " }}")?;
writeln!(f, " }})")?;
writeln!(f, " }}")?;
for (name, ret, args) in map.iter().copied() {
let mut args_names = String::new();
Expand Down
103 changes: 78 additions & 25 deletions src/gfx_apis/gl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,53 @@ macro_rules! egl_transparent {
};
}

macro_rules! dynload {
(
$item:ident: $container:ident from $name:literal {
$(
$fun:ident: $ty:ty,
)*
}
) => {
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct $container {
_lib: libloading::Library,
$(
pub $fun: $ty,
)*
}

pub static $item: once_cell::sync::Lazy<Option<$container>> = once_cell::sync::Lazy::new(|| unsafe {
use crate::utils::errorfmt::ErrorFmt;
let lib = match libloading::Library::new($name) {
Ok(l) => l,
Err(e) => {
log::error!("Could not load lib{}: {}", $name, ErrorFmt(e));
return None;
}
};
$(
#[allow(non_snake_case)]
let $fun: $ty =
match lib.get(stringify!($fun).as_bytes()) {
Ok(s) => *s,
Err(e) => {
log::error!("Could not load {} from {}: {}", stringify!($fun), $name, ErrorFmt(e));
return None;
}
};
)*
Some($container {
_lib: lib,
$(
$fun,
)*
})
});
};
}

use {
crate::{
gfx_api::{
Expand All @@ -27,10 +74,8 @@ use {
gl::texture::image_target,
renderer::{context::GlRenderContext, framebuffer::Framebuffer, texture::Texture},
sys::{
glActiveTexture, glBindTexture, glDisable, glDisableVertexAttribArray,
glDrawArrays, glEnable, glEnableVertexAttribArray, glTexParameteri, glUniform1i,
glUniform4f, glUseProgram, glVertexAttribPointer, GL_BLEND, GL_FALSE, GL_FLOAT,
GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP,
GL_BLEND, GL_FALSE, GL_FLOAT, GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_MIN_FILTER,
GL_TRIANGLES, GL_TRIANGLE_STRIP,
},
},
theme::Color,
Expand Down Expand Up @@ -69,6 +114,12 @@ pub(super) fn create_gfx_context(drm: &Drm) -> Result<Rc<dyn GfxContext>, GfxErr

#[derive(Debug, Error)]
enum RenderError {
#[error("Could not load libEGL")]
LoadEgl,
#[error("Could not load libGLESv2")]
LoadGlesV2,
#[error("Could not load extension functions from libEGL")]
LoadEglProcs,
#[error("EGL library does not support `EGL_EXT_platform_base`")]
ExtPlatformBase,
#[error("Could not compile a shader")]
Expand Down Expand Up @@ -221,20 +272,21 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
}

fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
let gles = ctx.ctx.dpy.gles;
unsafe {
glUseProgram(ctx.fill_prog.prog);
glUniform4f(ctx.fill_prog_color, color.r, color.g, color.b, color.a);
glVertexAttribPointer(
(gles.glUseProgram)(ctx.fill_prog.prog);
(gles.glUniform4f)(ctx.fill_prog_color, color.r, color.g, color.b, color.a);
(gles.glVertexAttribPointer)(
ctx.fill_prog_pos as _,
2,
GL_FLOAT,
GL_FALSE,
0,
boxes.as_ptr() as _,
);
glEnableVertexAttribArray(ctx.fill_prog_pos as _);
glDrawArrays(GL_TRIANGLES, 0, (boxes.len() / 2) as _);
glDisableVertexAttribArray(ctx.fill_prog_pos as _);
(gles.glEnableVertexAttribArray)(ctx.fill_prog_pos as _);
(gles.glDrawArrays)(GL_TRIANGLES, 0, (boxes.len() / 2) as _);
(gles.glDisableVertexAttribArray)(ctx.fill_prog_pos as _);
}
}

Expand All @@ -248,13 +300,14 @@ fn render_texture(
src: &BufferPoints,
) {
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
let gles = ctx.ctx.dpy.gles;
unsafe {
glActiveTexture(GL_TEXTURE0);
(gles.glActiveTexture)(GL_TEXTURE0);

let target = image_target(texture.gl.external_only);

glBindTexture(target, texture.gl.tex);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
(gles.glBindTexture)(target, texture.gl.tex);
(gles.glTexParameteri)(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

let progs = match texture.gl.external_only {
true => match &ctx.tex_external {
Expand All @@ -268,18 +321,18 @@ fn render_texture(
};
let prog = match texture.gl.format.has_alpha {
true => {
glEnable(GL_BLEND);
(gles.glEnable)(GL_BLEND);
&progs.alpha
}
false => {
glDisable(GL_BLEND);
(gles.glDisable)(GL_BLEND);
&progs.solid
}
};

glUseProgram(prog.prog.prog);
(gles.glUseProgram)(prog.prog.prog);

glUniform1i(prog.tex, 0);
(gles.glUniform1i)(prog.tex, 0);

let texcoord = [
src.top_right.x,
Expand All @@ -299,25 +352,25 @@ fn render_texture(
x1, y2, // bottom left
];

glVertexAttribPointer(
(gles.glVertexAttribPointer)(
prog.texcoord as _,
2,
GL_FLOAT,
GL_FALSE,
0,
texcoord.as_ptr() as _,
);
glVertexAttribPointer(prog.pos as _, 2, GL_FLOAT, GL_FALSE, 0, pos.as_ptr() as _);
(gles.glVertexAttribPointer)(prog.pos as _, 2, GL_FLOAT, GL_FALSE, 0, pos.as_ptr() as _);

glEnableVertexAttribArray(prog.texcoord as _);
glEnableVertexAttribArray(prog.pos as _);
(gles.glEnableVertexAttribArray)(prog.texcoord as _);
(gles.glEnableVertexAttribArray)(prog.pos as _);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
(gles.glDrawArrays)(GL_TRIANGLE_STRIP, 0, 4);

glDisableVertexAttribArray(prog.texcoord as _);
glDisableVertexAttribArray(prog.pos as _);
(gles.glDisableVertexAttribArray)(prog.texcoord as _);
(gles.glDisableVertexAttribArray)(prog.pos as _);

glBindTexture(target, 0);
(gles.glBindTexture)(target, 0);
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/gfx_apis/gl/egl.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use {
crate::gfx_apis::gl::{
egl::sys::{
eglBindAPI, EGLAttrib, EGLLabelKHR, EGLenum, EGLint, EGL_DEBUG_MSG_CRITICAL_KHR,
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_NONE,
EGL_OPENGL_ES_API, EGL_TRUE,
},
ext::{get_client_ext, ClientExt, EXT_PLATFORM_BASE, KHR_DEBUG, KHR_PLATFORM_GBM},
proc::ExtProc,
sys::EGL,
RenderError,
},
bstr::ByteSlice,
Expand All @@ -27,11 +28,17 @@ pub mod display;
pub mod image;
pub mod sys;

pub(crate) static PROCS: Lazy<ExtProc> = Lazy::new(ExtProc::load);
pub(crate) static PROCS: Lazy<Option<ExtProc>> = Lazy::new(ExtProc::load);

pub(crate) static EXTS: Lazy<ClientExt> = Lazy::new(get_client_ext);

pub(in crate::gfx_apis::gl) fn init() -> Result<(), RenderError> {
let Some(egl) = EGL.as_ref() else {
return Err(RenderError::LoadEgl);
};
let Some(procs) = PROCS.as_ref() else {
return Err(RenderError::LoadEglProcs);
};
if !EXTS.contains(EXT_PLATFORM_BASE) {
return Err(RenderError::ExtPlatformBase);
}
Expand All @@ -51,10 +58,10 @@ pub(in crate::gfx_apis::gl) fn init() -> Result<(), RenderError> {
EGL_NONE as _,
];
unsafe {
PROCS.eglDebugMessageControlKHR(egl_log, attrib.as_ptr());
procs.eglDebugMessageControlKHR(egl_log, attrib.as_ptr());
}
}
if unsafe { eglBindAPI(EGL_OPENGL_ES_API) } != EGL_TRUE {
if unsafe { (egl.eglBindAPI)(EGL_OPENGL_ES_API) } != EGL_TRUE {
return Err(RenderError::BindFailed);
}
Ok(())
Expand Down
15 changes: 7 additions & 8 deletions src/gfx_apis/gl/egl/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use {
gfx_apis::gl::{
egl::{
display::EglDisplay,
sys::{
eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE,
},
PROCS,
sys::{EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE},
},
ext::{GlExt, EXT_CREATE_CONTEXT_ROBUSTNESS},
sys::{
Expand All @@ -32,7 +29,7 @@ pub struct EglContext {
impl Drop for EglContext {
fn drop(&mut self) {
unsafe {
if eglDestroyContext(self.dpy.dpy, self.ctx) != EGL_TRUE {
if (self.dpy.egl.eglDestroyContext)(self.dpy.dpy, self.ctx) != EGL_TRUE {
log::warn!("`eglDestroyContext` failed");
}
}
Expand All @@ -48,7 +45,7 @@ impl EglContext {
return None;
}
let status = self.with_current(|| unsafe {
let status = match PROCS.glGetGraphicsResetStatusKHR() {
let status = match self.dpy.procs.glGetGraphicsResetStatusKHR() {
0 => return Ok(None),
GL_GUILTY_CONTEXT_RESET_ARB => ResetStatus::Guilty,
GL_INNOCENT_CONTEXT_RESET_ARB => ResetStatus::Innocent,
Expand Down Expand Up @@ -78,7 +75,7 @@ impl EglContext {
&self,
f: F,
) -> Result<T, RenderError> {
if eglMakeCurrent(
if (self.dpy.egl.eglMakeCurrent)(
self.dpy.dpy,
EGLSurface::none(),
EGLSurface::none(),
Expand All @@ -90,7 +87,9 @@ impl EglContext {
let prev = CURRENT;
CURRENT = self.ctx;
let res = f();
if eglMakeCurrent(self.dpy.dpy, EGLSurface::none(), EGLSurface::none(), prev) == EGL_FALSE {
if (self.dpy.egl.eglMakeCurrent)(self.dpy.dpy, EGLSurface::none(), EGLSurface::none(), prev)
== EGL_FALSE
{
panic!("Could not restore EGLContext");
}
CURRENT = prev;
Expand Down
Loading

0 comments on commit c992722

Please sign in to comment.