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
322
src/gfx_apis/gl.rs
Normal file
322
src/gfx_apis/gl.rs
Normal 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
126
src/gfx_apis/gl/egl.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
96
src/gfx_apis/gl/egl/context.rs
Normal file
96
src/gfx_apis/gl/egl/context.rs
Normal 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
|
||||
}
|
||||
}
|
||||
325
src/gfx_apis/gl/egl/display.rs
Normal file
325
src/gfx_apis/gl/egl/display.rs
Normal 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))
|
||||
}
|
||||
26
src/gfx_apis/gl/egl/image.rs
Normal file
26
src/gfx_apis/gl/egl/image.rs
Normal 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
107
src/gfx_apis/gl/egl/sys.rs
Normal 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
140
src/gfx_apis/gl/ext.rs
Normal 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
6
src/gfx_apis/gl/gl.rs
Normal 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;
|
||||
31
src/gfx_apis/gl/gl/frame_buffer.rs
Normal file
31
src/gfx_apis/gl/gl/frame_buffer.rs
Normal 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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
72
src/gfx_apis/gl/gl/program.rs
Normal file
72
src/gfx_apis/gl/gl/program.rs
Normal 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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/gfx_apis/gl/gl/render_buffer.rs
Normal file
80
src/gfx_apis/gl/gl/render_buffer.rs
Normal 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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
50
src/gfx_apis/gl/gl/shader.rs
Normal file
50
src/gfx_apis/gl/gl/shader.rs
Normal 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
151
src/gfx_apis/gl/gl/sys.rs
Normal 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);
|
||||
}
|
||||
112
src/gfx_apis/gl/gl/texture.rs
Normal file
112
src/gfx_apis/gl/gl/texture.rs
Normal 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
3
src/gfx_apis/gl/proc.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#![allow(non_snake_case, dead_code, clippy::unused_unit)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/egl_procs.rs"));
|
||||
6
src/gfx_apis/gl/renderer.rs
Normal file
6
src/gfx_apis/gl/renderer.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pub use {context::*, framebuffer::*, image::*, texture::*};
|
||||
|
||||
mod context;
|
||||
mod framebuffer;
|
||||
mod image;
|
||||
mod texture;
|
||||
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
|
||||
}
|
||||
}
|
||||
6
src/gfx_apis/gl/shaders/fill.frag.glsl
Normal file
6
src/gfx_apis/gl/shaders/fill.frag.glsl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
precision mediump float;
|
||||
uniform vec4 color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = color;
|
||||
}
|
||||
5
src/gfx_apis/gl/shaders/fill.vert.glsl
Normal file
5
src/gfx_apis/gl/shaders/fill.vert.glsl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
attribute vec2 pos;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
7
src/gfx_apis/gl/shaders/tex-alpha.frag.glsl
Normal file
7
src/gfx_apis/gl/shaders/tex-alpha.frag.glsl
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
precision mediump float;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(tex, v_texcoord);
|
||||
}
|
||||
9
src/gfx_apis/gl/shaders/tex-external-alpha.frag.glsl
Normal file
9
src/gfx_apis/gl/shaders/tex-external-alpha.frag.glsl
Normal 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);
|
||||
}
|
||||
9
src/gfx_apis/gl/shaders/tex-external.frag.glsl
Normal file
9
src/gfx_apis/gl/shaders/tex-external.frag.glsl
Normal 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);
|
||||
}
|
||||
7
src/gfx_apis/gl/shaders/tex.frag.glsl
Normal file
7
src/gfx_apis/gl/shaders/tex.frag.glsl
Normal 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);
|
||||
}
|
||||
8
src/gfx_apis/gl/shaders/tex.vert.glsl
Normal file
8
src/gfx_apis/gl/shaders/tex.vert.glsl
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue