1
0
Fork 0
forked from wry/wry

render: support shm screencopy from direct scanout

This commit is contained in:
Julian Orth 2024-02-28 15:59:20 +01:00
parent 9de63bddf3
commit 9fba5f9b45
13 changed files with 203 additions and 62 deletions

View file

@ -597,7 +597,6 @@ impl MetalConnector {
output.has_fullscreen(), output.has_fullscreen(),
); );
let try_direct_scanout = try_direct_scanout let try_direct_scanout = try_direct_scanout
&& !output.global.have_shm_screencopies()
&& self.direct_scanout_enabled() && self.direct_scanout_enabled()
// at least on AMD, using a FB on a different device for rendering will fail // at least on AMD, using a FB on a different device for rendering will fail
// and destroy the render context. it's possible to work around this by waiting // and destroy the render context. it's possible to work around this by waiting

View file

@ -1,6 +1,6 @@
use { use {
crate::{ crate::{
gfx_apis::gl::sys::{GLint, GL_BGRA_EXT, GL_RGBA, GL_UNSIGNED_BYTE}, gfx_apis::gl::sys::{GLenum, GLint, GL_BGRA_EXT, GL_RGBA, GL_RGBA8, GL_UNSIGNED_BYTE},
pipewire::pw_pod::{ pipewire::pw_pod::{
SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SpaVideoFormat, SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SpaVideoFormat, SPA_VIDEO_FORMAT_BGRA,
SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_RGBA,
@ -18,6 +18,7 @@ pub struct Format {
pub name: &'static str, pub name: &'static str,
pub bpp: u32, pub bpp: u32,
pub gl_format: GLint, pub gl_format: GLint,
pub gl_internal_format: GLenum,
pub gl_type: GLint, pub gl_type: GLint,
pub vk_format: vk::Format, pub vk_format: vk::Format,
pub drm: u32, pub drm: u32,
@ -92,6 +93,7 @@ pub static ARGB8888: &Format = &Format {
name: "argb8888", name: "argb8888",
bpp: 4, bpp: 4,
gl_format: GL_BGRA_EXT, gl_format: GL_BGRA_EXT,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE, gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::B8G8R8A8_UNORM, vk_format: vk::Format::B8G8R8A8_UNORM,
drm: ARGB8888_DRM, drm: ARGB8888_DRM,
@ -107,6 +109,7 @@ pub static XRGB8888: &Format = &Format {
name: "xrgb8888", name: "xrgb8888",
bpp: 4, bpp: 4,
gl_format: GL_BGRA_EXT, gl_format: GL_BGRA_EXT,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE, gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::B8G8R8A8_UNORM, vk_format: vk::Format::B8G8R8A8_UNORM,
drm: XRGB8888_DRM, drm: XRGB8888_DRM,
@ -122,6 +125,7 @@ static ABGR8888: &Format = &Format {
name: "abgr8888", name: "abgr8888",
bpp: 4, bpp: 4,
gl_format: GL_RGBA, gl_format: GL_RGBA,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE, gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::R8G8B8A8_UNORM, vk_format: vk::Format::R8G8B8A8_UNORM,
drm: fourcc_code('A', 'B', '2', '4'), drm: fourcc_code('A', 'B', '2', '4'),
@ -137,6 +141,7 @@ static XBGR8888: &Format = &Format {
name: "xbgr8888", name: "xbgr8888",
bpp: 4, bpp: 4,
gl_format: GL_RGBA, gl_format: GL_RGBA,
gl_internal_format: GL_RGBA8,
gl_type: GL_UNSIGNED_BYTE, gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::R8G8B8A8_UNORM, vk_format: vk::Format::R8G8B8A8_UNORM,
drm: fourcc_code('X', 'B', '2', '4'), drm: fourcc_code('X', 'B', '2', '4'),

View file

@ -415,6 +415,14 @@ pub trait GfxContext: Debug {
fn gbm(&self) -> &GbmDevice; fn gbm(&self) -> &GbmDevice;
fn gfx_api(&self) -> GfxApi; fn gfx_api(&self) -> GfxApi;
fn create_fb(
self: Rc<Self>,
width: i32,
height: i32,
stride: i32,
format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,25 +1,53 @@
use { use {
crate::gfx_apis::gl::{ crate::{
egl::{context::EglContext, image::EglImage}, format::Format,
gl::{ gfx_apis::gl::{
frame_buffer::GlFrameBuffer, egl::{context::EglContext, image::EglImage},
sys::{ gl::{
GLeglImageOES, GLuint, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER, frame_buffer::GlFrameBuffer,
GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER, sys::{
GLeglImageOES, GLuint, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER,
GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER,
},
}, },
RenderError,
}, },
RenderError,
}, },
std::rc::Rc, std::rc::Rc,
}; };
pub struct GlRenderBuffer { pub struct GlRenderBuffer {
pub img: Rc<EglImage>, pub _img: Option<Rc<EglImage>>,
pub ctx: Rc<EglContext>, pub ctx: Rc<EglContext>,
pub width: i32,
pub height: i32,
pub format: &'static Format,
rbo: GLuint, rbo: GLuint,
} }
impl GlRenderBuffer { impl GlRenderBuffer {
pub(in crate::gfx_apis::gl) unsafe fn new(
ctx: &Rc<EglContext>,
width: i32,
height: i32,
format: &'static Format,
) -> Result<Rc<GlRenderBuffer>, RenderError> {
let gles = &ctx.dpy.gles;
let mut rbo = 0;
(gles.glGenRenderbuffers)(1, &mut rbo);
(gles.glBindRenderbuffer)(GL_RENDERBUFFER, rbo);
(gles.glRenderbufferStorage)(GL_RENDERBUFFER, format.gl_internal_format, width, height);
(gles.glBindRenderbuffer)(GL_RENDERBUFFER, 0);
Ok(Rc::new(GlRenderBuffer {
_img: None,
ctx: ctx.clone(),
width,
height,
format,
rbo,
}))
}
pub(in crate::gfx_apis::gl) unsafe fn from_image( pub(in crate::gfx_apis::gl) unsafe fn from_image(
img: &Rc<EglImage>, img: &Rc<EglImage>,
ctx: &Rc<EglContext>, ctx: &Rc<EglContext>,
@ -36,8 +64,11 @@ impl GlRenderBuffer {
.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, GLeglImageOES(img.img.0)); .glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, GLeglImageOES(img.img.0));
(gles.glBindRenderbuffer)(GL_RENDERBUFFER, 0); (gles.glBindRenderbuffer)(GL_RENDERBUFFER, 0);
Ok(Rc::new(GlRenderBuffer { Ok(Rc::new(GlRenderBuffer {
img: img.clone(), _img: Some(img.clone()),
ctx: ctx.clone(), ctx: ctx.clone(),
width: img.dmabuf.width,
height: img.dmabuf.height,
format: img.dmabuf.format,
rbo, rbo,
})) }))
} }
@ -62,8 +93,8 @@ impl GlRenderBuffer {
_tex: None, _tex: None,
ctx: self.ctx.clone(), ctx: self.ctx.clone(),
fbo, fbo,
width: self.img.dmabuf.width, width: self.width,
height: self.img.dmabuf.height, height: self.height,
}; };
if status != GL_FRAMEBUFFER_COMPLETE { if status != GL_FRAMEBUFFER_COMPLETE {
return Err(RenderError::CreateFramebuffer); return Err(RenderError::CreateFramebuffer);

View file

@ -14,6 +14,7 @@ pub type GLuint = c::c_uint;
egl_transparent!(GLeglImageOES); egl_transparent!(GLeglImageOES);
pub const GL_RGBA: GLint = 0x1908; pub const GL_RGBA: GLint = 0x1908;
pub const GL_RGBA8: GLenum = 0x8058;
pub const GL_BGRA_EXT: GLint = 0x80E1; pub const GL_BGRA_EXT: GLint = 0x80E1;
pub const GL_CLAMP_TO_EDGE: GLint = 0x812F; pub const GL_CLAMP_TO_EDGE: GLint = 0x812F;
pub const GL_COLOR_ATTACHMENT0: GLenum = 0x8CE0; pub const GL_COLOR_ATTACHMENT0: GLenum = 0x8CE0;
@ -49,6 +50,7 @@ dynload! {
GLESV2: GlesV2 from "libGLESv2.so" { GLESV2: GlesV2 from "libGLESv2.so" {
glGetString: unsafe fn(name: GLenum) -> *const u8, glGetString: unsafe fn(name: GLenum) -> *const u8,
glGenRenderbuffers: unsafe fn(n: GLsizei, renderbuffers: *mut GLuint), glGenRenderbuffers: unsafe fn(n: GLsizei, renderbuffers: *mut GLuint),
glRenderbufferStorage: unsafe fn(target: GLenum, format: GLenum, width: GLsizei, height: GLsizei),
glDeleteRenderbuffers: unsafe fn(n: GLsizei, renderbuffers: *const GLuint), glDeleteRenderbuffers: unsafe fn(n: GLsizei, renderbuffers: *const GLuint),
glBindRenderbuffer: unsafe fn(target: GLenum, renderbuffer: GLuint), glBindRenderbuffer: unsafe fn(target: GLenum, renderbuffer: GLuint),
glGenFramebuffers: unsafe fn(n: GLsizei, framebuffers: *mut GLuint), glGenFramebuffers: unsafe fn(n: GLsizei, framebuffers: *mut GLuint),

View file

@ -255,4 +255,17 @@ impl GfxContext for GlRenderContext {
fn gfx_api(&self) -> GfxApi { fn gfx_api(&self) -> GfxApi {
GfxApi::OpenGl GfxApi::OpenGl
} }
fn create_fb(
self: Rc<Self>,
width: i32,
height: i32,
_stride: i32,
format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
let fb = self.ctx.with_current(|| unsafe {
GlRenderBuffer::new(&self.ctx, width, height, format)?.create_framebuffer()
})?;
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
}
} }

View file

@ -120,6 +120,6 @@ impl GfxFramebuffer for Framebuffer {
} }
fn format(&self) -> &'static Format { fn format(&self) -> &'static Format {
self.gl.rb.img.dmabuf.format self.gl.rb.format
} }
} }

View file

@ -18,7 +18,9 @@ use {
crate::{ crate::{
async_engine::AsyncEngine, async_engine::AsyncEngine,
format::Format, format::Format,
gfx_api::{GfxContext, GfxError, GfxFormat, GfxImage, GfxTexture, ResetStatus}, gfx_api::{
GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxTexture, ResetStatus,
},
gfx_apis::vulkan::{ gfx_apis::vulkan::{
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer, image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
}, },
@ -255,6 +257,19 @@ impl GfxContext for Context {
fn gfx_api(&self) -> GfxApi { fn gfx_api(&self) -> GfxApi {
GfxApi::Vulkan GfxApi::Vulkan
} }
fn create_fb(
self: Rc<Self>,
width: i32,
height: i32,
stride: i32,
format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
let fb = self
.0
.create_shm_texture(format, width, height, stride, &[], true)?;
Ok(fb)
}
} }
impl Drop for Context { impl Drop for Context {

View file

@ -176,7 +176,6 @@ impl JayScreencast {
self.client.state.perform_screencopy( self.client.state.perform_screencopy(
texture, texture,
&buffer.fb, &buffer.fb,
on.global.preferred_scale.get(),
on.global.pos.get(), on.global.pos.get(),
render_hardware_cursors, render_hardware_cursors,
x_off, x_off,

View file

@ -18,7 +18,6 @@ use {
buffd::{MsgParser, MsgParserError}, buffd::{MsgParser, MsgParserError},
clonecell::CloneCell, clonecell::CloneCell,
copyhashmap::CopyHashMap, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
linkedlist::LinkedList, linkedlist::LinkedList,
}, },
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id}, wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
@ -201,10 +200,6 @@ impl WlOutputGlobal {
Ok(()) Ok(())
} }
pub fn have_shm_screencopies(&self) -> bool {
self.pending_captures.iter().any(|c| c.is_shm.get())
}
pub fn perform_screencopies( pub fn perform_screencopies(
&self, &self,
tex: &Rc<dyn GfxTexture>, tex: &Rc<dyn GfxTexture>,
@ -235,30 +230,17 @@ impl WlOutputGlobal {
if let Some(WlBufferStorage::Shm { mem, stride }) = if let Some(WlBufferStorage::Shm { mem, stride }) =
wl_buffer.storage.borrow_mut().deref() wl_buffer.storage.borrow_mut().deref()
{ {
let acc = mem.access(|mem| { self.state.perform_shm_screencopy(
tex.clone().read_pixels( tex,
capture.rect.x1(), self.pos.get(),
capture.rect.y1(), x_off,
capture.rect.width(), y_off,
capture.rect.height(), size,
*stride, &capture,
wl_buffer.format, mem,
mem, *stride,
) wl_buffer.format,
}); );
let res = match acc {
Ok(res) => res,
Err(e) => {
capture.client.error(e);
continue;
}
};
if let Err(e) = res {
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
// capture.send_flags(FLAGS_Y_INVERT);
} else { } else {
let fb = match wl_buffer.famebuffer.get() { let fb = match wl_buffer.famebuffer.get() {
Some(fb) => fb, Some(fb) => fb,
@ -271,7 +253,6 @@ impl WlOutputGlobal {
self.state.perform_screencopy( self.state.perform_screencopy(
tex, tex,
&fb, &fb,
self.preferred_scale.get(),
self.pos.get(), self.pos.get(),
render_hardware_cursors, render_hardware_cursors,
x_off - capture.rect.x1(), x_off - capture.rect.x1(),

View file

@ -33,7 +33,6 @@ pub struct ZwlrScreencopyFrameV1 {
pub with_damage: Cell<bool>, pub with_damage: Cell<bool>,
pub output_link: Cell<Option<LinkedNode<Rc<Self>>>>, pub output_link: Cell<Option<LinkedNode<Rc<Self>>>>,
pub buffer: Cell<Option<Rc<WlBuffer>>>, pub buffer: Cell<Option<Rc<WlBuffer>>>,
pub is_shm: Cell<bool>,
pub version: u32, pub version: u32,
} }
@ -121,14 +120,6 @@ impl ZwlrScreencopyFrameV1 {
return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride);
} }
} }
let is_shm = match &*buffer.storage.borrow() {
None => false,
Some(s) => match s {
WlBufferStorage::Shm { .. } => true,
WlBufferStorage::Dmabuf(_) => false,
},
};
self.is_shm.set(is_shm);
self.buffer.set(Some(buffer)); self.buffer.set(Some(buffer));
if !with_damage { if !with_damage {
self.output.connector.connector.damage(); self.output.connector.connector.damage();

View file

@ -119,7 +119,6 @@ impl ZwlrScreencopyManagerV1 {
with_damage: Cell::new(false), with_damage: Cell::new(false),
output_link: Cell::new(None), output_link: Cell::new(None),
buffer: Cell::new(None), buffer: Cell::new(None),
is_shm: Cell::new(false),
version: self.version, version: self.version,
}); });
track!(self.client, frame); track!(self.client, frame);

View file

@ -9,12 +9,14 @@ use {
backends::dummy::DummyBackend, backends::dummy::DummyBackend,
cli::RunArgs, cli::RunArgs,
client::{Client, ClientId, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES}, client::{Client, ClientId, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES},
clientmem::ClientMemOffset,
config::ConfigProxy, config::ConfigProxy,
cursor::{Cursor, ServerCursors}, cursor::{Cursor, ServerCursors},
dbus::Dbus, dbus::Dbus,
drm_feedback::{DrmFeedback, DrmFeedbackIds}, drm_feedback::{DrmFeedback, DrmFeedbackIds},
fixed::Fixed, fixed::Fixed,
forker::ForkerProxy, forker::ForkerProxy,
format::Format,
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture}, gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
gfx_apis::create_gfx_context, gfx_apis::create_gfx_context,
globals::{Globals, GlobalsError, WaylandGlobal}, globals::{Globals, GlobalsError, WaylandGlobal},
@ -30,6 +32,7 @@ use {
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
NoneSurfaceExt, WlSurface, NoneSurfaceExt, WlSurface,
}, },
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
}, },
@ -759,7 +762,6 @@ impl State {
&self, &self,
src: &Rc<dyn GfxTexture>, src: &Rc<dyn GfxTexture>,
target: &Rc<dyn GfxFramebuffer>, target: &Rc<dyn GfxFramebuffer>,
scale: Scale,
position: Rect, position: Rect,
render_hardware_cursors: bool, render_hardware_cursors: bool,
x_off: i32, x_off: i32,
@ -771,9 +773,9 @@ impl State {
let mut renderer = Renderer { let mut renderer = Renderer {
base: RendererBase { base: RendererBase {
ops: &mut ops, ops: &mut ops,
scaled: scale != 1, scaled: false,
scale, scale: Scale::from_int(1),
scalef: scale.to_f64(), scalef: 1.0,
}, },
state: self, state: self,
result: None, result: None,
@ -782,7 +784,7 @@ impl State {
}; };
renderer renderer
.base .base
.render_texture(src, x_off, y_off, None, size, scale, None); .render_texture(src, x_off, y_off, None, size, Scale::from_int(1), None);
if render_hardware_cursors { if render_hardware_cursors {
for seat in self.globals.lock_seats().values() { for seat in self.globals.lock_seats().values() {
if let Some(cursor) = seat.get_cursor() { if let Some(cursor) = seat.get_cursor() {
@ -797,4 +799,100 @@ impl State {
} }
target.render(ops, Some(&Color::SOLID_BLACK)); target.render(ops, Some(&Color::SOLID_BLACK));
} }
fn have_hardware_cursor(&self) -> bool {
for seat in self.globals.lock_seats().values() {
if seat.get_cursor().is_some() {
if seat.hardware_cursor() {
return true;
}
}
}
false
}
pub fn perform_shm_screencopy(
&self,
src: &Rc<dyn GfxTexture>,
position: Rect,
x_off: i32,
y_off: i32,
size: Option<(i32, i32)>,
capture: &ZwlrScreencopyFrameV1,
mem: &ClientMemOffset,
stride: i32,
format: &'static Format,
) {
let (src_width, src_height) = src.size();
let mut needs_copy = capture.rect.x1() < x_off
|| capture.rect.x2() > x_off + src_width
|| capture.rect.y1() < y_off
|| capture.rect.y2() > y_off + src_height
|| self.have_hardware_cursor();
if let Some((target_width, target_height)) = size {
if (target_width, target_height) != (src_width, src_height) {
needs_copy = true;
}
}
let acc = if needs_copy {
let Some(ctx) = self.render_ctx.get() else {
log::warn!("Cannot perform shm screencopy because there is no render context");
return;
};
let fb =
match ctx.create_fb(capture.rect.width(), capture.rect.height(), stride, format) {
Ok(f) => f,
Err(e) => {
log::warn!(
"Could not create temporary fb for screencopy: {}",
ErrorFmt(e)
);
return;
}
};
self.perform_screencopy(
src,
&fb,
position,
true,
x_off - capture.rect.x1(),
y_off - capture.rect.y1(),
size,
);
mem.access(|mem| {
fb.copy_to_shm(
0,
0,
capture.rect.width(),
capture.rect.height(),
stride,
format,
mem,
)
})
} else {
mem.access(|mem| {
src.clone().read_pixels(
capture.rect.x1() - x_off,
capture.rect.y1() - y_off,
capture.rect.width(),
capture.rect.height(),
stride,
format,
mem,
)
})
};
let res = match acc {
Ok(res) => res,
Err(e) => {
capture.client.error(e);
return;
}
};
if let Err(e) = res {
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
capture.send_failed();
}
}
} }