diff options
Diffstat (limited to 'gfx/wr/webrender/src/capture.rs')
-rw-r--r-- | gfx/wr/webrender/src/capture.rs | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/gfx/wr/webrender/src/capture.rs b/gfx/wr/webrender/src/capture.rs new file mode 100644 index 0000000000..5cc1f90bab --- /dev/null +++ b/gfx/wr/webrender/src/capture.rs @@ -0,0 +1,290 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::fs::File; +use std::path::{Path, PathBuf}; + +use api::{ExternalImageData, ImageDescriptor}; +#[cfg(feature = "png")] +use api::ImageFormat; +use api::units::TexelRect; +#[cfg(feature = "png")] +use api::units::DeviceIntSize; +#[cfg(feature = "capture")] +use crate::print_tree::{PrintableTree, PrintTree}; +use crate::render_api::CaptureBits; +use ron; +use serde; + + +#[derive(Clone)] +pub struct CaptureConfig { + pub root: PathBuf, + pub bits: CaptureBits, + /// Scene sequence ID when capturing multiple frames. Zero for a single frame capture. + pub scene_id: u32, + /// Frame sequence ID when capturing multiple frames. Zero for a single frame capture. + pub frame_id: u32, + /// Resource sequence ID when capturing multiple frames. Zero for a single frame capture. + pub resource_id: u32, + #[cfg(feature = "capture")] + pretty: ron::ser::PrettyConfig, +} + +impl CaptureConfig { + #[cfg(any(feature = "capture", feature = "replay"))] + pub fn new(root: PathBuf, bits: CaptureBits) -> Self { + CaptureConfig { + root, + bits, + scene_id: 0, + frame_id: 0, + resource_id: 0, + #[cfg(feature = "capture")] + pretty: ron::ser::PrettyConfig::new() + .enumerate_arrays(true) + .indentor(" ".to_string()), + } + } + + #[cfg(feature = "capture")] + pub fn prepare_scene(&mut self) { + use std::fs::create_dir_all; + self.scene_id += 1; + let _ = create_dir_all(&self.scene_root()); + } + + #[cfg(feature = "capture")] + pub fn prepare_frame(&mut self) { + use std::fs::create_dir_all; + self.frame_id += 1; + let _ = create_dir_all(&self.frame_root()); + } + + #[cfg(feature = "capture")] + pub fn prepare_resource(&mut self) { + use std::fs::create_dir_all; + self.resource_id += 1; + let _ = create_dir_all(&self.resource_root()); + } + + #[cfg(any(feature = "capture", feature = "replay"))] + pub fn scene_root(&self) -> PathBuf { + if self.scene_id > 0 { + let path = format!("scenes/{:05}", self.scene_id); + self.root.join(path) + } else { + self.root.clone() + } + } + + #[cfg(any(feature = "capture", feature = "replay"))] + pub fn frame_root(&self) -> PathBuf { + if self.frame_id > 0 { + let path = format!("frames/{:05}", self.frame_id); + self.scene_root().join(path) + } else { + self.root.clone() + } + } + + #[cfg(any(feature = "capture", feature = "replay"))] + pub fn resource_root(&self) -> PathBuf { + if self.resource_id > 0 { + let path = format!("resources/{:05}", self.resource_id); + self.root.join(path) + } else { + self.root.clone() + } + } + + #[cfg(feature = "capture")] + pub fn serialize_for_scene<T, P>(&self, data: &T, name: P) + where + T: serde::Serialize, + P: AsRef<Path>, + { + self.serialize(data, self.scene_root(), name) + } + + #[cfg(feature = "capture")] + pub fn serialize_for_frame<T, P>(&self, data: &T, name: P) + where + T: serde::Serialize, + P: AsRef<Path>, + { + self.serialize(data, self.frame_root(), name) + } + + #[cfg(feature = "capture")] + pub fn serialize_for_resource<T, P>(&self, data: &T, name: P) + where + T: serde::Serialize, + P: AsRef<Path>, + { + self.serialize(data, self.resource_root(), name) + } + + #[cfg(feature = "capture")] + pub fn file_path_for_frame<P>(&self, name: P, ext: &str) -> PathBuf + where P: AsRef<Path> { + self.frame_root().join(name).with_extension(ext) + } + + #[cfg(feature = "capture")] + fn serialize<T, P>(&self, data: &T, path: PathBuf, name: P) + where + T: serde::Serialize, + P: AsRef<Path>, + { + use std::io::Write; + let ron = ron::ser::to_string_pretty(data, self.pretty.clone()) + .unwrap(); + let mut file = File::create(path.join(name).with_extension("ron")) + .unwrap(); + write!(file, "{}\n", ron) + .unwrap(); + } + + #[cfg(feature = "capture")] + fn serialize_tree<T, P>(data: &T, root: PathBuf, name: P) + where + T: PrintableTree, + P: AsRef<Path> + { + let path = root + .join(name) + .with_extension("tree"); + let file = File::create(path) + .unwrap(); + let mut pt = PrintTree::new_with_sink("", file); + data.print_with(&mut pt); + } + + #[cfg(feature = "capture")] + pub fn serialize_tree_for_frame<T, P>(&self, data: &T, name: P) + where + T: PrintableTree, + P: AsRef<Path> + { + Self::serialize_tree(data, self.frame_root(), name) + } + + #[cfg(feature = "replay")] + fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T> + where + T: for<'a> serde::Deserialize<'a>, + P: AsRef<Path>, + { + use std::io::Read; + + let mut string = String::new(); + let path = root + .join(name.as_ref()) + .with_extension("ron"); + File::open(path) + .ok()? + .read_to_string(&mut string) + .unwrap(); + match ron::de::from_str(&string) { + Ok(out) => Some(out), + Err(e) => panic!("File {:?} deserialization failed: {:?}", name.as_ref(), e), + } + } + + #[cfg(feature = "replay")] + pub fn deserialize_for_scene<T, P>(&self, name: P) -> Option<T> + where + T: for<'a> serde::Deserialize<'a>, + P: AsRef<Path>, + { + Self::deserialize(&self.scene_root(), name) + } + + #[cfg(feature = "replay")] + pub fn deserialize_for_frame<T, P>(&self, name: P) -> Option<T> + where + T: for<'a> serde::Deserialize<'a>, + P: AsRef<Path>, + { + Self::deserialize(&self.frame_root(), name) + } + + #[cfg(feature = "replay")] + pub fn deserialize_for_resource<T, P>(&self, name: P) -> Option<T> + where + T: for<'a> serde::Deserialize<'a>, + P: AsRef<Path>, + { + Self::deserialize(&self.resource_root(), name) + } + + #[cfg(feature = "png")] + pub fn save_png( + path: PathBuf, size: DeviceIntSize, format: ImageFormat, stride: Option<i32>, data: &[u8], + ) { + use png::{BitDepth, ColorType, Encoder}; + use std::io::BufWriter; + use std::borrow::Cow; + + // `png` expects + let data = match stride { + Some(stride) if stride != format.bytes_per_pixel() * size.width => { + let mut unstrided = Vec::new(); + for y in 0..size.height { + let start = (y * stride) as usize; + unstrided.extend_from_slice(&data[start..start+(size.width * format.bytes_per_pixel()) as usize]); + } + Cow::from(unstrided) + } + _ => Cow::from(data), + }; + + let color_type = match format { + ImageFormat::RGBA8 => ColorType::RGBA, + ImageFormat::BGRA8 => { + warn!("Unable to swizzle PNG of BGRA8 type"); + ColorType::RGBA + }, + ImageFormat::R8 => ColorType::Grayscale, + ImageFormat::RG8 => ColorType::GrayscaleAlpha, + _ => { + error!("Unable to save PNG of {:?}", format); + return; + } + }; + let w = BufWriter::new(File::create(path).unwrap()); + let mut enc = Encoder::new(w, size.width as u32, size.height as u32); + enc.set_color(color_type); + enc.set_depth(BitDepth::Eight); + enc + .write_header() + .unwrap() + .write_image_data(&*data) + .unwrap(); + } +} + +/// An image that `ResourceCache` is unable to resolve during a capture. +/// The image has to be transferred to `Renderer` and locked with the +/// external image handler to get the actual contents and serialize them. +#[derive(Deserialize, Serialize)] +pub struct ExternalCaptureImage { + pub short_path: String, + pub descriptor: ImageDescriptor, + pub external: ExternalImageData, +} + +/// A short description of an external image to be saved separately as +/// "externals/XX.ron", redirecting into a specific texture/blob with +/// the corresponding UV rectangle. +#[derive(Deserialize, Serialize)] +pub struct PlainExternalImage { + /// Path to the RON file describing the texel data. + pub data: String, + /// External image data source. + pub external: ExternalImageData, + /// UV sub-rectangle of the image. + pub uv: TexelRect, +} |