webrender/
capture.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::fs::File;
6use std::path::{Path, PathBuf};
7
8use api::{ExternalImageData, ImageDescriptor};
9#[cfg(feature = "png")]
10use api::ImageFormat;
11use api::units::TexelRect;
12#[cfg(feature = "png")]
13use api::units::DeviceIntSize;
14#[cfg(feature = "capture")]
15use crate::print_tree::{PrintableTree, PrintTree};
16use crate::render_api::CaptureBits;
17
18
19#[derive(Clone)]
20pub struct CaptureConfig {
21    pub root: PathBuf,
22    pub bits: CaptureBits,
23    /// Scene sequence ID when capturing multiple frames. Zero for a single frame capture.
24    pub scene_id: u32,
25    /// Frame sequence ID when capturing multiple frames. Zero for a single frame capture.
26    pub frame_id: u32,
27    /// Resource sequence ID when capturing multiple frames. Zero for a single frame capture.
28    pub resource_id: u32,
29    #[cfg(feature = "capture")]
30    pretty: ron::ser::PrettyConfig,
31}
32
33impl CaptureConfig {
34    #[cfg(any(feature = "capture", feature = "replay"))]
35    pub fn new(root: PathBuf, bits: CaptureBits) -> Self {
36        CaptureConfig {
37            root,
38            bits,
39            scene_id: 0,
40            frame_id: 0,
41            resource_id: 0,
42            #[cfg(feature = "capture")]
43            pretty: ron::ser::PrettyConfig::new()
44                .enumerate_arrays(true)
45                .indentor(" ".to_string()),
46        }
47    }
48
49    #[cfg(feature = "capture")]
50    pub fn prepare_scene(&mut self) {
51        use std::fs::create_dir_all;
52        self.scene_id += 1;
53        let _ = create_dir_all(&self.scene_root());
54    }
55
56    #[cfg(feature = "capture")]
57    pub fn prepare_frame(&mut self) {
58        use std::fs::create_dir_all;
59        self.frame_id += 1;
60        let _ = create_dir_all(&self.frame_root());
61    }
62
63    #[cfg(feature = "capture")]
64    pub fn prepare_resource(&mut self) {
65        use std::fs::create_dir_all;
66        self.resource_id += 1;
67        let _ = create_dir_all(&self.resource_root());
68    }
69
70    #[cfg(any(feature = "capture", feature = "replay"))]
71    pub fn scene_root(&self) -> PathBuf {
72        if self.scene_id > 0 {
73            let path = format!("scenes/{:05}", self.scene_id);
74            self.root.join(path)
75        } else {
76            self.root.clone()
77        }
78    }
79
80    #[cfg(any(feature = "capture", feature = "replay"))]
81    pub fn frame_root(&self) -> PathBuf {
82        if self.frame_id > 0 {
83            let path = format!("frames/{:05}", self.frame_id);
84            self.scene_root().join(path)
85        } else {
86            self.root.clone()
87        }
88    }
89
90    #[cfg(any(feature = "capture", feature = "replay"))]
91    pub fn resource_root(&self) -> PathBuf {
92        if self.resource_id > 0 {
93            let path = format!("resources/{:05}", self.resource_id);
94            self.root.join(path)
95        } else {
96            self.root.clone()
97        }
98    }
99
100    #[cfg(feature = "capture")]
101    pub fn serialize_for_scene<T, P>(&self, data: &T, name: P)
102    where
103        T: serde::Serialize,
104        P: AsRef<Path>,
105    {
106        self.serialize(data, self.scene_root(), name)
107    }
108
109    #[cfg(feature = "capture")]
110    pub fn serialize_for_frame<T, P>(&self, data: &T, name: P)
111    where
112        T: serde::Serialize,
113        P: AsRef<Path>,
114    {
115        self.serialize(data, self.frame_root(), name)
116    }
117
118    #[cfg(feature = "capture")]
119    pub fn serialize_for_resource<T, P>(&self, data: &T, name: P)
120    where
121        T: serde::Serialize,
122        P: AsRef<Path>,
123    {
124        self.serialize(data, self.resource_root(), name)
125    }
126
127    #[cfg(feature = "capture")]
128    pub fn file_path_for_frame<P>(&self, name: P, ext: &str) -> PathBuf
129    where P: AsRef<Path> {
130        self.frame_root().join(name).with_extension(ext)
131    }
132
133    #[cfg(feature = "capture")]
134    fn serialize<T, P>(&self, data: &T, path: PathBuf, name: P)
135    where
136        T: serde::Serialize,
137        P: AsRef<Path>,
138    {
139        use std::io::Write;
140        let ron = ron::ser::to_string_pretty(data, self.pretty.clone())
141            .unwrap();
142        let mut file = File::create(path.join(name).with_extension("ron"))
143            .unwrap();
144        write!(file, "{}\n", ron)
145            .unwrap();
146    }
147
148    #[cfg(feature = "capture")]
149    fn serialize_tree<T, P>(data: &T, root: PathBuf, name: P)
150    where
151        T: PrintableTree,
152        P: AsRef<Path>
153    {
154        let path = root
155            .join(name)
156            .with_extension("tree");
157        let file = File::create(path)
158            .unwrap();
159        let mut pt = PrintTree::new_with_sink("", file);
160        data.print_with(&mut pt);
161    }
162
163    #[cfg(feature = "capture")]
164    pub fn serialize_tree_for_frame<T, P>(&self, data: &T, name: P)
165    where
166        T: PrintableTree,
167        P: AsRef<Path>
168    {
169        Self::serialize_tree(data, self.frame_root(), name)
170    }
171
172    #[cfg(feature = "replay")]
173    fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T>
174    where
175        T: for<'a> serde::Deserialize<'a>,
176        P: AsRef<Path>,
177    {
178        use std::io::Read;
179
180        let mut string = String::new();
181        let path = root
182            .join(name.as_ref())
183            .with_extension("ron");
184        File::open(path)
185            .ok()?
186            .read_to_string(&mut string)
187            .unwrap();
188        match ron::de::from_str(&string) {
189            Ok(out) => Some(out),
190            Err(e) => panic!("File {:?} deserialization failed: {:?}", name.as_ref(), e),
191        }
192    }
193
194    #[cfg(feature = "replay")]
195    pub fn deserialize_for_scene<T, P>(&self, name: P) -> Option<T>
196    where
197        T: for<'a> serde::Deserialize<'a>,
198        P: AsRef<Path>,
199    {
200        Self::deserialize(&self.scene_root(), name)
201    }
202
203    #[cfg(feature = "replay")]
204    pub fn deserialize_for_frame<T, P>(&self, name: P) -> Option<T>
205    where
206        T: for<'a> serde::Deserialize<'a>,
207        P: AsRef<Path>,
208    {
209        Self::deserialize(&self.frame_root(), name)
210    }
211
212    #[cfg(feature = "replay")]
213    pub fn deserialize_for_resource<T, P>(&self, name: P) -> Option<T>
214    where
215        T: for<'a> serde::Deserialize<'a>,
216        P: AsRef<Path>,
217    {
218        Self::deserialize(&self.resource_root(), name)
219    }
220
221    #[cfg(feature = "png")]
222    pub fn save_png(
223        path: PathBuf, size: DeviceIntSize, format: ImageFormat, stride: Option<i32>, data: &[u8],
224    ) {
225        use png::{BitDepth, ColorType, Encoder};
226        use std::io::BufWriter;
227        use std::borrow::Cow;
228
229        // `png` expects
230        let data = match stride {
231            Some(stride) if stride != format.bytes_per_pixel() * size.width => {
232                let mut unstrided = Vec::new();
233                for y in 0..size.height {
234                    let start = (y * stride) as usize;
235                    unstrided.extend_from_slice(&data[start..start+(size.width * format.bytes_per_pixel()) as usize]);
236                }
237                Cow::from(unstrided)
238            }
239            _ => Cow::from(data),
240        };
241
242        let color_type = match format {
243            ImageFormat::RGBA8 => ColorType::RGBA,
244            ImageFormat::BGRA8 => {
245                warn!("Unable to swizzle PNG of BGRA8 type");
246                ColorType::RGBA
247            },
248            ImageFormat::R8 => ColorType::Grayscale,
249            ImageFormat::RG8 => ColorType::GrayscaleAlpha,
250            _ => {
251                error!("Unable to save PNG of {:?}", format);
252                return;
253            }
254        };
255        let w = BufWriter::new(File::create(path).unwrap());
256        let mut enc = Encoder::new(w, size.width as u32, size.height as u32);
257        enc.set_color(color_type);
258        enc.set_depth(BitDepth::Eight);
259        enc
260            .write_header()
261            .unwrap()
262            .write_image_data(&*data)
263            .unwrap();
264    }
265}
266
267/// An image that `ResourceCache` is unable to resolve during a capture.
268/// The image has to be transferred to `Renderer` and locked with the
269/// external image handler to get the actual contents and serialize them.
270#[derive(Deserialize, Serialize)]
271pub struct ExternalCaptureImage {
272    pub short_path: String,
273    pub descriptor: ImageDescriptor,
274    pub external: ExternalImageData,
275}
276
277/// A short description of an external image to be saved separately as
278/// "externals/XX.ron", redirecting into a specific texture/blob with
279/// the corresponding UV rectangle.
280#[derive(Deserialize, Serialize)]
281pub struct PlainExternalImage {
282    /// Path to the RON file describing the texel data.
283    pub data: String,
284    /// External image data source.
285    pub external: ExternalImageData,
286    /// UV sub-rectangle of the image.
287    pub uv: TexelRect,
288}