1
0
Fork 0
forked from wry/wry

wayland: add support for NV12 format

This commit is contained in:
Julian Orth 2022-05-28 22:04:00 +02:00
parent 95327685c1
commit 97e8d487a0
15 changed files with 182 additions and 119 deletions

View file

@ -16,7 +16,9 @@ pub struct Format {
pub gl_type: GLint,
pub drm: u32,
pub wl_id: Option<u32>,
pub external_only_guess: bool,
pub has_alpha: bool,
pub shm_supported: bool,
}
static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
@ -72,7 +74,9 @@ pub static FORMATS: &[Format] = &[
gl_type: GL_UNSIGNED_BYTE,
drm: ARGB8888_DRM,
wl_id: Some(ARGB8888_ID),
external_only_guess: false,
has_alpha: true,
shm_supported: true,
},
Format {
name: "xrgb8888",
@ -81,7 +85,20 @@ pub static FORMATS: &[Format] = &[
gl_type: GL_UNSIGNED_BYTE,
drm: XRGB8888_DRM,
wl_id: Some(XRGB8888_ID),
external_only_guess: false,
has_alpha: false,
shm_supported: true,
},
Format {
name: "nv12",
bpp: 1, // wrong but only used for shm
gl_format: 0, // wrong but only used for shm
gl_type: GL_UNSIGNED_BYTE, // wrong but only used for shm
drm: fourcc_code('N', 'V', '1', '2'),
wl_id: None,
external_only_guess: true,
has_alpha: false,
shm_supported: false,
},
// Format {
// id: fourcc_code('C', '8', ' ', ' '),

View file

@ -44,10 +44,12 @@ impl WlShmGlobal {
track!(client, obj);
client.add_client_obj(&obj)?;
for format in FORMATS {
client.event(Format {
self_id: id,
format: format.wl_id.unwrap_or(format.drm),
});
if format.shm_supported {
client.event(Format {
self_id: id,
format: format.wl_id.unwrap_or(format.drm),
});
}
}
Ok(())
}

View file

@ -45,7 +45,7 @@ impl WlShmPool {
let req: CreateBuffer = self.client.parse(self, parser)?;
let drm_format = map_wayland_format_id(req.format);
let format = match formats().get(&drm_format) {
Some(f) => *f,
Some(f) if f.shm_supported => *f,
_ => return Err(WlShmPoolError::InvalidFormat(req.format)),
};
if req.height < 0 || req.width < 0 || req.stride < 0 || req.offset < 0 {

View file

@ -38,9 +38,15 @@ impl ZwpLinuxDmabufV1Global {
if let Some(ctx) = client.state.render_ctx.get() {
let formats = ctx.formats();
for format in formats.values() {
if format.implicit_external_only && !ctx.supports_external_texture() {
continue;
}
obj.send_format(format.format.drm);
if version >= MODIFIERS_SINCE_VERSION {
for modifier in format.modifiers.values() {
if modifier.external_only && !ctx.supports_external_texture() {
continue;
}
obj.send_modifier(format.format.drm, modifier.modifier);
}
}

View file

@ -88,4 +88,12 @@ pub enum RenderError {
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,
}

View file

@ -38,6 +38,7 @@ use {
#[derive(Debug)]
pub struct EglFormat {
pub format: &'static Format,
pub implicit_external_only: bool,
pub modifiers: AHashMap<u64, EglModifier>,
}
@ -142,6 +143,13 @@ impl EglDisplay {
}
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,
@ -212,6 +220,7 @@ impl EglDisplay {
img,
width: buf.width,
height: buf.height,
external_only: format.external_only,
}))
}
}
@ -243,11 +252,13 @@ unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, EglFormat>, Ren
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: *format,
modifiers: query_modifiers(dpy, fmt)?,
implicit_external_only: external_only,
modifiers,
},
);
}
@ -257,14 +268,15 @@ unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, EglFormat>, Ren
unsafe fn query_modifiers(
dpy: EGLDisplay,
format: EGLint,
) -> Result<AHashMap<u64, EglModifier>, RenderError> {
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,
format,
gl_format,
num,
ptr::null_mut(),
ptr::null_mut(),
@ -277,7 +289,7 @@ unsafe fn query_modifiers(
ext_only.reserve_exact(num as usize);
let res = PROCS.eglQueryDmaBufModifiersEXT(
dpy,
format,
gl_format,
num,
mods.as_mut_ptr(),
ext_only.as_mut_ptr(),
@ -289,13 +301,6 @@ unsafe fn query_modifiers(
mods.set_len(num as usize);
ext_only.set_len(num as usize);
let mut res = AHashMap::new();
res.insert(
INVALID_MODIFIER,
EglModifier {
modifier: INVALID_MODIFIER,
external_only: false,
},
);
for (modifier, ext_only) in mods.iter().copied().zip(ext_only.iter().copied()) {
res.insert(
modifier as _,
@ -305,5 +310,16 @@ unsafe fn query_modifiers(
},
);
}
Ok(res)
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

@ -12,6 +12,7 @@ pub struct EglImage {
pub img: EGLImageKHR,
pub width: i32,
pub height: i32,
pub external_only: bool,
}
impl Drop for EglImage {

View file

@ -118,11 +118,18 @@ pub(super) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt {
bitflags::bitflags! {
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)];
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(),

View file

@ -26,6 +26,9 @@ impl GlRenderBuffer {
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);

View file

@ -30,6 +30,7 @@ pub const GL_RENDERBUFFER: GLenum = 0x8D41;
pub const GL_SCISSOR_TEST: GLenum = 0x0C11;
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;

View file

@ -3,22 +3,17 @@ use {
format::Format,
render::{
egl::{context::EglContext, image::EglImage, PROCS},
gl::{
frame_buffer::GlFrameBuffer,
sys::{
glBindFramebuffer, glBindTexture, glCheckFramebufferStatus, glDeleteTextures,
glFramebufferTexture2D, glGenFramebuffers, glGenTextures, glPixelStorei,
glTexImage2D, glTexParameteri, GLint, GLuint, GL_CLAMP_TO_EDGE,
GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_LINEAR,
GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S,
GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT,
},
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,
sys::{GLeglImageOES, GLenum, GL_TEXTURE_EXTERNAL_OES},
RenderError,
},
},
std::{cell::Cell, ptr, rc::Rc},
std::{cell::Cell, rc::Rc},
};
pub struct GlTexture {
@ -27,84 +22,30 @@ pub struct GlTexture {
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 {
#[allow(dead_code)]
pub fn new(
ctx: &Rc<EglContext>,
format: &'static Format,
width: i32,
height: i32,
) -> Result<Rc<GlTexture>, RenderError> {
let tex = ctx.with_current(|| unsafe {
let mut tex = 0;
glGenTextures(1, &mut tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(
GL_TEXTURE_2D,
0,
format.gl_format,
width,
height,
0,
format.gl_format as _,
format.gl_type as _,
ptr::null(),
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
Ok(tex)
})?;
Ok(Rc::new(GlTexture {
ctx: ctx.clone(),
img: None,
tex,
width,
height,
}))
}
#[allow(dead_code)]
pub unsafe fn to_framebuffer(self: &Rc<Self>) -> Result<Rc<GlFrameBuffer>, RenderError> {
self.ctx.with_current(|| {
let mut fbo = 0;
glGenFramebuffers(1, &mut fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
self.tex,
0,
);
let fb = GlFrameBuffer {
_rb: None,
_tex: Some(self.clone()),
ctx: self.ctx.clone(),
fbo,
width: self.width,
height: self.height,
};
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if status != GL_FRAMEBUFFER_COMPLETE {
return Err(RenderError::CreateFramebuffer);
}
Ok(Rc::new(fb))
})
}
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(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);
PROCS.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, GLeglImageOES(img.img.0));
glBindTexture(GL_TEXTURE_2D, 0);
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 {
@ -113,6 +54,7 @@ impl GlTexture {
tex,
width: img.width,
height: img.height,
external_only: img.external_only,
})
}
@ -155,6 +97,7 @@ impl GlTexture {
tex,
width,
height,
external_only: false,
})
}
}

View file

@ -6,6 +6,7 @@ use {
context::EglContext,
display::{EglDisplay, EglFormat},
},
ext::GlExt,
gl::{
program::GlProgram, render_buffer::GlRenderBuffer, sys::GLint, texture::GlTexture,
},
@ -46,14 +47,19 @@ impl TexProg {
}
}
pub(super) struct TexProgs {
pub alpha: TexProg,
pub solid: TexProg,
}
pub struct RenderContext {
pub(super) ctx: Rc<EglContext>,
pub gbm: Rc<GbmDevice>,
pub(super) render_node: Rc<CString>,
pub(super) tex_prog: TexProg,
pub(super) tex_alpha_prog: TexProg,
pub(super) tex_internal: TexProgs,
pub(super) tex_external: Option<TexProgs>,
pub(super) fill_prog: GlProgram,
pub(super) fill_prog_pos: GLint,
@ -79,6 +85,10 @@ impl RenderContext {
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
@ -97,16 +107,32 @@ impl RenderContext {
}
unsafe fn new(ctx: &Rc<EglContext>, node: &Rc<CString>) -> Result<Self, RenderError> {
let tex_prog = GlProgram::from_shaders(
ctx,
include_str!("../shaders/tex.vert.glsl"),
include_str!("../shaders/tex.frag.glsl"),
)?;
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,
include_str!("../shaders/tex.vert.glsl"),
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"),
@ -118,8 +144,11 @@ impl RenderContext {
render_node: node.clone(),
tex_prog: TexProg::from(tex_prog),
tex_alpha_prog: TexProg::from(tex_alpha_prog),
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")),

View file

@ -17,9 +17,9 @@ use {
glActiveTexture, glBindTexture, glDisableVertexAttribArray, glDrawArrays,
glEnableVertexAttribArray, glTexParameteri, glUniform1i, glUniform4f,
glUseProgram, glVertexAttribPointer, GL_FALSE, GL_FLOAT, GL_LINEAR,
GL_TEXTURE0, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_TRIANGLES,
GL_TRIANGLE_STRIP,
GL_TEXTURE0, GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP,
},
texture::image_target,
},
renderer::context::RenderContext,
sys::{glDisable, glEnable, GL_BLEND},
@ -359,17 +359,29 @@ impl Renderer<'_> {
unsafe {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.gl.tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
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 &self.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 => &self.ctx.tex_internal,
};
let prog = match format.has_alpha {
true => {
glEnable(GL_BLEND);
&self.ctx.tex_alpha_prog
&progs.alpha
}
false => {
glDisable(GL_BLEND);
&self.ctx.tex_prog
&progs.solid
}
};
@ -423,7 +435,7 @@ impl Renderer<'_> {
glDisableVertexAttribArray(prog.texcoord as _);
glDisableVertexAttribArray(prog.pos as _);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(target, 0);
}
}

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