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

322
src/gfx_apis/gl.rs Normal file
View file

@ -0,0 +1,322 @@
macro_rules! egl_transparent {
($name:ident) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct $name(pub *mut u8);
impl $name {
#[allow(dead_code)]
pub const fn none() -> Self {
Self(std::ptr::null_mut())
}
#[allow(dead_code)]
pub fn is_none(self) -> bool {
self.0.is_null()
}
}
};
}
pub use renderer::*;
use {
crate::{
format::Format,
gfx_api::{BufferPoints, CopyTexture, FillRect, GfxApiOpt},
gfx_apis::gl::{
gl::texture::image_target,
sys::{
glActiveTexture, glBindTexture, glClear, glClearColor, glDisable,
glDisableVertexAttribArray, glDrawArrays, glEnable, glEnableVertexAttribArray,
glTexParameteri, glUniform1i, glUniform4f, glUseProgram, glVertexAttribPointer,
GL_BLEND, GL_COLOR_BUFFER_BIT, GL_FALSE, GL_FLOAT, GL_LINEAR, GL_TEXTURE0,
GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP,
},
},
theme::Color,
utils::{rc_eq::rc_eq, vecstorage::VecStorage},
video::{drm::DrmError, gbm::GbmError},
},
isnt::std_1::vec::IsntVecExt,
std::cell::RefCell,
thiserror::Error,
};
mod egl;
mod ext;
mod gl;
mod proc;
mod renderer;
pub mod sys {
pub use super::{egl::sys::*, gl::sys::*};
}
pub fn init() -> Result<(), RenderError> {
egl::init()
}
#[derive(Debug, Error)]
pub enum RenderError {
#[error("EGL library does not support `EGL_EXT_platform_base`")]
ExtPlatformBase,
#[error("Could not compile a shader")]
ShaderCompileFailed,
#[error("Could not link a program")]
ProgramLink,
#[error("Could not bind to `EGL_OPENGL_ES_API`")]
BindFailed,
#[error("EGL library does not support the GBM platform")]
GbmExt,
#[error("Could not create a GBM device")]
Gbm(#[source] GbmError),
#[error("`eglCreateContext` failed")]
CreateContext,
#[error("`eglMakeCurrent` failed")]
MakeCurrent,
#[error("`eglCreateImageKHR` failed")]
CreateImage,
#[error("Image buffer is too small")]
SmallImageBuffer,
#[error("Binding a renderbuffer to a framebuffer failed")]
CreateFramebuffer,
#[error("`eglGetPlatformDisplayEXT` failed")]
GetDisplay,
#[error("`eglInitialize` failed")]
Initialize,
#[error("EGL display does not support `EGL_EXT_image_dma_buf_import_modifiers`")]
DmaBufImport,
#[error("GLES driver does not support `GL_OES_EGL_image`")]
OesEglImage,
#[error("EGL display does not support `EGL_KHR_image_base`")]
ImageBase,
#[error(
"EGL display does not support `EGL_KHR_no_config_context` or `EGL_MESA_configless_context`"
)]
ConfiglessContext,
#[error("EGL display does not support `EGL_KHR_surfaceless_context`")]
SurfacelessContext,
#[error("`eglQueryDmaBufFormatsEXT` failed")]
QueryDmaBufFormats,
#[error("`eglQueryDmaBufModifiersEXT` failed")]
QueryDmaBufModifiers,
#[error(transparent)]
DrmError(#[from] DrmError),
#[error("The GLES driver does not support the XRGB8888 format")]
XRGB888,
#[error("The DRM device does not have a render node")]
NoRenderNode,
#[error("The requested format is not supported")]
UnsupportedFormat,
#[error("The requested modifier is not supported")]
UnsupportedModifier,
#[error("Image is external only and cannot be rendered to")]
ExternalOnly,
#[error("OpenGL context does not support external textures")]
ExternalUnsupported,
}
#[derive(Default)]
pub struct GfxGlState {
triangles: RefCell<Vec<f32>>,
fill_rect: VecStorage<&'static FillRect>,
copy_tex: VecStorage<&'static CopyTexture>,
}
pub fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
let mut state = fb.ctx.gl_state.borrow_mut();
let state = &mut *state;
let mut fill_rect = state.fill_rect.take();
let fill_rect = &mut *fill_rect;
let mut copy_tex = state.copy_tex.take();
let copy_tex = &mut *copy_tex;
let mut triangles = state.triangles.borrow_mut();
let triangles = &mut *triangles;
let width = fb.gl.width as f32;
let height = fb.gl.height as f32;
let mut i = 0;
while i < ops.len() {
macro_rules! has_ops {
() => {
fill_rect.is_not_empty() || copy_tex.is_not_empty()
};
}
fill_rect.clear();
copy_tex.clear();
while i < ops.len() {
match &ops[i] {
GfxApiOpt::Sync => {
i += 1;
if has_ops!() {
break;
}
}
GfxApiOpt::Clear(c) => {
if has_ops!() {
break;
}
clear(&c.color);
i += 1;
}
GfxApiOpt::FillRect(f) => {
fill_rect.push(f);
i += 1;
}
GfxApiOpt::CopyTexture(c) => {
copy_tex.push(c);
i += 1;
}
}
}
if fill_rect.is_not_empty() {
fill_rect.sort_unstable_by_key(|f| f.color);
let mut i = 0;
while i < fill_rect.len() {
triangles.clear();
let mut color = None;
while i < fill_rect.len() {
let fr = fill_rect[i];
match color {
None => color = Some(fr.color),
Some(c) if c == fr.color => {}
_ => break,
}
let x1 = 2.0 * (fr.rect.x1 / width) - 1.0;
let x2 = 2.0 * (fr.rect.x2 / width) - 1.0;
let y1 = 2.0 * (fr.rect.y1 / height) - 1.0;
let y2 = 2.0 * (fr.rect.y2 / height) - 1.0;
triangles.extend_from_slice(&[
// triangle 1
x2, y1, // top right
x1, y1, // top left
x1, y2, // bottom left
// triangle 2
x2, y1, // top right
x1, y2, // bottom left
x2, y2, // bottom right
]);
i += 1;
}
if let Some(color) = color {
fill_boxes3(&fb.ctx, triangles, &color);
}
}
}
for tex in &*copy_tex {
let x1 = 2.0 * (tex.target.x1 / width) - 1.0;
let y1 = 2.0 * (tex.target.y1 / height) - 1.0;
let x2 = 2.0 * (tex.target.x2 / width) - 1.0;
let y2 = 2.0 * (tex.target.y2 / height) - 1.0;
render_texture(&fb.ctx, &tex.tex, tex.format, x1, y1, x2, y2, &tex.source)
}
}
}
fn clear(c: &Color) {
unsafe {
glClearColor(c.r, c.g, c.b, c.a);
glClear(GL_COLOR_BUFFER_BIT);
}
}
fn fill_boxes3(ctx: &RenderContext, boxes: &[f32], color: &Color) {
unsafe {
glUseProgram(ctx.fill_prog.prog);
glUniform4f(ctx.fill_prog_color, color.r, color.g, color.b, color.a);
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 _);
}
}
fn render_texture(
ctx: &RenderContext,
texture: &Texture,
format: &Format,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
src: &BufferPoints,
) {
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
unsafe {
glActiveTexture(GL_TEXTURE0);
let target = image_target(texture.gl.external_only);
glBindTexture(target, texture.gl.tex);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
let progs = match texture.gl.external_only {
true => match &ctx.tex_external {
Some(p) => p,
_ => {
log::error!("Trying to render an external-only texture but context does not support the required extension");
return;
}
},
false => &ctx.tex_internal,
};
let prog = match format.has_alpha {
true => {
glEnable(GL_BLEND);
&progs.alpha
}
false => {
glDisable(GL_BLEND);
&progs.solid
}
};
glUseProgram(prog.prog.prog);
glUniform1i(prog.tex, 0);
let texcoord = [
src.top_right.x,
src.top_right.y,
src.top_left.x,
src.top_left.y,
src.bottom_right.x,
src.bottom_right.y,
src.bottom_left.x,
src.bottom_left.y,
];
let pos = [
x2, y1, // top right
x1, y1, // top left
x2, y2, // bottom right
x1, y2, // bottom left
];
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 _);
glEnableVertexAttribArray(prog.texcoord as _);
glEnableVertexAttribArray(prog.pos as _);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(prog.texcoord as _);
glDisableVertexAttribArray(prog.pos as _);
glBindTexture(target, 0);
}
}

126
src/gfx_apis/gl/egl.rs Normal file
View file

@ -0,0 +1,126 @@
use {
crate::gfx_apis::gl::{
egl::sys::{
eglBindAPI, 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},
proc::ExtProc,
RenderError,
},
bstr::ByteSlice,
log::Level,
once_cell::sync::Lazy,
std::ffi::CStr,
sys::{
EGL_BAD_ACCESS, EGL_BAD_ALLOC, EGL_BAD_ATTRIBUTE, EGL_BAD_CONFIG, EGL_BAD_CONTEXT,
EGL_BAD_CURRENT_SURFACE, EGL_BAD_DEVICE_EXT, EGL_BAD_DISPLAY, EGL_BAD_MATCH,
EGL_BAD_NATIVE_PIXMAP, EGL_BAD_NATIVE_WINDOW, EGL_BAD_PARAMETER, EGL_BAD_SURFACE,
EGL_CONTEXT_LOST, EGL_NOT_INITIALIZED, EGL_SUCCESS,
},
uapi::c,
};
pub mod context;
pub mod display;
pub mod image;
pub mod sys;
pub(crate) static PROCS: Lazy<ExtProc> = Lazy::new(ExtProc::load);
pub(crate) static EXTS: Lazy<ClientExt> = Lazy::new(get_client_ext);
pub fn init() -> Result<(), RenderError> {
if !EXTS.contains(ClientExt::EXT_PLATFORM_BASE) {
return Err(RenderError::ExtPlatformBase);
}
if !EXTS.contains(ClientExt::KHR_PLATFORM_GBM) {
return Err(RenderError::GbmExt);
}
if EXTS.contains(ClientExt::KHR_DEBUG) {
let attrib: &[EGLAttrib] = &[
EGL_DEBUG_MSG_CRITICAL_KHR as _,
EGL_TRUE as _,
EGL_DEBUG_MSG_ERROR_KHR as _,
EGL_TRUE as _,
EGL_DEBUG_MSG_WARN_KHR as _,
EGL_TRUE as _,
EGL_DEBUG_MSG_INFO_KHR as _,
EGL_TRUE as _,
EGL_NONE as _,
];
unsafe {
PROCS.eglDebugMessageControlKHR(egl_log, attrib.as_ptr());
}
}
if unsafe { eglBindAPI(EGL_OPENGL_ES_API) } != EGL_TRUE {
return Err(RenderError::BindFailed);
}
Ok(())
}
unsafe extern "C" fn egl_log(
error: EGLenum,
command: *const c::c_char,
message_type: EGLint,
_thread_label: EGLLabelKHR,
_object_label: EGLLabelKHR,
message: *const c::c_char,
) {
let level = match message_type {
EGL_DEBUG_MSG_CRITICAL_KHR => Level::Error,
EGL_DEBUG_MSG_ERROR_KHR => Level::Error,
EGL_DEBUG_MSG_WARN_KHR => Level::Warn,
EGL_DEBUG_MSG_INFO_KHR => Level::Info,
_ => Level::Warn,
};
let command = if !command.is_null() {
CStr::from_ptr(command).to_bytes()
} else {
b"none"
};
let message = if !message.is_null() {
CStr::from_ptr(message).to_bytes()
} else {
b"none"
};
let err_name = error_name(error);
log::log!(
level,
"EGL: command: {}, error: {} (0x{:x}), message: {}",
command.as_bstr(),
err_name,
error,
message.as_bstr()
);
}
fn error_name(error: EGLenum) -> &'static str {
macro_rules! en {
($($name:ident,)*) => {
match error as _ {
$($name => stringify!($name),)*
_ => "unknown",
}
}
}
en! {
EGL_SUCCESS,
EGL_NOT_INITIALIZED,
EGL_BAD_ACCESS,
EGL_BAD_ALLOC,
EGL_BAD_ATTRIBUTE,
EGL_BAD_CONTEXT,
EGL_BAD_CONFIG,
EGL_BAD_CURRENT_SURFACE,
EGL_BAD_DISPLAY,
EGL_BAD_DEVICE_EXT,
EGL_BAD_SURFACE,
EGL_BAD_MATCH,
EGL_BAD_PARAMETER,
EGL_BAD_NATIVE_PIXMAP,
EGL_BAD_NATIVE_WINDOW,
EGL_CONTEXT_LOST,
}
}

View file

@ -0,0 +1,96 @@
use {
crate::gfx_apis::gl::{
egl::{
display::EglDisplay,
sys::{eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE},
PROCS,
},
ext::{DisplayExt, GlExt},
sys::{
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB,
GL_UNKNOWN_CONTEXT_RESET_ARB,
},
RenderError, ResetStatus,
},
std::rc::Rc,
};
#[derive(Debug, Clone)]
pub struct EglContext {
pub dpy: Rc<EglDisplay>,
pub ext: GlExt,
pub ctx: EGLContext,
}
impl Drop for EglContext {
fn drop(&mut self) {
unsafe {
if eglDestroyContext(self.dpy.dpy, self.ctx) != EGL_TRUE {
log::warn!("`eglDestroyContext` failed");
}
}
}
}
#[thread_local]
static mut CURRENT: EGLContext = EGLContext::none();
impl EglContext {
pub fn reset_status(&self) -> Option<ResetStatus> {
if !self
.dpy
.exts
.contains(DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS)
{
return None;
}
let status = self.with_current(|| unsafe {
let status = match PROCS.glGetGraphicsResetStatusKHR() {
0 => return Ok(None),
GL_GUILTY_CONTEXT_RESET_ARB => ResetStatus::Guilty,
GL_INNOCENT_CONTEXT_RESET_ARB => ResetStatus::Innocent,
GL_UNKNOWN_CONTEXT_RESET_ARB => ResetStatus::Unknown,
n => ResetStatus::Other(n),
};
Ok(Some(status))
});
status.unwrap_or_default()
}
#[inline]
pub fn with_current<T, F: FnOnce() -> Result<T, RenderError>>(
&self,
f: F,
) -> Result<T, RenderError> {
unsafe {
if CURRENT == self.ctx {
return f();
}
self.with_current_slow(f)
}
}
#[cold]
unsafe fn with_current_slow<T, F: FnOnce() -> Result<T, RenderError>>(
&self,
f: F,
) -> Result<T, RenderError> {
if eglMakeCurrent(
self.dpy.dpy,
EGLSurface::none(),
EGLSurface::none(),
self.ctx,
) == EGL_FALSE
{
return Err(RenderError::MakeCurrent);
}
let prev = CURRENT;
CURRENT = self.ctx;
let res = f();
if eglMakeCurrent(self.dpy.dpy, EGLSurface::none(), EGLSurface::none(), prev) == EGL_FALSE {
panic!("Could not restore EGLContext");
}
CURRENT = prev;
res
}
}

View file

@ -0,0 +1,325 @@
use {
crate::{
format::{formats, Format},
gfx_apis::gl::{
egl::{
context::EglContext,
image::EglImage,
sys::{
eglCreateContext, eglTerminate, EGLClientBuffer, EGLConfig, EGLContext,
EGLDisplay, EGLint, EGL_CONTEXT_CLIENT_VERSION, EGL_DMA_BUF_PLANE0_FD_EXT,
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT,
EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT,
EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE2_FD_EXT,
EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT,
EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT,
EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_HEIGHT, EGL_IMAGE_PRESERVED_KHR,
EGL_LINUX_DMA_BUF_EXT, EGL_LINUX_DRM_FOURCC_EXT, EGL_NONE, EGL_TRUE, EGL_WIDTH,
},
PROCS,
},
ext::{get_display_ext, get_gl_ext, DisplayExt, GlExt},
sys::{
eglInitialize, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
EGL_LOSE_CONTEXT_ON_RESET_EXT, EGL_PLATFORM_GBM_KHR,
},
RenderError,
},
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice, INVALID_MODIFIER},
},
ahash::AHashMap,
std::{ptr, rc::Rc},
};
#[derive(Debug)]
pub struct EglFormat {
pub format: &'static Format,
pub implicit_external_only: bool,
pub modifiers: AHashMap<u64, EglModifier>,
}
#[derive(Debug)]
pub struct EglModifier {
pub modifier: u64,
pub external_only: bool,
}
#[derive(Debug)]
pub struct EglDisplay {
pub exts: DisplayExt,
pub formats: Rc<AHashMap<u32, EglFormat>>,
pub gbm: Rc<GbmDevice>,
pub dpy: EGLDisplay,
}
impl EglDisplay {
pub fn create(drm: &Drm) -> Result<Rc<Self>, RenderError> {
unsafe {
let gbm = match GbmDevice::new(drm) {
Ok(gbm) => gbm,
Err(e) => return Err(RenderError::Gbm(e)),
};
let dpy = PROCS.eglGetPlatformDisplayEXT(
EGL_PLATFORM_GBM_KHR as _,
gbm.raw() as _,
ptr::null(),
);
if dpy.is_none() {
return Err(RenderError::GetDisplay);
}
let mut dpy = EglDisplay {
exts: DisplayExt::empty(),
formats: Rc::new(AHashMap::new()),
gbm: Rc::new(gbm),
dpy,
};
let mut major = 0;
let mut minor = 0;
if eglInitialize(dpy.dpy, &mut major, &mut minor) != EGL_TRUE {
return Err(RenderError::Initialize);
}
dpy.exts = get_display_ext(dpy.dpy);
if !dpy.exts.intersects(DisplayExt::KHR_IMAGE_BASE) {
return Err(RenderError::ImageBase);
}
if !dpy
.exts
.intersects(DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS)
{
return Err(RenderError::DmaBufImport);
}
if !dpy
.exts
.intersects(DisplayExt::KHR_NO_CONFIG_CONTEXT | DisplayExt::MESA_CONFIGLESS_CONTEXT)
{
return Err(RenderError::ConfiglessContext);
}
if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) {
return Err(RenderError::SurfacelessContext);
}
dpy.formats = Rc::new(query_formats(dpy.dpy)?);
Ok(Rc::new(dpy))
}
}
pub fn create_context(self: &Rc<Self>) -> Result<Rc<EglContext>, RenderError> {
let mut attrib = vec![EGL_CONTEXT_CLIENT_VERSION, 2];
if self
.exts
.contains(DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS)
{
attrib.push(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
attrib.push(EGL_LOSE_CONTEXT_ON_RESET_EXT);
} else {
log::warn!("EGL display does not support gpu reset notifications");
}
attrib.push(EGL_NONE);
unsafe {
let ctx = eglCreateContext(
self.dpy,
EGLConfig::none(),
EGLContext::none(),
attrib.as_ptr(),
);
if ctx.is_none() {
return Err(RenderError::CreateContext);
}
let mut ctx = EglContext {
dpy: self.clone(),
ext: GlExt::empty(),
ctx,
};
ctx.ext = ctx.with_current(|| Ok(get_gl_ext()))?;
if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) {
return Err(RenderError::OesEglImage);
}
Ok(Rc::new(ctx))
}
}
pub fn import_dmabuf(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<EglImage>, RenderError> {
let format = match self.formats.get(&buf.format.drm) {
Some(fmt) => match fmt.modifiers.get(&buf.modifier) {
Some(fmt) => fmt,
_ => return Err(RenderError::UnsupportedModifier),
},
_ => return Err(RenderError::UnsupportedFormat),
};
struct PlaneKey {
fd: EGLint,
offset: EGLint,
pitch: EGLint,
mod_lo: EGLint,
mod_hi: EGLint,
}
const PLANE_KEYS: [PlaneKey; 4] = [
PlaneKey {
fd: EGL_DMA_BUF_PLANE0_FD_EXT,
offset: EGL_DMA_BUF_PLANE0_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE0_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
},
PlaneKey {
fd: EGL_DMA_BUF_PLANE1_FD_EXT,
offset: EGL_DMA_BUF_PLANE1_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE1_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
},
PlaneKey {
fd: EGL_DMA_BUF_PLANE2_FD_EXT,
offset: EGL_DMA_BUF_PLANE2_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE2_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
},
PlaneKey {
fd: EGL_DMA_BUF_PLANE3_FD_EXT,
offset: EGL_DMA_BUF_PLANE3_OFFSET_EXT,
pitch: EGL_DMA_BUF_PLANE3_PITCH_EXT,
mod_lo: EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
mod_hi: EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
},
];
let mut attribs = Vec::with_capacity(19);
attribs.extend_from_slice(&[EGL_WIDTH, buf.width]);
attribs.extend_from_slice(&[EGL_HEIGHT, buf.height]);
attribs.extend_from_slice(&[EGL_LINUX_DRM_FOURCC_EXT, buf.format.drm as _]);
attribs.extend_from_slice(&[EGL_IMAGE_PRESERVED_KHR, EGL_TRUE as _]);
for (key, plane) in PLANE_KEYS.iter().zip(buf.planes.iter()) {
attribs.extend_from_slice(&[key.fd, plane.fd.raw()]);
attribs.extend_from_slice(&[key.pitch, plane.stride as _]);
attribs.extend_from_slice(&[key.offset, plane.offset as _]);
if buf.modifier != INVALID_MODIFIER {
attribs.extend_from_slice(&[key.mod_lo, buf.modifier as i32]);
attribs.extend_from_slice(&[key.mod_hi, (buf.modifier >> 32) as i32]);
}
}
attribs.push(EGL_NONE);
let img = unsafe {
PROCS.eglCreateImageKHR(
self.dpy,
EGLContext::none(),
EGL_LINUX_DMA_BUF_EXT as _,
EGLClientBuffer::none(),
attribs.as_ptr(),
)
};
if img.is_none() {
return Err(RenderError::CreateImage);
}
Ok(Rc::new(EglImage {
dpy: self.clone(),
img,
width: buf.width,
height: buf.height,
external_only: format.external_only,
}))
}
}
impl Drop for EglDisplay {
fn drop(&mut self) {
unsafe {
if eglTerminate(self.dpy) != EGL_TRUE {
log::warn!("`eglTerminate` failed");
}
}
}
}
unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, EglFormat>, RenderError> {
let mut vec = vec![];
let mut num = 0;
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, ptr::null_mut(), &mut num);
if res != EGL_TRUE {
return Err(RenderError::QueryDmaBufFormats);
}
vec.reserve_exact(num as usize);
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, vec.as_mut_ptr(), &mut num);
if res != EGL_TRUE {
return Err(RenderError::QueryDmaBufFormats);
}
vec.set_len(num as usize);
let mut res = AHashMap::new();
let formats = formats();
for fmt in vec {
if let Some(format) = formats.get(&(fmt as u32)) {
let (modifiers, external_only) = query_modifiers(dpy, fmt, format)?;
res.insert(
format.drm,
EglFormat {
format,
implicit_external_only: external_only,
modifiers,
},
);
}
}
Ok(res)
}
unsafe fn query_modifiers(
dpy: EGLDisplay,
gl_format: EGLint,
format: &'static Format,
) -> Result<(AHashMap<u64, EglModifier>, bool), RenderError> {
let mut mods = vec![];
let mut ext_only = vec![];
let mut num = 0;
let res = PROCS.eglQueryDmaBufModifiersEXT(
dpy,
gl_format,
num,
ptr::null_mut(),
ptr::null_mut(),
&mut num,
);
if res != EGL_TRUE {
return Err(RenderError::QueryDmaBufModifiers);
}
mods.reserve_exact(num as usize);
ext_only.reserve_exact(num as usize);
let res = PROCS.eglQueryDmaBufModifiersEXT(
dpy,
gl_format,
num,
mods.as_mut_ptr(),
ext_only.as_mut_ptr(),
&mut num,
);
if res != EGL_TRUE {
return Err(RenderError::QueryDmaBufModifiers);
}
mods.set_len(num as usize);
ext_only.set_len(num as usize);
let mut res = AHashMap::new();
for (modifier, ext_only) in mods.iter().copied().zip(ext_only.iter().copied()) {
res.insert(
modifier as _,
EglModifier {
modifier: modifier as _,
external_only: ext_only == EGL_TRUE,
},
);
}
let mut external_only = format.external_only_guess;
if res.len() > 0 {
external_only = res.values().any(|f| f.external_only);
}
res.insert(
INVALID_MODIFIER,
EglModifier {
modifier: INVALID_MODIFIER,
external_only,
},
);
Ok((res, external_only))
}

View file

@ -0,0 +1,26 @@
use {
crate::gfx_apis::gl::egl::{
display::EglDisplay,
sys::{EGLImageKHR, EGL_FALSE},
PROCS,
},
std::rc::Rc,
};
pub struct EglImage {
pub dpy: Rc<EglDisplay>,
pub img: EGLImageKHR,
pub width: i32,
pub height: i32,
pub external_only: bool,
}
impl Drop for EglImage {
fn drop(&mut self) {
unsafe {
if PROCS.eglDestroyImageKHR(self.dpy.dpy, self.img) == EGL_FALSE {
log::warn!("`eglDestroyImageKHR` failed");
}
}
}
}

107
src/gfx_apis/gl/egl/sys.rs Normal file
View file

@ -0,0 +1,107 @@
use {crate::gfx_apis::gl::sys::GLenum, uapi::c};
pub type EGLint = i32;
pub type EGLenum = c::c_uint;
pub type EGLBoolean = c::c_uint;
#[allow(dead_code)]
pub type EGLuint64KHR = u64;
pub type EGLAttrib = isize;
egl_transparent!(EGLDisplay);
egl_transparent!(EGLSurface);
egl_transparent!(EGLConfig);
egl_transparent!(EGLImageKHR);
egl_transparent!(EGLContext);
egl_transparent!(EGLClientBuffer);
egl_transparent!(EGLLabelKHR);
pub type EGLDEBUGPROCKHR = unsafe extern "C" fn(
error: EGLenum,
command: *const c::c_char,
message_type: EGLint,
thread_label: EGLLabelKHR,
object_label: EGLLabelKHR,
message: *const c::c_char,
);
pub const EGL_EXTENSIONS: EGLint = 0x3055;
pub const EGL_DEBUG_MSG_CRITICAL_KHR: EGLint = 0x33B9;
pub const EGL_DEBUG_MSG_ERROR_KHR: EGLint = 0x33BA;
pub const EGL_DEBUG_MSG_WARN_KHR: EGLint = 0x33BB;
pub const EGL_DEBUG_MSG_INFO_KHR: EGLint = 0x33BC;
pub const EGL_TRUE: EGLBoolean = 1;
pub const EGL_FALSE: EGLBoolean = 0;
pub const EGL_NONE: EGLint = 0x3038;
pub const EGL_SUCCESS: EGLint = 0x3000;
pub const EGL_NOT_INITIALIZED: EGLint = 0x3001;
pub const EGL_BAD_ACCESS: EGLint = 0x3002;
pub const EGL_BAD_ALLOC: EGLint = 0x3003;
pub const EGL_BAD_ATTRIBUTE: EGLint = 0x3004;
pub const EGL_BAD_CONFIG: EGLint = 0x3005;
pub const EGL_BAD_CONTEXT: EGLint = 0x3006;
pub const EGL_BAD_CURRENT_SURFACE: EGLint = 0x3007;
pub const EGL_BAD_DISPLAY: EGLint = 0x3008;
pub const EGL_BAD_MATCH: EGLint = 0x3009;
pub const EGL_BAD_NATIVE_PIXMAP: EGLint = 0x300A;
pub const EGL_BAD_NATIVE_WINDOW: EGLint = 0x300B;
pub const EGL_BAD_PARAMETER: EGLint = 0x300C;
pub const EGL_BAD_SURFACE: EGLint = 0x300D;
pub const EGL_CONTEXT_LOST: EGLint = 0x300E;
pub const EGL_BAD_DEVICE_EXT: EGLint = 0x322B;
pub const EGL_OPENGL_ES_API: EGLenum = 0x30A0;
pub const EGL_PLATFORM_GBM_KHR: EGLint = 0x31D7;
pub const EGL_CONTEXT_CLIENT_VERSION: EGLint = 0x3098;
pub const EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT: EGLint = 0x3138;
pub const EGL_LOSE_CONTEXT_ON_RESET_EXT: EGLint = 0x31BF;
pub const GL_GUILTY_CONTEXT_RESET_ARB: GLenum = 0x8253;
pub const GL_INNOCENT_CONTEXT_RESET_ARB: GLenum = 0x8254;
pub const GL_UNKNOWN_CONTEXT_RESET_ARB: GLenum = 0x8255;
pub const EGL_WIDTH: EGLint = 0x3057;
pub const EGL_HEIGHT: EGLint = 0x3056;
pub const EGL_LINUX_DRM_FOURCC_EXT: EGLint = 0x3271;
pub const EGL_DMA_BUF_PLANE0_FD_EXT: EGLint = 0x3272;
pub const EGL_DMA_BUF_PLANE0_OFFSET_EXT: EGLint = 0x3273;
pub const EGL_DMA_BUF_PLANE0_PITCH_EXT: EGLint = 0x3274;
pub const EGL_DMA_BUF_PLANE1_FD_EXT: EGLint = 0x3275;
pub const EGL_DMA_BUF_PLANE1_OFFSET_EXT: EGLint = 0x3276;
pub const EGL_DMA_BUF_PLANE1_PITCH_EXT: EGLint = 0x3277;
pub const EGL_DMA_BUF_PLANE2_FD_EXT: EGLint = 0x3278;
pub const EGL_DMA_BUF_PLANE2_OFFSET_EXT: EGLint = 0x3279;
pub const EGL_DMA_BUF_PLANE2_PITCH_EXT: EGLint = 0x327A;
pub const EGL_DMA_BUF_PLANE3_FD_EXT: EGLint = 0x3440;
pub const EGL_DMA_BUF_PLANE3_OFFSET_EXT: EGLint = 0x3441;
pub const EGL_DMA_BUF_PLANE3_PITCH_EXT: EGLint = 0x3442;
pub const EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT: EGLint = 0x3443;
pub const EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT: EGLint = 0x3444;
pub const EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT: EGLint = 0x3445;
pub const EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT: EGLint = 0x3446;
pub const EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT: EGLint = 0x3447;
pub const EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT: EGLint = 0x3448;
pub const EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT: EGLint = 0x3449;
pub const EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT: EGLint = 0x344A;
pub const EGL_IMAGE_PRESERVED_KHR: EGLint = 0x30D2;
pub const EGL_LINUX_DMA_BUF_EXT: EGLint = 0x3270;
#[link(name = "EGL")]
extern "C" {
pub fn eglQueryString(dpy: EGLDisplay, name: EGLint) -> *const c::c_char;
pub fn eglGetProcAddress(procname: *const c::c_char) -> *mut u8;
pub fn eglBindAPI(api: EGLenum) -> EGLBoolean;
pub fn eglTerminate(dpy: EGLDisplay) -> EGLBoolean;
pub fn eglInitialize(dpy: EGLDisplay, major: *mut EGLint, minor: *mut EGLint) -> EGLBoolean;
pub fn eglCreateContext(
dpy: EGLDisplay,
config: EGLConfig,
share_context: EGLContext,
attrib_list: *const EGLint,
) -> EGLContext;
pub fn eglDestroyContext(dpy: EGLDisplay, ctx: EGLContext) -> EGLBoolean;
pub fn eglMakeCurrent(
dpy: EGLDisplay,
draw: EGLSurface,
read: EGLSurface,
ctx: EGLContext,
) -> EGLBoolean;
}

140
src/gfx_apis/gl/ext.rs Normal file
View file

@ -0,0 +1,140 @@
use {
crate::{
gfx_apis::gl::{
egl::sys::{eglQueryString, EGLDisplay, EGL_EXTENSIONS},
gl::sys::{glGetString, GL_EXTENSIONS},
},
utils::trim::AsciiTrim,
},
ahash::AHashSet,
bstr::ByteSlice,
std::{ffi::CStr, ops::BitOrAssign, str},
uapi::c,
};
unsafe fn get_extensions(ext: *const c::c_char) -> Option<AHashSet<String>> {
if ext.is_null() {
return None;
}
let mut res = AHashSet::new();
let ext = CStr::from_ptr(ext).to_bytes();
for part in ext.split_str(" ") {
let name = part.trim();
if name.len() > 0 {
if let Ok(s) = str::from_utf8(name) {
res.insert(s.to_string());
}
}
}
Some(res)
}
unsafe fn get_dpy_extensions(dpy: EGLDisplay) -> Option<AHashSet<String>> {
let ext = eglQueryString(dpy, EGL_EXTENSIONS);
get_extensions(ext)
}
fn get_typed_ext<T>(exts: &AHashSet<String>, mut base: T, map: &[(&str, T)]) -> T
where
T: BitOrAssign + Copy,
{
for (name, ext) in map.iter().copied() {
if exts.contains(name) {
base |= ext;
}
}
base
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug)]
pub struct ClientExt: u32 {
const EXT_CLIENT_EXTENSION = 1 << 0;
const EXT_PLATFORM_BASE = 1 << 1;
const KHR_PLATFORM_GBM = 1 << 2;
const KHR_DEBUG = 1 << 3;
}
}
pub fn get_client_ext() -> ClientExt {
let map = [
("EGL_EXT_platform_base", ClientExt::EXT_PLATFORM_BASE),
("EGL_KHR_platform_gbm", ClientExt::KHR_PLATFORM_GBM),
("EGL_KHR_debug", ClientExt::KHR_DEBUG),
];
match unsafe { get_dpy_extensions(EGLDisplay::none()) } {
Some(exts) => get_typed_ext(&exts, ClientExt::EXT_CLIENT_EXTENSION, &map),
_ => ClientExt::empty(),
}
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug)]
pub struct DisplayExt: u32 {
const KHR_IMAGE_BASE = 1 << 0;
const EXT_IMAGE_DMA_BUF_IMPORT = 1 << 1;
const EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS = 1 << 2;
const KHR_NO_CONFIG_CONTEXT = 1 << 3;
const MESA_CONFIGLESS_CONTEXT = 1 << 4;
const KHR_SURFACELESS_CONTEXT = 1 << 5;
const IMG_CONTEXT_PRIORITY = 1 << 6;
const EXT_CREATE_CONTEXT_ROBUSTNESS = 1 << 7;
}
}
pub(crate) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt {
let map = [
("EGL_KHR_image_base", DisplayExt::KHR_IMAGE_BASE),
(
"EGL_EXT_image_dma_buf_import",
DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT,
),
(
"EGL_EXT_image_dma_buf_import_modifiers",
DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS,
),
(
"EGL_KHR_no_config_context",
DisplayExt::KHR_NO_CONFIG_CONTEXT,
),
(
"EGL_MESA_configless_context",
DisplayExt::MESA_CONFIGLESS_CONTEXT,
),
(
"EGL_KHR_surfaceless_context",
DisplayExt::KHR_SURFACELESS_CONTEXT,
),
("EGL_IMG_context_priority", DisplayExt::IMG_CONTEXT_PRIORITY),
(
"EGL_EXT_create_context_robustness",
DisplayExt::EXT_CREATE_CONTEXT_ROBUSTNESS,
),
];
match get_dpy_extensions(dpy) {
Some(exts) => get_typed_ext(&exts, DisplayExt::empty(), &map),
_ => DisplayExt::empty(),
}
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug)]
pub struct GlExt: u32 {
const GL_OES_EGL_IMAGE = 1 << 0;
const GL_OES_EGL_IMAGE_EXTERNAL = 1 << 1;
}
}
pub fn get_gl_ext() -> GlExt {
let map = [
("GL_OES_EGL_image", GlExt::GL_OES_EGL_IMAGE),
(
"GL_OES_EGL_image_external",
GlExt::GL_OES_EGL_IMAGE_EXTERNAL,
),
];
match unsafe { get_extensions(glGetString(GL_EXTENSIONS) as _) } {
Some(exts) => get_typed_ext(&exts, GlExt::empty(), &map),
_ => GlExt::empty(),
}
}

6
src/gfx_apis/gl/gl.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod frame_buffer;
pub mod program;
pub mod render_buffer;
pub mod shader;
pub mod sys;
pub mod texture;

View file

@ -0,0 +1,31 @@
use {
crate::gfx_apis::gl::{
egl::context::EglContext,
gl::{
render_buffer::GlRenderBuffer,
sys::{glDeleteFramebuffers, GLuint},
texture::GlTexture,
},
},
std::rc::Rc,
};
pub struct GlFrameBuffer {
pub _rb: Option<Rc<GlRenderBuffer>>,
pub _tex: Option<Rc<GlTexture>>,
pub ctx: Rc<EglContext>,
pub width: i32,
pub height: i32,
pub fbo: GLuint,
}
impl Drop for GlFrameBuffer {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| {
unsafe {
glDeleteFramebuffers(1, &self.fbo);
}
Ok(())
});
}
}

View file

@ -0,0 +1,72 @@
use {
crate::gfx_apis::gl::{
egl::context::EglContext,
gl::{
shader::GlShader,
sys::{
glAttachShader, glCreateProgram, glDeleteProgram, glDetachShader,
glGetAttribLocation, glGetProgramiv, glGetUniformLocation, glLinkProgram, GLint,
GLuint, GL_FALSE, GL_FRAGMENT_SHADER, GL_LINK_STATUS, GL_VERTEX_SHADER,
},
},
RenderError,
},
std::rc::Rc,
uapi::Ustr,
};
pub struct GlProgram {
pub _ctx: Rc<EglContext>,
pub prog: GLuint,
}
impl GlProgram {
pub unsafe fn from_shaders(
ctx: &Rc<EglContext>,
vert: &str,
frag: &str,
) -> Result<Self, RenderError> {
let vert = GlShader::compile(ctx, GL_VERTEX_SHADER, vert)?;
let frag = GlShader::compile(ctx, GL_FRAGMENT_SHADER, frag)?;
Self::link(&vert, &frag)
}
pub unsafe fn link(vert: &GlShader, frag: &GlShader) -> Result<Self, RenderError> {
let res = GlProgram {
_ctx: vert.ctx.clone(),
prog: glCreateProgram(),
};
glAttachShader(res.prog, vert.shader);
glAttachShader(res.prog, frag.shader);
glLinkProgram(res.prog);
glDetachShader(res.prog, vert.shader);
glDetachShader(res.prog, frag.shader);
let mut ok = 0;
glGetProgramiv(res.prog, GL_LINK_STATUS, &mut ok);
if ok == GL_FALSE as GLint {
return Err(RenderError::ProgramLink);
}
Ok(res)
}
pub unsafe fn get_uniform_location(&self, name: &Ustr) -> GLint {
glGetUniformLocation(self.prog, name.as_ptr() as _)
}
pub unsafe fn get_attrib_location(&self, name: &Ustr) -> GLint {
glGetAttribLocation(self.prog, name.as_ptr() as _)
}
}
impl Drop for GlProgram {
fn drop(&mut self) {
unsafe {
let _ = self._ctx.with_current(|| {
glDeleteProgram(self.prog);
Ok(())
});
}
}
}

View file

@ -0,0 +1,80 @@
use {
crate::gfx_apis::gl::{
egl::{context::EglContext, image::EglImage, PROCS},
gl::{
frame_buffer::GlFrameBuffer,
sys::{
glBindFramebuffer, glBindRenderbuffer, glCheckFramebufferStatus,
glDeleteRenderbuffers, glFramebufferRenderbuffer, glGenFramebuffers,
glGenRenderbuffers, GLeglImageOES, GLuint, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER,
GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER,
},
},
RenderError,
},
std::rc::Rc,
};
pub struct GlRenderBuffer {
pub img: Rc<EglImage>,
pub ctx: Rc<EglContext>,
rbo: GLuint,
}
impl GlRenderBuffer {
pub unsafe fn from_image(
img: &Rc<EglImage>,
ctx: &Rc<EglContext>,
) -> Result<Rc<GlRenderBuffer>, RenderError> {
if img.external_only {
return Err(RenderError::ExternalOnly);
}
let mut rbo = 0;
glGenRenderbuffers(1, &mut rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
PROCS.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, GLeglImageOES(img.img.0));
glBindRenderbuffer(GL_RENDERBUFFER, 0);
Ok(Rc::new(GlRenderBuffer {
img: img.clone(),
ctx: ctx.clone(),
rbo,
}))
}
pub unsafe fn create_framebuffer(self: &Rc<Self>) -> Result<GlFrameBuffer, RenderError> {
let mut fbo = 0;
glGenFramebuffers(1, &mut fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
self.rbo,
);
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
let fb = GlFrameBuffer {
_rb: Some(self.clone()),
_tex: None,
ctx: self.ctx.clone(),
fbo,
width: self.img.width,
height: self.img.height,
};
if status != GL_FRAMEBUFFER_COMPLETE {
return Err(RenderError::CreateFramebuffer);
}
Ok(fb)
}
}
impl Drop for GlRenderBuffer {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| {
unsafe {
glDeleteRenderbuffers(1, &self.rbo);
}
Ok(())
});
}
}

View file

@ -0,0 +1,50 @@
use {
crate::gfx_apis::gl::{
egl::context::EglContext,
gl::sys::{
glCompileShader, glCreateShader, glDeleteShader, glGetShaderiv, glShaderSource, GLenum,
GLuint, GL_COMPILE_STATUS, GL_FALSE,
},
sys::GLint,
RenderError,
},
std::rc::Rc,
};
pub struct GlShader {
pub ctx: Rc<EglContext>,
pub shader: GLuint,
}
impl GlShader {
pub unsafe fn compile(
ctx: &Rc<EglContext>,
ty: GLenum,
src: &str,
) -> Result<Self, RenderError> {
let shader = glCreateShader(ty);
let res = GlShader {
ctx: ctx.clone(),
shader,
};
let len = src.len() as _;
glShaderSource(shader, 1, &(src.as_ptr() as _), &len);
glCompileShader(shader);
let mut ok = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &mut ok);
if ok == GL_FALSE as GLint {
return Err(RenderError::ShaderCompileFailed);
}
Ok(res)
}
}
impl Drop for GlShader {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| unsafe {
glDeleteShader(self.shader);
Ok(())
});
}
}

151
src/gfx_apis/gl/gl/sys.rs Normal file
View file

@ -0,0 +1,151 @@
pub use uapi::c;
pub type GLbitfield = c::c_uint;
pub type GLboolean = c::c_uchar;
pub type GLchar = c::c_char;
pub type GLenum = c::c_uint;
pub type GLfloat = f32;
pub type GLint = c::c_int;
pub type GLsizei = c::c_int;
#[allow(dead_code)]
pub type GLubyte = u8;
pub type GLuint = c::c_uint;
egl_transparent!(GLeglImageOES);
pub const GL_RGBA: GLint = 0x1908;
pub const GL_BGRA_EXT: GLint = 0x80E1;
pub const GL_CLAMP_TO_EDGE: GLint = 0x812F;
pub const GL_COLOR_ATTACHMENT0: GLenum = 0x8CE0;
pub const GL_COLOR_BUFFER_BIT: GLbitfield = 0x00004000;
pub const GL_COMPILE_STATUS: GLenum = 0x8B81;
pub const GL_EXTENSIONS: GLenum = 0x1F03;
pub const GL_FALSE: GLboolean = 0;
pub const GL_FLOAT: GLenum = 0x1406;
pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30;
pub const GL_FRAMEBUFFER_COMPLETE: GLenum = 0x8CD5;
pub const GL_FRAMEBUFFER: GLenum = 0x8D40;
pub const GL_LINEAR: GLint = 0x2601;
pub const GL_LINK_STATUS: GLenum = 0x8B82;
pub const GL_RENDERBUFFER: GLenum = 0x8D41;
pub const GL_TEXTURE0: GLenum = 0x84C0;
pub const GL_TEXTURE_2D: GLenum = 0x0DE1;
pub const GL_TEXTURE_EXTERNAL_OES: GLenum = 0x8D65;
#[allow(dead_code)]
pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800;
pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801;
pub const GL_TEXTURE_WRAP_S: GLenum = 0x2802;
pub const GL_TEXTURE_WRAP_T: GLenum = 0x2803;
pub const GL_TRIANGLE_STRIP: GLenum = 0x0005;
pub const GL_TRIANGLES: GLenum = 0x0004;
pub const GL_UNPACK_ROW_LENGTH_EXT: GLenum = 0x0CF2;
pub const GL_UNSIGNED_BYTE: GLint = 0x1401;
pub const GL_VERTEX_SHADER: GLenum = 0x8B31;
pub const GL_BLEND: GLenum = 0x0BE2;
pub const GL_ONE: GLenum = 1;
pub const GL_ONE_MINUS_SRC_ALPHA: GLenum = 0x0303;
#[link(name = "GLESv2")]
extern "C" {
pub fn glGetString(name: GLenum) -> *const u8;
pub fn glGenRenderbuffers(n: GLsizei, renderbuffers: *mut GLuint);
pub fn glDeleteRenderbuffers(n: GLsizei, renderbuffers: *const GLuint);
pub fn glBindRenderbuffer(target: GLenum, renderbuffer: GLuint);
pub fn glGenFramebuffers(n: GLsizei, framebuffers: *mut GLuint);
pub fn glDeleteFramebuffers(n: GLsizei, framebuffers: *const GLuint);
pub fn glBindFramebuffer(target: GLenum, framebuffer: GLuint);
pub fn glFramebufferRenderbuffer(
target: GLenum,
attachment: GLenum,
renderbuffertarget: GLenum,
renderbuffer: GLuint,
);
#[allow(dead_code)]
pub fn glFramebufferTexture2D(
target: GLenum,
attachment: GLenum,
textarget: GLenum,
texture: GLenum,
level: GLint,
);
pub fn glCheckFramebufferStatus(target: GLenum) -> GLenum;
pub fn glClear(mask: GLbitfield);
pub fn glBlendFunc(sfactor: GLenum, dfactor: GLenum);
pub fn glClearColor(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat);
#[allow(dead_code)]
pub fn glFlush();
pub fn glReadnPixels(
x: GLint,
y: GLint,
width: GLsizei,
height: GLsizei,
format: GLenum,
ty: GLenum,
buf_size: GLsizei,
data: *mut c::c_void,
);
pub fn glGenTextures(n: GLsizei, textures: *mut GLuint);
pub fn glDeleteTextures(n: GLsizei, textures: *const GLuint);
pub fn glBindTexture(target: GLenum, texture: GLuint);
pub fn glTexParameteri(target: GLenum, pname: GLenum, param: GLint);
pub fn glPixelStorei(pname: GLenum, param: GLint);
pub fn glTexImage2D(
target: GLenum,
level: GLint,
internalformat: GLint,
width: GLsizei,
height: GLsizei,
border: GLint,
format: GLenum,
ty: GLenum,
pixels: *const c::c_void,
);
pub fn glEnable(cap: GLenum);
pub fn glDisable(cap: GLenum);
pub fn glViewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
pub fn glCreateShader(ty: GLenum) -> GLuint;
pub fn glDeleteShader(shader: GLuint);
pub fn glShaderSource(
shader: GLuint,
count: GLsizei,
string: *const *const GLchar,
length: *const GLint,
);
pub fn glCompileShader(shader: GLuint);
pub fn glGetShaderiv(shader: GLuint, pname: GLenum, params: *mut GLint);
pub fn glCreateProgram() -> GLuint;
pub fn glDeleteProgram(prog: GLuint);
pub fn glAttachShader(prog: GLuint, shader: GLuint);
pub fn glDetachShader(prog: GLuint, shader: GLuint);
pub fn glLinkProgram(prog: GLuint);
pub fn glGetProgramiv(program: GLuint, pname: GLenum, params: *mut GLint);
pub fn glUseProgram(program: GLuint);
pub fn glGetUniformLocation(prog: GLuint, name: *const GLchar) -> GLint;
pub fn glGetAttribLocation(prog: GLuint, name: *const GLchar) -> GLint;
pub fn glUniform1i(location: GLint, v0: GLint);
#[allow(dead_code)]
pub fn glUniform1f(location: GLint, v0: GLfloat);
pub fn glUniform4f(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat);
pub fn glVertexAttribPointer(
index: GLuint,
size: GLint,
ty: GLenum,
normalized: GLboolean,
stride: GLsizei,
pointer: *const u8,
);
pub fn glActiveTexture(texture: GLuint);
pub fn glEnableVertexAttribArray(idx: GLuint);
pub fn glDisableVertexAttribArray(idx: GLuint);
pub fn glDrawArrays(mode: GLenum, first: GLint, count: GLsizei);
}

View file

@ -0,0 +1,112 @@
use {
crate::{
format::Format,
gfx_apis::gl::{
egl::{context::EglContext, image::EglImage, PROCS},
ext::GlExt,
gl::sys::{
glBindTexture, glDeleteTextures, glGenTextures, glPixelStorei, glTexImage2D,
glTexParameteri, GLint, GLuint, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT,
},
sys::{GLeglImageOES, GLenum, GL_TEXTURE_EXTERNAL_OES},
RenderError,
},
},
std::{cell::Cell, rc::Rc},
};
pub struct GlTexture {
pub(crate) ctx: Rc<EglContext>,
pub img: Option<Rc<EglImage>>,
pub tex: GLuint,
pub width: i32,
pub height: i32,
pub external_only: bool,
}
pub fn image_target(external_only: bool) -> GLenum {
match external_only {
true => GL_TEXTURE_EXTERNAL_OES,
false => GL_TEXTURE_2D,
}
}
impl GlTexture {
pub fn import_img(ctx: &Rc<EglContext>, img: &Rc<EglImage>) -> Result<GlTexture, RenderError> {
if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE_EXTERNAL) {
return Err(RenderError::ExternalUnsupported);
}
let target = image_target(img.external_only);
let tex = ctx.with_current(|| unsafe {
let mut tex = 0;
glGenTextures(1, &mut tex);
glBindTexture(target, tex);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PROCS.glEGLImageTargetTexture2DOES(target, GLeglImageOES(img.img.0));
glBindTexture(target, 0);
Ok(tex)
})?;
Ok(GlTexture {
ctx: ctx.clone(),
img: Some(img.clone()),
tex,
width: img.width,
height: img.height,
external_only: img.external_only,
})
}
pub fn import_shm(
ctx: &Rc<EglContext>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
height: i32,
stride: i32,
) -> Result<GlTexture, RenderError> {
if (stride * height) as usize > data.len() {
return Err(RenderError::SmallImageBuffer);
}
let tex = ctx.with_current(|| unsafe {
let mut tex = 0;
glGenTextures(1, &mut tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format.bpp as GLint);
glTexImage2D(
GL_TEXTURE_2D,
0,
format.gl_format,
width,
height,
0,
format.gl_format as _,
format.gl_type as _,
data.as_ptr() as _,
);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
Ok(tex)
})?;
Ok(GlTexture {
ctx: ctx.clone(),
img: None,
tex,
width,
height,
external_only: false,
})
}
}
impl Drop for GlTexture {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| unsafe {
glDeleteTextures(1, &self.tex);
Ok(())
});
}
}

3
src/gfx_apis/gl/proc.rs Normal file
View file

@ -0,0 +1,3 @@
#![allow(non_snake_case, dead_code, clippy::unused_unit)]
include!(concat!(env!("OUT_DIR"), "/egl_procs.rs"));

View file

@ -0,0 +1,6 @@
pub use {context::*, framebuffer::*, image::*, texture::*};
mod context;
mod framebuffer;
mod image;
mod texture;

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
}
}

View file

@ -0,0 +1,6 @@
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}

View file

@ -0,0 +1,5 @@
attribute vec2 pos;
void main() {
gl_Position = vec4(pos, 0.0, 1.0);
}

View file

@ -0,0 +1,7 @@
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
}

View file

@ -0,0 +1,9 @@
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 v_texcoord;
uniform samplerExternalOES tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
}

View file

@ -0,0 +1,9 @@
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 v_texcoord;
uniform samplerExternalOES tex;
void main() {
gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0);
}

View file

@ -0,0 +1,7 @@
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0);
}

View file

@ -0,0 +1,8 @@
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
gl_Position = vec4(pos, 0.0, 1.0);
v_texcoord = texcoord;
}