1
0
Fork 0
forked from wry/wry

render: propagate errors

This commit is contained in:
Julian Orth 2024-03-22 13:26:56 +01:00
parent d9fa3f6732
commit 1b4492c670
14 changed files with 169 additions and 105 deletions

View file

@ -114,6 +114,14 @@ pub enum MetalError {
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")]
RenderFrame(#[source] GfxError),
#[error("Could not copy frame to output device")]
CopyToOutput(#[source] GfxError),
#[error("Could not perform atomic commit")]
Commit(#[source] DrmError),
#[error("Could not clear framebuffer")]
Clear(#[source] GfxError),
}
pub struct MetalBackend {

View file

@ -376,7 +376,9 @@ impl MetalConnector {
async fn present_loop(self: Rc<Self>) {
loop {
self.present_trigger.triggered().await;
let _ = self.present(true);
if let Err(e) = self.present(true) {
log::error!("Could not present: {}", ErrorFmt(e));
}
}
}
@ -585,7 +587,7 @@ impl MetalConnector {
plane: &Rc<MetalPlane>,
output: &OutputNode,
try_direct_scanout: bool,
) -> PresentFb {
) -> Result<PresentFb, MetalError> {
self.trim_scanout_cache();
let buffer_fb = buffer.render_fb();
let render_hw_cursor = !self.cursor_enabled.get();
@ -630,23 +632,23 @@ impl MetalConnector {
}
let fb = match &direct_scanout_data {
None => {
buffer_fb
.perform_render_pass(pass)
.map_err(MetalError::RenderFrame)?;
buffer.copy_to_dev()?;
self.next_buffer.fetch_add(1);
buffer_fb.perform_render_pass(pass);
if let Some(tex) = &buffer.dev_tex {
buffer.dev_fb.copy_texture(tex, 0, 0);
}
output.perform_screencopies(&buffer.render_tex, !render_hw_cursor, 0, 0, None);
buffer.drm.clone()
}
Some(dsd) => dsd.fb.clone(),
};
PresentFb {
Ok(PresentFb {
fb,
direct_scanout_data,
}
})
}
pub fn present(&self, try_direct_scanout: bool) -> Result<(), ()> {
pub fn present(&self, try_direct_scanout: bool) -> Result<(), MetalError> {
let crtc = match self.crtc.get() {
Some(crtc) => crtc,
_ => return Ok(()),
@ -676,7 +678,7 @@ impl MetalConnector {
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
let mut rr = self.render_result.borrow_mut();
let fb =
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout);
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout)?;
rr.dispatch_frame_requests();
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
match &fb.direct_scanout_data {
@ -721,9 +723,7 @@ impl MetalConnector {
let buffers = self.cursor_buffers.get().unwrap();
let buffer = &buffers[front_buffer % buffers.len()];
if cursor_swap_buffer {
if let Some(tex) = &buffer.dev_tex {
buffer.dev_fb.copy_texture(tex, 0, 0);
}
buffer.copy_to_dev()?;
}
let (width, height) = buffer.dev_fb.physical_size();
changes.change_object(plane.id, |c| {
@ -746,32 +746,28 @@ impl MetalConnector {
}
}
if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) {
match e {
DrmError::Atomic(OsError(c::EACCES)) => {
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
}
_ => 'handle_failure: {
if let Some(fb) = &new_fb {
if let Some(dsd) = &fb.direct_scanout_data {
if self.present(false).is_ok() {
let mut cache = self.scanout_buffers.borrow_mut();
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
cache.insert(
dsd.dma_buf_id,
DirectScanoutCache {
tex: buffer.tex,
fb: None,
},
);
}
break 'handle_failure;
}
if let DrmError::Atomic(OsError(c::EACCES)) = e {
log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master");
return Ok(());
}
if let Some(fb) = &new_fb {
if let Some(dsd) = &fb.direct_scanout_data {
if self.present(false).is_ok() {
let mut cache = self.scanout_buffers.borrow_mut();
if let Some(buffer) = cache.remove(&dsd.dma_buf_id) {
cache.insert(
dsd.dma_buf_id,
DirectScanoutCache {
tex: buffer.tex,
fb: None,
},
);
}
return Ok(());
}
log::error!("Could not set plane framebuffer: {}", ErrorFmt(e));
}
}
Err(())
Err(MetalError::Commit(e))
} else {
if let Some(fb) = new_fb {
self.next_framebuffer.set(Some(fb));
@ -2160,7 +2156,7 @@ impl MetalBackend {
Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportFb(e)),
};
dev_fb.clear();
dev_fb.clear().map_err(MetalError::Clear)?;
let (dev_tex, render_tex, render_fb) = if dev.id == render_ctx.dev_id {
let render_tex = match dev_img.to_texture() {
Ok(fb) => fb,
@ -2209,7 +2205,7 @@ impl MetalBackend {
Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportFb(e)),
};
render_fb.clear();
render_fb.clear().map_err(MetalError::Clear)?;
let render_tex = match render_img.to_texture() {
Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportTexture(e)),
@ -2429,6 +2425,15 @@ impl RenderBuffer {
.clone()
.unwrap_or_else(|| self.dev_fb.clone())
}
fn copy_to_dev(&self) -> Result<(), MetalError> {
let Some(tex) = &self.dev_tex else {
return Ok(());
};
self.dev_fb
.copy_texture(tex, 0, 0)
.map_err(MetalError::CopyToOutput)
}
}
fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool {

View file

@ -738,13 +738,17 @@ impl XBackend {
image.last_serial.set(serial);
if let Some(node) = self.state.root.outputs.get(&output.id) {
self.state.present_output(
let res = self.state.present_output(
&node,
&image.fb.get(),
&image.tex.get(),
&mut self.render_result.borrow_mut(),
true,
);
if let Err(e) = res {
log::error!("Could not render screen: {}", ErrorFmt(e));
return;
}
}
let pp = PresentPixmap {

View file

@ -189,7 +189,7 @@ pub trait GfxFramebuffer: Debug {
fn physical_size(&self) -> (i32, i32);
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>);
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) -> Result<(), GfxError>;
fn copy_to_shm(
self: Rc<Self>,
@ -206,13 +206,13 @@ pub trait GfxFramebuffer: Debug {
}
impl dyn GfxFramebuffer {
pub fn clear(&self) {
self.clear_with(0.0, 0.0, 0.0, 0.0);
pub fn clear(&self) -> Result<(), GfxError> {
self.clear_with(0.0, 0.0, 0.0, 0.0)
}
pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) {
pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) -> Result<(), GfxError> {
let ops = self.take_render_ops();
self.render(ops, Some(&Color { r, g, b, a }));
self.render(ops, Some(&Color { r, g, b, a }))
}
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
@ -237,13 +237,18 @@ impl dyn GfxFramebuffer {
}
}
pub fn copy_texture(&self, texture: &Rc<dyn GfxTexture>, x: i32, y: i32) {
pub fn copy_texture(
&self,
texture: &Rc<dyn GfxTexture>,
x: i32,
y: i32,
) -> Result<(), GfxError> {
let mut ops = self.take_render_ops();
let scale = Scale::from_int(1);
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
renderer.render_texture(texture, x, y, None, None, scale, None, None);
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
self.render(ops, clear);
self.render(ops, clear)
}
pub fn render_custom(
@ -251,11 +256,11 @@ impl dyn GfxFramebuffer {
scale: Scale,
clear: Option<&Color>,
f: &mut dyn FnMut(&mut RendererBase),
) {
) -> Result<(), GfxError> {
let mut ops = self.take_render_ops();
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
f(&mut renderer);
self.render(ops, clear);
self.render(ops, clear)
}
pub fn create_render_pass(
@ -326,7 +331,7 @@ impl dyn GfxFramebuffer {
}
}
pub fn perform_render_pass(&self, pass: GfxRenderPass) {
pub fn perform_render_pass(&self, pass: GfxRenderPass) -> Result<(), GfxError> {
self.render(pass.ops, pass.clear.as_ref())
}
@ -338,7 +343,7 @@ impl dyn GfxFramebuffer {
result: Option<&mut RenderResult>,
scale: Scale,
render_hardware_cursor: bool,
) {
) -> Result<(), GfxError> {
self.render_node(
node,
state,
@ -361,7 +366,7 @@ impl dyn GfxFramebuffer {
render_hardware_cursor: bool,
black_background: bool,
transform: Transform,
) {
) -> Result<(), GfxError> {
let pass = self.create_render_pass(
node,
state,
@ -372,7 +377,7 @@ impl dyn GfxFramebuffer {
black_background,
transform,
);
self.perform_render_pass(pass);
self.perform_render_pass(pass)
}
pub fn render_hardware_cursor(
@ -381,7 +386,7 @@ impl dyn GfxFramebuffer {
state: &State,
scale: Scale,
transform: Transform,
) {
) -> Result<(), GfxError> {
let mut ops = self.take_render_ops();
let mut renderer = Renderer {
base: self.renderer_base(&mut ops, scale, transform),
@ -394,7 +399,7 @@ impl dyn GfxFramebuffer {
},
};
cursor.render_hardware_cursor(&mut renderer);
self.render(ops, Some(&Color::TRANSPARENT));
self.render(ops, Some(&Color::TRANSPARENT))
}
}

View file

@ -10,6 +10,7 @@ use {
renderer::context::GlRenderContext,
run_ops,
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
RenderError,
},
theme::Color,
},
@ -64,9 +65,9 @@ impl Framebuffer {
});
}
pub fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
pub fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) -> Result<(), RenderError> {
let gles = self.ctx.ctx.dpy.gles;
let _ = self.ctx.ctx.with_current(|| {
let res = self.ctx.ctx.with_current(|| {
unsafe {
(gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo);
(gles.glViewport)(0, 0, self.gl.width, self.gl.height);
@ -83,6 +84,7 @@ impl Framebuffer {
Ok(())
});
*self.ctx.gfx_ops.borrow_mut() = ops;
res
}
}
@ -101,8 +103,8 @@ impl GfxFramebuffer for Framebuffer {
(self.gl.width, self.gl.height)
}
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
self.render(ops, clear);
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) -> Result<(), GfxError> {
self.render(ops, clear).map_err(|e| e.into())
}
fn copy_to_shm(

View file

@ -173,6 +173,8 @@ pub enum VulkanError {
height: i32,
stride: i32,
},
#[error(transparent)]
GfxError(GfxError),
}
impl From<VulkanError> for GfxError {

View file

@ -525,8 +525,10 @@ impl GfxFramebuffer for VulkanImage {
(self.width as _, self.height as _)
}
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
self.renderer.execute(self, &ops, clear).unwrap();
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) -> Result<(), GfxError> {
self.renderer
.execute(self, &ops, clear)
.map_err(|e| e.into())
}
fn copy_to_shm(

View file

@ -698,7 +698,9 @@ impl VulkanRenderer {
&[],
true,
)?;
(&*tmp_tex as &dyn GfxFramebuffer).copy_texture(&(tex.clone() as _), x, y);
(&*tmp_tex as &dyn GfxFramebuffer)
.copy_texture(&(tex.clone() as _), x, y)
.map_err(VulkanError::GfxError)?;
self.read_all_pixels(&tmp_tex, stride, dst)
}

View file

@ -173,7 +173,7 @@ impl JayScreencast {
let mut buffer = self.buffers.borrow_mut();
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
if buffer.free {
self.client.state.perform_screencopy(
let res = self.client.state.perform_screencopy(
texture,
&buffer.fb,
on.global.pos.get(),
@ -183,12 +183,20 @@ impl JayScreencast {
size,
on.global.persistent.transform.get(),
);
self.client.event(Ready {
self_id: self.id,
idx: idx as _,
});
buffer.free = false;
return;
match res {
Ok(_) => {
self.client.event(Ready {
self_id: self.id,
idx: idx as _,
});
buffer.free = false;
return;
}
Err(e) => {
log::error!("Could not perform screencopy: {}", ErrorFmt(e));
break;
}
}
}
}
self.missed_frame.set(true);

View file

@ -18,6 +18,7 @@ use {
buffd::{MsgParser, MsgParserError},
clonecell::CloneCell,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
linkedlist::LinkedList,
transform_ext::TransformExt,
},
@ -243,7 +244,7 @@ impl WlOutputGlobal {
if let Some(WlBufferStorage::Shm { mem, stride }) =
wl_buffer.storage.borrow_mut().deref()
{
self.state.perform_shm_screencopy(
let res = self.state.perform_shm_screencopy(
tex,
self.pos.get(),
x_off,
@ -255,6 +256,11 @@ impl WlOutputGlobal {
wl_buffer.format,
Transform::None,
);
if let Err(e) = res {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
} else {
let fb = match wl_buffer.famebuffer.get() {
Some(fb) => fb,
@ -264,7 +270,7 @@ impl WlOutputGlobal {
continue;
}
};
self.state.perform_screencopy(
let res = self.state.perform_screencopy(
tex,
&fb,
self.pos.get(),
@ -274,6 +280,11 @@ impl WlOutputGlobal {
size,
Transform::None,
);
if let Err(e) = res {
log::warn!("Could not perform screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
}
if capture.with_damage.get() {
capture.send_damage();

View file

@ -299,13 +299,20 @@ impl WlSeatGlobal {
if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) {
if render {
let buffer = hc.get_buffer();
buffer.render_hardware_cursor(
let res = buffer.render_hardware_cursor(
cursor.deref(),
&self.state,
scale,
transform,
);
hc.swap_buffer();
match res {
Ok(_) => {
hc.swap_buffer();
}
Err(e) => {
log::error!("Could not render hardware cursor: {}", ErrorFmt(e));
}
}
}
hc.set_enabled(true);
let mode = output.global.mode.get();

View file

@ -628,6 +628,19 @@ impl WindowData {
}
return;
};
let res = buf
.fb
.render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| {
if let Some(content) = self.content.get() {
content.render_at(r, 0.0, 0.0)
}
});
if let Err(e) = res {
log::error!("Could not render frame: {}", ErrorFmt(e));
return;
}
self.frame_missed.set(false);
self.surface.frame({
@ -643,13 +656,6 @@ impl WindowData {
self.have_frame.set(false);
buf.free.set(false);
buf.fb
.render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| {
if let Some(content) = self.content.get() {
content.render_at(r, 0.0, 0.0)
}
});
self.surface.attach(&buf.wl);
self.surface.commit();
}

View file

@ -78,7 +78,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
true,
false,
Transform::None,
);
)?;
let drm = gbm.drm.dup_render()?.fd().clone();
Ok(Screenshot { drm, bo })
}

View file

@ -81,6 +81,7 @@ use {
sync::Arc,
time::Duration,
},
thiserror::Error,
};
pub struct State {
@ -769,7 +770,7 @@ impl State {
tex: &Rc<dyn GfxTexture>,
rr: &mut RenderResult,
render_hw_cursor: bool,
) {
) -> Result<(), GfxError> {
fb.render_output(
output,
self,
@ -777,9 +778,10 @@ impl State {
Some(rr),
output.global.persistent.scale.get(),
render_hw_cursor,
);
)?;
output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None);
rr.dispatch_frame_requests();
Ok(())
}
pub fn perform_screencopy(
@ -792,7 +794,7 @@ impl State {
y_off: i32,
size: Option<(i32, i32)>,
transform: Transform,
) {
) -> Result<(), GfxError> {
let mut ops = target.take_render_ops();
let mut renderer = Renderer {
base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None),
@ -828,7 +830,7 @@ impl State {
}
}
}
target.render(ops, Some(&Color::SOLID_BLACK));
target.render(ops, Some(&Color::SOLID_BLACK))
}
fn have_hardware_cursor(&self) -> bool {
@ -854,7 +856,7 @@ impl State {
stride: i32,
format: &'static Format,
transform: Transform,
) {
) -> Result<(), ShmScreencopyError> {
let (src_width, src_height) = src.size();
let mut needs_copy = capture.rect.x1() < x_off
|| capture.rect.x2() > x_off + src_width
@ -869,20 +871,11 @@ impl State {
}
let acc = if needs_copy {
let Some(ctx) = self.render_ctx.get() else {
log::warn!("Cannot perform shm screencopy because there is no render context");
return;
return Err(ShmScreencopyError::NoRenderContext);
};
let fb =
match ctx.create_fb(capture.rect.width(), capture.rect.height(), stride, format) {
Ok(f) => f,
Err(e) => {
log::warn!(
"Could not create temporary fb for screencopy: {}",
ErrorFmt(e)
);
return;
}
};
let fb = ctx
.create_fb(capture.rect.width(), capture.rect.height(), stride, format)
.map_err(ShmScreencopyError::CreateTemporaryFb)?;
self.perform_screencopy(
src,
&fb,
@ -892,7 +885,8 @@ impl State {
y_off - capture.rect.y1(),
size,
transform,
);
)
.map_err(ShmScreencopyError::CopyToTemporary)?;
mem.access(|mem| {
fb.copy_to_shm(
0,
@ -917,16 +911,12 @@ impl State {
)
})
};
let res = match acc {
Ok(res) => res,
match acc {
Ok(res) => res.map_err(ShmScreencopyError::ReadPixels),
Err(e) => {
capture.client.error(e);
return;
Ok(())
}
};
if let Err(e) = res {
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
capture.send_failed();
}
}
@ -937,3 +927,15 @@ impl State {
seat
}
}
#[derive(Debug, Error)]
pub enum ShmScreencopyError {
#[error("There is no render context")]
NoRenderContext,
#[error("Could not create a bridge framebuffer")]
CreateTemporaryFb(#[source] GfxError),
#[error("Could not copy texture to bridge framebuffer")]
CopyToTemporary(#[source] GfxError),
#[error("Could not read pixels from texture")]
ReadPixels(#[source] GfxError),
}