capture.rs (8789B)
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 5 use std::fs::File; 6 use std::path::{Path, PathBuf}; 7 8 use api::{ExternalImageData, ImageDescriptor}; 9 #[cfg(feature = "png")] 10 use api::ImageFormat; 11 use api::units::TexelRect; 12 #[cfg(feature = "png")] 13 use api::units::DeviceIntSize; 14 #[cfg(feature = "capture")] 15 use crate::print_tree::{PrintableTree, PrintTree}; 16 use crate::render_api::CaptureBits; 17 18 19 #[derive(Clone)] 20 pub 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 33 impl 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)] 271 pub 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)] 281 pub 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 }