1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use api::{ApiMsg, FrameMsg, SceneMsg};
use bincode::serialize;
use byteorder::{LittleEndian, WriteBytesExt};
use std::any::TypeId;
use std::fmt::Debug;
use std::fs::File;
use std::io::Write;
use std::mem;
use std::path::PathBuf;
pub static WEBRENDER_RECORDING_HEADER: u64 = 0xbeefbeefbeefbe01u64;
pub trait ApiRecordingReceiver: Send + Debug {
fn write_msg(&mut self, frame: u32, msg: &ApiMsg);
fn write_payload(&mut self, frame: u32, data: &[u8]);
}
#[derive(Debug)]
pub struct BinaryRecorder {
file: File,
}
impl BinaryRecorder {
pub fn new(dest: &PathBuf) -> BinaryRecorder {
let mut file = File::create(dest).unwrap();
let apimsg_type_id = unsafe {
assert!(mem::size_of::<TypeId>() == mem::size_of::<u64>());
mem::transmute::<TypeId, u64>(TypeId::of::<ApiMsg>())
};
file.write_u64::<LittleEndian>(WEBRENDER_RECORDING_HEADER)
.ok();
file.write_u64::<LittleEndian>(apimsg_type_id).ok();
BinaryRecorder { file }
}
fn write_length_and_data(&mut self, data: &[u8]) {
self.file.write_u32::<LittleEndian>(data.len() as u32).ok();
self.file.write(data).ok();
}
}
impl ApiRecordingReceiver for BinaryRecorder {
fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
if should_record_msg(msg) {
let buf = serialize(msg).unwrap();
self.write_length_and_data(&buf);
}
}
fn write_payload(&mut self, _: u32, data: &[u8]) {
self.file.write_u32::<LittleEndian>(0).ok();
self.write_length_and_data(data);
}
}
pub fn should_record_msg(msg: &ApiMsg) -> bool {
match *msg {
ApiMsg::UpdateResources(..) |
ApiMsg::AddDocument { .. } |
ApiMsg::DeleteDocument(..) => true,
ApiMsg::UpdateDocument(_, ref msgs) => {
if msgs.generate_frame {
return true;
}
for msg in &msgs.scene_ops {
match *msg {
SceneMsg::SetDisplayList { .. } |
SceneMsg::SetRootPipeline { .. } => return true,
_ => {}
}
}
for msg in &msgs.frame_ops {
match *msg {
FrameMsg::GetScrollNodeState(..) |
FrameMsg::HitTest(..) => {}
_ => return true,
}
}
false
}
_ => false,
}
}