1
0
Fork 0
forked from wry/wry

metal: add support for copy-device based prime methods

This commit is contained in:
Julian Orth 2026-02-15 19:04:25 +01:00
parent fa897f0f76
commit a77929741a
7 changed files with 223 additions and 50 deletions

View file

@ -6,20 +6,22 @@ use {
video::{MetalDrmDevice, MetalRenderContext},
},
cmm::cmm_description::ColorDescription,
copy_device::{CopyDevice, CopyDeviceError, CopyDeviceSupport},
format::Format,
gfx_api::{
AcquireSync, GfxBlendBuffer, GfxError, GfxFormat, GfxFramebuffer, GfxTexture,
GfxWriteModifier, ReleaseSync, SyncFile, needs_render_usage,
},
rect::{DamageQueue, Rect},
rect::{DamageQueue, Rect, Region},
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq},
video::{
Modifier,
LINEAR_MODIFIER, Modifier,
dmabuf::DmaBuf,
drm::{DrmError, DrmFramebuffer},
gbm::{GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT, GbmBo, GbmError},
},
},
ahash::HashSet,
arrayvec::ArrayVec,
bstr::ByteSlice,
indexmap::{IndexMap, IndexSet},
@ -78,6 +80,10 @@ pub enum RenderBufferError {
GfxError(#[from] GfxError),
#[error("Could not copy frame to output device")]
CopyToOutput(#[source] GfxError),
#[error("Could not create a copy device copy")]
CreateCopyDeviceCopy(#[source] CopyDeviceError),
#[error("Could not execute a copy device copy")]
ExecuteCopyDeviceCopy(#[source] CopyDeviceError),
}
#[derive(Default)]
@ -99,6 +105,7 @@ impl RenderBuffer {
pub fn copy_to_dev(
&self,
cd: &Rc<ColorDescription>,
_region: Option<&Region>,
sync_file: Option<SyncFile>,
) -> Result<RenderBufferCopy, RenderBufferError> {
match &self.prime {
@ -158,6 +165,14 @@ impl RenderBuffer {
return Err(RenderBufferError::NotSameSize);
}
if let Some(dev) = new.dev_copy_device().or(old.dev_copy_device()) {
return dev
.create_copy(old.dev_bo().dmabuf(), new.dev_bo().dmabuf())
.map_err(RenderBufferError::CreateCopyDeviceCopy)?
.execute(None, None)
.map_err(RenderBufferError::ExecuteCopyDeviceCopy);
}
let copy_texture_impl = |fb: &Rc<dyn GfxFramebuffer>, tex: &Rc<dyn GfxTexture>| {
fb.copy_texture(
AcquireSync::Unnecessary,
@ -235,6 +250,13 @@ impl RenderBuffer {
RenderBufferPrime::Sampling { dev_bo, .. } => dev_bo,
}
}
pub fn dev_copy_device(&self) -> Option<&Rc<CopyDevice>> {
match &self.prime {
RenderBufferPrime::None => None,
RenderBufferPrime::Sampling { .. } => None,
}
}
}
struct Builder<'a> {
@ -459,11 +481,15 @@ impl RenderBufferPrime {
#[derive(Default, Debug)]
struct RenderBufferAllocationDebug {
dev_copy_src_modifiers: Option<Vec<Modifier>>,
dev_copy_dst_modifiers: Option<Vec<Modifier>>,
dev_gfx_write_modifiers: Option<Vec<Modifier>>,
dev_gfx_read_modifiers: Option<Vec<Modifier>>,
dev_modifiers_possible: Option<Vec<Modifier>>,
dev_usage: Option<u32>,
dev_modifier: Option<Modifier>,
render_copy_src_modifiers: Option<Vec<Modifier>>,
render_copy_dst_modifiers: Option<Vec<Modifier>>,
render_gfx_write_modifiers: Option<Vec<Modifier>>,
render_gfx_read_modifiers: Option<Vec<Modifier>>,
render_modifiers_possible: Option<Vec<Modifier>>,
@ -496,6 +522,12 @@ impl Display for ScanoutBufferError {
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.dbg.dev_copy_src_modifiers {
writeln!(f, "scanout copy src modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dbg.dev_copy_dst_modifiers {
writeln!(f, "scanout copy dst modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dbg.dev_gfx_write_modifiers {
writeln!(f, "scanout gfx writable modifiers: {:x?}", v)?;
}
@ -511,6 +543,12 @@ impl Display for ScanoutBufferError {
if let Some(v) = &self.render_name {
writeln!(f, "render device: {}", v)?;
}
if let Some(v) = &self.dbg.render_copy_src_modifiers {
writeln!(f, "render copy src modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dbg.render_copy_dst_modifiers {
writeln!(f, "render copy dst modifiers: {:x?}", v)?;
}
if let Some(v) = &self.dbg.render_gfx_write_modifiers {
writeln!(f, "render gfx writable modifiers: {:x?}", v)?;
}
@ -729,6 +767,43 @@ impl Builder<'_> {
})
}
fn copy_modifiers_iter(&self, support: &[CopyDeviceSupport]) -> impl Iterator<Item = Modifier> {
let Builder { width, height, .. } = *self;
support
.iter()
.filter(move |s| s.max_width >= width as _ && s.max_height >= height as _)
.map(move |s| s.modifier)
}
fn copy_modifiers(&self, support: &[CopyDeviceSupport]) -> Vec<Modifier> {
self.copy_modifiers_iter(support).collect()
}
#[expect(dead_code)]
fn copy_src_modifiers(&self, dev: &CopyDevice) -> Vec<Modifier> {
self.copy_modifiers(dev.src_support(self.format))
}
#[expect(dead_code)]
fn copy_dst_modifiers(&self, dev: &CopyDevice) -> Vec<Modifier> {
self.copy_modifiers(dev.dst_support(self.format))
}
fn copy_supports_linear(&self, support: &[CopyDeviceSupport]) -> bool {
self.copy_modifiers_iter(support)
.any(|m| m == LINEAR_MODIFIER)
}
#[expect(dead_code)]
fn copy_src_supports_linear(&self, dev: &CopyDevice) -> bool {
self.copy_supports_linear(dev.src_support(self.format))
}
#[expect(dead_code)]
fn copy_dst_supports_linear(&self, dev: &CopyDevice) -> bool {
self.copy_supports_linear(dev.dst_support(self.format))
}
fn prepare_prime_none(
&self,
dbg: &RefCell<RenderBufferAllocationDebug>,
@ -936,3 +1011,34 @@ fn sample_modifiers(fmt: &GfxFormat) -> Vec<Modifier> {
fn render_modifiers(fmt: &GfxFormat) -> Vec<Modifier> {
fmt.write_modifiers.keys().copied().collect()
}
fn intersect_modifiers<'a>(
left: impl IntoIterator<Item = &'a Modifier>,
right: impl IntoIterator<Item = &'a Modifier>,
) -> Vec<Modifier> {
let right: HashSet<_> = right.into_iter().copied().collect();
left.into_iter()
.copied()
.filter(|m| right.contains(m))
.collect()
}
#[expect(dead_code)]
fn intersect_render_modifiers<'a>(
left: &'a GfxFormat,
right: impl IntoIterator<Item = &'a Modifier>,
) -> Vec<Modifier> {
intersect_modifiers(
left.write_modifiers
.keys()
.filter(|m| left.read_modifiers.contains(*m)),
right,
)
}
#[expect(dead_code)]
fn make_linear_only(modifiers: &mut Vec<Modifier>) {
if modifiers.contains(&LINEAR_MODIFIER) {
*modifiers = vec![LINEAR_MODIFIER];
}
}

View file

@ -131,6 +131,7 @@ impl MetalBackend {
}
fn handle_drm_device_removed(self: &Rc<Self>, dev: &Rc<MetalDrmDeviceData>) {
self.state.copy_device_registry.remove(dev.dev.devnum);
log::info!("Device removed: {}", dev.dev.devnode.to_bytes().as_bstr());
}

View file

@ -557,7 +557,7 @@ impl MetalConnector {
if let Some(sf) = c.cursor_swap_buffer.take() {
let sf = c
.cursor_buffer
.copy_to_dev(cd, sf)
.copy_to_dev(cd, None, sf)
.map_err(MetalError::CopyToDev)?
.present_block;
self.cursor_sync_file.set(sf);
@ -875,7 +875,9 @@ impl MetalConnector {
blend_cd,
)
.map_err(MetalError::RenderFrame)?;
copy = buffer.copy_to_dev(cd, sf).map_err(MetalError::CopyToDev)?;
copy = buffer
.copy_to_dev(cd, Some(&latched.damage), sf)
.map_err(MetalError::CopyToDev)?;
fb = buffer.drm.clone();
tex = buffer.render.tex.clone();
}

View file

@ -22,6 +22,7 @@ use {
transaction::{DrmConnectorState, DrmCrtcState, DrmPlaneState, MetalDeviceTransaction},
},
cmm::{cmm_description::ColorDescription, cmm_primaries::Primaries},
copy_device::{CopyDevice, CopyDeviceRegistry},
drm_feedback::DrmFeedback,
edid::{CtaDataBlock, Descriptor, EdidExtension},
format::{Format, XRGB8888},
@ -58,7 +59,7 @@ use {
isnt::std_1::collections::IsntHashMapExt,
jay_config::video::GfxApi,
std::{
cell::{Cell, RefCell},
cell::{Cell, OnceCell, RefCell},
collections::hash_map::Entry,
ffi::CString,
fmt::{Debug, Formatter},
@ -84,6 +85,19 @@ pub struct MetalRenderContext {
pub gfx: Rc<dyn GfxContext>,
pub gbm: Rc<GbmDevice>,
pub devnode: CString,
pub copy_device: Rc<CopyDeviceHolder>,
}
pub struct CopyDeviceHolder {
pub registry: Rc<CopyDeviceRegistry>,
pub devnum: dev_t,
pub dev: OnceCell<Option<Rc<CopyDevice>>>,
}
impl Debug for CopyDeviceHolder {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CopyDeviceHolder").finish_non_exhaustive()
}
}
pub struct MetalDrmDevice {
@ -105,6 +119,8 @@ pub struct MetalDrmDevice {
pub gbm: Rc<GbmDevice>,
pub handle_events: HandleEvents,
pub ctx: CloneCell<Rc<MetalRenderContext>>,
#[expect(dead_code)]
pub copy_device: Rc<CopyDeviceHolder>,
pub on_change: OnChange<crate::backend::DrmEvent>,
pub direct_scanout_enabled: Cell<Option<bool>>,
pub is_nvidia: bool,
@ -1987,6 +2003,12 @@ impl MetalBackend {
Err(e) => return Err(MetalError::GbmDevice(e)),
};
let copy_device = Rc::new(CopyDeviceHolder {
registry: self.state.copy_device_registry.clone(),
devnum: pending.devnum,
dev: Default::default(),
});
let gfx = match self.state.create_gfx_context(master, None) {
Ok(r) => r,
Err(e) => return Err(MetalError::CreateRenderContex(e)),
@ -1996,6 +2018,7 @@ impl MetalBackend {
gfx,
gbm: gbm.clone(),
devnode: pending.devnode.clone(),
copy_device: copy_device.clone(),
});
let mut is_nvidia = false;
@ -2037,6 +2060,7 @@ impl MetalBackend {
handle_events: Cell::new(None),
},
ctx: CloneCell::new(ctx),
copy_device,
on_change: Default::default(),
direct_scanout_enabled: Default::default(),
is_nvidia,
@ -2462,6 +2486,7 @@ impl MetalBackend {
gfx,
gbm: old_ctx.gbm.clone(),
devnode: old_ctx.devnode.clone(),
copy_device: old_ctx.copy_device.clone(),
}));
if dev.is_render_device() {
self.make_render_device(dev, true);
@ -2622,3 +2647,24 @@ impl MetalBackend {
connector.schedule_present();
}
}
impl CopyDeviceHolder {
#[expect(dead_code)]
pub fn get(&self) -> Option<Rc<CopyDevice>> {
self.dev
.get_or_init(
|| match self.registry.get(self.devnum)?.create_device().map(Some) {
Ok(d) => d,
Err(e) => {
log::error!(
"Could not get copy device for {}: {}",
self.devnum,
ErrorFmt(e),
);
None
}
},
)
.clone()
}
}