1
0
Fork 0
forked from wry/wry

Merge pull request #602 from mahkoh/jorth/multi-nvidia-fixes

metal: improve buffer allocation failures
This commit is contained in:
mahkoh 2025-09-13 23:19:06 +02:00 committed by GitHub
commit 8aeae2d2af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 304 additions and 67 deletions

View file

@ -18,6 +18,7 @@ use {
}, },
dbus::{DbusError, SignalHandler}, dbus::{DbusError, SignalHandler},
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
format::Format,
gfx_api::{GfxError, SyncFile}, gfx_api::{GfxError, SyncFile},
ifs::{ ifs::{
wl_output::OutputId, wl_output::OutputId,
@ -48,14 +49,15 @@ use {
smallmap::SmallMap, smallmap::SmallMap,
syncqueue::SyncQueue, syncqueue::SyncQueue,
}, },
video::{drm::DrmError, gbm::GbmError}, video::{Modifier, drm::DrmError, gbm::GbmError},
}, },
bstr::ByteSlice, bstr::ByteSlice,
indexmap::IndexSet,
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
error::Error, error::Error,
ffi::{CStr, CString}, ffi::{CStr, CString},
fmt::{Debug, Formatter}, fmt::{Debug, Display, Formatter},
future::pending, future::pending,
rc::Rc, rc::Rc,
}, },
@ -85,16 +87,6 @@ pub enum MetalError {
UpdateProperties(#[source] DrmError), UpdateProperties(#[source] DrmError),
#[error("Could not create a render context")] #[error("Could not create a render context")]
CreateRenderContex(#[source] GfxError), CreateRenderContex(#[source] GfxError),
#[error("Could not allocate scanout buffer")]
ScanoutBuffer(#[source] GbmError),
#[error("addfb2 failed")]
Framebuffer(#[source] DrmError),
#[error("Could not import a framebuffer into the graphics API")]
ImportFb(#[source] GfxError),
#[error("Could not import a texture into the graphics API")]
ImportTexture(#[source] GfxError),
#[error("Could not import an image into the graphics API")]
ImportImage(#[source] GfxError),
#[error("Could not perform modeset")] #[error("Could not perform modeset")]
Modeset(#[source] BackendConnectorTransactionError), Modeset(#[source] BackendConnectorTransactionError),
#[error("Could not enable atomic modesetting")] #[error("Could not enable atomic modesetting")]
@ -111,22 +103,12 @@ pub enum MetalError {
DevicePauseSignalHandler(#[source] DbusError), DevicePauseSignalHandler(#[source] DbusError),
#[error("Could not create device-resumed signal handler")] #[error("Could not create device-resumed signal handler")]
DeviceResumeSignalHandler(#[source] DbusError), DeviceResumeSignalHandler(#[source] DbusError),
#[error("Device render context does not support required format {0}")]
MissingDevFormat(&'static str),
#[error("Render context does not support required format {0}")]
MissingRenderFormat(&'static str),
#[error("Device cannot scan out any buffers writable by its GFX API (format {0})")]
MissingDevModifier(&'static str),
#[error("Device GFX API cannot read any buffers writable by the render GFX API (format {0})")]
MissingRenderModifier(&'static str),
#[error("Could not render the frame")] #[error("Could not render the frame")]
RenderFrame(#[source] GfxError), RenderFrame(#[source] GfxError),
#[error("Could not copy frame to output device")] #[error("Could not copy frame to output device")]
CopyToOutput(#[source] GfxError), CopyToOutput(#[source] GfxError),
#[error("Could not perform atomic commit")] #[error("Could not perform atomic commit")]
Commit(#[source] DrmError), Commit(#[source] DrmError),
#[error("Could not clear framebuffer")]
Clear(#[source] GfxError),
#[error("The present configuration is out of date")] #[error("The present configuration is out of date")]
OutOfDate, OutOfDate,
#[error("Could not add connector to transaction")] #[error("Could not add connector to transaction")]
@ -135,6 +117,119 @@ pub enum MetalError {
CalculateDrmState(#[source] BackendConnectorTransactionError), CalculateDrmState(#[source] BackendConnectorTransactionError),
#[error("Could not calculate DRM change set")] #[error("Could not calculate DRM change set")]
CalculateDrmChange(#[source] BackendConnectorTransactionError), CalculateDrmChange(#[source] BackendConnectorTransactionError),
#[error("Could not create plane buffer")]
AllocateScanoutBuffer(#[source] Box<ScanoutBufferError>),
}
#[derive(Debug, Error)]
pub enum ScanoutBufferErrorKind {
#[error("Scanout device: The format is not supported")]
SodUnsupportedFormat,
#[error(
"Scanout device: The intersection of the modifiers supported by the plane and modifiers writable by the gfx API is empty"
)]
SodNoWritableModifier,
#[error("Scanout device: Buffer allocation failed")]
SodBufferAllocation(#[source] GbmError),
#[error("Scanout device: addfb2 failed")]
SodAddfb2(#[source] DrmError),
#[error("Scanout device: Could not import SCANOUT buffer into the gfx API")]
SodImportSodImage(#[source] GfxError),
#[error("Scanout device: Could not turn imported SCANOUT buffer into gfx API FB")]
SodImportFb(#[source] GfxError),
#[error("Scanout device: Could not clear SCANOUT buffer")]
SodClear(#[source] GfxError),
#[error("Scanout device: Could not turn imported SCANOUT buffer into gfx API texture")]
SodImportSodTexture(#[source] GfxError),
#[error("Render device: The format is not supported")]
RenderUnsupportedFormat,
#[error(
"Render device: The intersection of the modifiers readable by the scanout device and modifiers writable by the gfx API is empty"
)]
RenderNoWritableModifier,
#[error("Render device: Buffer allocation failed")]
RenderBufferAllocation(#[source] GbmError),
#[error("Render device: Could not import RENDER buffer into the gfx API")]
RenderImportImage(#[source] GfxError),
#[error("Render device: Could not turn imported RENDER buffer into gfx API FB")]
RenderImportFb(#[source] GfxError),
#[error("Render device: Could not clear RENDER buffer")]
RenderClear(#[source] GfxError),
#[error("Render device: Could not turn imported RENDER buffer into gfx API texture")]
RenderImportRenderTexture(#[source] GfxError),
#[error("Scanout device: Could not import RENDER buffer into the gfx API")]
SodImportRenderImage(#[source] GfxError),
#[error("Scanout device: Could not turn imported RENDER buffer into gfx API texture")]
SodImportRenderTexture(#[source] GfxError),
}
#[derive(Debug)]
pub struct ScanoutBufferError {
dev: String,
format: &'static Format,
plane_modifiers: IndexSet<Modifier>,
width: i32,
height: i32,
cursor: bool,
dev_gfx_write_modifiers: Option<IndexSet<Modifier>>,
dev_gfx_read_modifiers: Option<IndexSet<Modifier>>,
dev_modifiers_possible: Option<IndexSet<Modifier>>,
dev_usage: Option<u32>,
dev_modifier: Option<Modifier>,
render_name: Option<String>,
render_gfx_write_modifiers: Option<IndexSet<Modifier>>,
render_modifiers_possible: Option<IndexSet<Modifier>>,
render_usage: Option<u32>,
render_modifier: Option<Modifier>,
kind: ScanoutBufferErrorKind,
}
impl Display for ScanoutBufferError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f)?;
writeln!(f, "scanout device: {}", self.dev)?;
writeln!(f, "format: {}", self.format.name)?;
writeln!(f, "plane modifiers: {:x?}", self.plane_modifiers)?;
writeln!(f, "size: {}x{}", self.width, self.height)?;
writeln!(f, "cursor: {}", self.cursor)?;
if let Some(v) = &self.dev_gfx_write_modifiers {
writeln!(f, "scanout gfx writable modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dev_modifiers_possible {
writeln!(f, "scanout dev possible modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dev_usage {
writeln!(f, "scanout dev gbm usage: {:x}", v)?;
}
if let Some(v) = &self.dev_modifier {
writeln!(f, "scanout dev modifier: {:x}", v)?;
}
if let Some(v) = &self.render_name {
writeln!(f, "render device: {}", v)?;
}
if let Some(v) = &self.render_gfx_write_modifiers {
writeln!(f, "render gfx writable modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dev_gfx_read_modifiers {
writeln!(f, "scanout gfx readable modifiers: {:x?}", v)?;
}
if let Some(v) = &self.render_modifiers_possible {
writeln!(f, "render dev possible modifiers: {:x?}", v)?;
}
if let Some(v) = &self.render_usage {
writeln!(f, "render dev gbm usage: {:x}", v)?;
}
if let Some(v) = &self.render_modifier {
writeln!(f, "render dev modifier: {:x}", v)?;
}
Ok(())
}
}
impl Error for ScanoutBufferError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.kind)
}
} }
pub struct MetalBackend { pub struct MetalBackend {

View file

@ -232,6 +232,10 @@ impl MetalBackend {
Some(d) if d.id == id => d, Some(d) if d.id == id => d,
_ => return, _ => return,
}; };
log::info!(
"Received logind response for drm device {}",
dev.devnode.to_bytes().as_bstr(),
);
let res = match res { let res = match res {
Ok(r) => r, Ok(r) => r,
Err(e) => { Err(e) => {

View file

@ -385,6 +385,7 @@ impl MetalDeviceTransaction {
} }
*plane = p.id; *plane = p.id;
unused_planes.remove(&p.id); unused_planes.remove(&p.id);
break;
} }
} }
if crtc_planes.primary.is_none() { if crtc_planes.primary.is_none() {
@ -417,11 +418,11 @@ impl MetalDeviceTransaction {
crtc.new.mode_blob = Some(Rc::new(blob)); crtc.new.mode_blob = Some(Rc::new(blob));
mode.clone() mode.clone()
}; };
for plane in [crtc_planes.primary, crtc_planes.cursor] { for plane_id in [&mut crtc_planes.primary, &mut crtc_planes.cursor] {
if plane.is_none() { if plane_id.is_none() {
continue; continue;
} }
let plane = slf.planes.get_mut(&plane).unwrap(); let plane = slf.planes.get_mut(plane_id).unwrap();
plane.new.assigned_crtc = crtc.obj.id; plane.new.assigned_crtc = crtc.obj.id;
plane.changed.extend(crtc.changed.iter().cloned()); plane.changed.extend(crtc.changed.iter().cloned());
let (x, y, width, height, format, old_buffers); let (x, y, width, height, format, old_buffers);
@ -483,7 +484,8 @@ impl MetalDeviceTransaction {
None => { None => {
let modifiers = &plane.obj.formats.get(&format.drm).unwrap().modifiers; let modifiers = &plane.obj.formats.get(&format.drm).unwrap().modifiers;
connector.changed.set(true); connector.changed.set(true);
let buffers = slf let is_cursor = plane.obj.ty == PlaneType::Cursor;
let res = slf
.dev .dev
.dev .dev
.backend .backend
@ -494,15 +496,28 @@ impl MetalDeviceTransaction {
width, width,
height, height,
render_ctx, render_ctx,
plane.obj.ty == PlaneType::Cursor, is_cursor,
) )
.map_err(|e| { .map_err(|e| {
BackendConnectorTransactionError::AllocateScanoutBuffers( BackendConnectorTransactionError::AllocateScanoutBuffers(
connector.obj.kernel_id(), connector.obj.kernel_id(),
Box::new(e), Box::new(e),
) )
})?; });
let buffers = Rc::new(buffers); if let Err(e) = &res
&& is_cursor
{
log::error!(
"Could not allocate buffers for cursor plane of {}: {}",
connector.obj.kernel_id(),
ErrorFmt(e),
);
plane.new = DrmPlaneState::default();
unused_planes.insert(*plane_id, ());
*plane_id = DrmPlane::NONE;
continue;
}
let buffers = Rc::new(res?);
plane.new.buffers = Some(buffers.clone()); plane.new.buffers = Some(buffers.clone());
new_buffers = Some(buffers.clone()); new_buffers = Some(buffers.clone());
buffers buffers

View file

@ -14,7 +14,7 @@ use {
}, },
}, },
backends::metal::{ backends::metal::{
MetalBackend, MetalError, MetalBackend, MetalError, ScanoutBufferError, ScanoutBufferErrorKind,
present::{ present::{
DEFAULT_POST_COMMIT_MARGIN, DEFAULT_PRE_COMMIT_MARGIN, DirectScanoutCache, DEFAULT_POST_COMMIT_MARGIN, DEFAULT_PRE_COMMIT_MARGIN, DirectScanoutCache,
POST_COMMIT_MARGIN_DELTA, PresentFb, POST_COMMIT_MARGIN_DELTA, PresentFb,
@ -41,7 +41,7 @@ use {
asyncevent::AsyncEvent, binary_search_map::BinarySearchMap, bitflags::BitflagsExt, asyncevent::AsyncEvent, binary_search_map::BinarySearchMap, bitflags::BitflagsExt,
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
geometric_decay::GeometricDecay, numcell::NumCell, on_change::OnChange, geometric_decay::GeometricDecay, numcell::NumCell, on_change::OnChange,
opaque_cell::OpaqueCell, ordered_float::F64, oserror::OsError, on_drop::OnDrop2, opaque_cell::OpaqueCell, ordered_float::F64, oserror::OsError,
}, },
video::{ video::{
INVALID_MODIFIER, Modifier, INVALID_MODIFIER, Modifier,
@ -88,6 +88,7 @@ pub struct MetalRenderContext {
pub dev_id: DrmDeviceId, pub dev_id: DrmDeviceId,
pub gfx: Rc<dyn GfxContext>, pub gfx: Rc<dyn GfxContext>,
pub gbm: Rc<GbmDevice>, pub gbm: Rc<GbmDevice>,
pub devnode: CString,
} }
pub struct MetalDrmDevice { pub struct MetalDrmDevice {
@ -1083,6 +1084,11 @@ fn create_connector(
dev: &Rc<MetalDrmDevice>, dev: &Rc<MetalDrmDevice>,
) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> { ) -> Result<(Rc<MetalConnector>, ConnectorFutures), DrmError> {
let display = create_connector_display_data(connector, dev)?; let display = create_connector_display_data(connector, dev)?;
log::info!(
"Creating connector {} for device {}",
display.connector_id,
dev.devnode.as_bytes().as_bstr(),
);
let slf = Rc::new(MetalConnector { let slf = Rc::new(MetalConnector {
id: connector, id: connector,
kernel_id: Cell::new(display.connector_id), kernel_id: Cell::new(display.connector_id),
@ -1799,6 +1805,11 @@ impl MetalBackend {
for c in removed_connectors { for c in removed_connectors {
dev.futures.remove(&c); dev.futures.remove(&c);
if let Some(c) = dev.connectors.remove(&c) { if let Some(c) = dev.connectors.remove(&c) {
log::info!(
"Removing connector {} from device {}",
c.kernel_id.get(),
dev.dev.devnode.as_bytes().as_bstr(),
);
if let Some(lease_id) = c.lease.get() if let Some(lease_id) = c.lease.get()
&& let Some(lease) = dev.dev.leases.remove(&lease_id) && let Some(lease) = dev.dev.leases.remove(&lease_id)
&& !lease.try_revoke() && !lease.try_revoke()
@ -1990,6 +2001,7 @@ impl MetalBackend {
dev_id: pending.id, dev_id: pending.id,
gfx, gfx,
gbm: gbm.clone(), gbm: gbm.clone(),
devnode: pending.devnode.clone(),
}); });
let mut is_nvidia = false; let mut is_nvidia = false;
@ -2455,6 +2467,7 @@ impl MetalBackend {
dev_id: dev.id, dev_id: dev.id,
gfx, gfx,
gbm: old_ctx.gbm.clone(), gbm: old_ctx.gbm.clone(),
devnode: old_ctx.devnode.clone(),
})); }));
if dev.is_render_device() { if dev.is_render_device() {
self.make_render_device(dev, true); self.make_render_device(dev, true);
@ -2467,7 +2480,11 @@ impl MetalBackend {
fn re_init_drm_device(&self, dev: &Rc<MetalDrmDeviceData>) { fn re_init_drm_device(&self, dev: &Rc<MetalDrmDeviceData>) {
if let Err(e) = self.init_drm_device(dev) { if let Err(e) = self.init_drm_device(dev) {
log::error!("Could not initialize device: {}", ErrorFmt(e)); log::error!(
"Could not initialize drm device {}: {}",
dev.dev.devnode.as_bytes().as_bstr(),
ErrorFmt(e),
);
} }
for connector in dev.connectors.lock().values() { for connector in dev.connectors.lock().values() {
if connector.connected() { if connector.connected() {
@ -2552,7 +2569,11 @@ impl MetalBackend {
Ok(_) => break, Ok(_) => break,
Err(e) => e, Err(e) => e,
}; };
log::error!("Could not initialize DRM device: {}", ErrorFmt(&err)); log::error!(
"Could not initialize DRM device {}: {}",
dev.dev.devnode.as_bytes().as_bstr(),
ErrorFmt(&err),
);
let Some(q) = quirks.pop() else { let Some(q) = quirks.pop() else {
return Err(err); return Err(err);
}; };
@ -2629,25 +2650,102 @@ impl MetalBackend {
damage_queue: DamageQueue, damage_queue: DamageQueue,
blend_buffer: Option<Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
) -> Result<RenderBuffer, MetalError> { ) -> Result<RenderBuffer, MetalError> {
let mut dev_gfx_write_modifiers = None;
let mut dev_gfx_read_modifiers = None;
let mut dev_modifiers_possible = None;
let mut dev_usage = None;
let mut dev_modifier = None;
let mut render_name = None;
let mut render_gfx_write_modifiers = None;
let mut render_modifiers_possible = None;
let mut render_usage = None;
let mut render_modifier = None;
self.create_scanout_buffer_(
dev,
format,
plane_modifiers,
width,
height,
render_ctx,
cursor,
damage_queue,
blend_buffer,
&mut dev_gfx_write_modifiers,
&mut dev_gfx_read_modifiers,
&mut dev_modifiers_possible,
&mut dev_usage,
&mut dev_modifier,
&mut render_name,
&mut render_gfx_write_modifiers,
&mut render_modifiers_possible,
&mut render_usage,
&mut render_modifier,
)
.map_err(|kind| ScanoutBufferError {
dev: dev.devnode.as_bytes().as_bstr().to_string(),
format,
plane_modifiers: plane_modifiers.clone(),
width,
height,
cursor,
dev_gfx_write_modifiers,
dev_gfx_read_modifiers,
dev_modifiers_possible,
dev_usage,
dev_modifier,
render_name,
render_gfx_write_modifiers,
render_modifiers_possible,
render_usage,
render_modifier,
kind,
})
.map_err(Box::new)
.map_err(MetalError::AllocateScanoutBuffer)
}
fn create_scanout_buffer_(
&self,
dev: &Rc<MetalDrmDevice>,
format: &'static Format,
plane_modifiers: &IndexSet<Modifier>,
width: i32,
height: i32,
render_ctx: &Rc<MetalRenderContext>,
cursor: bool,
damage_queue: DamageQueue,
blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
dbg_dev_gfx_write_modifiers: &mut Option<IndexSet<Modifier>>,
dbg_dev_gfx_read_modifiers: &mut Option<IndexSet<Modifier>>,
dbg_dev_modifiers_possible: &mut Option<IndexSet<Modifier>>,
dbg_dev_usage: &mut Option<u32>,
dbg_dev_modifier: &mut Option<Modifier>,
dbg_render_name: &mut Option<String>,
dbg_render_gfx_write_modifiers: &mut Option<IndexSet<Modifier>>,
dbg_render_modifiers_possible: &mut Option<IndexSet<Modifier>>,
dbg_render_usage: &mut Option<u32>,
dbg_render_modifier: &mut Option<Modifier>,
) -> Result<RenderBuffer, ScanoutBufferErrorKind> {
let dev_ctx = dev.ctx.get(); let dev_ctx = dev.ctx.get();
let dev_gfx_formats = dev_ctx.gfx.formats(); let dev_gfx_formats = dev_ctx.gfx.formats();
let dev_gfx_format = match dev_gfx_formats.get(&format.drm) { let Some(dev_gfx_format) = dev_gfx_formats.get(&format.drm) else {
None => return Err(MetalError::MissingDevFormat(format.name)), return Err(ScanoutBufferErrorKind::SodUnsupportedFormat);
Some(f) => f,
}; };
let send_dev_gfx_write_modifiers = OnDrop2::new(|| {
*dbg_dev_gfx_write_modifiers =
Some(dev_gfx_format.write_modifiers.keys().copied().collect())
});
let possible_modifiers: IndexMap<_, _> = dev_gfx_format let possible_modifiers: IndexMap<_, _> = dev_gfx_format
.write_modifiers .write_modifiers
.iter() .iter()
.filter(|(m, _)| plane_modifiers.contains(*m)) .filter(|(m, _)| plane_modifiers.contains(*m))
.map(|(m, v)| (*m, v)) .map(|(m, v)| (*m, v))
.collect(); .collect();
let send_dev_modifiers_possible = OnDrop2::new(|| {
*dbg_dev_modifiers_possible = Some(possible_modifiers.keys().copied().collect())
});
if possible_modifiers.is_empty() { if possible_modifiers.is_empty() {
log::warn!("Scanout modifiers: {:?}", plane_modifiers); return Err(ScanoutBufferErrorKind::SodNoWritableModifier);
log::warn!(
"DEV GFX modifiers: {:?}",
dev_gfx_format.write_modifiers.keys()
);
return Err(MetalError::MissingDevModifier(format.name));
} }
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
if !needs_render_usage(possible_modifiers.values().copied()) { if !needs_render_usage(possible_modifiers.values().copied()) {
@ -2656,6 +2754,7 @@ impl MetalBackend {
if cursor { if cursor {
usage |= GBM_BO_USE_LINEAR; usage |= GBM_BO_USE_LINEAR;
}; };
*dbg_dev_usage = Some(usage);
let dev_bo = dev.gbm.create_bo( let dev_bo = dev.gbm.create_bo(
&self.state.dma_buf_ids, &self.state.dma_buf_ids,
width, width,
@ -2666,19 +2765,20 @@ impl MetalBackend {
); );
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(ScanoutBufferErrorKind::SodBufferAllocation(e)),
}; };
*dbg_dev_modifier = Some(dev_bo.dmabuf().modifier);
let drm_fb = match dev.master.add_fb(dev_bo.dmabuf(), None) { 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(ScanoutBufferErrorKind::SodAddfb2(e)),
}; };
let dev_img = match dev_ctx.gfx.clone().dmabuf_img(dev_bo.dmabuf()) { let dev_img = match dev_ctx.gfx.clone().dmabuf_img(dev_bo.dmabuf()) {
Ok(img) => img, Ok(img) => img,
Err(e) => return Err(MetalError::ImportImage(e)), Err(e) => return Err(ScanoutBufferErrorKind::SodImportSodImage(e)),
}; };
let dev_fb = match dev_img.clone().to_framebuffer() { let dev_fb = match dev_img.clone().to_framebuffer() {
Ok(fb) => fb, Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportFb(e)), Err(e) => return Err(ScanoutBufferErrorKind::SodImportFb(e)),
}; };
dev_fb dev_fb
.clear( .clear(
@ -2686,57 +2786,74 @@ impl MetalBackend {
ReleaseSync::None, ReleaseSync::None,
self.state.color_manager.srgb_gamma22(), self.state.color_manager.srgb_gamma22(),
) )
.map_err(MetalError::Clear)?; .map_err(ScanoutBufferErrorKind::SodClear)?;
let render_gfx_formats;
let render_possible_modifiers: IndexMap<_, _>;
let mut send_render_dev_name = None;
let mut send_render_gfx_write_modifiers = None;
let mut send_dev_gfx_read_modifiers = None;
let mut send_render_possible_modifiers = None;
let (dev_tex, render_tex, render_fb, render_bo) = if dev.id == render_ctx.dev_id { let (dev_tex, render_tex, render_fb, render_bo) = if dev.id == render_ctx.dev_id {
let render_tex = match dev_img.to_texture() { let render_tex = match dev_img.to_texture() {
Ok(fb) => fb, Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportTexture(e)), Err(e) => return Err(ScanoutBufferErrorKind::SodImportSodTexture(e)),
}; };
(None, render_tex, None, None) (None, render_tex, None, None)
} else { } else {
send_render_dev_name = Some(OnDrop2::new(|| {
*dbg_render_name = Some(render_ctx.devnode.as_bytes().as_bstr().to_string());
}));
// Create a _bridge_ BO in the render device // Create a _bridge_ BO in the render device
let render_gfx_formats = render_ctx.gfx.formats(); render_gfx_formats = render_ctx.gfx.formats();
let render_gfx_format = match render_gfx_formats.get(&format.drm) { let render_gfx_format = match render_gfx_formats.get(&format.drm) {
None => return Err(MetalError::MissingRenderFormat(format.name)), None => return Err(ScanoutBufferErrorKind::RenderUnsupportedFormat),
Some(f) => f, Some(f) => f,
}; };
let possible_modifiers: IndexMap<_, _> = render_gfx_format send_render_gfx_write_modifiers = Some(OnDrop2::new(|| {
*dbg_render_gfx_write_modifiers =
Some(render_gfx_format.write_modifiers.keys().copied().collect())
}));
send_dev_gfx_read_modifiers = Some(OnDrop2::new(|| {
*dbg_dev_gfx_read_modifiers = Some(dev_gfx_format.read_modifiers.clone());
}));
render_possible_modifiers = render_gfx_format
.write_modifiers .write_modifiers
.iter() .iter()
.filter(|(m, _)| dev_gfx_format.read_modifiers.contains(*m)) .filter(|(m, _)| dev_gfx_format.read_modifiers.contains(*m))
.map(|(m, v)| (*m, v)) .map(|(m, v)| (*m, v))
.collect(); .collect();
if possible_modifiers.is_empty() { send_render_possible_modifiers = Some(OnDrop2::new(|| {
log::warn!( *dbg_render_modifiers_possible =
"Render GFX modifiers: {:?}", Some(render_possible_modifiers.keys().copied().collect())
render_gfx_format.write_modifiers.keys() }));
); if render_possible_modifiers.is_empty() {
log::warn!("DEV GFX modifiers: {:?}", dev_gfx_format.read_modifiers); return Err(ScanoutBufferErrorKind::RenderNoWritableModifier);
return Err(MetalError::MissingRenderModifier(format.name));
} }
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR; usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
if !needs_render_usage(possible_modifiers.values().copied()) { if !needs_render_usage(render_possible_modifiers.values().copied()) {
usage &= !GBM_BO_USE_RENDERING; usage &= !GBM_BO_USE_RENDERING;
} }
*dbg_render_usage = Some(usage);
let render_bo = render_ctx.gbm.create_bo( let render_bo = render_ctx.gbm.create_bo(
&self.state.dma_buf_ids, &self.state.dma_buf_ids,
width, width,
height, height,
format, format,
possible_modifiers.keys(), render_possible_modifiers.keys(),
usage, 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(ScanoutBufferErrorKind::RenderBufferAllocation(e)),
}; };
*dbg_render_modifier = Some(render_bo.dmabuf().modifier);
let render_img = match render_ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) { let render_img = match render_ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) {
Ok(img) => img, Ok(img) => img,
Err(e) => return Err(MetalError::ImportImage(e)), Err(e) => return Err(ScanoutBufferErrorKind::RenderImportImage(e)),
}; };
let render_fb = match render_img.clone().to_framebuffer() { let render_fb = match render_img.clone().to_framebuffer() {
Ok(fb) => fb, Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportFb(e)), Err(e) => return Err(ScanoutBufferErrorKind::RenderImportFb(e)),
}; };
render_fb render_fb
.clear( .clear(
@ -2744,24 +2861,30 @@ impl MetalBackend {
ReleaseSync::None, ReleaseSync::None,
self.state.color_manager.srgb_gamma22(), self.state.color_manager.srgb_gamma22(),
) )
.map_err(MetalError::Clear)?; .map_err(ScanoutBufferErrorKind::RenderClear)?;
let render_tex = match render_img.to_texture() { let render_tex = match render_img.to_texture() {
Ok(fb) => fb, Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportTexture(e)), Err(e) => return Err(ScanoutBufferErrorKind::RenderImportRenderTexture(e)),
}; };
// Import the bridge BO into the current device // Import the bridge BO into the current device
let dev_img = match dev_ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) { let dev_img = match dev_ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) {
Ok(img) => img, Ok(img) => img,
Err(e) => return Err(MetalError::ImportImage(e)), Err(e) => return Err(ScanoutBufferErrorKind::SodImportRenderImage(e)),
}; };
let dev_tex = match dev_img.to_texture() { let dev_tex = match dev_img.to_texture() {
Ok(fb) => fb, Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportTexture(e)), Err(e) => return Err(ScanoutBufferErrorKind::SodImportRenderTexture(e)),
}; };
(Some(dev_tex), render_tex, Some(render_fb), Some(render_bo)) (Some(dev_tex), render_tex, Some(render_fb), Some(render_bo))
}; };
send_dev_gfx_write_modifiers.forget();
send_dev_modifiers_possible.forget();
send_render_dev_name.map(|o| o.forget());
send_render_gfx_write_modifiers.map(|o| o.forget());
send_dev_gfx_read_modifiers.map(|o| o.forget());
send_render_possible_modifiers.map(|o| o.forget());
Ok(RenderBuffer { Ok(RenderBuffer {
width, width,
height, height,