Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

render: load libEGL and libGLESv2 at runtime #104

Merged
merged 2 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading