Merge pull request #96 from mahkoh/jorth/direct-scanout
metal: implement direct scanout
This commit is contained in:
commit
60f2c6e49d
49 changed files with 873 additions and 215 deletions
|
|
@ -511,6 +511,10 @@ impl Client {
|
||||||
self.send(&ClientMessage::SetGfxApi { device, api });
|
self.send(&ClientMessage::SetGfxApi { device, api });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_direct_scanout_enabled(&self, device: Option<DrmDevice>, enabled: bool) {
|
||||||
|
self.send(&ClientMessage::SetDirectScanoutEnabled { device, enabled });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connector_connected(&self, connector: Connector) -> bool {
|
pub fn connector_connected(&self, connector: Connector) -> bool {
|
||||||
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
||||||
get_response!(res, false, ConnectorConnected { connected });
|
get_response!(res, false, ConnectorConnected { connected });
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,10 @@ pub enum ClientMessage<'a> {
|
||||||
device: Option<DrmDevice>,
|
device: Option<DrmDevice>,
|
||||||
api: GfxApi,
|
api: GfxApi,
|
||||||
},
|
},
|
||||||
|
SetDirectScanoutEnabled {
|
||||||
|
device: Option<DrmDevice>,
|
||||||
|
enabled: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,11 @@ impl DrmDevice {
|
||||||
pub fn set_gfx_api(self, gfx_api: GfxApi) {
|
pub fn set_gfx_api(self, gfx_api: GfxApi) {
|
||||||
get!().set_gfx_api(Some(self), gfx_api);
|
get!().set_gfx_api(Some(self), gfx_api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables or disables direct scanout of client surfaces for this device.
|
||||||
|
pub fn set_direct_scanout_enabled(self, enabled: bool) {
|
||||||
|
get!().set_direct_scanout_enabled(Some(self), enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A graphics API.
|
/// A graphics API.
|
||||||
|
|
@ -389,3 +394,12 @@ pub enum GfxApi {
|
||||||
pub fn set_gfx_api(gfx_api: GfxApi) {
|
pub fn set_gfx_api(gfx_api: GfxApi) {
|
||||||
get!().set_gfx_api(None, gfx_api);
|
get!().set_gfx_api(None, gfx_api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables or disables direct scanout of client surfaces.
|
||||||
|
///
|
||||||
|
/// The default is `true`.
|
||||||
|
///
|
||||||
|
/// This setting can be overwritten per-device with [DrmDevice::set_direct_scanout_enabled].
|
||||||
|
pub fn set_direct_scanout_enabled(enabled: bool) {
|
||||||
|
get!().set_direct_scanout_enabled(None, enabled);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxFramebuffer,
|
gfx_api::GfxFramebuffer,
|
||||||
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||||
|
|
@ -79,6 +80,9 @@ pub trait Connector {
|
||||||
fn damage(&self);
|
fn damage(&self);
|
||||||
fn drm_dev(&self) -> Option<DrmDeviceId>;
|
fn drm_dev(&self) -> Option<DrmDeviceId>;
|
||||||
fn set_enabled(&self, enabled: bool);
|
fn set_enabled(&self, enabled: bool);
|
||||||
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -226,4 +230,5 @@ pub trait BackendDrmDevice {
|
||||||
fn set_gfx_api(&self, api: GfxApi);
|
fn set_gfx_api(&self, api: GfxApi);
|
||||||
fn gtx_api(&self) -> GfxApi;
|
fn gtx_api(&self) -> GfxApi;
|
||||||
fn version(&self) -> Result<DrmVersion, DrmError>;
|
fn version(&self) -> Result<DrmVersion, DrmError>;
|
||||||
|
fn set_direct_scanout_enabled(&self, enabled: bool);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
},
|
},
|
||||||
backends::metal::video::{MetalDrmDeviceData, MetalRenderContext, PendingDrmDevice},
|
backends::metal::video::{MetalDrmDeviceData, MetalRenderContext, PendingDrmDevice},
|
||||||
dbus::{DbusError, SignalHandler},
|
dbus::{DbusError, SignalHandler},
|
||||||
|
drm_feedback::DrmFeedback,
|
||||||
gfx_api::GfxError,
|
gfx_api::GfxError,
|
||||||
libinput::{
|
libinput::{
|
||||||
consts::{
|
consts::{
|
||||||
|
|
@ -130,6 +131,7 @@ pub struct MetalBackend {
|
||||||
pause_handler: Cell<Option<SignalHandler>>,
|
pause_handler: Cell<Option<SignalHandler>>,
|
||||||
resume_handler: Cell<Option<SignalHandler>>,
|
resume_handler: Cell<Option<SignalHandler>>,
|
||||||
ctx: CloneCell<Option<Rc<MetalRenderContext>>>,
|
ctx: CloneCell<Option<Rc<MetalRenderContext>>>,
|
||||||
|
default_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for MetalBackend {
|
impl Debug for MetalBackend {
|
||||||
|
|
@ -253,6 +255,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
|
||||||
pause_handler: Default::default(),
|
pause_handler: Default::default(),
|
||||||
resume_handler: Default::default(),
|
resume_handler: Default::default(),
|
||||||
ctx: Default::default(),
|
ctx: Default::default(),
|
||||||
|
default_feedback: Default::default(),
|
||||||
});
|
});
|
||||||
metal.pause_handler.set(Some({
|
metal.pause_handler.set(Some({
|
||||||
let mtl = metal.clone();
|
let mtl = metal.clone();
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ use {
|
||||||
ConnectorKernelId, DrmDeviceId, HardwareCursor, MonitorInfo,
|
ConnectorKernelId, DrmDeviceId, HardwareCursor, MonitorInfo,
|
||||||
},
|
},
|
||||||
backends::metal::{MetalBackend, MetalError},
|
backends::metal::{MetalBackend, MetalError},
|
||||||
|
drm_feedback::DrmFeedback,
|
||||||
edid::Descriptor,
|
edid::Descriptor,
|
||||||
format::{Format, ARGB8888, XRGB8888},
|
format::{Format, ARGB8888, XRGB8888},
|
||||||
gfx_api::{GfxContext, GfxFramebuffer, GfxTexture},
|
gfx_api::{BufferPoints, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, GfxTexture},
|
||||||
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
||||||
renderer::RenderResult,
|
renderer::RenderResult,
|
||||||
state::State,
|
state::State,
|
||||||
|
tree::OutputNode,
|
||||||
udev::UdevDevice,
|
udev::UdevDevice,
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell,
|
asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell,
|
||||||
|
|
@ -19,6 +21,7 @@ use {
|
||||||
oserror::OsError, syncqueue::SyncQueue,
|
oserror::OsError, syncqueue::SyncQueue,
|
||||||
},
|
},
|
||||||
video::{
|
video::{
|
||||||
|
dmabuf::DmaBufId,
|
||||||
drm::{
|
drm::{
|
||||||
drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector,
|
drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector,
|
||||||
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFramebuffer, DrmMaster, DrmModeInfo,
|
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFramebuffer, DrmMaster, DrmModeInfo,
|
||||||
|
|
@ -36,13 +39,14 @@ use {
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
collections::VecDeque,
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem,
|
mem,
|
||||||
ops::DerefMut,
|
ops::DerefMut,
|
||||||
rc::Rc,
|
rc::{Rc, Weak},
|
||||||
},
|
},
|
||||||
uapi::{c, c::dev_t},
|
uapi::c::{self, dev_t},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PendingDrmDevice {
|
pub struct PendingDrmDevice {
|
||||||
|
|
@ -77,6 +81,16 @@ pub struct MetalDrmDevice {
|
||||||
pub handle_events: HandleEvents,
|
pub handle_events: HandleEvents,
|
||||||
pub ctx: CloneCell<Rc<MetalRenderContext>>,
|
pub ctx: CloneCell<Rc<MetalRenderContext>>,
|
||||||
pub on_change: OnChange<crate::backend::DrmEvent>,
|
pub on_change: OnChange<crate::backend::DrmEvent>,
|
||||||
|
pub direct_scanout_enabled: Cell<Option<bool>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetalDrmDevice {
|
||||||
|
pub fn is_render_device(&self) -> bool {
|
||||||
|
if let Some(ctx) = self.backend.ctx.get() {
|
||||||
|
return ctx.dev_id == self.id;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendDrmDevice for MetalDrmDevice {
|
impl BackendDrmDevice for MetalDrmDevice {
|
||||||
|
|
@ -111,6 +125,10 @@ impl BackendDrmDevice for MetalDrmDevice {
|
||||||
fn version(&self) -> Result<DrmVersion, DrmError> {
|
fn version(&self) -> Result<DrmVersion, DrmError> {
|
||||||
self.gbm.drm.version()
|
self.gbm.drm.version()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_direct_scanout_enabled(&self, enabled: bool) {
|
||||||
|
self.direct_scanout_enabled.set(Some(enabled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HandleEvents {
|
pub struct HandleEvents {
|
||||||
|
|
@ -202,6 +220,11 @@ pub struct MetalConnector {
|
||||||
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
||||||
pub cursor_front_buffer: NumCell<usize>,
|
pub cursor_front_buffer: NumCell<usize>,
|
||||||
pub cursor_swap_buffer: Cell<bool>,
|
pub cursor_swap_buffer: Cell<bool>,
|
||||||
|
|
||||||
|
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
|
||||||
|
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
|
||||||
|
pub active_framebuffers: RefCell<VecDeque<PresentFb>>,
|
||||||
|
pub direct_scanout_active: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -310,11 +333,39 @@ impl<T> Debug for OnChange<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DirectScanoutCache {
|
||||||
|
tex: Weak<dyn GfxTexture>,
|
||||||
|
fb: Option<Rc<DrmFramebuffer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DirectScanoutData {
|
||||||
|
tex: Rc<dyn GfxTexture>,
|
||||||
|
fb: Rc<DrmFramebuffer>,
|
||||||
|
dma_buf_id: DmaBufId,
|
||||||
|
acquired: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DirectScanoutData {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.acquired.replace(false) {
|
||||||
|
self.tex.reservations().release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PresentFb {
|
||||||
|
fb: Rc<DrmFramebuffer>,
|
||||||
|
direct_scanout_data: Option<DirectScanoutData>,
|
||||||
|
}
|
||||||
|
|
||||||
impl MetalConnector {
|
impl MetalConnector {
|
||||||
async fn present_loop(self: Rc<Self>) {
|
async fn present_loop(self: Rc<Self>) {
|
||||||
loop {
|
loop {
|
||||||
self.present_trigger.triggered().await;
|
self.present_trigger.triggered().await;
|
||||||
self.present();
|
let _ = self.present(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,49 +402,193 @@ impl MetalConnector {
|
||||||
self.present_trigger.trigger();
|
self.present_trigger.trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn present(&self) {
|
fn trim_scanout_cache(&self) {
|
||||||
let crtc = match self.crtc.get() {
|
self.scanout_buffers
|
||||||
Some(crtc) => crtc,
|
.borrow_mut()
|
||||||
_ => return,
|
.retain(|_, buffer| buffer.tex.strong_count() > 0);
|
||||||
};
|
}
|
||||||
if (!self.has_damage.get() && !self.cursor_changed.get()) || !self.can_present.get() {
|
|
||||||
return;
|
fn prepare_direct_scanout(
|
||||||
|
&self,
|
||||||
|
pass: &GfxRenderPass,
|
||||||
|
plane: &Rc<MetalPlane>,
|
||||||
|
) -> Option<DirectScanoutData> {
|
||||||
|
if pass.ops.len() != 1 {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
if !crtc.active.value.get() {
|
let GfxApiOpt::CopyTexture(ct) = &pass.ops[0] else {
|
||||||
return;
|
return None;
|
||||||
|
};
|
||||||
|
if ct.source != BufferPoints::identity() {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
let plane = match self.primary_plane.get() {
|
if ct.target.x1 != 0.0
|
||||||
Some(p) => p,
|
|| ct.target.y1 != 0.0
|
||||||
_ => return,
|
|| ct.target.x2 != plane.mode_w.get() as f32
|
||||||
|
|| ct.target.y2 != plane.mode_h.get() as f32
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let Some(dmabuf) = ct.tex.dmabuf() else {
|
||||||
|
return None;
|
||||||
};
|
};
|
||||||
let buffers = match self.buffers.get() {
|
let mut cache = self.scanout_buffers.borrow_mut();
|
||||||
Some(b) => b,
|
if let Some(buffer) = cache.get(&dmabuf.id) {
|
||||||
_ => return,
|
return buffer.fb.as_ref().map(|fb| DirectScanoutData {
|
||||||
};
|
tex: buffer.tex.upgrade().unwrap(),
|
||||||
let cursor = self.cursor_plane.get();
|
fb: fb.clone(),
|
||||||
let mut changes = self.master.change();
|
dma_buf_id: dmabuf.id,
|
||||||
if self.has_damage.get() {
|
acquired: Default::default(),
|
||||||
if !self.backend.check_render_context(&self.dev) {
|
});
|
||||||
return;
|
}
|
||||||
|
let format = 'format: {
|
||||||
|
if let Some(f) = plane.formats.get(&dmabuf.format.drm) {
|
||||||
|
break 'format f;
|
||||||
}
|
}
|
||||||
let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()];
|
if let Some(opaque) = dmabuf.format.opaque {
|
||||||
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
if let Some(f) = plane.formats.get(&opaque.drm) {
|
||||||
let mut rr = self.render_result.borrow_mut();
|
break 'format f;
|
||||||
let render_fb = buffer.render_fb();
|
}
|
||||||
self.state.present_output(
|
}
|
||||||
&node,
|
return None;
|
||||||
&render_fb,
|
};
|
||||||
&buffer.render_tex,
|
if !format.modifiers.contains(&dmabuf.modifier) {
|
||||||
&mut rr,
|
return None;
|
||||||
!self.cursor_enabled.get(),
|
}
|
||||||
|
let data = match self.dev.master.add_fb(dmabuf, Some(format.format)) {
|
||||||
|
Ok(fb) => Some(DirectScanoutData {
|
||||||
|
tex: ct.tex.clone(),
|
||||||
|
fb: Rc::new(fb),
|
||||||
|
dma_buf_id: dmabuf.id,
|
||||||
|
acquired: Default::default(),
|
||||||
|
}),
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!(
|
||||||
|
"Could not import dmabuf for direct scanout: {}",
|
||||||
|
ErrorFmt(e)
|
||||||
);
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cache.insert(
|
||||||
|
dmabuf.id,
|
||||||
|
DirectScanoutCache {
|
||||||
|
tex: Rc::downgrade(&ct.tex),
|
||||||
|
fb: data.as_ref().map(|dsd| dsd.fb.clone()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn direct_scanout_enabled(&self) -> bool {
|
||||||
|
self.dev
|
||||||
|
.direct_scanout_enabled
|
||||||
|
.get()
|
||||||
|
.unwrap_or(self.state.direct_scanout_enabled.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_present_fb(
|
||||||
|
&self,
|
||||||
|
rr: &mut RenderResult,
|
||||||
|
buffer: &RenderBuffer,
|
||||||
|
plane: &Rc<MetalPlane>,
|
||||||
|
output: &OutputNode,
|
||||||
|
try_direct_scanout: bool,
|
||||||
|
) -> PresentFb {
|
||||||
|
self.trim_scanout_cache();
|
||||||
|
let buffer_fb = buffer.render_fb();
|
||||||
|
let render_hw_cursor = !self.cursor_enabled.get();
|
||||||
|
let pass = buffer_fb.create_render_pass(
|
||||||
|
output,
|
||||||
|
&self.state,
|
||||||
|
Some(output.global.pos.get()),
|
||||||
|
Some(rr),
|
||||||
|
output.global.preferred_scale.get(),
|
||||||
|
render_hw_cursor,
|
||||||
|
);
|
||||||
|
let try_direct_scanout = try_direct_scanout
|
||||||
|
&& !output.global.have_shm_screencopies()
|
||||||
|
&& self.direct_scanout_enabled()
|
||||||
|
// 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
|
||||||
|
// until the FB is no longer being scanned out, but if a notification pops up
|
||||||
|
// then we must be able to disable direct scanout immediately.
|
||||||
|
// https://gitlab.freedesktop.org/drm/amd/-/issues/3186
|
||||||
|
&& self.dev.is_render_device();
|
||||||
|
let mut direct_scanout_data = None;
|
||||||
|
if try_direct_scanout {
|
||||||
|
if let Some(dsd) = self.prepare_direct_scanout(&pass, plane) {
|
||||||
|
output.perform_screencopies(None, &dsd.tex, !render_hw_cursor);
|
||||||
|
direct_scanout_data = Some(dsd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let direct_scanout_active = direct_scanout_data.is_some();
|
||||||
|
if self.direct_scanout_active.replace(direct_scanout_active) != direct_scanout_active {
|
||||||
|
let change = match direct_scanout_active {
|
||||||
|
true => "Enabling",
|
||||||
|
false => "Disabling",
|
||||||
|
};
|
||||||
|
log::debug!("{} direct scanout on {}", change, self.kernel_id());
|
||||||
|
}
|
||||||
|
let fb = match &direct_scanout_data {
|
||||||
|
None => {
|
||||||
|
self.next_buffer.fetch_add(1);
|
||||||
|
buffer_fb.perform_render_pass(pass);
|
||||||
if let Some(tex) = &buffer.dev_tex {
|
if let Some(tex) = &buffer.dev_tex {
|
||||||
buffer.dev_fb.copy_texture(tex, 0, 0);
|
buffer.dev_fb.copy_texture(tex, 0, 0);
|
||||||
}
|
}
|
||||||
|
output.perform_screencopies(
|
||||||
|
Some(&*buffer_fb),
|
||||||
|
&buffer.render_tex,
|
||||||
|
!render_hw_cursor,
|
||||||
|
);
|
||||||
|
buffer.drm.clone()
|
||||||
|
}
|
||||||
|
Some(dsd) => dsd.fb.clone(),
|
||||||
|
};
|
||||||
|
PresentFb {
|
||||||
|
fb,
|
||||||
|
direct_scanout_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn present(&self, try_direct_scanout: bool) -> Result<(), ()> {
|
||||||
|
let crtc = match self.crtc.get() {
|
||||||
|
Some(crtc) => crtc,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
if (!self.has_damage.get() && !self.cursor_changed.get()) || !self.can_present.get() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if !crtc.active.value.get() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let plane = match self.primary_plane.get() {
|
||||||
|
Some(p) => p,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
let buffers = match self.buffers.get() {
|
||||||
|
Some(b) => b,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
let cursor = self.cursor_plane.get();
|
||||||
|
let mut new_fb = None;
|
||||||
|
let mut changes = self.master.change();
|
||||||
|
if self.has_damage.get() {
|
||||||
|
if !self.backend.check_render_context(&self.dev) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
||||||
|
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
|
||||||
|
let mut rr = self.render_result.borrow_mut();
|
||||||
|
let fb =
|
||||||
|
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout);
|
||||||
|
rr.dispatch_frame_requests();
|
||||||
|
changes.change_object(plane.id, |c| {
|
||||||
|
c.change(plane.fb_id, fb.fb.id().0 as _);
|
||||||
|
});
|
||||||
|
new_fb = Some(fb);
|
||||||
}
|
}
|
||||||
changes.change_object(plane.id, |c| {
|
|
||||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if self.cursor_changed.get() && cursor.is_some() {
|
if self.cursor_changed.get() && cursor.is_some() {
|
||||||
let plane = cursor.unwrap();
|
let plane = cursor.unwrap();
|
||||||
|
|
@ -434,12 +629,66 @@ impl MetalConnector {
|
||||||
DrmError::Atomic(OsError(c::EACCES)) => {
|
DrmError::Atomic(OsError(c::EACCES)) => {
|
||||||
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
|
||||||
}
|
}
|
||||||
_ => log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)),
|
_ => 'handle_failure: {
|
||||||
|
if let Some(fb) = &new_fb {
|
||||||
|
if let Some(dsd) = &fb.direct_scanout_data {
|
||||||
|
if self.present(false).is_ok() {
|
||||||
|
let mut cache = self.scanout_buffers.borrow_mut();
|
||||||
|
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
|
||||||
|
cache.insert(
|
||||||
|
dsd.dma_buf_id,
|
||||||
|
DirectScanoutCache {
|
||||||
|
tex: buffer.tex,
|
||||||
|
fb: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break 'handle_failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::error!("Could not set plane framebuffer: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
|
if let Some(fb) = new_fb {
|
||||||
|
if let Some(dsd) = &fb.direct_scanout_data {
|
||||||
|
dsd.tex.reservations().acquire();
|
||||||
|
dsd.acquired.set(true);
|
||||||
|
}
|
||||||
|
self.active_framebuffers.borrow_mut().push_back(fb);
|
||||||
|
}
|
||||||
self.can_present.set(false);
|
self.can_present.set(false);
|
||||||
self.has_damage.set(false);
|
self.has_damage.set(false);
|
||||||
self.cursor_changed.set(false);
|
self.cursor_changed.set(false);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_drm_feedback(&self) {
|
||||||
|
let fb = self.compute_drm_feedback();
|
||||||
|
self.drm_feedback.set(fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
|
if !self.dev.is_render_device() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let default = self.backend.default_feedback.get()?;
|
||||||
|
let plane = self.primary_plane.get()?;
|
||||||
|
let mut formats = vec![];
|
||||||
|
for (format, info) in &plane.formats {
|
||||||
|
for modifier in &info.modifiers {
|
||||||
|
formats.push((*format, *modifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match default.for_scanout(&self.state.drm_feedback_ids, self.dev.devnum, &formats) {
|
||||||
|
Ok(fb) => fb.map(Rc::new),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not compute connector feedback: {}", ErrorFmt(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -488,6 +737,10 @@ impl Connector for MetalConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drm_feedback(&self) -> Option<Rc<DrmFeedback>> {
|
||||||
|
self.drm_feedback.get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -522,7 +775,7 @@ pub enum PlaneType {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlaneFormat {
|
pub struct PlaneFormat {
|
||||||
_format: &'static Format,
|
format: &'static Format,
|
||||||
modifiers: IndexSet<Modifier>,
|
modifiers: IndexSet<Modifier>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,6 +791,9 @@ pub struct MetalPlane {
|
||||||
|
|
||||||
pub assigned: Cell<bool>,
|
pub assigned: Cell<bool>,
|
||||||
|
|
||||||
|
pub mode_w: Cell<i32>,
|
||||||
|
pub mode_h: Cell<i32>,
|
||||||
|
|
||||||
pub crtc_id: MutableProperty<DrmCrtc>,
|
pub crtc_id: MutableProperty<DrmCrtc>,
|
||||||
pub crtc_x: MutableProperty<i32>,
|
pub crtc_x: MutableProperty<i32>,
|
||||||
pub crtc_y: MutableProperty<i32>,
|
pub crtc_y: MutableProperty<i32>,
|
||||||
|
|
@ -611,6 +867,10 @@ fn create_connector(
|
||||||
cursor_changed: Cell::new(false),
|
cursor_changed: Cell::new(false),
|
||||||
cursor_front_buffer: Default::default(),
|
cursor_front_buffer: Default::default(),
|
||||||
cursor_swap_buffer: Cell::new(false),
|
cursor_swap_buffer: Cell::new(false),
|
||||||
|
drm_feedback: Default::default(),
|
||||||
|
scanout_buffers: Default::default(),
|
||||||
|
active_framebuffers: Default::default(),
|
||||||
|
direct_scanout_active: Cell::new(false),
|
||||||
});
|
});
|
||||||
let futures = ConnectorFutures {
|
let futures = ConnectorFutures {
|
||||||
present: backend
|
present: backend
|
||||||
|
|
@ -786,7 +1046,7 @@ fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, D
|
||||||
formats.insert(
|
formats.insert(
|
||||||
format.format,
|
format.format,
|
||||||
PlaneFormat {
|
PlaneFormat {
|
||||||
_format: f,
|
format: f,
|
||||||
modifiers: format.modifiers,
|
modifiers: format.modifiers,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -798,7 +1058,7 @@ fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, D
|
||||||
formats.insert(
|
formats.insert(
|
||||||
format,
|
format,
|
||||||
PlaneFormat {
|
PlaneFormat {
|
||||||
_format: f,
|
format: f,
|
||||||
modifiers: indexset![INVALID_MODIFIER],
|
modifiers: indexset![INVALID_MODIFIER],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -846,6 +1106,8 @@ fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, D
|
||||||
src_h: props.get("SRC_H")?.map(|v| v as u32),
|
src_h: props.get("SRC_H")?.map(|v| v as u32),
|
||||||
in_fence_fd: props.get("IN_FENCE_FD")?.id,
|
in_fence_fd: props.get("IN_FENCE_FD")?.id,
|
||||||
assigned: Cell::new(false),
|
assigned: Cell::new(false),
|
||||||
|
mode_w: Cell::new(0),
|
||||||
|
mode_h: Cell::new(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1164,6 +1426,7 @@ impl MetalBackend {
|
||||||
},
|
},
|
||||||
ctx: CloneCell::new(ctx),
|
ctx: CloneCell::new(ctx),
|
||||||
on_change: Default::default(),
|
on_change: Default::default(),
|
||||||
|
direct_scanout_enabled: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
|
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
|
||||||
|
|
@ -1294,6 +1557,12 @@ impl MetalBackend {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
connector.can_present.set(true);
|
connector.can_present.set(true);
|
||||||
|
{
|
||||||
|
let mut scanout_buffers = connector.active_framebuffers.borrow_mut();
|
||||||
|
while scanout_buffers.len() > 1 {
|
||||||
|
scanout_buffers.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
if connector.has_damage.get() || connector.cursor_changed.get() {
|
if connector.has_damage.get() || connector.cursor_changed.get() {
|
||||||
connector.schedule_present();
|
connector.schedule_present();
|
||||||
}
|
}
|
||||||
|
|
@ -1465,6 +1734,14 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
let ctx = dev.ctx.get();
|
let ctx = dev.ctx.get();
|
||||||
self.state.set_render_ctx(Some(ctx.gfx.clone()));
|
self.state.set_render_ctx(Some(ctx.gfx.clone()));
|
||||||
|
let fb = match DrmFeedback::new(&self.state.drm_feedback_ids, &*ctx.gfx) {
|
||||||
|
Ok(fb) => Some(Rc::new(fb)),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not create feedback for new context: {}", ErrorFmt(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.default_feedback.set(fb);
|
||||||
self.ctx.set(Some(ctx));
|
self.ctx.set(Some(ctx));
|
||||||
for dev in self.device_holder.drm_devices.lock().values() {
|
for dev in self.device_holder.drm_devices.lock().values() {
|
||||||
self.re_init_drm_device(&dev);
|
self.re_init_drm_device(&dev);
|
||||||
|
|
@ -1492,13 +1769,7 @@ impl MetalBackend {
|
||||||
dev_id: dev.id,
|
dev_id: dev.id,
|
||||||
gfx,
|
gfx,
|
||||||
}));
|
}));
|
||||||
let mut is_render_ctx = false;
|
if dev.is_render_device() {
|
||||||
if let Some(render_ctx) = self.ctx.get() {
|
|
||||||
if render_ctx.dev_id == dev.id {
|
|
||||||
is_render_ctx = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if is_render_ctx {
|
|
||||||
self.make_render_device(dev, true);
|
self.make_render_device(dev, true);
|
||||||
} else {
|
} else {
|
||||||
if let Some(dev) = self.device_holder.drm_devices.get(&dev.devnum) {
|
if let Some(dev) = self.device_holder.drm_devices.get(&dev.devnum) {
|
||||||
|
|
@ -1562,6 +1833,7 @@ impl MetalBackend {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
connector.send_hardware_cursor();
|
connector.send_hardware_cursor();
|
||||||
|
connector.update_drm_feedback();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -1692,14 +1964,19 @@ impl MetalBackend {
|
||||||
if cursor {
|
if cursor {
|
||||||
usage |= GBM_BO_USE_LINEAR;
|
usage |= GBM_BO_USE_LINEAR;
|
||||||
};
|
};
|
||||||
let dev_bo = dev
|
let dev_bo = dev.gbm.create_bo(
|
||||||
.gbm
|
&self.state.dma_buf_ids,
|
||||||
.create_bo(width, height, format, &possible_modifiers, usage);
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
&possible_modifiers,
|
||||||
|
usage,
|
||||||
|
);
|
||||||
let dev_bo = match dev_bo {
|
let dev_bo = match dev_bo {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
||||||
};
|
};
|
||||||
let drm_fb = match dev.master.add_fb(dev_bo.dmabuf()) {
|
let drm_fb = match dev.master.add_fb(dev_bo.dmabuf(), None) {
|
||||||
Ok(fb) => Rc::new(fb),
|
Ok(fb) => Rc::new(fb),
|
||||||
Err(e) => return Err(MetalError::Framebuffer(e)),
|
Err(e) => return Err(MetalError::Framebuffer(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -1740,11 +2017,14 @@ impl MetalBackend {
|
||||||
return Err(MetalError::MissingRenderModifier(format.name));
|
return Err(MetalError::MissingRenderModifier(format.name));
|
||||||
}
|
}
|
||||||
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
|
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
|
||||||
let render_bo =
|
let render_bo = render_ctx.gfx.gbm().create_bo(
|
||||||
render_ctx
|
&self.state.dma_buf_ids,
|
||||||
.gfx
|
width,
|
||||||
.gbm()
|
height,
|
||||||
.create_bo(width, height, format, &possible_modifiers, usage);
|
format,
|
||||||
|
&possible_modifiers,
|
||||||
|
usage,
|
||||||
|
);
|
||||||
let render_bo = match render_bo {
|
let render_bo = match render_bo {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
||||||
|
|
@ -1909,6 +2189,8 @@ impl MetalBackend {
|
||||||
c.change(primary_plane.src_h.id, (mode.vdisplay as u64) << 16);
|
c.change(primary_plane.src_h.id, (mode.vdisplay as u64) << 16);
|
||||||
});
|
});
|
||||||
primary_plane.assigned.set(true);
|
primary_plane.assigned.set(true);
|
||||||
|
primary_plane.mode_w.set(mode.hdisplay as _);
|
||||||
|
primary_plane.mode_h.set(mode.vdisplay as _);
|
||||||
primary_plane.crtc_id.value.set(crtc.id);
|
primary_plane.crtc_id.value.set(crtc.id);
|
||||||
primary_plane.crtc_x.value.set(0);
|
primary_plane.crtc_x.value.set(0);
|
||||||
primary_plane.crtc_y.value.set(0);
|
primary_plane.crtc_y.value.set(0);
|
||||||
|
|
|
||||||
|
|
@ -393,9 +393,14 @@ impl XBackend {
|
||||||
panic!("Neither linear nor invalid modifier is supported");
|
panic!("Neither linear nor invalid modifier is supported");
|
||||||
};
|
};
|
||||||
for image in &mut images {
|
for image in &mut images {
|
||||||
let bo = self
|
let bo = self.gbm.create_bo(
|
||||||
.gbm
|
&self.state.dma_buf_ids,
|
||||||
.create_bo(width, height, XRGB8888, modifier, usage)?;
|
width,
|
||||||
|
height,
|
||||||
|
XRGB8888,
|
||||||
|
modifier,
|
||||||
|
usage,
|
||||||
|
)?;
|
||||||
let dma = bo.dmabuf();
|
let dma = bo.dmabuf();
|
||||||
assert!(dma.planes.len() == 1);
|
assert!(dma.planes.len() == 1);
|
||||||
let plane = dma.planes.first().unwrap();
|
let plane = dma.planes.first().unwrap();
|
||||||
|
|
@ -984,6 +989,10 @@ impl BackendDrmDevice for XDrmDevice {
|
||||||
fn version(&self) -> Result<DrmVersion, DrmError> {
|
fn version(&self) -> Result<DrmVersion, DrmError> {
|
||||||
self.backend.gbm.drm.version()
|
self.backend.gbm.drm.version()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_direct_scanout_enabled(&self, enabled: bool) {
|
||||||
|
let _ = enabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XOutput {
|
struct XOutput {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use {
|
||||||
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||||
utils::{errorfmt::ErrorFmt, queue::AsyncQueue},
|
utils::{errorfmt::ErrorFmt, queue::AsyncQueue},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
|
||||||
drm::Drm,
|
drm::Drm,
|
||||||
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||||
},
|
},
|
||||||
|
|
@ -55,7 +55,7 @@ async fn run(screenshot: Rc<Screenshot>) {
|
||||||
fatal!("Could not take a screenshot: {}", e);
|
fatal!("Could not take a screenshot: {}", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let data = buf_to_qoi(&buf);
|
let data = buf_to_qoi(&DmaBufIds::default(), &buf);
|
||||||
let filename = screenshot
|
let filename = screenshot
|
||||||
.args
|
.args
|
||||||
.filename
|
.filename
|
||||||
|
|
@ -67,7 +67,7 @@ async fn run(screenshot: Rc<Screenshot>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buf_to_qoi(buf: &Dmabuf) -> Vec<u8> {
|
pub fn buf_to_qoi(dma_buf_ids: &DmaBufIds, buf: &Dmabuf) -> Vec<u8> {
|
||||||
let drm = match Drm::reopen(buf.drm_dev.raw(), false) {
|
let drm = match Drm::reopen(buf.drm_dev.raw(), false) {
|
||||||
Ok(drm) => drm,
|
Ok(drm) => drm,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -87,6 +87,7 @@ pub fn buf_to_qoi(buf: &Dmabuf) -> Vec<u8> {
|
||||||
fd: buf.fd.clone(),
|
fd: buf.fd.clone(),
|
||||||
});
|
});
|
||||||
let dmabuf = DmaBuf {
|
let dmabuf = DmaBuf {
|
||||||
|
id: dma_buf_ids.next(),
|
||||||
width: buf.width as _,
|
width: buf.width as _,
|
||||||
height: buf.height as _,
|
height: buf.height as _,
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,9 @@ fn start_compositor2(
|
||||||
default_gfx_api: Cell::new(GfxApi::OpenGl),
|
default_gfx_api: Cell::new(GfxApi::OpenGl),
|
||||||
activation_tokens: Default::default(),
|
activation_tokens: Default::default(),
|
||||||
toplevel_lists: Default::default(),
|
toplevel_lists: Default::default(),
|
||||||
|
dma_buf_ids: Default::default(),
|
||||||
|
drm_feedback_ids: Default::default(),
|
||||||
|
direct_scanout_enabled: Cell::new(true),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
|
||||||
|
|
@ -590,6 +590,21 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_direct_scanout_enabled(
|
||||||
|
&self,
|
||||||
|
device: Option<DrmDevice>,
|
||||||
|
enabled: bool,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
match device {
|
||||||
|
Some(dev) => self
|
||||||
|
.get_drm_device(dev)?
|
||||||
|
.dev
|
||||||
|
.set_direct_scanout_enabled(enabled),
|
||||||
|
_ => self.state.direct_scanout_enabled.set(enabled),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_get_default_workspace_capture(&self) {
|
fn handle_get_default_workspace_capture(&self) {
|
||||||
self.respond(Response::GetDefaultWorkspaceCapture {
|
self.respond(Response::GetDefaultWorkspaceCapture {
|
||||||
capture: self.state.default_workspace_capture.get(),
|
capture: self.state.default_workspace_capture.get(),
|
||||||
|
|
@ -1320,6 +1335,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetGfxApi { device, api } => {
|
ClientMessage::SetGfxApi { device, api } => {
|
||||||
self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?
|
self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?
|
||||||
}
|
}
|
||||||
|
ClientMessage::SetDirectScanoutEnabled { device, enabled } => self
|
||||||
|
.handle_set_direct_scanout_enabled(device, enabled)
|
||||||
|
.wrn("set_direct_scanout_enabled")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,45 @@
|
||||||
use {
|
use {
|
||||||
crate::{gfx_api::GfxContext, utils::oserror::OsError},
|
crate::{gfx_api::GfxContext, utils::oserror::OsError, video::Modifier},
|
||||||
|
ahash::AHashMap,
|
||||||
byteorder::{NativeEndian, WriteBytesExt},
|
byteorder::{NativeEndian, WriteBytesExt},
|
||||||
std::{io::Write, rc::Rc},
|
std::{io::Write, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{c, OwnedFd},
|
uapi::{c, OwnedFd},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DrmFeedback {
|
linear_ids!(DrmFeedbackIds, DrmFeedbackId);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrmFeedbackShared {
|
||||||
pub fd: Rc<OwnedFd>,
|
pub fd: Rc<OwnedFd>,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
pub indices: Vec<u16>,
|
|
||||||
pub main_device: c::dev_t,
|
pub main_device: c::dev_t,
|
||||||
|
pub indices: AHashMap<(u32, Modifier), u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrmFeedback {
|
||||||
|
pub id: DrmFeedbackId,
|
||||||
|
pub shared: Rc<DrmFeedbackShared>,
|
||||||
|
pub tranches: Vec<DrmFeedbackTranche>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DrmFeedbackTranche {
|
||||||
|
pub device: c::dev_t,
|
||||||
|
pub indices: Vec<u16>,
|
||||||
|
pub scanout: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmFeedback {
|
impl DrmFeedback {
|
||||||
pub fn new(ctx: &dyn GfxContext) -> Result<Self, DrmFeedbackError> {
|
pub fn new(
|
||||||
let dev_t = uapi::fstat(ctx.gbm().drm.raw())
|
ids: &DrmFeedbackIds,
|
||||||
|
render_ctx: &dyn GfxContext,
|
||||||
|
) -> Result<Self, DrmFeedbackError> {
|
||||||
|
let main_device = uapi::fstat(render_ctx.gbm().drm.raw())
|
||||||
.map_err(OsError::from)?
|
.map_err(OsError::from)?
|
||||||
.st_rdev;
|
.st_rdev;
|
||||||
let data = create_fd_data(ctx);
|
let (data, index_map) = create_fd_data(render_ctx);
|
||||||
let mut memfd =
|
let mut memfd =
|
||||||
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
|
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
|
||||||
memfd.write_all(&data).unwrap();
|
memfd.write_all(&data).unwrap();
|
||||||
|
|
@ -28,27 +49,69 @@ impl DrmFeedback {
|
||||||
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let num_indices = data.len() / 16;
|
|
||||||
let indices = (0..num_indices).map(|v| v as u16).collect();
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
fd: Rc::new(memfd),
|
id: ids.next(),
|
||||||
size: data.len(),
|
tranches: vec![DrmFeedbackTranche {
|
||||||
indices,
|
device: main_device,
|
||||||
main_device: dev_t,
|
indices: (0..index_map.len()).map(|v| v as u16).collect(),
|
||||||
|
scanout: false,
|
||||||
|
}],
|
||||||
|
shared: Rc::new(DrmFeedbackShared {
|
||||||
|
fd: Rc::new(memfd),
|
||||||
|
size: data.len(),
|
||||||
|
main_device,
|
||||||
|
indices: index_map,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_scanout(
|
||||||
|
&self,
|
||||||
|
ids: &DrmFeedbackIds,
|
||||||
|
devnum: c::dev_t,
|
||||||
|
formats: &[(u32, Modifier)],
|
||||||
|
) -> Result<Option<Self>, DrmFeedbackError> {
|
||||||
|
let mut tranches = vec![];
|
||||||
|
{
|
||||||
|
let mut indices = vec![];
|
||||||
|
for (format, modifier) in formats {
|
||||||
|
if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) {
|
||||||
|
indices.push(*idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if indices.len() > 0 {
|
||||||
|
tranches.push(DrmFeedbackTranche {
|
||||||
|
device: devnum,
|
||||||
|
indices,
|
||||||
|
scanout: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tranches.extend(self.tranches.iter().cloned());
|
||||||
|
Ok(Some(Self {
|
||||||
|
id: ids.next(),
|
||||||
|
shared: self.shared.clone(),
|
||||||
|
tranches,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_fd_data(ctx: &dyn GfxContext) -> Vec<u8> {
|
fn create_fd_data(ctx: &dyn GfxContext) -> (Vec<u8>, AHashMap<(u32, Modifier), u16>) {
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
|
let mut map = AHashMap::new();
|
||||||
|
let mut pos = 0;
|
||||||
for (format, info) in &*ctx.formats() {
|
for (format, info) in &*ctx.formats() {
|
||||||
for modifier in &info.read_modifiers {
|
for modifier in &info.read_modifiers {
|
||||||
vec.write_u32::<NativeEndian>(*format).unwrap();
|
vec.write_u32::<NativeEndian>(*format).unwrap();
|
||||||
vec.write_u32::<NativeEndian>(0).unwrap();
|
vec.write_u32::<NativeEndian>(0).unwrap();
|
||||||
vec.write_u64::<NativeEndian>(*modifier).unwrap();
|
vec.write_u64::<NativeEndian>(*modifier).unwrap();
|
||||||
|
map.insert((*format, *modifier), pos);
|
||||||
|
pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vec
|
(vec, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ pub struct Format {
|
||||||
pub has_alpha: bool,
|
pub has_alpha: bool,
|
||||||
pub shm_supported: bool,
|
pub shm_supported: bool,
|
||||||
pub pipewire: SpaVideoFormat,
|
pub pipewire: SpaVideoFormat,
|
||||||
|
pub opaque: Option<&'static Format>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Format {
|
impl PartialEq for Format {
|
||||||
|
|
@ -87,7 +88,6 @@ pub fn map_wayland_format_id(id: u32) -> u32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub static ARGB8888: &Format = &Format {
|
pub static ARGB8888: &Format = &Format {
|
||||||
name: "argb8888",
|
name: "argb8888",
|
||||||
bpp: 4,
|
bpp: 4,
|
||||||
|
|
@ -100,6 +100,7 @@ pub static ARGB8888: &Format = &Format {
|
||||||
has_alpha: true,
|
has_alpha: true,
|
||||||
shm_supported: true,
|
shm_supported: true,
|
||||||
pipewire: SPA_VIDEO_FORMAT_BGRA,
|
pipewire: SPA_VIDEO_FORMAT_BGRA,
|
||||||
|
opaque: Some(XRGB8888),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static XRGB8888: &Format = &Format {
|
pub static XRGB8888: &Format = &Format {
|
||||||
|
|
@ -114,38 +115,43 @@ pub static XRGB8888: &Format = &Format {
|
||||||
has_alpha: false,
|
has_alpha: false,
|
||||||
shm_supported: true,
|
shm_supported: true,
|
||||||
pipewire: SPA_VIDEO_FORMAT_BGRx,
|
pipewire: SPA_VIDEO_FORMAT_BGRx,
|
||||||
|
opaque: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ABGR8888: &Format = &Format {
|
||||||
|
name: "abgr8888",
|
||||||
|
bpp: 4,
|
||||||
|
gl_format: GL_RGBA,
|
||||||
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
vk_format: vk::Format::R8G8B8A8_UNORM,
|
||||||
|
drm: fourcc_code('A', 'B', '2', '4'),
|
||||||
|
wl_id: None,
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: true,
|
||||||
|
shm_supported: true,
|
||||||
|
pipewire: SPA_VIDEO_FORMAT_RGBA,
|
||||||
|
opaque: Some(XBGR8888),
|
||||||
|
};
|
||||||
|
|
||||||
|
static XBGR8888: &Format = &Format {
|
||||||
|
name: "xbgr8888",
|
||||||
|
bpp: 4,
|
||||||
|
gl_format: GL_RGBA,
|
||||||
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
vk_format: vk::Format::R8G8B8A8_UNORM,
|
||||||
|
drm: fourcc_code('X', 'B', '2', '4'),
|
||||||
|
wl_id: None,
|
||||||
|
external_only_guess: false,
|
||||||
|
has_alpha: false,
|
||||||
|
shm_supported: true,
|
||||||
|
pipewire: SPA_VIDEO_FORMAT_RGBx,
|
||||||
|
opaque: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static FORMATS: &[Format] = &[
|
pub static FORMATS: &[Format] = &[
|
||||||
*ARGB8888,
|
*ARGB8888, *XRGB8888, *ABGR8888,
|
||||||
*XRGB8888,
|
*XBGR8888,
|
||||||
// *NV12,
|
// *NV12,
|
||||||
Format {
|
|
||||||
name: "abgr8888",
|
|
||||||
bpp: 4,
|
|
||||||
gl_format: GL_RGBA,
|
|
||||||
gl_type: GL_UNSIGNED_BYTE,
|
|
||||||
vk_format: vk::Format::R8G8B8A8_UNORM,
|
|
||||||
drm: fourcc_code('A', 'B', '2', '4'),
|
|
||||||
wl_id: None,
|
|
||||||
external_only_guess: false,
|
|
||||||
has_alpha: true,
|
|
||||||
shm_supported: true,
|
|
||||||
pipewire: SPA_VIDEO_FORMAT_RGBA,
|
|
||||||
},
|
|
||||||
Format {
|
|
||||||
name: "xbgr8888",
|
|
||||||
bpp: 4,
|
|
||||||
gl_format: GL_RGBA,
|
|
||||||
gl_type: GL_UNSIGNED_BYTE,
|
|
||||||
vk_format: vk::Format::R8G8B8A8_UNORM,
|
|
||||||
drm: fourcc_code('X', 'B', '2', '4'),
|
|
||||||
wl_id: None,
|
|
||||||
external_only_guess: false,
|
|
||||||
has_alpha: false,
|
|
||||||
shm_supported: true,
|
|
||||||
pipewire: SPA_VIDEO_FORMAT_RGBx,
|
|
||||||
},
|
|
||||||
// Format {
|
// Format {
|
||||||
// name: "nv12",
|
// name: "nv12",
|
||||||
// bpp: 1, // wrong but only used for shm
|
// bpp: 1, // wrong but only used for shm
|
||||||
|
|
|
||||||
102
src/gfx_api.rs
102
src/gfx_api.rs
|
|
@ -9,6 +9,7 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
tree::Node,
|
tree::Node,
|
||||||
|
utils::numcell::NumCell,
|
||||||
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -31,7 +32,12 @@ pub enum GfxApiOpt {
|
||||||
CopyTexture(CopyTexture),
|
CopyTexture(CopyTexture),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone)]
|
pub struct GfxRenderPass {
|
||||||
|
pub ops: Vec<GfxApiOpt>,
|
||||||
|
pub clear: Option<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct BufferPoint {
|
pub struct BufferPoint {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
|
|
@ -41,9 +47,25 @@ impl BufferPoint {
|
||||||
pub fn is_leq_1(&self) -> bool {
|
pub fn is_leq_1(&self) -> bool {
|
||||||
self.x <= 1.0 && self.y <= 1.0
|
self.x <= 1.0 && self.y <= 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn top_left() -> Self {
|
||||||
|
Self { x: 0.0, y: 0.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top_right() -> Self {
|
||||||
|
Self { x: 1.0, y: 0.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottom_left() -> Self {
|
||||||
|
Self { x: 0.0, y: 1.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottom_right() -> Self {
|
||||||
|
Self { x: 1.0, y: 1.0 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct BufferPoints {
|
pub struct BufferPoints {
|
||||||
pub top_left: BufferPoint,
|
pub top_left: BufferPoint,
|
||||||
pub top_right: BufferPoint,
|
pub top_right: BufferPoint,
|
||||||
|
|
@ -79,6 +101,15 @@ impl BufferPoints {
|
||||||
&& self.bottom_left.is_leq_1()
|
&& self.bottom_left.is_leq_1()
|
||||||
&& self.bottom_right.is_leq_1()
|
&& self.bottom_right.is_leq_1()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn identity() -> Self {
|
||||||
|
Self {
|
||||||
|
top_left: BufferPoint::top_left(),
|
||||||
|
top_right: BufferPoint::top_right(),
|
||||||
|
bottom_left: BufferPoint::bottom_left(),
|
||||||
|
bottom_right: BufferPoint::bottom_right(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -172,7 +203,7 @@ impl dyn GfxFramebuffer {
|
||||||
self.render(ops, clear);
|
self.render(ops, clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_node(
|
pub fn create_render_pass(
|
||||||
&self,
|
&self,
|
||||||
node: &dyn Node,
|
node: &dyn Node,
|
||||||
state: &State,
|
state: &State,
|
||||||
|
|
@ -180,7 +211,7 @@ impl dyn GfxFramebuffer {
|
||||||
result: Option<&mut RenderResult>,
|
result: Option<&mut RenderResult>,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
) {
|
) -> GfxRenderPass {
|
||||||
let mut ops = self.take_render_ops();
|
let mut ops = self.take_render_ops();
|
||||||
let (width, height) = self.size();
|
let (width, height) = self.size();
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
|
|
@ -221,7 +252,34 @@ impl dyn GfxFramebuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let c = state.theme.colors.background.get();
|
let c = state.theme.colors.background.get();
|
||||||
self.render(ops, Some(&c));
|
GfxRenderPass {
|
||||||
|
ops,
|
||||||
|
clear: Some(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perform_render_pass(&self, pass: GfxRenderPass) {
|
||||||
|
self.render(pass.ops, pass.clear.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_node(
|
||||||
|
&self,
|
||||||
|
node: &dyn Node,
|
||||||
|
state: &State,
|
||||||
|
cursor_rect: Option<Rect>,
|
||||||
|
result: Option<&mut RenderResult>,
|
||||||
|
scale: Scale,
|
||||||
|
render_hardware_cursor: bool,
|
||||||
|
) {
|
||||||
|
let pass = self.create_render_pass(
|
||||||
|
node,
|
||||||
|
state,
|
||||||
|
cursor_rect,
|
||||||
|
result,
|
||||||
|
scale,
|
||||||
|
render_hardware_cursor,
|
||||||
|
);
|
||||||
|
self.perform_render_pass(pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
|
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
|
||||||
|
|
@ -253,6 +311,38 @@ pub trait GfxImage {
|
||||||
fn height(&self) -> i32;
|
fn height(&self) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TextureReservations {
|
||||||
|
reservations: NumCell<usize>,
|
||||||
|
on_release: Cell<Option<Box<dyn FnOnce()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureReservations {
|
||||||
|
pub fn has_reservation(&self) -> bool {
|
||||||
|
self.reservations.get() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire(&self) {
|
||||||
|
self.reservations.fetch_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(&self) {
|
||||||
|
if self.reservations.fetch_sub(1) == 1 {
|
||||||
|
if let Some(cb) = self.on_release.take() {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_released<C: FnOnce() + 'static>(&self, cb: C) {
|
||||||
|
if self.has_reservation() {
|
||||||
|
self.on_release.set(Some(Box::new(cb)));
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait GfxTexture: Debug {
|
pub trait GfxTexture: Debug {
|
||||||
fn size(&self) -> (i32, i32);
|
fn size(&self) -> (i32, i32);
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
@ -267,6 +357,8 @@ pub trait GfxTexture: Debug {
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
shm: &[Cell<u8>],
|
shm: &[Cell<u8>],
|
||||||
) -> Result<(), GfxError>;
|
) -> Result<(), GfxError>;
|
||||||
|
fn dmabuf(&self) -> Option<&DmaBuf>;
|
||||||
|
fn reservations(&self) -> &TextureReservations;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GfxContext: Debug {
|
pub trait GfxContext: Debug {
|
||||||
|
|
|
||||||
|
|
@ -259,10 +259,8 @@ impl EglDisplay {
|
||||||
Ok(Rc::new(EglImage {
|
Ok(Rc::new(EglImage {
|
||||||
dpy: self.clone(),
|
dpy: self.clone(),
|
||||||
img,
|
img,
|
||||||
width: buf.width,
|
|
||||||
height: buf.height,
|
|
||||||
external_only: format.external_only,
|
external_only: format.external_only,
|
||||||
format: buf.format,
|
dmabuf: buf.clone(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
|
||||||
gfx_apis::gl::egl::{
|
gfx_apis::gl::egl::{
|
||||||
display::EglDisplay,
|
display::EglDisplay,
|
||||||
sys::{EGLImageKHR, EGL_FALSE},
|
sys::{EGLImageKHR, EGL_FALSE},
|
||||||
PROCS,
|
PROCS,
|
||||||
},
|
},
|
||||||
|
video::dmabuf::DmaBuf,
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
@ -13,10 +13,8 @@ use {
|
||||||
pub struct EglImage {
|
pub struct EglImage {
|
||||||
pub dpy: Rc<EglDisplay>,
|
pub dpy: Rc<EglDisplay>,
|
||||||
pub img: EGLImageKHR,
|
pub img: EGLImageKHR,
|
||||||
pub width: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub external_only: bool,
|
pub external_only: bool,
|
||||||
pub format: &'static Format,
|
pub dmabuf: DmaBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EglImage {
|
impl Drop for EglImage {
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ impl GlRenderBuffer {
|
||||||
_tex: None,
|
_tex: None,
|
||||||
ctx: self.ctx.clone(),
|
ctx: self.ctx.clone(),
|
||||||
fbo,
|
fbo,
|
||||||
width: self.img.width,
|
width: self.img.dmabuf.width,
|
||||||
height: self.img.height,
|
height: self.img.dmabuf.height,
|
||||||
};
|
};
|
||||||
if status != GL_FRAMEBUFFER_COMPLETE {
|
if status != GL_FRAMEBUFFER_COMPLETE {
|
||||||
return Err(RenderError::CreateFramebuffer);
|
return Err(RenderError::CreateFramebuffer);
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,10 @@ impl GlTexture {
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
img: Some(img.clone()),
|
img: Some(img.clone()),
|
||||||
tex,
|
tex,
|
||||||
width: img.width,
|
width: img.dmabuf.width,
|
||||||
height: img.height,
|
height: img.dmabuf.height,
|
||||||
external_only: img.external_only,
|
external_only: img.external_only,
|
||||||
format: img.format,
|
format: img.dmabuf.format,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,7 @@ impl GlRenderContext {
|
||||||
Ok(Rc::new(Texture {
|
Ok(Rc::new(Texture {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
gl,
|
gl,
|
||||||
|
resv: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,6 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self) -> &'static Format {
|
fn format(&self) -> &'static Format {
|
||||||
self.gl.rb.img.format
|
self.gl.rb.img.dmabuf.format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,18 @@ pub struct Image {
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn width(&self) -> i32 {
|
pub fn width(&self) -> i32 {
|
||||||
self.gl.width
|
self.gl.dmabuf.width
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> i32 {
|
pub fn height(&self) -> i32 {
|
||||||
self.gl.height
|
self.gl.dmabuf.height
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_texture(self: &Rc<Self>) -> Result<Rc<Texture>, RenderError> {
|
fn to_texture(self: &Rc<Self>) -> Result<Rc<Texture>, RenderError> {
|
||||||
Ok(Rc::new(Texture {
|
Ok(Rc::new(Texture {
|
||||||
ctx: self.ctx.clone(),
|
ctx: self.ctx.clone(),
|
||||||
gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?,
|
gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?,
|
||||||
|
resv: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxError, GfxTexture},
|
gfx_api::{GfxError, GfxTexture, TextureReservations},
|
||||||
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext, RenderError},
|
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext, RenderError},
|
||||||
|
video::dmabuf::DmaBuf,
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
|
@ -15,6 +16,7 @@ use {
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
pub(in crate::gfx_apis::gl) ctx: Rc<GlRenderContext>,
|
pub(in crate::gfx_apis::gl) ctx: Rc<GlRenderContext>,
|
||||||
pub(in crate::gfx_apis::gl) gl: GlTexture,
|
pub(in crate::gfx_apis::gl) gl: GlTexture,
|
||||||
|
pub(in crate::gfx_apis::gl) resv: TextureReservations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Texture {
|
impl Debug for Texture {
|
||||||
|
|
@ -58,4 +60,12 @@ impl GfxTexture for Texture {
|
||||||
) -> Result<(), GfxError> {
|
) -> Result<(), GfxError> {
|
||||||
Err(RenderError::UnsupportedOperation.into())
|
Err(RenderError::UnsupportedOperation.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dmabuf(&self) -> Option<&DmaBuf> {
|
||||||
|
self.gl.img.as_ref().map(|i| &i.dmabuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reservations(&self) -> &TextureReservations {
|
||||||
|
&self.resv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture},
|
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, TextureReservations},
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
||||||
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
||||||
},
|
},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::clonecell::CloneCell,
|
utils::clonecell::CloneCell,
|
||||||
video::{
|
video::dmabuf::{DmaBuf, PlaneVec},
|
||||||
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
|
||||||
Modifier,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ash::vk::{
|
ash::vk::{
|
||||||
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
||||||
|
|
@ -36,12 +33,10 @@ use {
|
||||||
|
|
||||||
pub struct VulkanDmaBufImageTemplate {
|
pub struct VulkanDmaBufImageTemplate {
|
||||||
pub(super) renderer: Rc<VulkanRenderer>,
|
pub(super) renderer: Rc<VulkanRenderer>,
|
||||||
pub(super) format: &'static Format,
|
|
||||||
pub(super) width: u32,
|
pub(super) width: u32,
|
||||||
pub(super) height: u32,
|
pub(super) height: u32,
|
||||||
pub(super) modifier: Modifier,
|
|
||||||
pub(super) disjoint: bool,
|
pub(super) disjoint: bool,
|
||||||
pub(super) planes: PlaneVec<DmaBufPlane>,
|
pub(super) dmabuf: DmaBuf,
|
||||||
pub(super) render_max_extents: Option<VulkanMaxExtents>,
|
pub(super) render_max_extents: Option<VulkanMaxExtents>,
|
||||||
pub(super) texture_max_extents: Option<VulkanMaxExtents>,
|
pub(super) texture_max_extents: Option<VulkanMaxExtents>,
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +53,7 @@ pub struct VulkanImage {
|
||||||
pub(super) is_undefined: Cell<bool>,
|
pub(super) is_undefined: Cell<bool>,
|
||||||
pub(super) ty: VulkanImageMemory,
|
pub(super) ty: VulkanImageMemory,
|
||||||
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
||||||
|
pub(super) resv: TextureReservations,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum VulkanImageMemory {
|
pub enum VulkanImageMemory {
|
||||||
|
|
@ -216,6 +212,7 @@ impl VulkanRenderer {
|
||||||
is_undefined: Cell::new(true),
|
is_undefined: Cell::new(true),
|
||||||
ty: VulkanImageMemory::Internal(shm),
|
ty: VulkanImageMemory::Internal(shm),
|
||||||
render_ops: Default::default(),
|
render_ops: Default::default(),
|
||||||
|
resv: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,12 +257,10 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
Ok(Rc::new(VulkanDmaBufImageTemplate {
|
Ok(Rc::new(VulkanDmaBufImageTemplate {
|
||||||
renderer: self.clone(),
|
renderer: self.clone(),
|
||||||
format: dmabuf.format,
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
modifier: dmabuf.modifier,
|
|
||||||
disjoint,
|
disjoint,
|
||||||
planes: dmabuf.planes.clone(),
|
dmabuf: dmabuf.clone(),
|
||||||
render_max_extents: modifier.render_max_extents,
|
render_max_extents: modifier.render_max_extents,
|
||||||
texture_max_extents: modifier.texture_max_extents,
|
texture_max_extents: modifier.texture_max_extents,
|
||||||
}))
|
}))
|
||||||
|
|
@ -332,6 +327,7 @@ impl VulkanDmaBufImageTemplate {
|
||||||
}
|
}
|
||||||
let image = {
|
let image = {
|
||||||
let plane_layouts: PlaneVec<_> = self
|
let plane_layouts: PlaneVec<_> = self
|
||||||
|
.dmabuf
|
||||||
.planes
|
.planes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| SubresourceLayout {
|
.map(|p| SubresourceLayout {
|
||||||
|
|
@ -343,7 +339,7 @@ impl VulkanDmaBufImageTemplate {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut mod_info = ImageDrmFormatModifierExplicitCreateInfoEXT::builder()
|
let mut mod_info = ImageDrmFormatModifierExplicitCreateInfoEXT::builder()
|
||||||
.drm_format_modifier(self.modifier)
|
.drm_format_modifier(self.dmabuf.modifier)
|
||||||
.plane_layouts(&plane_layouts)
|
.plane_layouts(&plane_layouts)
|
||||||
.build();
|
.build();
|
||||||
let mut memory_image_create_info = ExternalMemoryImageCreateInfo::builder()
|
let mut memory_image_create_info = ExternalMemoryImageCreateInfo::builder()
|
||||||
|
|
@ -361,7 +357,7 @@ impl VulkanDmaBufImageTemplate {
|
||||||
};
|
};
|
||||||
let create_info = ImageCreateInfo::builder()
|
let create_info = ImageCreateInfo::builder()
|
||||||
.image_type(ImageType::TYPE_2D)
|
.image_type(ImageType::TYPE_2D)
|
||||||
.format(self.format.vk_format)
|
.format(self.dmabuf.format.vk_format)
|
||||||
.mip_levels(1)
|
.mip_levels(1)
|
||||||
.array_layers(1)
|
.array_layers(1)
|
||||||
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
|
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
|
||||||
|
|
@ -383,14 +379,14 @@ impl VulkanDmaBufImageTemplate {
|
||||||
};
|
};
|
||||||
let destroy_image = OnDrop(|| unsafe { device.device.destroy_image(image, None) });
|
let destroy_image = OnDrop(|| unsafe { device.device.destroy_image(image, None) });
|
||||||
let num_device_memories = match self.disjoint {
|
let num_device_memories = match self.disjoint {
|
||||||
true => self.planes.len(),
|
true => self.dmabuf.planes.len(),
|
||||||
false => 1,
|
false => 1,
|
||||||
};
|
};
|
||||||
let mut device_memories = PlaneVec::new();
|
let mut device_memories = PlaneVec::new();
|
||||||
let mut free_device_memories = PlaneVec::new();
|
let mut free_device_memories = PlaneVec::new();
|
||||||
let mut bind_image_plane_memory_infos = PlaneVec::new();
|
let mut bind_image_plane_memory_infos = PlaneVec::new();
|
||||||
for plane_idx in 0..num_device_memories {
|
for plane_idx in 0..num_device_memories {
|
||||||
let dma_buf_plane = &self.planes[plane_idx];
|
let dma_buf_plane = &self.dmabuf.planes[plane_idx];
|
||||||
let memory_fd_properties = unsafe {
|
let memory_fd_properties = unsafe {
|
||||||
device.external_memory_fd.get_memory_fd_properties(
|
device.external_memory_fd.get_memory_fd_properties(
|
||||||
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
|
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
|
||||||
|
|
@ -467,8 +463,8 @@ impl VulkanDmaBufImageTemplate {
|
||||||
}
|
}
|
||||||
let res = unsafe { device.device.bind_image_memory2(&bind_image_memory_infos) };
|
let res = unsafe { device.device.bind_image_memory2(&bind_image_memory_infos) };
|
||||||
res.map_err(VulkanError::BindImageMemory)?;
|
res.map_err(VulkanError::BindImageMemory)?;
|
||||||
let texture_view = device.create_image_view(image, self.format, false)?;
|
let texture_view = device.create_image_view(image, self.dmabuf.format, false)?;
|
||||||
let render_view = device.create_image_view(image, self.format, true)?;
|
let render_view = device.create_image_view(image, self.dmabuf.format, true)?;
|
||||||
free_device_memories.drain(..).for_each(mem::forget);
|
free_device_memories.drain(..).for_each(mem::forget);
|
||||||
mem::forget(destroy_image);
|
mem::forget(destroy_image);
|
||||||
Ok(Rc::new(VulkanImage {
|
Ok(Rc::new(VulkanImage {
|
||||||
|
|
@ -484,8 +480,9 @@ impl VulkanDmaBufImageTemplate {
|
||||||
template: self.clone(),
|
template: self.clone(),
|
||||||
mems: device_memories,
|
mems: device_memories,
|
||||||
}),
|
}),
|
||||||
format: self.format,
|
format: self.dmabuf.format,
|
||||||
is_undefined: Cell::new(true),
|
is_undefined: Cell::new(true),
|
||||||
|
resv: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -579,4 +576,15 @@ impl GfxTexture for VulkanImage {
|
||||||
.read_pixels(&self, x, y, width, height, stride, format, shm)
|
.read_pixels(&self, x, y, width, height, stride, format, shm)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dmabuf(&self) -> Option<&DmaBuf> {
|
||||||
|
match &self.ty {
|
||||||
|
VulkanImageMemory::DmaBuf(b) => Some(&b.template.dmabuf),
|
||||||
|
VulkanImageMemory::Internal(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reservations(&self) -> &TextureReservations {
|
||||||
|
&self.resv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,7 @@ impl VulkanRenderer {
|
||||||
flag: u32|
|
flag: u32|
|
||||||
-> Result<(), VulkanError> {
|
-> Result<(), VulkanError> {
|
||||||
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
||||||
for plane in &buf.template.planes {
|
for plane in &buf.template.dmabuf.planes {
|
||||||
let fd = dma_buf_export_sync_file(&plane.fd, flag)
|
let fd = dma_buf_export_sync_file(&plane.fd, flag)
|
||||||
.map_err(VulkanError::IoctlExportSyncFile)?;
|
.map_err(VulkanError::IoctlExportSyncFile)?;
|
||||||
let semaphore = self.allocate_semaphore()?;
|
let semaphore = self.allocate_semaphore()?;
|
||||||
|
|
@ -573,7 +573,7 @@ impl VulkanRenderer {
|
||||||
};
|
};
|
||||||
let import = |img: &VulkanImage, flag: u32| {
|
let import = |img: &VulkanImage, flag: u32| {
|
||||||
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
if let VulkanImageMemory::DmaBuf(buf) = &img.ty {
|
||||||
for plane in &buf.template.planes {
|
for plane in &buf.template.dmabuf.planes {
|
||||||
let res = dma_buf_import_sync_file(&plane.fd, flag, &syncfile)
|
let res = dma_buf_import_sync_file(&plane.fd, flag, &syncfile)
|
||||||
.map_err(VulkanError::IoctlImportSyncFile);
|
.map_err(VulkanError::IoctlImportSyncFile);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
|
@ -764,7 +764,7 @@ impl VulkanRenderer {
|
||||||
let mut semaphores = vec![];
|
let mut semaphores = vec![];
|
||||||
let mut semaphore_infos = vec![];
|
let mut semaphore_infos = vec![];
|
||||||
if let VulkanImageMemory::DmaBuf(buf) = &tex.ty {
|
if let VulkanImageMemory::DmaBuf(buf) = &tex.ty {
|
||||||
for plane in &buf.template.planes {
|
for plane in &buf.template.dmabuf.planes {
|
||||||
let fd = dma_buf_export_sync_file(&plane.fd, DMA_BUF_SYNC_READ)
|
let fd = dma_buf_export_sync_file(&plane.fd, DMA_BUF_SYNC_READ)
|
||||||
.map_err(VulkanError::IoctlExportSyncFile)?;
|
.map_err(VulkanError::IoctlExportSyncFile)?;
|
||||||
let semaphore = self.allocate_semaphore()?;
|
let semaphore = self.allocate_semaphore()?;
|
||||||
|
|
|
||||||
|
|
@ -236,9 +236,14 @@ impl JayScreencast {
|
||||||
}
|
}
|
||||||
false => &format.write_modifiers,
|
false => &format.write_modifiers,
|
||||||
};
|
};
|
||||||
let buffer =
|
let buffer = ctx.gbm().create_bo(
|
||||||
ctx.gbm()
|
&self.client.state.dma_buf_ids,
|
||||||
.create_bo(mode.width, mode.height, XRGB8888, modifiers, usage)?;
|
mode.width,
|
||||||
|
mode.height,
|
||||||
|
XRGB8888,
|
||||||
|
modifiers,
|
||||||
|
usage,
|
||||||
|
)?;
|
||||||
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
|
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
|
||||||
buffers.push(ScreencastBuffer {
|
buffers.push(ScreencastBuffer {
|
||||||
dmabuf: buffer.dmabuf().clone(),
|
dmabuf: buffer.dmabuf().clone(),
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ impl WlDrm {
|
||||||
None => return Err(WlDrmError::InvalidFormat(req.format)),
|
None => return Err(WlDrmError::InvalidFormat(req.format)),
|
||||||
};
|
};
|
||||||
let mut dmabuf = DmaBuf {
|
let mut dmabuf = DmaBuf {
|
||||||
|
id: self.client.state.dma_buf_ids.next(),
|
||||||
width: req.width,
|
width: req.width,
|
||||||
height: req.height,
|
height: req.height,
|
||||||
format,
|
format,
|
||||||
|
|
|
||||||
|
|
@ -202,9 +202,13 @@ 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,
|
||||||
fb: &dyn GfxFramebuffer,
|
fb: Option<&dyn GfxFramebuffer>,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
render_hardware_cursors: bool,
|
render_hardware_cursors: bool,
|
||||||
) {
|
) {
|
||||||
|
|
@ -232,12 +236,13 @@ impl WlOutputGlobal {
|
||||||
wl_buffer.storage.borrow_mut().deref()
|
wl_buffer.storage.borrow_mut().deref()
|
||||||
{
|
{
|
||||||
let acc = mem.access(|mem| {
|
let acc = mem.access(|mem| {
|
||||||
fb.copy_to_shm(
|
tex.clone().read_pixels(
|
||||||
rect.x1(),
|
capture.rect.x1(),
|
||||||
rect.y1(),
|
capture.rect.y1(),
|
||||||
rect.width(),
|
capture.rect.width(),
|
||||||
rect.height(),
|
capture.rect.height(),
|
||||||
XRGB8888,
|
*stride,
|
||||||
|
wl_buffer.format,
|
||||||
mem,
|
mem,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
@ -249,24 +254,25 @@ impl WlOutputGlobal {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
let acc = mem.access(|mem| {
|
if let Some(fb) = fb {
|
||||||
tex.clone().read_pixels(
|
let acc = mem.access(|mem| {
|
||||||
capture.rect.x1(),
|
fb.copy_to_shm(
|
||||||
capture.rect.y1(),
|
rect.x1(),
|
||||||
capture.rect.width(),
|
rect.y1(),
|
||||||
capture.rect.height(),
|
rect.width(),
|
||||||
*stride,
|
rect.height(),
|
||||||
wl_buffer.format,
|
XRGB8888,
|
||||||
mem,
|
mem,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
res = match acc {
|
res = match acc {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
capture.client.error(e);
|
capture.client.error(e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
|
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
client::{Client, ClientError, RequestParser},
|
client::{Client, ClientError, RequestParser},
|
||||||
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{BufferPoint, BufferPoints},
|
gfx_api::{BufferPoint, BufferPoints},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -36,6 +37,7 @@ use {
|
||||||
},
|
},
|
||||||
wp_content_type_v1::ContentType,
|
wp_content_type_v1::ContentType,
|
||||||
wp_presentation_feedback::WpPresentationFeedback,
|
wp_presentation_feedback::WpPresentationFeedback,
|
||||||
|
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::Object,
|
object::Object,
|
||||||
|
|
@ -53,7 +55,10 @@ use {
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
},
|
},
|
||||||
wire::{wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id},
|
wire::{
|
||||||
|
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||||
|
ZwpLinuxDmabufFeedbackV1Id,
|
||||||
|
},
|
||||||
xkbcommon::ModifierState,
|
xkbcommon::ModifierState,
|
||||||
xwayland::XWaylandEvent,
|
xwayland::XWaylandEvent,
|
||||||
},
|
},
|
||||||
|
|
@ -259,6 +264,7 @@ pub struct WlSurface {
|
||||||
version: u32,
|
version: u32,
|
||||||
pub has_content_type_manager: Cell<bool>,
|
pub has_content_type_manager: Cell<bool>,
|
||||||
content_type: Cell<Option<ContentType>>,
|
content_type: Cell<Option<ContentType>>,
|
||||||
|
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -411,6 +417,7 @@ impl WlSurface {
|
||||||
version,
|
version,
|
||||||
has_content_type_manager: Default::default(),
|
has_content_type_manager: Default::default(),
|
||||||
content_type: Default::default(),
|
content_type: Default::default(),
|
||||||
|
drm_feedback: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -762,7 +769,23 @@ impl WlSurface {
|
||||||
if let Some(buffer) = self.buffer.take() {
|
if let Some(buffer) = self.buffer.take() {
|
||||||
old_raw_size = Some(buffer.rect);
|
old_raw_size = Some(buffer.rect);
|
||||||
if !buffer.destroyed() {
|
if !buffer.destroyed() {
|
||||||
buffer.send_release();
|
'handle_release: {
|
||||||
|
if let Some(tex) = buffer.texture.get() {
|
||||||
|
let resv = tex.reservations();
|
||||||
|
if resv.has_reservation() {
|
||||||
|
let buffer = Rc::downgrade(&buffer);
|
||||||
|
resv.on_released(move || {
|
||||||
|
if let Some(buffer) = buffer.upgrade() {
|
||||||
|
if !buffer.destroyed() {
|
||||||
|
buffer.send_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break 'handle_release;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.send_release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(buffer) = buffer_change {
|
if let Some(buffer) = buffer_change {
|
||||||
|
|
@ -1065,6 +1088,12 @@ impl WlSurface {
|
||||||
tl.tl_data().request_attention(tl.tl_as_node());
|
tl.tl_data().request_attention(tl.tl_as_node());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_feedback(&self, fb: &DrmFeedback) {
|
||||||
|
for consumer in self.drm_feedback.lock().values() {
|
||||||
|
consumer.send_feedback(fb);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
@ -1100,6 +1129,7 @@ impl Object for WlSurface {
|
||||||
self.fractional_scale.take();
|
self.fractional_scale.take();
|
||||||
self.tearing_control.take();
|
self.tearing_control.take();
|
||||||
self.constraints.clear();
|
self.constraints.clear();
|
||||||
|
self.drm_feedback.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,10 @@ impl ToplevelNode for Xwindow {
|
||||||
self.display_link.borrow_mut().take();
|
self.display_link.borrow_mut().take();
|
||||||
self.x.surface.destroy_node();
|
self.x.surface.destroy_node();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
||||||
|
Some(self.x.surface.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackedNode for Xwindow {
|
impl StackedNode for Xwindow {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ use {
|
||||||
ifs::{
|
ifs::{
|
||||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||||
wl_seat::{NodeSeatState, SeatId, WlSeatGlobal},
|
wl_seat::{NodeSeatState, SeatId, WlSeatGlobal},
|
||||||
wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt},
|
wl_surface::{
|
||||||
|
xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt},
|
||||||
|
WlSurface,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::Object,
|
object::Object,
|
||||||
|
|
@ -544,6 +547,10 @@ impl ToplevelNode for XdgToplevel {
|
||||||
// self.map_tiled()
|
// self.map_tiled()
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
||||||
|
Some(self.xdg.surface.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XdgSurfaceExt for XdgToplevel {
|
impl XdgSurfaceExt for XdgToplevel {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +120,14 @@ 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();
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ impl ZwpLinuxBufferParamsV1 {
|
||||||
return Err(ZwpLinuxBufferParamsV1Error::InvalidModifier(modifier));
|
return Err(ZwpLinuxBufferParamsV1Error::InvalidModifier(modifier));
|
||||||
}
|
}
|
||||||
let mut dmabuf = DmaBuf {
|
let mut dmabuf = DmaBuf {
|
||||||
|
id: self.parent.client.state.dma_buf_ids.next(),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format: format.format,
|
format: format.format,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::{DrmFeedback, DrmFeedbackId},
|
||||||
|
ifs::wl_surface::WlSurface,
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::Object,
|
object::Object,
|
||||||
utils::buffd::{MsgParser, MsgParserError},
|
utils::buffd::{MsgParser, MsgParserError},
|
||||||
wire::{zwp_linux_dmabuf_feedback_v1::*, ZwpLinuxDmabufFeedbackV1Id},
|
wire::{zwp_linux_dmabuf_feedback_v1::*, ZwpLinuxDmabufFeedbackV1Id},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::{cell::Cell, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{c, OwnedFd},
|
uapi::{c, OwnedFd},
|
||||||
};
|
};
|
||||||
|
|
@ -19,24 +20,37 @@ pub struct ZwpLinuxDmabufFeedbackV1 {
|
||||||
pub id: ZwpLinuxDmabufFeedbackV1Id,
|
pub id: ZwpLinuxDmabufFeedbackV1Id,
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
|
pub last_feedback: Cell<Option<DrmFeedbackId>>,
|
||||||
|
pub surface: Option<Rc<WlSurface>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZwpLinuxDmabufFeedbackV1 {
|
impl ZwpLinuxDmabufFeedbackV1 {
|
||||||
pub fn new(id: ZwpLinuxDmabufFeedbackV1Id, client: &Rc<Client>) -> Self {
|
pub fn new(
|
||||||
|
id: ZwpLinuxDmabufFeedbackV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
surface: Option<&Rc<WlSurface>>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
last_feedback: Default::default(),
|
||||||
|
surface: surface.cloned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_feedback(&self, feedback: &DrmFeedback) {
|
pub fn send_feedback(&self, feedback: &DrmFeedback) {
|
||||||
self.send_format_table(&feedback.fd, feedback.size);
|
if self.last_feedback.replace(Some(feedback.id)) == Some(feedback.id) {
|
||||||
self.send_main_device(feedback.main_device);
|
return;
|
||||||
self.send_tranche_target_device(feedback.main_device);
|
}
|
||||||
self.send_tranche_formats(&feedback.indices);
|
self.send_format_table(&feedback.shared.fd, feedback.shared.size);
|
||||||
self.send_tranche_flags(0);
|
self.send_main_device(feedback.shared.main_device);
|
||||||
self.send_tranche_done();
|
for tranch in &feedback.tranches {
|
||||||
|
self.send_tranche_target_device(tranch.device);
|
||||||
|
self.send_tranche_formats(&tranch.indices);
|
||||||
|
self.send_tranche_flags(if tranch.scanout { SCANOUT } else { 0 });
|
||||||
|
self.send_tranche_done();
|
||||||
|
}
|
||||||
self.send_done();
|
self.send_done();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,6 +110,9 @@ impl ZwpLinuxDmabufFeedbackV1 {
|
||||||
.state
|
.state
|
||||||
.drm_feedback_consumers
|
.drm_feedback_consumers
|
||||||
.remove(&(self.client.id, self.id));
|
.remove(&(self.client.id, self.id));
|
||||||
|
if let Some(surface) = &self.surface {
|
||||||
|
surface.drm_feedback.remove(&self.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{
|
ifs::{
|
||||||
zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
|
wl_surface::WlSurface, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
|
||||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
|
|
@ -120,8 +120,9 @@ impl ZwpLinuxDmabufV1 {
|
||||||
fn get_feedback(
|
fn get_feedback(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
id: ZwpLinuxDmabufFeedbackV1Id,
|
id: ZwpLinuxDmabufFeedbackV1Id,
|
||||||
) -> Result<(), ZwpLinuxDmabufV1Error> {
|
surface: Option<&Rc<WlSurface>>,
|
||||||
let fb = Rc::new(ZwpLinuxDmabufFeedbackV1::new(id, &self.client));
|
) -> Result<Rc<ZwpLinuxDmabufFeedbackV1>, ZwpLinuxDmabufV1Error> {
|
||||||
|
let fb = Rc::new(ZwpLinuxDmabufFeedbackV1::new(id, &self.client, surface));
|
||||||
track!(self.client, fb);
|
track!(self.client, fb);
|
||||||
self.client.add_client_obj(&fb)?;
|
self.client.add_client_obj(&fb)?;
|
||||||
self.client
|
self.client
|
||||||
|
|
@ -131,7 +132,7 @@ impl ZwpLinuxDmabufV1 {
|
||||||
if let Some(feedback) = self.client.state.drm_feedback.get() {
|
if let Some(feedback) = self.client.state.drm_feedback.get() {
|
||||||
fb.send_feedback(&feedback);
|
fb.send_feedback(&feedback);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(fb)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_default_feedback(
|
fn get_default_feedback(
|
||||||
|
|
@ -139,7 +140,8 @@ impl ZwpLinuxDmabufV1 {
|
||||||
parser: MsgParser<'_, '_>,
|
parser: MsgParser<'_, '_>,
|
||||||
) -> Result<(), ZwpLinuxDmabufV1Error> {
|
) -> Result<(), ZwpLinuxDmabufV1Error> {
|
||||||
let req: GetDefaultFeedback = self.client.parse(&**self, parser)?;
|
let req: GetDefaultFeedback = self.client.parse(&**self, parser)?;
|
||||||
self.get_feedback(req.id)
|
self.get_feedback(req.id, None)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_surface_feedback(
|
fn get_surface_feedback(
|
||||||
|
|
@ -147,8 +149,10 @@ impl ZwpLinuxDmabufV1 {
|
||||||
parser: MsgParser<'_, '_>,
|
parser: MsgParser<'_, '_>,
|
||||||
) -> Result<(), ZwpLinuxDmabufV1Error> {
|
) -> Result<(), ZwpLinuxDmabufV1Error> {
|
||||||
let req: GetSurfaceFeedback = self.client.parse(&**self, parser)?;
|
let req: GetSurfaceFeedback = self.client.parse(&**self, parser)?;
|
||||||
let _surface = self.client.lookup(req.surface)?;
|
let surface = self.client.lookup(req.surface)?;
|
||||||
self.get_feedback(req.id)
|
let fb = self.get_feedback(req.id, Some(&surface))?;
|
||||||
|
surface.drm_feedback.set(req.id, fb);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ impl TestClient {
|
||||||
|
|
||||||
pub async fn take_screenshot(&self) -> Result<Vec<u8>, TestError> {
|
pub async fn take_screenshot(&self) -> Result<Vec<u8>, TestError> {
|
||||||
let dmabuf = self.jc.take_screenshot().await?;
|
let dmabuf = self.jc.take_screenshot().await?;
|
||||||
let qoi = buf_to_qoi(&dmabuf);
|
let qoi = buf_to_qoi(&self.server.state.dma_buf_ids, &dmabuf);
|
||||||
Ok(qoi)
|
Ok(qoi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use {
|
||||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
||||||
run_toplevel::RunToplevel, xrd::xrd,
|
run_toplevel::RunToplevel, xrd::xrd,
|
||||||
},
|
},
|
||||||
|
video::dmabuf::DmaBufIds,
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire_dbus::org,
|
wire_dbus::org,
|
||||||
},
|
},
|
||||||
|
|
@ -84,6 +85,7 @@ async fn run_async(eng: Rc<AsyncEngine>, ring: Rc<IoUring>) {
|
||||||
screencasts: Default::default(),
|
screencasts: Default::default(),
|
||||||
next_id: NumCell::new(1),
|
next_id: NumCell::new(1),
|
||||||
render_ctxs: Default::default(),
|
render_ctxs: Default::default(),
|
||||||
|
dma_buf_ids: Default::default(),
|
||||||
});
|
});
|
||||||
let _root = {
|
let _root = {
|
||||||
let obj = state
|
let obj = state
|
||||||
|
|
@ -143,6 +145,7 @@ struct PortalState {
|
||||||
screencasts: CopyHashMap<String, Rc<ScreencastSession>>,
|
screencasts: CopyHashMap<String, Rc<ScreencastSession>>,
|
||||||
next_id: NumCell<u32>,
|
next_id: NumCell<u32>,
|
||||||
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
||||||
|
dma_buf_ids: Rc<DmaBufIds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PortalState {
|
impl PortalState {
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,16 @@ async fn maybe_add_display(state: &Rc<PortalState>, name: &str) {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let path = format!("{}/{}", state.xrd, name);
|
let path = format!("{}/{}", state.xrd, name);
|
||||||
let con = match UsrCon::new(&state.ring, &state.wheel, &state.eng, &path, num).await {
|
let con = match UsrCon::new(
|
||||||
|
&state.ring,
|
||||||
|
&state.wheel,
|
||||||
|
&state.eng,
|
||||||
|
&state.dma_buf_ids,
|
||||||
|
&path,
|
||||||
|
num,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
|
|
|
||||||
|
|
@ -710,6 +710,7 @@ impl WindowData {
|
||||||
}
|
}
|
||||||
for _ in 0..NUM_BUFFERS {
|
for _ in 0..NUM_BUFFERS {
|
||||||
let bo = match ctx.ctx.gbm().create_bo(
|
let bo = match ctx.ctx.gbm().create_bo(
|
||||||
|
&self.dpy.state.dma_buf_ids,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
ARGB8888,
|
ARGB8888,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,15 @@ pub struct RenderResult {
|
||||||
pub presentation_feedbacks: Vec<Rc<WpPresentationFeedback>>,
|
pub presentation_feedbacks: Vec<Rc<WpPresentationFeedback>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RenderResult {
|
||||||
|
pub fn dispatch_frame_requests(&mut self) {
|
||||||
|
for fr in self.frame_requests.drain(..) {
|
||||||
|
fr.send_done();
|
||||||
|
let _ = fr.client.remove_obj(&*fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for RenderResult {
|
impl Debug for RenderResult {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("RenderResult").finish_non_exhaustive()
|
f.debug_struct("RenderResult").finish_non_exhaustive()
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
||||||
};
|
};
|
||||||
let gbm = ctx.gbm();
|
let gbm = ctx.gbm();
|
||||||
let bo = gbm.create_bo(
|
let bo = gbm.create_bo(
|
||||||
|
&state.dma_buf_ids,
|
||||||
extents.width(),
|
extents.width(),
|
||||||
extents.height(),
|
extents.height(),
|
||||||
XRGB8888,
|
XRGB8888,
|
||||||
|
|
|
||||||
16
src/state.rs
16
src/state.rs
|
|
@ -12,7 +12,7 @@ use {
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
cursor::{Cursor, ServerCursors},
|
cursor::{Cursor, ServerCursors},
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::{DrmFeedback, DrmFeedbackIds},
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
||||||
|
|
@ -50,7 +50,7 @@ use {
|
||||||
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||||
run_toplevel::RunToplevel,
|
run_toplevel::RunToplevel,
|
||||||
},
|
},
|
||||||
video::drm::Drm,
|
video::{dmabuf::DmaBufIds, drm::Drm},
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire::{
|
wire::{
|
||||||
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
||||||
|
|
@ -142,6 +142,9 @@ pub struct State {
|
||||||
pub activation_tokens: CopyHashMap<ActivationToken, ()>,
|
pub activation_tokens: CopyHashMap<ActivationToken, ()>,
|
||||||
pub toplevel_lists:
|
pub toplevel_lists:
|
||||||
CopyHashMap<(ClientId, ExtForeignToplevelListV1Id), Rc<ExtForeignToplevelListV1>>,
|
CopyHashMap<(ClientId, ExtForeignToplevelListV1Id), Rc<ExtForeignToplevelListV1>>,
|
||||||
|
pub dma_buf_ids: DmaBufIds,
|
||||||
|
pub drm_feedback_ids: DrmFeedbackIds,
|
||||||
|
pub direct_scanout_enabled: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -347,7 +350,7 @@ impl State {
|
||||||
|
|
||||||
'handle_new_feedback: {
|
'handle_new_feedback: {
|
||||||
if let Some(ctx) = &ctx {
|
if let Some(ctx) = &ctx {
|
||||||
let feedback = match DrmFeedback::new(&**ctx) {
|
let feedback = match DrmFeedback::new(&self.drm_feedback_ids, &**ctx) {
|
||||||
Ok(fb) => fb,
|
Ok(fb) => fb,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not create new DRM feedback: {}", ErrorFmt(e));
|
log::error!("Could not create new DRM feedback: {}", ErrorFmt(e));
|
||||||
|
|
@ -749,11 +752,8 @@ impl State {
|
||||||
output.global.preferred_scale.get(),
|
output.global.preferred_scale.get(),
|
||||||
render_hw_cursor,
|
render_hw_cursor,
|
||||||
);
|
);
|
||||||
for fr in rr.frame_requests.drain(..) {
|
output.perform_screencopies(Some(&**fb), tex, !render_hw_cursor);
|
||||||
fr.send_done();
|
rr.dispatch_frame_requests();
|
||||||
let _ = fr.client.remove_obj(&*fr);
|
|
||||||
}
|
|
||||||
output.perform_screencopies(&**fb, tex, !render_hw_cursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_screencopy(
|
pub fn perform_screencopy(
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ pub async fn output_render_data(state: Rc<State>) {
|
||||||
impl OutputNode {
|
impl OutputNode {
|
||||||
pub fn perform_screencopies(
|
pub fn perform_screencopies(
|
||||||
&self,
|
&self,
|
||||||
fb: &dyn GfxFramebuffer,
|
fb: Option<&dyn GfxFramebuffer>,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use {
|
||||||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||||
wl_seat::{collect_kb_foci, collect_kb_foci2, NodeSeatState, SeatId},
|
wl_seat::{collect_kb_foci, collect_kb_foci2, NodeSeatState, SeatId},
|
||||||
|
wl_surface::WlSurface,
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -161,6 +162,10 @@ pub trait ToplevelNode: Node {
|
||||||
fn tl_last_active_child(self: Rc<Self>) -> Rc<dyn ToplevelNode> {
|
fn tl_last_active_child(self: Rc<Self>) -> Rc<dyn ToplevelNode> {
|
||||||
self.tl_into_dyn()
|
self.tl_into_dyn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_scanout_surface(&self) -> Option<Rc<WlSurface>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FullscreenedData {
|
pub struct FullscreenedData {
|
||||||
|
|
@ -356,8 +361,8 @@ impl ToplevelData {
|
||||||
});
|
});
|
||||||
drop(data);
|
drop(data);
|
||||||
self.is_fullscreen.set(true);
|
self.is_fullscreen.set(true);
|
||||||
ws.set_fullscreen_node(&node);
|
|
||||||
node.tl_set_parent(ws.clone());
|
node.tl_set_parent(ws.clone());
|
||||||
|
ws.set_fullscreen_node(&node);
|
||||||
node.clone().tl_set_workspace(ws);
|
node.clone().tl_set_workspace(ws);
|
||||||
node.clone()
|
node.clone()
|
||||||
.tl_change_extents(&ws.output.get().global.pos.get());
|
.tl_change_extents(&ws.output.get().global.pos.get());
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,11 @@ impl WorkspaceNode {
|
||||||
if plane_was_visible {
|
if plane_was_visible {
|
||||||
self.plane_set_visible(false);
|
self.plane_set_visible(false);
|
||||||
}
|
}
|
||||||
|
if let Some(surface) = node.tl_scanout_surface() {
|
||||||
|
if let Some(fb) = self.output.get().global.connector.connector.drm_feedback() {
|
||||||
|
surface.send_feedback(&fb);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_fullscreen_node(&self) {
|
pub fn remove_fullscreen_node(&self) {
|
||||||
|
|
@ -151,6 +156,11 @@ impl WorkspaceNode {
|
||||||
if self.visible.get() {
|
if self.visible.get() {
|
||||||
self.plane_set_visible(true);
|
self.plane_set_visible(true);
|
||||||
}
|
}
|
||||||
|
if let Some(surface) = node.tl_scanout_surface() {
|
||||||
|
if let Some(fb) = surface.client.state.drm_feedback.get() {
|
||||||
|
surface.send_feedback(&fb);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,18 @@ use {
|
||||||
uapi::{c::ioctl, OwnedFd, _IOW, _IOWR},
|
uapi::{c::ioctl, OwnedFd, _IOW, _IOWR},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DmaBufPlane {
|
pub struct DmaBufPlane {
|
||||||
pub offset: u32,
|
pub offset: u32,
|
||||||
pub stride: u32,
|
pub stride: u32,
|
||||||
pub fd: Rc<OwnedFd>,
|
pub fd: Rc<OwnedFd>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
linear_ids!(DmaBufIds, DmaBufId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct DmaBuf {
|
pub struct DmaBuf {
|
||||||
|
pub id: DmaBufId,
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub format: &'static Format,
|
pub format: &'static Format,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ use {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend,
|
backend,
|
||||||
|
format::Format,
|
||||||
io_uring::{IoUring, IoUringError},
|
io_uring::{IoUring, IoUringError},
|
||||||
utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt},
|
utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt},
|
||||||
video::{
|
video::{
|
||||||
|
|
@ -306,7 +307,11 @@ impl DrmMaster {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_fb(self: &Rc<Self>, dma: &DmaBuf) -> Result<DrmFramebuffer, DrmError> {
|
pub fn add_fb(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
dma: &DmaBuf,
|
||||||
|
format: Option<&Format>,
|
||||||
|
) -> Result<DrmFramebuffer, DrmError> {
|
||||||
let mut modifier = 0;
|
let mut modifier = 0;
|
||||||
let mut flags = 0;
|
let mut flags = 0;
|
||||||
if dma.modifier != INVALID_MODIFIER {
|
if dma.modifier != INVALID_MODIFIER {
|
||||||
|
|
@ -330,7 +335,7 @@ impl DrmMaster {
|
||||||
self.raw(),
|
self.raw(),
|
||||||
dma.width as _,
|
dma.width as _,
|
||||||
dma.height as _,
|
dma.height as _,
|
||||||
dma.format.drm,
|
format.unwrap_or(dma.format).drm,
|
||||||
flags,
|
flags,
|
||||||
handles,
|
handles,
|
||||||
strides,
|
strides,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use {
|
||||||
format::{formats, Format},
|
format::{formats, Format},
|
||||||
utils::oserror::OsError,
|
utils::oserror::OsError,
|
||||||
video::{
|
video::{
|
||||||
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
|
||||||
drm::{Drm, DrmError},
|
drm::{Drm, DrmError},
|
||||||
Modifier, INVALID_MODIFIER,
|
Modifier, INVALID_MODIFIER,
|
||||||
},
|
},
|
||||||
|
|
@ -150,8 +150,9 @@ impl GbmBoMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn export_bo(bo: *mut Bo) -> Result<DmaBuf, GbmError> {
|
unsafe fn export_bo(dmabuf_ids: &DmaBufIds, bo: *mut Bo) -> Result<DmaBuf, GbmError> {
|
||||||
Ok(DmaBuf {
|
Ok(DmaBuf {
|
||||||
|
id: dmabuf_ids.next(),
|
||||||
width: gbm_bo_get_width(bo) as _,
|
width: gbm_bo_get_width(bo) as _,
|
||||||
height: gbm_bo_get_height(bo) as _,
|
height: gbm_bo_get_height(bo) as _,
|
||||||
modifier: gbm_bo_get_modifier(bo),
|
modifier: gbm_bo_get_modifier(bo),
|
||||||
|
|
@ -199,6 +200,7 @@ impl GbmDevice {
|
||||||
|
|
||||||
pub fn create_bo<'a>(
|
pub fn create_bo<'a>(
|
||||||
&self,
|
&self,
|
||||||
|
dma_buf_ids: &DmaBufIds,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
|
|
@ -229,7 +231,7 @@ impl GbmDevice {
|
||||||
return Err(GbmError::CreateBo(OsError::default()));
|
return Err(GbmError::CreateBo(OsError::default()));
|
||||||
}
|
}
|
||||||
let bo = BoHolder { bo };
|
let bo = BoHolder { bo };
|
||||||
let dma = export_bo(bo.bo)?;
|
let dma = export_bo(dma_buf_ids, bo.bo)?;
|
||||||
Ok(GbmBo { bo, dmabuf: dma })
|
Ok(GbmBo { bo, dmabuf: dma })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use {
|
||||||
oserror::OsError,
|
oserror::OsError,
|
||||||
vec_ext::VecExt,
|
vec_ext::VecExt,
|
||||||
},
|
},
|
||||||
|
video::dmabuf::DmaBufIds,
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire::wl_display,
|
wire::wl_display,
|
||||||
wl_usr::{
|
wl_usr::{
|
||||||
|
|
@ -77,6 +78,7 @@ pub struct UsrCon {
|
||||||
outgoing: Cell<Option<SpawnedFuture<()>>>,
|
outgoing: Cell<Option<SpawnedFuture<()>>>,
|
||||||
pub owner: CloneCell<Option<Rc<dyn UsrConOwner>>>,
|
pub owner: CloneCell<Option<Rc<dyn UsrConOwner>>>,
|
||||||
dead: Cell<bool>,
|
dead: Cell<bool>,
|
||||||
|
dma_buf_ids: Rc<DmaBufIds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UsrConOwner {
|
pub trait UsrConOwner {
|
||||||
|
|
@ -88,6 +90,7 @@ impl UsrCon {
|
||||||
ring: &Rc<IoUring>,
|
ring: &Rc<IoUring>,
|
||||||
wheel: &Rc<Wheel>,
|
wheel: &Rc<Wheel>,
|
||||||
eng: &Rc<AsyncEngine>,
|
eng: &Rc<AsyncEngine>,
|
||||||
|
dma_buf_ids: &Rc<DmaBufIds>,
|
||||||
path: &str,
|
path: &str,
|
||||||
server_id: u32,
|
server_id: u32,
|
||||||
) -> Result<Rc<Self>, UsrConError> {
|
) -> Result<Rc<Self>, UsrConError> {
|
||||||
|
|
@ -122,6 +125,7 @@ impl UsrCon {
|
||||||
outgoing: Default::default(),
|
outgoing: Default::default(),
|
||||||
owner: Default::default(),
|
owner: Default::default(),
|
||||||
dead: Cell::new(false),
|
dead: Cell::new(false),
|
||||||
|
dma_buf_ids: dma_buf_ids.clone(),
|
||||||
});
|
});
|
||||||
slf.objects.set(
|
slf.objects.set(
|
||||||
WL_DISPLAY_ID.into(),
|
WL_DISPLAY_ID.into(),
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ impl UsrJayScreencast {
|
||||||
_ => return Err(UsrJayScreencastError::UnknownFormat(ev.format)),
|
_ => return Err(UsrJayScreencastError::UnknownFormat(ev.format)),
|
||||||
};
|
};
|
||||||
self.pending_buffers.borrow_mut().push(DmaBuf {
|
self.pending_buffers.borrow_mut().push(DmaBuf {
|
||||||
|
id: self.con.dma_buf_ids.next(),
|
||||||
width: ev.width,
|
width: ev.width,
|
||||||
height: ev.height,
|
height: ev.height,
|
||||||
format,
|
format,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue