1
0
Fork 0
forked from wry/wry

metal: move direct-scanout preparation to shared code

This commit is contained in:
Julian Orth 2026-03-18 16:53:10 +01:00
parent 0cb887c4b5
commit 479cb1d795
2 changed files with 143 additions and 124 deletions

View file

@ -1214,3 +1214,138 @@ impl FdSync {
}
}
}
#[derive(Debug)]
pub struct DirectScanoutPosition {
pub src_width: i32,
pub src_height: i32,
pub crtc_x: i32,
pub crtc_y: i32,
pub crtc_width: i32,
pub crtc_height: i32,
}
impl GfxRenderPass {
pub fn prepare_direct_scanout(
&self,
mode_w: i32,
mode_h: i32,
blend_cd: &Rc<ColorDescription>,
cd: &Rc<ColorDescription>,
no_scaling: bool,
) -> Option<(&CopyTexture, DirectScanoutPosition)> {
let ct = 'ct: {
let mut ops = self.ops.iter().rev();
let ct = 'ct2: {
for opt in &mut ops {
match opt {
GfxApiOpt::Sync => {}
GfxApiOpt::FillRect(_) => {
// Top-most layer must be a texture.
return None;
}
GfxApiOpt::CopyTexture(ct) => break 'ct2 ct,
}
}
return None;
};
if ct.alpha_mode != AlphaMode::PremultipliedElectrical {
// Direct scanout requires premultiplied electrical alpha.
return None;
}
if !ct.cd.embeds_into(cd) {
// Direct scanout requires embeddable color descriptions.
return None;
}
if !ct.opaque && !ct.cd.embeds_into(blend_cd) {
// Blending changes the appearance of translucent buffers.
return None;
}
if ct.alpha.is_some() {
// Direct scanout with alpha factor is not supported.
return None;
}
if !ct.tex.format().has_alpha && ct.target.is_covering() {
// Texture covers the entire screen and is opaque.
break 'ct ct;
}
for opt in ops {
match opt {
GfxApiOpt::Sync => {}
GfxApiOpt::FillRect(fr) => {
if fr.effective_color() == Color::SOLID_BLACK {
// Black fills can be ignored because this is the CRTC background color.
if fr.rect.is_covering() {
// If fill covers the entire screen, we don't have to look further.
break 'ct ct;
}
} else {
// Fill could be visible.
return None;
}
}
GfxApiOpt::CopyTexture(_) => {
// Texture could be visible.
return None;
}
}
}
if let Some(clear) = self.clear
&& clear != Color::SOLID_BLACK
{
// Background could be visible.
return None;
}
ct
};
if let AcquireSync::None | AcquireSync::Implicit = ct.acquire_sync {
// Cannot perform scanout without explicit sync.
return None;
}
if ct.source.buffer_transform != ct.target.output_transform {
// Rotations and mirroring are not supported.
return None;
}
if !ct.source.is_covering() {
// Viewports are not supported.
return None;
}
if ct.target.x1 < -1.0 || ct.target.y1 < -1.0 || ct.target.x2 > 1.0 || ct.target.y2 > 1.0 {
// Rendering outside the screen is not supported.
return None;
}
let (tex_w, tex_h) = ct.tex.size();
let (x1, x2, y1, y2) = {
let plane_w = mode_w as f32;
let plane_h = mode_h as f32;
let ((x1, x2), (y1, y2)) = ct
.target
.output_transform
.maybe_swap(((ct.target.x1, ct.target.x2), (ct.target.y1, ct.target.y2)));
(
(x1 + 1.0) * plane_w / 2.0,
(x2 + 1.0) * plane_w / 2.0,
(y1 + 1.0) * plane_h / 2.0,
(y2 + 1.0) * plane_h / 2.0,
)
};
let (crtc_w, crtc_h) = (x2 - x1, y2 - y1);
if crtc_w < 0.0 || crtc_h < 0.0 {
// Flipping x or y axis is not supported.
return None;
}
if no_scaling && (tex_w as f32, tex_h as f32) != (crtc_w, crtc_h) {
// If scaling is not supported, we cannot scale the texture.
return None;
}
let position = DirectScanoutPosition {
src_width: tex_w,
src_height: tex_h,
crtc_x: x1 as _,
crtc_y: y1 as _,
crtc_width: crtc_w as _,
crtc_height: crtc_h as _,
};
Some((ct, position))
}
}