288 lines
8.6 KiB
Rust
288 lines
8.6 KiB
Rust
/* 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;
|
|
|
|
|
|
#[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,
|
|
}
|