Skip to main content

repose_render_wgpu/
commands.rs

1//! Shared application of render commands queued by `RenderContext`.
2//!
3//! Platform runners (desktop/android/web) and embedders (bevy, baseview)
4//! can move image set/remove commands from the reactive scene into the GPU
5//! texture cache through this.
6
7use repose_core::RenderCommand;
8
9use crate::WgpuSceneRenderer;
10
11/// A command that could not be applied. The command is consumed either way
12/// and is never retried, so callers that care about a specific handle get
13/// the handle back here.
14#[derive(Clone, Debug)]
15pub struct RenderCommandFailure {
16    pub kind: &'static str,
17    pub handle: u64,
18    pub error: String,
19}
20
21#[derive(Clone, Debug, Default)]
22pub struct RenderCommandReport {
23    pub applied: usize,
24    pub failures: Vec<RenderCommandFailure>,
25}
26
27impl RenderCommandReport {
28    /// Log every failure. Callers sit inside callbacks with no error
29    /// channel, so this is the usual way the report reaches a human.
30    pub fn log_failures(&self) {
31        for failure in &self.failures {
32            log::warn!(
33                "repose-render: {}({}) failed: {}",
34                failure.kind,
35                failure.handle,
36                failure.error
37            );
38        }
39    }
40}
41
42/// Apply a batch of [`RenderCommand`]s to a scene renderer.
43///
44/// `WgpuSurfaceBackend` derefs to `WgpuSceneRenderer`, so this works for
45/// both the windowed backends and offscreen-device embedders. Failures are
46/// collected rather than logged here: every caller sits inside a callback
47/// with no error channel (`winit` event loop, `requestAnimationFrame`),
48/// so reporting is the caller's job.
49pub fn apply_render_commands(
50    renderer: &mut WgpuSceneRenderer,
51    cmds: Vec<RenderCommand>,
52) -> RenderCommandReport {
53    let mut report = RenderCommandReport::default();
54    for cmd in cmds {
55        let (kind, handle, result) = match cmd {
56            RenderCommand::SetImageEncoded {
57                handle,
58                bytes,
59                srgb,
60            } => (
61                "SetImageEncoded",
62                handle,
63                renderer.set_image_from_bytes(handle, &bytes, srgb),
64            ),
65            RenderCommand::SetImageRgba8 {
66                handle,
67                w,
68                h,
69                rgba,
70                srgb,
71            } => (
72                "SetImageRgba8",
73                handle,
74                renderer.set_image_rgba8(handle, w, h, &rgba, srgb),
75            ),
76            RenderCommand::SetImageNv12 {
77                handle,
78                w,
79                h,
80                y,
81                uv,
82                color_info,
83            } => (
84                "SetImageNv12",
85                handle,
86                renderer.set_image_nv12(handle, w, h, &y, &uv, color_info),
87            ),
88            RenderCommand::SetImagePlanes {
89                handle,
90                w,
91                h,
92                pixel_format,
93                planes,
94                color_info,
95            } => {
96                let refs: Vec<&[u8]> = planes.iter().map(|p| p.as_ref()).collect();
97                (
98                    "SetImagePlanes",
99                    handle,
100                    renderer.set_image_planes(handle, w, h, pixel_format, &refs, color_info),
101                )
102            }
103            #[cfg(target_os = "linux")]
104            RenderCommand::SetImageDmaBuf {
105                handle,
106                w,
107                h,
108                fds,
109                fourcc,
110                modifier,
111                strides,
112                offsets,
113                color_info,
114            } => (
115                "SetImageDmaBuf",
116                handle,
117                renderer.set_image_dmabuf_fourcc(
118                    handle, w, h, fds, fourcc, modifier, strides, offsets, color_info,
119                ),
120            ),
121            RenderCommand::RemoveImage { handle } => {
122                renderer.remove_image(handle);
123                ("RemoveImage", handle, Ok(()))
124            }
125        };
126        match result {
127            Ok(()) => report.applied += 1,
128            Err(error) => report.failures.push(RenderCommandFailure {
129                kind,
130                handle,
131                error: format!("{error:#}"),
132            }),
133        }
134    }
135    report
136}