metal: refactor buffer allocation
This commit is contained in:
parent
0f556fc054
commit
f2a0221c9e
5 changed files with 1000 additions and 590 deletions
|
|
@ -1,3 +1,4 @@
|
|||
mod allocator;
|
||||
mod input;
|
||||
mod monitor;
|
||||
mod present;
|
||||
|
|
@ -12,13 +13,15 @@ use {
|
|||
InputDeviceClickMethod, InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, Leds,
|
||||
TransformMatrix, transaction::BackendConnectorTransactionError,
|
||||
},
|
||||
backends::metal::video::{
|
||||
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
||||
PersistentDisplayData,
|
||||
backends::metal::{
|
||||
allocator::{RenderBufferError, ScanoutBufferError, ScanoutBufferErrors},
|
||||
video::{
|
||||
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
|
||||
PersistentDisplayData,
|
||||
},
|
||||
},
|
||||
dbus::{DbusError, SignalHandler},
|
||||
drm_feedback::DrmFeedback,
|
||||
format::Format,
|
||||
gfx_api::{GfxError, SyncFile},
|
||||
ifs::{
|
||||
wl_output::OutputId,
|
||||
|
|
@ -49,15 +52,14 @@ use {
|
|||
smallmap::SmallMap,
|
||||
syncqueue::SyncQueue,
|
||||
},
|
||||
video::{Modifier, drm::DrmError, gbm::GbmError},
|
||||
video::{drm::DrmError, gbm::GbmError},
|
||||
},
|
||||
bstr::ByteSlice,
|
||||
indexmap::IndexSet,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
error::Error,
|
||||
ffi::{CStr, CString},
|
||||
fmt::{Debug, Display, Formatter},
|
||||
fmt::{Debug, Formatter},
|
||||
future::pending,
|
||||
rc::Rc,
|
||||
},
|
||||
|
|
@ -105,8 +107,6 @@ pub enum MetalError {
|
|||
DeviceResumeSignalHandler(#[source] DbusError),
|
||||
#[error("Could not render the frame")]
|
||||
RenderFrame(#[source] GfxError),
|
||||
#[error("Could not copy frame to output device")]
|
||||
CopyToOutput(#[source] GfxError),
|
||||
#[error("Could not perform atomic commit")]
|
||||
Commit(#[source] DrmError),
|
||||
#[error("The present configuration is out of date")]
|
||||
|
|
@ -117,119 +117,14 @@ pub enum MetalError {
|
|||
CalculateDrmState(#[source] BackendConnectorTransactionError),
|
||||
#[error("Could not calculate DRM change set")]
|
||||
CalculateDrmChange(#[source] BackendConnectorTransactionError),
|
||||
#[error("Could not create plane buffer")]
|
||||
#[error("Could not create non-prime 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")]
|
||||
#[error("Could not create non-prime plane buffer")]
|
||||
AllocateScanoutBufferPrime(#[source] ScanoutBufferErrors),
|
||||
#[error("Could not copy frame to output device")]
|
||||
CopyToDev(#[source] RenderBufferError),
|
||||
#[error("The render device does not support the format")]
|
||||
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 {
|
||||
|
|
|
|||
938
src/backends/metal/allocator.rs
Normal file
938
src/backends/metal/allocator.rs
Normal file
|
|
@ -0,0 +1,938 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::BufferObject,
|
||||
backends::metal::{
|
||||
MetalBackend, MetalError,
|
||||
video::{MetalDrmDevice, MetalRenderContext},
|
||||
},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AcquireSync, GfxBlendBuffer, GfxError, GfxFormat, GfxFramebuffer, GfxTexture,
|
||||
GfxWriteModifier, ReleaseSync, SyncFile, needs_render_usage,
|
||||
},
|
||||
rect::{DamageQueue, Rect},
|
||||
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq},
|
||||
video::{
|
||||
Modifier,
|
||||
dmabuf::DmaBuf,
|
||||
drm::{DrmError, DrmFramebuffer},
|
||||
gbm::{GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT, GbmBo, GbmError},
|
||||
},
|
||||
},
|
||||
arrayvec::ArrayVec,
|
||||
bstr::ByteSlice,
|
||||
indexmap::{IndexMap, IndexSet},
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
linearize::{Linearize, LinearizeExt, StaticMap},
|
||||
log::Level,
|
||||
run_on_drop::on_drop,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
error::Error,
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
rc::Rc,
|
||||
sync::LazyLock,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderBuffer {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub locked: Cell<bool>,
|
||||
pub format: &'static Format,
|
||||
pub drm: Rc<DrmFramebuffer>,
|
||||
pub damage_queue: DamageQueue,
|
||||
pub blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
|
||||
pub render: RenderBufferRender,
|
||||
pub dev_ctx: Rc<MetalRenderContext>,
|
||||
pub prime: RenderBufferPrime,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderBufferRender {
|
||||
pub ctx: Rc<MetalRenderContext>,
|
||||
pub bo: GbmBo,
|
||||
pub tex: Rc<dyn GfxTexture>,
|
||||
pub fb: Rc<dyn GfxFramebuffer>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RenderBufferPrime {
|
||||
None,
|
||||
Sampling {
|
||||
dev_bo: GbmBo,
|
||||
dev_fb: Rc<dyn GfxFramebuffer>,
|
||||
// Import of the render dmabuf into the dev ctx.
|
||||
dev_render_tex: Rc<dyn GfxTexture>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RenderBufferError {
|
||||
#[error("Cannot copy between buffers of different size")]
|
||||
NotSameSize,
|
||||
#[error(transparent)]
|
||||
GfxError(#[from] GfxError),
|
||||
#[error("Could not copy frame to output device")]
|
||||
CopyToOutput(#[source] GfxError),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RenderBufferCopy {
|
||||
pub render_block: Option<SyncFile>,
|
||||
pub present_block: Option<SyncFile>,
|
||||
}
|
||||
|
||||
impl RenderBufferCopy {
|
||||
pub fn for_both(sf: Option<SyncFile>) -> Self {
|
||||
Self {
|
||||
render_block: sf.clone(),
|
||||
present_block: sf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBuffer {
|
||||
pub fn copy_to_dev(
|
||||
&self,
|
||||
cd: &Rc<ColorDescription>,
|
||||
sync_file: Option<SyncFile>,
|
||||
) -> Result<RenderBufferCopy, RenderBufferError> {
|
||||
match &self.prime {
|
||||
RenderBufferPrime::None => Ok(RenderBufferCopy {
|
||||
render_block: None,
|
||||
present_block: sync_file,
|
||||
}),
|
||||
RenderBufferPrime::Sampling {
|
||||
dev_render_tex,
|
||||
dev_fb,
|
||||
..
|
||||
} => dev_fb
|
||||
.copy_texture(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
cd,
|
||||
dev_render_tex,
|
||||
cd,
|
||||
None,
|
||||
AcquireSync::from_sync_file(sync_file),
|
||||
ReleaseSync::None,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
.map_err(RenderBufferError::CopyToOutput)
|
||||
.map(RenderBufferCopy::for_both),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn damage_full(&self) {
|
||||
let rect = Rect::new_sized_saturating(0, 0, self.width, self.height);
|
||||
self.damage_queue.clear_all();
|
||||
self.damage_queue.damage(&[rect]);
|
||||
}
|
||||
|
||||
pub fn clear(&self, cd: &Rc<ColorDescription>) -> Result<Option<SyncFile>, RenderBufferError> {
|
||||
match &self.prime {
|
||||
RenderBufferPrime::None => self
|
||||
.render
|
||||
.fb
|
||||
.clear(AcquireSync::Unnecessary, ReleaseSync::Explicit, cd)
|
||||
.map_err(Into::into),
|
||||
RenderBufferPrime::Sampling { dev_fb, .. } => dev_fb
|
||||
.clear(AcquireSync::Unnecessary, ReleaseSync::Explicit, cd)
|
||||
.map_err(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_to_new(
|
||||
&self,
|
||||
new: &Self,
|
||||
cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, RenderBufferError> {
|
||||
let old = self;
|
||||
|
||||
if (old.width, old.height) != (new.width, new.height) {
|
||||
return Err(RenderBufferError::NotSameSize);
|
||||
}
|
||||
|
||||
let copy_texture_impl = |fb: &Rc<dyn GfxFramebuffer>, tex: &Rc<dyn GfxTexture>| {
|
||||
fb.copy_texture(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
cd,
|
||||
tex,
|
||||
cd,
|
||||
None,
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
let copy_texture = |new_ctx: &Rc<MetalRenderContext>,
|
||||
fb: &Rc<dyn GfxFramebuffer>,
|
||||
old_ctx: &Rc<MetalRenderContext>,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
dma_buf: &DmaBuf| {
|
||||
if rc_eq(&new_ctx.gfx, &old_ctx.gfx) {
|
||||
return copy_texture_impl(fb, tex);
|
||||
}
|
||||
let tex = new_ctx.gfx.clone().dmabuf_img(dma_buf)?.to_texture()?;
|
||||
copy_texture_impl(fb, &tex)
|
||||
};
|
||||
|
||||
let sf = match &old.prime {
|
||||
RenderBufferPrime::None => match &new.prime {
|
||||
RenderBufferPrime::None => copy_texture(
|
||||
&new.render.ctx,
|
||||
&new.render.fb,
|
||||
&old.render.ctx,
|
||||
&old.render.tex,
|
||||
old.render.bo.dmabuf(),
|
||||
)?,
|
||||
RenderBufferPrime::Sampling {
|
||||
dev_fb: new_dev_fb, ..
|
||||
} => copy_texture(
|
||||
&new.dev_ctx,
|
||||
new_dev_fb,
|
||||
&old.render.ctx,
|
||||
&old.render.tex,
|
||||
old.render.bo.dmabuf(),
|
||||
)?,
|
||||
},
|
||||
RenderBufferPrime::Sampling {
|
||||
dev_render_tex: old_dev_render_tex,
|
||||
dev_bo: old_dev_bo,
|
||||
..
|
||||
} => match &new.prime {
|
||||
RenderBufferPrime::None => copy_texture(
|
||||
&new.render.ctx,
|
||||
&new.render.fb,
|
||||
&old.dev_ctx,
|
||||
old_dev_render_tex,
|
||||
old_dev_bo.dmabuf(),
|
||||
)?,
|
||||
RenderBufferPrime::Sampling {
|
||||
dev_fb: new_dev_fb, ..
|
||||
} => copy_texture(
|
||||
&new.dev_ctx,
|
||||
&new_dev_fb,
|
||||
&old.dev_ctx,
|
||||
old_dev_render_tex,
|
||||
old_dev_bo.dmabuf(),
|
||||
)?,
|
||||
},
|
||||
};
|
||||
Ok(sf)
|
||||
}
|
||||
|
||||
pub fn dev_bo(&self) -> &GbmBo {
|
||||
match &self.prime {
|
||||
RenderBufferPrime::None => &self.render.bo,
|
||||
RenderBufferPrime::Sampling { dev_bo, .. } => dev_bo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Builder<'a> {
|
||||
slf: &'a MetalBackend,
|
||||
dev: &'a Rc<MetalDrmDevice>,
|
||||
dev_ctx: &'a Rc<MetalRenderContext>,
|
||||
format: &'static Format,
|
||||
render_fmt: &'a GfxFormat,
|
||||
plane_modifiers: &'a IndexSet<Modifier>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
render_ctx: &'a Rc<MetalRenderContext>,
|
||||
cursor: bool,
|
||||
blend_buffer: Option<&'a Rc<dyn GfxBlendBuffer>>,
|
||||
}
|
||||
|
||||
struct BoAllocationSettings {
|
||||
modifiers: Vec<Modifier>,
|
||||
usage: u32,
|
||||
}
|
||||
|
||||
struct NoPrime {
|
||||
allocation_settings: BoAllocationSettings,
|
||||
}
|
||||
|
||||
struct PrimeSampling {
|
||||
render_allocation_settings: BoAllocationSettings,
|
||||
dev_allocation_settings: BoAllocationSettings,
|
||||
}
|
||||
|
||||
impl MetalBackend {
|
||||
pub fn create_scanout_buffers<const N: usize>(
|
||||
&self,
|
||||
dev: &Rc<MetalDrmDevice>,
|
||||
format: &'static Format,
|
||||
plane_modifiers: &IndexSet<Modifier>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
render_ctx: &Rc<MetalRenderContext>,
|
||||
cursor: bool,
|
||||
) -> Result<[RenderBuffer; N], MetalError> {
|
||||
let Some(render_fmt) = render_ctx.gfx.formats().get(&format.drm) else {
|
||||
return Err(MetalError::RenderUnsupportedFormat);
|
||||
};
|
||||
let mut blend_buffer = None;
|
||||
if !cursor {
|
||||
match render_ctx.gfx.acquire_blend_buffer(width, height) {
|
||||
Ok(bb) => blend_buffer = Some(bb),
|
||||
Err(e) => {
|
||||
log::warn!("Could not create blend buffer: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
let builder = Builder {
|
||||
slf: self,
|
||||
dev,
|
||||
dev_ctx: &dev.ctx.get(),
|
||||
format,
|
||||
render_fmt,
|
||||
plane_modifiers,
|
||||
width,
|
||||
height,
|
||||
render_ctx,
|
||||
cursor,
|
||||
blend_buffer: blend_buffer.as_ref(),
|
||||
};
|
||||
if render_ctx.dev_id == dev.id {
|
||||
return wrap_error(&builder, None, |dbg| {
|
||||
let prepared = &builder.prepare_prime_none(dbg)?;
|
||||
self.create_scanout_buffers_(|damage| {
|
||||
builder.create_prime_none(prepared, damage, dbg)
|
||||
})
|
||||
})
|
||||
.map_err(MetalError::AllocateScanoutBuffer);
|
||||
}
|
||||
let mut errors = ScanoutBufferErrors::default();
|
||||
for &method in &*PRIME_METHODS {
|
||||
let res = wrap_error(&builder, Some(method), |dbg| {
|
||||
macro_rules! x {
|
||||
($prepare:ident, $create:ident $(,)?) => {{
|
||||
let prepared = &builder.$prepare(dbg)?;
|
||||
self.create_scanout_buffers_(|damage| {
|
||||
builder.$create(prepared, damage, dbg)
|
||||
})
|
||||
}};
|
||||
}
|
||||
match method {
|
||||
PrimeMethod::Sampling => {
|
||||
x!(prepare_prime_sampling, create_prime_sampling)
|
||||
}
|
||||
}
|
||||
});
|
||||
match res {
|
||||
Err(e) => errors.errors.push(e),
|
||||
Ok(b) => {
|
||||
if errors.errors.is_not_empty() {
|
||||
log::warn!("Preferred prime methods failed");
|
||||
let debug = log::log_enabled!(Level::Debug);
|
||||
for error in &errors.errors {
|
||||
let Some(method) = error.prime else {
|
||||
continue;
|
||||
};
|
||||
if debug {
|
||||
log::warn!("- {method}: {}", ErrorFmt(error));
|
||||
} else {
|
||||
log::warn!("- {method}: {}", ErrorFmt(&error.kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(MetalError::AllocateScanoutBufferPrime(errors))
|
||||
}
|
||||
|
||||
fn create_scanout_buffers_<const N: usize>(
|
||||
&self,
|
||||
allocate: impl Fn(DamageQueue) -> Result<RenderBuffer, ScanoutBufferErrorKind>,
|
||||
) -> Result<[RenderBuffer; N], ScanoutBufferErrorKind> {
|
||||
let mut damage_queue = ArrayVec::from(DamageQueue::new::<N>());
|
||||
let mut array = ArrayVec::<_, N>::new();
|
||||
for _ in 0..N {
|
||||
let damage_queue = damage_queue.pop().unwrap();
|
||||
array.push(allocate(damage_queue)?);
|
||||
}
|
||||
if let Some(buffer) = array.first() {
|
||||
buffer.damage_full();
|
||||
}
|
||||
Ok(array.into_inner().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ScanoutBufferErrorKind {
|
||||
#[error("Scanout device: The format is not supported")]
|
||||
SodUnsupportedFormat,
|
||||
#[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("Render device: The intersection of render/sample/sod_sample modifiers is empty")]
|
||||
RenderWriteReadSodReadIntersection,
|
||||
#[error("Scanout device: The intersection of render/sample/plane modifiers is empty")]
|
||||
SodWriteReadPlaneIntersection,
|
||||
#[error("Scanout device: The intersection of render/plane modifiers is empty")]
|
||||
SodWritePlaneIntersection,
|
||||
#[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(Default, Debug)]
|
||||
pub struct ScanoutBufferErrors {
|
||||
#[expect(clippy::vec_box)]
|
||||
errors: Vec<Box<ScanoutBufferError>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScanoutBufferError {
|
||||
dev: String,
|
||||
render_name: Option<String>,
|
||||
format: &'static Format,
|
||||
plane_modifiers: IndexSet<Modifier>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
cursor: bool,
|
||||
dbg: RenderBufferAllocationDebug,
|
||||
kind: ScanoutBufferErrorKind,
|
||||
prime: Option<PrimeMethod>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Linearize)]
|
||||
pub enum PrimeMethod {
|
||||
Sampling,
|
||||
}
|
||||
|
||||
impl PrimeMethod {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
PrimeMethod::Sampling => "direct-sampling",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PrimeMethod {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PrimeMethod {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBufferPrime {
|
||||
pub fn method(&self) -> Option<PrimeMethod> {
|
||||
let method = match self {
|
||||
RenderBufferPrime::None => return None,
|
||||
RenderBufferPrime::Sampling { .. } => PrimeMethod::Sampling,
|
||||
};
|
||||
Some(method)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct RenderBufferAllocationDebug {
|
||||
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_gfx_write_modifiers: Option<Vec<Modifier>>,
|
||||
render_gfx_read_modifiers: Option<Vec<Modifier>>,
|
||||
render_modifiers_possible: Option<Vec<Modifier>>,
|
||||
render_usage: Option<u32>,
|
||||
render_modifier: Option<Modifier>,
|
||||
}
|
||||
|
||||
impl Display for ScanoutBufferErrors {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
for (idx, error) in self.errors.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
writeln!(f, "\n------")?;
|
||||
}
|
||||
write!(f, "{}", ErrorFmt(error))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ScanoutBufferErrors {}
|
||||
|
||||
impl Display for ScanoutBufferError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
if let Some(v) = self.prime {
|
||||
writeln!(f, "prime type: {}", v)?;
|
||||
}
|
||||
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.dbg.dev_gfx_write_modifiers {
|
||||
writeln!(f, "scanout gfx writable modifiers: {:x?}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.dev_modifiers_possible {
|
||||
writeln!(f, "scanout dev possible modifiers: {:x?}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.dev_usage {
|
||||
writeln!(f, "scanout dev gbm usage: {:x}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.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.dbg.render_gfx_write_modifiers {
|
||||
writeln!(f, "render gfx writable modifiers: {:x?}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.render_gfx_read_modifiers {
|
||||
writeln!(f, "render gfx readable modifiers: {:x?}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.dev_gfx_read_modifiers {
|
||||
writeln!(f, "scanout gfx readable modifiers: {:x?}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.render_modifiers_possible {
|
||||
writeln!(f, "render dev possible modifiers: {:x?}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.render_usage {
|
||||
writeln!(f, "render dev gbm usage: {:x}", v)?;
|
||||
}
|
||||
if let Some(v) = &self.dbg.render_modifier {
|
||||
writeln!(f, "render dev modifier: {:x}", v)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ScanoutBufferError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_error<T>(
|
||||
common: &Builder<'_>,
|
||||
prime: Option<PrimeMethod>,
|
||||
f: impl FnOnce(&RefCell<RenderBufferAllocationDebug>) -> Result<T, ScanoutBufferErrorKind>,
|
||||
) -> Result<T, Box<ScanoutBufferError>> {
|
||||
let dbg = Default::default();
|
||||
f(&dbg)
|
||||
.map_err(|kind| ScanoutBufferError {
|
||||
dev: common.dev.devnode.as_bytes().as_bstr().to_string(),
|
||||
render_name: (common.dev.id != common.render_ctx.dev_id)
|
||||
.then(|| common.render_ctx.devnode.as_bytes().as_bstr().to_string()),
|
||||
format: common.format,
|
||||
plane_modifiers: common.plane_modifiers.clone(),
|
||||
width: common.width,
|
||||
height: common.height,
|
||||
cursor: common.cursor,
|
||||
dbg: dbg.into_inner(),
|
||||
kind,
|
||||
prime,
|
||||
})
|
||||
.map_err(Box::new)
|
||||
}
|
||||
|
||||
impl BoAllocationSettings {
|
||||
fn new1(
|
||||
common: &Builder<'_>,
|
||||
modifiers: &IndexMap<Modifier, &GfxWriteModifier>,
|
||||
scanout: bool,
|
||||
rendering: bool,
|
||||
usage_out: &mut Option<u32>,
|
||||
) -> Self {
|
||||
let needs_render_usage = rendering && needs_render_usage(modifiers.values().copied());
|
||||
Self::new3(
|
||||
common,
|
||||
modifiers.keys(),
|
||||
scanout,
|
||||
needs_render_usage,
|
||||
usage_out,
|
||||
)
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
fn new2<'a>(
|
||||
common: &Builder<'_>,
|
||||
modifiers: impl IntoIterator<Item = &'a Modifier> + Clone,
|
||||
fmt: &GfxFormat,
|
||||
scanout: bool,
|
||||
rendering: bool,
|
||||
usage_out: &mut Option<u32>,
|
||||
) -> Self {
|
||||
let needs_render_usage = rendering
|
||||
&& needs_render_usage(
|
||||
modifiers
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(|m| fmt.write_modifiers.get(m)),
|
||||
);
|
||||
Self::new3(common, modifiers, scanout, needs_render_usage, usage_out)
|
||||
}
|
||||
|
||||
fn new3<'a>(
|
||||
common: &Builder<'_>,
|
||||
modifiers: impl IntoIterator<Item = &'a Modifier>,
|
||||
scanout: bool,
|
||||
needs_render_usage: bool,
|
||||
usage_out: &mut Option<u32>,
|
||||
) -> Self {
|
||||
let mut usage = 0;
|
||||
if scanout {
|
||||
usage |= GBM_BO_USE_SCANOUT;
|
||||
if common.cursor {
|
||||
usage |= GBM_BO_USE_LINEAR;
|
||||
}
|
||||
}
|
||||
if needs_render_usage {
|
||||
usage |= GBM_BO_USE_RENDERING;
|
||||
}
|
||||
*usage_out = Some(usage);
|
||||
Self {
|
||||
modifiers: modifiers.into_iter().copied().collect(),
|
||||
usage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<'_> {
|
||||
fn create(
|
||||
&self,
|
||||
drm: Rc<DrmFramebuffer>,
|
||||
damage_queue: DamageQueue,
|
||||
render: RenderBufferRender,
|
||||
prime: RenderBufferPrime,
|
||||
) -> Result<RenderBuffer, ScanoutBufferErrorKind> {
|
||||
let Self {
|
||||
dev_ctx,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
blend_buffer,
|
||||
..
|
||||
} = *self;
|
||||
Ok(RenderBuffer {
|
||||
width,
|
||||
height,
|
||||
locked: Cell::new(true),
|
||||
format,
|
||||
drm,
|
||||
damage_queue,
|
||||
blend_buffer: blend_buffer.cloned(),
|
||||
render,
|
||||
dev_ctx: dev_ctx.clone(),
|
||||
prime,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_bo(
|
||||
&self,
|
||||
ctx: &MetalRenderContext,
|
||||
settings: &BoAllocationSettings,
|
||||
) -> Result<GbmBo, GbmError> {
|
||||
ctx.gbm.create_bo(
|
||||
&self.slf.state.dma_buf_ids,
|
||||
self.width,
|
||||
self.height,
|
||||
self.format,
|
||||
&settings.modifiers,
|
||||
settings.usage,
|
||||
)
|
||||
}
|
||||
|
||||
fn create_dev_bo(
|
||||
&self,
|
||||
settings: &BoAllocationSettings,
|
||||
dbg: &RefCell<RenderBufferAllocationDebug>,
|
||||
) -> Result<(GbmBo, Rc<DrmFramebuffer>), ScanoutBufferErrorKind> {
|
||||
let bo = self
|
||||
.create_bo(self.dev_ctx, settings)
|
||||
.map_err(ScanoutBufferErrorKind::SodBufferAllocation)?;
|
||||
let send_dev_modifier = on_drop(|| {
|
||||
dbg.borrow_mut().dev_modifier = Some(bo.dmabuf().modifier);
|
||||
});
|
||||
let drm = self
|
||||
.dev
|
||||
.master
|
||||
.add_fb(bo.dmabuf(), None)
|
||||
.map(Rc::new)
|
||||
.map_err(ScanoutBufferErrorKind::SodAddfb2)?;
|
||||
send_dev_modifier.forget();
|
||||
Ok((bo, drm))
|
||||
}
|
||||
|
||||
fn create_render_buffer_render(
|
||||
&self,
|
||||
settings: &BoAllocationSettings,
|
||||
dbg: &RefCell<RenderBufferAllocationDebug>,
|
||||
) -> Result<RenderBufferRender, ScanoutBufferErrorKind> {
|
||||
let Self { render_ctx, .. } = *self;
|
||||
let bo = self
|
||||
.create_bo(render_ctx, settings)
|
||||
.map_err(ScanoutBufferErrorKind::RenderBufferAllocation)?;
|
||||
let send_render_modifier = on_drop(|| {
|
||||
dbg.borrow_mut().render_modifier = Some(bo.dmabuf().modifier);
|
||||
});
|
||||
let img = render_ctx
|
||||
.gfx
|
||||
.clone()
|
||||
.dmabuf_img(bo.dmabuf())
|
||||
.map_err(ScanoutBufferErrorKind::RenderImportImage)?;
|
||||
let tex = img
|
||||
.clone()
|
||||
.to_texture()
|
||||
.map_err(ScanoutBufferErrorKind::RenderImportRenderTexture)?;
|
||||
let fb = img
|
||||
.to_framebuffer()
|
||||
.map_err(ScanoutBufferErrorKind::RenderImportFb)?;
|
||||
fb.clear(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
self.slf.state.color_manager.srgb_gamma22(),
|
||||
)
|
||||
.map_err(ScanoutBufferErrorKind::RenderClear)?;
|
||||
send_render_modifier.forget();
|
||||
Ok(RenderBufferRender {
|
||||
ctx: render_ctx.clone(),
|
||||
bo,
|
||||
tex,
|
||||
fb,
|
||||
})
|
||||
}
|
||||
|
||||
fn prepare_prime_none(
|
||||
&self,
|
||||
dbg: &RefCell<RenderBufferAllocationDebug>,
|
||||
) -> Result<NoPrime, ScanoutBufferErrorKind> {
|
||||
let dbg = &mut *dbg.borrow_mut();
|
||||
let Self {
|
||||
render_fmt,
|
||||
plane_modifiers,
|
||||
..
|
||||
} = *self;
|
||||
let modifiers: IndexMap<_, _> = render_fmt
|
||||
.write_modifiers
|
||||
.iter()
|
||||
.map(|(m, v)| (*m, v))
|
||||
.filter(|(m, _)| plane_modifiers.contains(m))
|
||||
.filter(|(m, _)| render_fmt.read_modifiers.contains(m))
|
||||
.collect();
|
||||
dbg.dev_gfx_write_modifiers = Some(render_modifiers(render_fmt));
|
||||
dbg.dev_gfx_read_modifiers = Some(sample_modifiers(render_fmt));
|
||||
dbg.dev_modifiers_possible = Some(modifiers.keys().copied().collect());
|
||||
if modifiers.is_empty() {
|
||||
return Err(ScanoutBufferErrorKind::SodWriteReadPlaneIntersection);
|
||||
}
|
||||
let allocation_settings =
|
||||
BoAllocationSettings::new1(self, &modifiers, true, true, &mut dbg.render_usage);
|
||||
Ok(NoPrime {
|
||||
allocation_settings,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_prime_none(
|
||||
&self,
|
||||
prepared: &NoPrime,
|
||||
damage_queue: DamageQueue,
|
||||
dbg: &RefCell<RenderBufferAllocationDebug>,
|
||||
) -> Result<RenderBuffer, ScanoutBufferErrorKind> {
|
||||
let NoPrime {
|
||||
allocation_settings,
|
||||
} = prepared;
|
||||
let Self { dev, .. } = *self;
|
||||
let render = self.create_render_buffer_render(allocation_settings, dbg)?;
|
||||
let send_dev_modifier = on_drop(|| {
|
||||
dbg.borrow_mut().dev_modifier = Some(render.bo.dmabuf().modifier);
|
||||
});
|
||||
let drm = dev
|
||||
.master
|
||||
.add_fb(render.bo.dmabuf(), None)
|
||||
.map(Rc::new)
|
||||
.map_err(ScanoutBufferErrorKind::SodAddfb2)?;
|
||||
send_dev_modifier.forget();
|
||||
let prime = RenderBufferPrime::None;
|
||||
self.create(drm, damage_queue, render, prime)
|
||||
}
|
||||
|
||||
fn prepare_prime_sampling(
|
||||
&self,
|
||||
dbg: &RefCell<RenderBufferAllocationDebug>,
|
||||
) -> Result<PrimeSampling, ScanoutBufferErrorKind> {
|
||||
let dbg = &mut *dbg.borrow_mut();
|
||||
let Self {
|
||||
dev_ctx,
|
||||
format,
|
||||
render_fmt,
|
||||
plane_modifiers,
|
||||
..
|
||||
} = *self;
|
||||
let Some(dev_fmt) = dev_ctx.gfx.formats().get(&format.drm) else {
|
||||
return Err(ScanoutBufferErrorKind::SodUnsupportedFormat);
|
||||
};
|
||||
let render_modifiers_possible: IndexMap<_, _> = render_fmt
|
||||
.write_modifiers
|
||||
.iter()
|
||||
.filter(|(m, _)| render_fmt.read_modifiers.contains(*m))
|
||||
.filter(|(m, _)| dev_fmt.read_modifiers.contains(*m))
|
||||
.map(|(m, v)| (*m, v))
|
||||
.collect();
|
||||
dbg.dev_gfx_read_modifiers = Some(sample_modifiers(dev_fmt));
|
||||
dbg.render_gfx_write_modifiers = Some(render_modifiers(render_fmt));
|
||||
dbg.render_gfx_read_modifiers = Some(sample_modifiers(render_fmt));
|
||||
dbg.render_modifiers_possible = Some(render_modifiers_possible.keys().copied().collect());
|
||||
if render_modifiers_possible.is_empty() {
|
||||
return Err(ScanoutBufferErrorKind::RenderWriteReadSodReadIntersection);
|
||||
}
|
||||
let dev_modifiers_possible: IndexMap<_, _> = dev_fmt
|
||||
.write_modifiers
|
||||
.iter()
|
||||
.filter(|(m, _)| plane_modifiers.contains(*m))
|
||||
.map(|(m, v)| (*m, v))
|
||||
.collect();
|
||||
dbg.dev_gfx_write_modifiers = Some(render_modifiers(dev_fmt));
|
||||
dbg.dev_modifiers_possible = Some(dev_modifiers_possible.keys().copied().collect());
|
||||
if dev_modifiers_possible.is_empty() {
|
||||
return Err(ScanoutBufferErrorKind::SodWritePlaneIntersection);
|
||||
}
|
||||
let render_allocation_settings = BoAllocationSettings::new1(
|
||||
self,
|
||||
&render_modifiers_possible,
|
||||
false,
|
||||
true,
|
||||
&mut dbg.render_usage,
|
||||
);
|
||||
let dev_allocation_settings = BoAllocationSettings::new1(
|
||||
self,
|
||||
&dev_modifiers_possible,
|
||||
true,
|
||||
true,
|
||||
&mut dbg.dev_usage,
|
||||
);
|
||||
Ok(PrimeSampling {
|
||||
render_allocation_settings,
|
||||
dev_allocation_settings,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_prime_sampling(
|
||||
&self,
|
||||
prepared: &PrimeSampling,
|
||||
damage_queue: DamageQueue,
|
||||
dbg: &RefCell<RenderBufferAllocationDebug>,
|
||||
) -> Result<RenderBuffer, ScanoutBufferErrorKind> {
|
||||
let PrimeSampling {
|
||||
render_allocation_settings,
|
||||
dev_allocation_settings,
|
||||
} = prepared;
|
||||
let Self { dev_ctx, .. } = *self;
|
||||
let render = self.create_render_buffer_render(render_allocation_settings, dbg)?;
|
||||
let send_render_modifier = on_drop(|| {
|
||||
dbg.borrow_mut().render_modifier = Some(render.bo.dmabuf().modifier);
|
||||
});
|
||||
let dev_render_tex = dev_ctx
|
||||
.gfx
|
||||
.clone()
|
||||
.dmabuf_img(render.bo.dmabuf())
|
||||
.map_err(ScanoutBufferErrorKind::SodImportRenderImage)?
|
||||
.to_texture()
|
||||
.map_err(ScanoutBufferErrorKind::SodImportRenderTexture)?;
|
||||
let (dev_bo, drm) = self.create_dev_bo(dev_allocation_settings, dbg)?;
|
||||
let send_dev_modifier = on_drop(|| {
|
||||
dbg.borrow_mut().dev_modifier = Some(dev_bo.dmabuf().modifier);
|
||||
});
|
||||
let dev_fb = dev_ctx
|
||||
.gfx
|
||||
.clone()
|
||||
.dmabuf_img(dev_bo.dmabuf())
|
||||
.map_err(ScanoutBufferErrorKind::SodImportSodImage)?
|
||||
.to_framebuffer()
|
||||
.map_err(ScanoutBufferErrorKind::SodImportFb)?;
|
||||
send_dev_modifier.forget();
|
||||
send_render_modifier.forget();
|
||||
let prime = RenderBufferPrime::Sampling {
|
||||
dev_bo,
|
||||
dev_fb,
|
||||
dev_render_tex,
|
||||
};
|
||||
self.create(drm, damage_queue, render, prime)
|
||||
}
|
||||
}
|
||||
|
||||
const JAY_PRIME_METHODS: &str = "JAY_PRIME_METHODS";
|
||||
|
||||
type PrimeMethods = ArrayVec<PrimeMethod, { PrimeMethod::LENGTH }>;
|
||||
|
||||
static PRIME_METHODS: LazyLock<PrimeMethods> = LazyLock::new(prime_methods);
|
||||
|
||||
fn prime_methods() -> PrimeMethods {
|
||||
let mut res = PrimeMethods::new();
|
||||
let mut seen = StaticMap::<_, bool>::default();
|
||||
let mut apply = |method: PrimeMethod, allow: bool| {
|
||||
if !seen[method] {
|
||||
seen[method] = true;
|
||||
if allow {
|
||||
res.push(method);
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Ok(var) = std::env::var(JAY_PRIME_METHODS) {
|
||||
for mut name in var.split(",") {
|
||||
name = name.trim();
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut allow = true;
|
||||
if let Some(m) = name.strip_prefix("-") {
|
||||
name = m;
|
||||
allow = false;
|
||||
}
|
||||
let Some(method) = PrimeMethod::variants().find(|m| m.name() == name) else {
|
||||
log::warn!("Unknown prime method {}", name);
|
||||
continue;
|
||||
};
|
||||
apply(method, allow);
|
||||
}
|
||||
}
|
||||
for method in PrimeMethod::variants() {
|
||||
apply(method, true);
|
||||
}
|
||||
log::info!("Prime methods: {:?}", res);
|
||||
res
|
||||
}
|
||||
|
||||
fn sample_modifiers(fmt: &GfxFormat) -> Vec<Modifier> {
|
||||
fmt.read_modifiers.iter().copied().collect()
|
||||
}
|
||||
|
||||
fn render_modifiers(fmt: &GfxFormat) -> Vec<Modifier> {
|
||||
fmt.write_modifiers.keys().copied().collect()
|
||||
}
|
||||
|
|
@ -3,11 +3,9 @@ use {
|
|||
backend::Connector,
|
||||
backends::metal::{
|
||||
MetalError,
|
||||
allocator::{RenderBuffer, RenderBufferCopy},
|
||||
transaction::{DrmConnectorState, DrmPlaneState},
|
||||
video::{
|
||||
MetalConnector, MetalCrtc, MetalHardwareCursorChange, MetalPlane, RenderBuffer,
|
||||
RenderBufferCopy,
|
||||
},
|
||||
video::{MetalConnector, MetalCrtc, MetalHardwareCursorChange, MetalPlane},
|
||||
},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
gfx_api::{
|
||||
|
|
@ -557,7 +555,11 @@ impl MetalConnector {
|
|||
let swap_buffers = c.cursor_swap_buffer.is_some();
|
||||
self.cursor_swap_buffer.set(swap_buffers);
|
||||
if let Some(sf) = c.cursor_swap_buffer.take() {
|
||||
let sf = c.cursor_buffer.copy_to_dev(cd, sf)?.present_block;
|
||||
let sf = c
|
||||
.cursor_buffer
|
||||
.copy_to_dev(cd, sf)
|
||||
.map_err(MetalError::CopyToDev)?
|
||||
.present_block;
|
||||
self.cursor_sync_file.set(sf);
|
||||
}
|
||||
let mut cursor_changed = false;
|
||||
|
|
@ -588,13 +590,12 @@ impl MetalConnector {
|
|||
}
|
||||
let buffer_idx = (front_buffer % buffers.len() as u64) as usize;
|
||||
let buffer = &buffers[buffer_idx];
|
||||
let (width, height) = buffer.dev_fb.physical_size();
|
||||
CursorProgrammingType::Enable {
|
||||
fb: buffer.drm.clone(),
|
||||
x: self.cursor_x.get(),
|
||||
y: self.cursor_y.get(),
|
||||
width,
|
||||
height,
|
||||
width: buffer.width,
|
||||
height: buffer.height,
|
||||
swap,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -862,7 +863,8 @@ impl MetalConnector {
|
|||
match &direct_scanout_data {
|
||||
None => {
|
||||
let sf = buffer
|
||||
.render_fb()
|
||||
.render
|
||||
.fb
|
||||
.perform_render_pass(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
|
|
@ -873,9 +875,9 @@ impl MetalConnector {
|
|||
blend_cd,
|
||||
)
|
||||
.map_err(MetalError::RenderFrame)?;
|
||||
copy = buffer.copy_to_dev(cd, sf)?;
|
||||
copy = buffer.copy_to_dev(cd, sf).map_err(MetalError::CopyToDev)?;
|
||||
fb = buffer.drm.clone();
|
||||
tex = buffer.render_tex.clone();
|
||||
tex = buffer.render.tex.clone();
|
||||
}
|
||||
Some(dsd) => {
|
||||
let sf = match &dsd.acquire_sync {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ use {
|
|||
BackendConnectorTransactionError, BackendPreparedConnectorTransaction,
|
||||
},
|
||||
},
|
||||
backends::metal::video::{
|
||||
FrontState, MetalConnector, MetalCrtc, MetalDrmDeviceData, MetalPlane, PlaneType,
|
||||
RenderBuffer,
|
||||
backends::metal::{
|
||||
allocator::RenderBuffer,
|
||||
video::{
|
||||
FrontState, MetalConnector, MetalCrtc, MetalDrmDeviceData, MetalPlane, PlaneType,
|
||||
},
|
||||
},
|
||||
format::{ARGB8888, Format},
|
||||
gfx_api::SyncFile,
|
||||
|
|
@ -464,7 +466,7 @@ impl MetalDeviceTransaction {
|
|||
if b[0].width != width || b[0].height != height || b[0].format != format {
|
||||
discard!();
|
||||
}
|
||||
if !rc_eq(render_ctx, &b[0].render_ctx) {
|
||||
if !rc_eq(render_ctx, &b[0].render.ctx) {
|
||||
discard!();
|
||||
}
|
||||
if !rc_eq(&dev_ctx, &b[0].dev_ctx) {
|
||||
|
|
@ -472,7 +474,7 @@ impl MetalDeviceTransaction {
|
|||
}
|
||||
let modifiers = &plane.obj.formats.get(&format.drm).unwrap().modifiers;
|
||||
for b in &**b {
|
||||
if !modifiers.contains(&b.dev_bo.dmabuf().modifier) {
|
||||
if !modifiers.contains(&b.dev_bo().dmabuf().modifier) {
|
||||
discard!();
|
||||
}
|
||||
}
|
||||
|
|
@ -517,7 +519,20 @@ impl MetalDeviceTransaction {
|
|||
*plane_id = DrmPlane::NONE;
|
||||
continue;
|
||||
}
|
||||
let buffers = Rc::new(res?);
|
||||
let res = res?;
|
||||
if let Some(method) = res[0].prime.method() {
|
||||
let plane = match plane.obj.ty {
|
||||
PlaneType::Overlay => "overlay",
|
||||
PlaneType::Primary => "primary",
|
||||
PlaneType::Cursor => "cursor",
|
||||
};
|
||||
let dev = slf.dev.dev.devnode.as_bytes().as_bstr();
|
||||
let connector = connector.obj.kernel_id();
|
||||
log::info!(
|
||||
"Using prime method {method} for {dev} {connector} ({plane})",
|
||||
);
|
||||
}
|
||||
let buffers = Rc::new(res);
|
||||
plane.new.buffers = Some(buffers.clone());
|
||||
new_buffers = Some(buffers.clone());
|
||||
buffers
|
||||
|
|
@ -572,7 +587,7 @@ impl MetalDeviceTransaction {
|
|||
match prev.copy_to_new(new_buffer, &cd) {
|
||||
Ok(sf) => Ok(sf),
|
||||
Err(e) => {
|
||||
log::warn!("Could not copy from old buffer: {}", ErrorFmt(&*e));
|
||||
log::warn!("Could not copy from old buffer: {}", ErrorFmt(e));
|
||||
new_buffer.clear(&cd)
|
||||
}
|
||||
}
|
||||
|
|
@ -582,7 +597,7 @@ impl MetalDeviceTransaction {
|
|||
match res {
|
||||
Ok(sf) => sync_files.extend(sf),
|
||||
Err(e) => {
|
||||
log::warn!("Could not clear new buffer: {}", ErrorFmt(&*e));
|
||||
log::warn!("Could not clear new buffer: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -623,7 +638,7 @@ impl MetalDeviceTransaction {
|
|||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not black out old buffer: {}",
|
||||
ErrorFmt(&*e),
|
||||
ErrorFmt(e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::BufferObject,
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
backend::{
|
||||
BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLease,
|
||||
|
|
@ -14,7 +13,8 @@ use {
|
|||
},
|
||||
},
|
||||
backends::metal::{
|
||||
MetalBackend, MetalError, ScanoutBufferError, ScanoutBufferErrorKind,
|
||||
MetalBackend, MetalError,
|
||||
allocator::RenderBuffer,
|
||||
present::{
|
||||
DEFAULT_POST_COMMIT_MARGIN, DEFAULT_PRE_COMMIT_MARGIN, DirectScanoutCache,
|
||||
POST_COMMIT_MARGIN_DELTA, PresentFb,
|
||||
|
|
@ -25,15 +25,11 @@ use {
|
|||
drm_feedback::DrmFeedback,
|
||||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||
format::{Format, XRGB8888},
|
||||
gfx_api::{
|
||||
AcquireSync, GfxBlendBuffer, GfxContext, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||
SyncFile, needs_render_usage,
|
||||
},
|
||||
gfx_api::{GfxContext, GfxFramebuffer, SyncFile},
|
||||
ifs::{
|
||||
wl_output::OutputId,
|
||||
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
|
||||
},
|
||||
rect::{DamageQueue, Rect},
|
||||
state::State,
|
||||
tree::OutputNode,
|
||||
udev::UdevDevice,
|
||||
|
|
@ -41,32 +37,29 @@ use {
|
|||
asyncevent::AsyncEvent, binary_search_map::BinarySearchMap, bitflags::BitflagsExt,
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||
geometric_decay::GeometricDecay, numcell::NumCell, on_change::OnChange,
|
||||
opaque_cell::OpaqueCell, ordered_float::F64, oserror::OsError, rc_eq::rc_eq,
|
||||
opaque_cell::OpaqueCell, ordered_float::F64, oserror::OsError,
|
||||
},
|
||||
video::{
|
||||
INVALID_MODIFIER, Modifier,
|
||||
dmabuf::DmaBufId,
|
||||
drm::{
|
||||
ConnectorStatus, ConnectorType, DRM_CLIENT_CAP_ATOMIC, DrmBlob, DrmConnector,
|
||||
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmFramebuffer, DrmLease,
|
||||
DrmMaster, DrmModeInfo, DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition,
|
||||
DrmPropertyType, DrmVersion, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo,
|
||||
DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFb, DrmLease, DrmMaster, DrmModeInfo,
|
||||
DrmObject, DrmPlane, DrmProperty, DrmPropertyDefinition, DrmPropertyType,
|
||||
DrmVersion, HDMI_EOTF_TRADITIONAL_GAMMA_SDR, drm_mode_modeinfo,
|
||||
hdr_output_metadata,
|
||||
},
|
||||
gbm::{GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT, GbmBo, GbmDevice},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::{AHashMap, AHashSet},
|
||||
arrayvec::ArrayVec,
|
||||
bstr::{BString, ByteSlice},
|
||||
indexmap::{IndexMap, IndexSet, indexset},
|
||||
indexmap::{IndexSet, indexset},
|
||||
isnt::std_1::collections::IsntHashMapExt,
|
||||
jay_config::video::GfxApi,
|
||||
run_on_drop::on_drop,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
error::Error,
|
||||
ffi::CString,
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
|
|
@ -588,7 +581,7 @@ impl HardwareCursorUpdate for MetalHardwareCursorChange<'_> {
|
|||
}
|
||||
|
||||
fn get_buffer(&self) -> Rc<dyn GfxFramebuffer> {
|
||||
self.cursor_buffer.render_fb()
|
||||
self.cursor_buffer.render.fb.clone()
|
||||
}
|
||||
|
||||
fn set_position(&mut self, x: i32, y: i32) {
|
||||
|
|
@ -2603,315 +2596,6 @@ impl MetalBackend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_scanout_buffers<const N: usize>(
|
||||
&self,
|
||||
dev: &Rc<MetalDrmDevice>,
|
||||
format: &'static Format,
|
||||
plane_modifiers: &IndexSet<Modifier>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
render_ctx: &Rc<MetalRenderContext>,
|
||||
cursor: bool,
|
||||
) -> Result<[RenderBuffer; N], MetalError> {
|
||||
let mut blend_buffer = None;
|
||||
if !cursor {
|
||||
match render_ctx.gfx.acquire_blend_buffer(width, height) {
|
||||
Ok(bb) => blend_buffer = Some(bb),
|
||||
Err(e) => {
|
||||
log::warn!("Could not create blend buffer: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut damage_queue = ArrayVec::from(DamageQueue::new::<N>());
|
||||
let mut create = || {
|
||||
self.create_scanout_buffer(
|
||||
dev,
|
||||
format,
|
||||
plane_modifiers,
|
||||
width,
|
||||
height,
|
||||
render_ctx,
|
||||
cursor,
|
||||
damage_queue.pop().unwrap(),
|
||||
blend_buffer.clone(),
|
||||
)
|
||||
};
|
||||
let mut array = ArrayVec::<_, N>::new();
|
||||
for _ in 0..N {
|
||||
array.push(create()?);
|
||||
}
|
||||
if let Some(buffer) = array.first() {
|
||||
buffer.damage_full();
|
||||
}
|
||||
Ok(array.into_inner().unwrap())
|
||||
}
|
||||
|
||||
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>>,
|
||||
) -> 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_gfx_formats = dev_ctx.gfx.formats();
|
||||
let Some(dev_gfx_format) = dev_gfx_formats.get(&format.drm) else {
|
||||
return Err(ScanoutBufferErrorKind::SodUnsupportedFormat);
|
||||
};
|
||||
let send_dev_gfx_write_modifiers = on_drop(|| {
|
||||
*dbg_dev_gfx_write_modifiers =
|
||||
Some(dev_gfx_format.write_modifiers.keys().copied().collect())
|
||||
});
|
||||
let possible_modifiers: IndexMap<_, _> = dev_gfx_format
|
||||
.write_modifiers
|
||||
.iter()
|
||||
.filter(|(m, _)| plane_modifiers.contains(*m))
|
||||
.map(|(m, v)| (*m, v))
|
||||
.collect();
|
||||
let send_dev_modifiers_possible = on_drop(|| {
|
||||
*dbg_dev_modifiers_possible = Some(possible_modifiers.keys().copied().collect())
|
||||
});
|
||||
if possible_modifiers.is_empty() {
|
||||
return Err(ScanoutBufferErrorKind::SodNoWritableModifier);
|
||||
}
|
||||
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
|
||||
if !needs_render_usage(possible_modifiers.values().copied()) {
|
||||
usage &= !GBM_BO_USE_RENDERING;
|
||||
}
|
||||
if cursor {
|
||||
usage |= GBM_BO_USE_LINEAR;
|
||||
};
|
||||
*dbg_dev_usage = Some(usage);
|
||||
let dev_bo = dev.gbm.create_bo(
|
||||
&self.state.dma_buf_ids,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
possible_modifiers.keys(),
|
||||
usage,
|
||||
);
|
||||
let dev_bo = match dev_bo {
|
||||
Ok(b) => b,
|
||||
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) {
|
||||
Ok(fb) => Rc::new(fb),
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::SodAddfb2(e)),
|
||||
};
|
||||
let dev_img = match dev_ctx.gfx.clone().dmabuf_img(dev_bo.dmabuf()) {
|
||||
Ok(img) => img,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::SodImportSodImage(e)),
|
||||
};
|
||||
let dev_fb = match dev_img.clone().to_framebuffer() {
|
||||
Ok(fb) => fb,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::SodImportFb(e)),
|
||||
};
|
||||
dev_fb
|
||||
.clear(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
self.state.color_manager.srgb_gamma22(),
|
||||
)
|
||||
.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 render_tex = match dev_img.to_texture() {
|
||||
Ok(fb) => fb,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::SodImportSodTexture(e)),
|
||||
};
|
||||
(None, render_tex, None, None)
|
||||
} else {
|
||||
send_render_dev_name = Some(on_drop(|| {
|
||||
*dbg_render_name = Some(render_ctx.devnode.as_bytes().as_bstr().to_string());
|
||||
}));
|
||||
// Create a _bridge_ BO in the render device
|
||||
render_gfx_formats = render_ctx.gfx.formats();
|
||||
let render_gfx_format = match render_gfx_formats.get(&format.drm) {
|
||||
None => return Err(ScanoutBufferErrorKind::RenderUnsupportedFormat),
|
||||
Some(f) => f,
|
||||
};
|
||||
send_render_gfx_write_modifiers = Some(on_drop(|| {
|
||||
*dbg_render_gfx_write_modifiers =
|
||||
Some(render_gfx_format.write_modifiers.keys().copied().collect())
|
||||
}));
|
||||
send_dev_gfx_read_modifiers = Some(on_drop(|| {
|
||||
*dbg_dev_gfx_read_modifiers = Some(dev_gfx_format.read_modifiers.clone());
|
||||
}));
|
||||
render_possible_modifiers = render_gfx_format
|
||||
.write_modifiers
|
||||
.iter()
|
||||
.filter(|(m, _)| dev_gfx_format.read_modifiers.contains(*m))
|
||||
.map(|(m, v)| (*m, v))
|
||||
.collect();
|
||||
send_render_possible_modifiers = Some(on_drop(|| {
|
||||
*dbg_render_modifiers_possible =
|
||||
Some(render_possible_modifiers.keys().copied().collect())
|
||||
}));
|
||||
if render_possible_modifiers.is_empty() {
|
||||
return Err(ScanoutBufferErrorKind::RenderNoWritableModifier);
|
||||
}
|
||||
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
|
||||
if !needs_render_usage(render_possible_modifiers.values().copied()) {
|
||||
usage &= !GBM_BO_USE_RENDERING;
|
||||
}
|
||||
*dbg_render_usage = Some(usage);
|
||||
let render_bo = render_ctx.gbm.create_bo(
|
||||
&self.state.dma_buf_ids,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
render_possible_modifiers.keys(),
|
||||
usage,
|
||||
);
|
||||
let render_bo = match render_bo {
|
||||
Ok(b) => b,
|
||||
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()) {
|
||||
Ok(img) => img,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::RenderImportImage(e)),
|
||||
};
|
||||
let render_fb = match render_img.clone().to_framebuffer() {
|
||||
Ok(fb) => fb,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::RenderImportFb(e)),
|
||||
};
|
||||
render_fb
|
||||
.clear(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
self.state.color_manager.srgb_gamma22(),
|
||||
)
|
||||
.map_err(ScanoutBufferErrorKind::RenderClear)?;
|
||||
let render_tex = match render_img.to_texture() {
|
||||
Ok(fb) => fb,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::RenderImportRenderTexture(e)),
|
||||
};
|
||||
|
||||
// Import the bridge BO into the current device
|
||||
let dev_img = match dev_ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) {
|
||||
Ok(img) => img,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::SodImportRenderImage(e)),
|
||||
};
|
||||
let dev_tex = match dev_img.to_texture() {
|
||||
Ok(fb) => fb,
|
||||
Err(e) => return Err(ScanoutBufferErrorKind::SodImportRenderTexture(e)),
|
||||
};
|
||||
|
||||
(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 {
|
||||
width,
|
||||
height,
|
||||
locked: Cell::new(true),
|
||||
format,
|
||||
dev_ctx,
|
||||
render_ctx: render_ctx.clone(),
|
||||
drm: drm_fb,
|
||||
damage_queue,
|
||||
dev_bo,
|
||||
_render_bo: render_bo,
|
||||
blend_buffer,
|
||||
dev_fb,
|
||||
dev_tex,
|
||||
render_tex,
|
||||
render_fb,
|
||||
})
|
||||
}
|
||||
|
||||
fn start_connector(&self, connector: &Rc<MetalConnector>, log_mode: bool) {
|
||||
let dd = &*connector.display.borrow();
|
||||
self.send_connected(connector, dd);
|
||||
|
|
@ -2938,127 +2622,3 @@ impl MetalBackend {
|
|||
connector.schedule_present();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderBuffer {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub locked: Cell<bool>,
|
||||
pub format: &'static Format,
|
||||
pub dev_ctx: Rc<MetalRenderContext>,
|
||||
pub render_ctx: Rc<MetalRenderContext>,
|
||||
pub drm: Rc<DrmFramebuffer>,
|
||||
pub damage_queue: DamageQueue,
|
||||
pub dev_bo: GbmBo,
|
||||
pub _render_bo: Option<GbmBo>,
|
||||
pub blend_buffer: Option<Rc<dyn GfxBlendBuffer>>,
|
||||
// ctx = dev
|
||||
// buffer location = dev
|
||||
pub dev_fb: Rc<dyn GfxFramebuffer>,
|
||||
// ctx = dev
|
||||
// buffer location = render
|
||||
pub dev_tex: Option<Rc<dyn GfxTexture>>,
|
||||
// ctx = render
|
||||
// buffer location = render
|
||||
pub render_tex: Rc<dyn GfxTexture>,
|
||||
// ctx = render
|
||||
// buffer location = render
|
||||
pub render_fb: Option<Rc<dyn GfxFramebuffer>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RenderBufferCopy {
|
||||
pub render_block: Option<SyncFile>,
|
||||
pub present_block: Option<SyncFile>,
|
||||
}
|
||||
|
||||
impl RenderBufferCopy {
|
||||
pub fn for_both(sf: Option<SyncFile>) -> Self {
|
||||
Self {
|
||||
render_block: sf.clone(),
|
||||
present_block: sf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBuffer {
|
||||
pub fn render_fb(&self) -> Rc<dyn GfxFramebuffer> {
|
||||
self.render_fb
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.dev_fb.clone())
|
||||
}
|
||||
|
||||
pub fn copy_to_dev(
|
||||
&self,
|
||||
cd: &Rc<ColorDescription>,
|
||||
sync_file: Option<SyncFile>,
|
||||
) -> Result<RenderBufferCopy, MetalError> {
|
||||
let Some(tex) = &self.dev_tex else {
|
||||
return Ok(RenderBufferCopy {
|
||||
render_block: None,
|
||||
present_block: sync_file,
|
||||
});
|
||||
};
|
||||
self.dev_fb
|
||||
.copy_texture(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
cd,
|
||||
tex,
|
||||
cd,
|
||||
None,
|
||||
AcquireSync::from_sync_file(sync_file),
|
||||
ReleaseSync::None,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
.map_err(MetalError::CopyToOutput)
|
||||
.map(RenderBufferCopy::for_both)
|
||||
}
|
||||
|
||||
pub fn damage_full(&self) {
|
||||
let dmabuf = self.dev_bo.dmabuf();
|
||||
let rect = Rect::new_sized_saturating(0, 0, dmabuf.width, dmabuf.height);
|
||||
self.damage_queue.clear_all();
|
||||
self.damage_queue.damage(&[rect]);
|
||||
}
|
||||
|
||||
pub fn clear(&self, cd: &Rc<ColorDescription>) -> Result<Option<SyncFile>, Box<dyn Error>> {
|
||||
self.dev_fb
|
||||
.clear(AcquireSync::Unnecessary, ReleaseSync::Explicit, cd)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn copy_to_new(
|
||||
&self,
|
||||
new: &Self,
|
||||
cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, Box<dyn Error>> {
|
||||
let old = self;
|
||||
let copy_texture = |new: &Rc<dyn GfxFramebuffer>, old: &Rc<dyn GfxTexture>| {
|
||||
new.copy_texture(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
cd,
|
||||
old,
|
||||
cd,
|
||||
None,
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
};
|
||||
if rc_eq(&old.dev_ctx, &new.dev_ctx) {
|
||||
return copy_texture(&new.dev_fb, old.dev_tex.as_ref().unwrap_or(&old.render_tex));
|
||||
}
|
||||
let tex = new
|
||||
.dev_ctx
|
||||
.gfx
|
||||
.clone()
|
||||
.dmabuf_img(old.dev_bo.dmabuf())?
|
||||
.to_texture()?;
|
||||
copy_texture(&new.dev_fb, &tex)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue