зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1605283 - Improve support for invalidation debugging and testing r=gw
Optionally serialize N frames into a circular memory buffer, and save them as part of wr-capture into tilecache/. The new tile_view utility reads the folder and converts the frames to svg for easier visualization, with a few extra features: - color code each primitive based on which slice it is on; - highlight new or moving primitives in red (brute force diff); - print all invalidated tiles at the top and the invalidation reason; - overlay the tile quadtree to visualize splitting & merging; - HTML and JS wrappers for animation playback, timeline scrubbing; Positioning of the tiles on the screen is a bit broken still; especially during scrolling and "special" pages (like about:config). Interning info is not used yet. Differential Revision: https://phabricator.services.mozilla.com/D59975 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
020066db8c
Коммит
242db56cbf
|
@ -590,6 +590,8 @@ static void WebRenderDebugPrefChangeCallback(const char* aPrefName, void*) {
|
|||
GFX_WEBRENDER_DEBUG(".texture-cache.clear-evicted",
|
||||
wr::DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
|
||||
GFX_WEBRENDER_DEBUG(".picture-caching", wr::DebugFlags::PICTURE_CACHING_DBG)
|
||||
GFX_WEBRENDER_DEBUG(".tile-cache-logging",
|
||||
wr::DebugFlags::TILE_CACHE_LOGGING_DBG)
|
||||
GFX_WEBRENDER_DEBUG(".primitives", wr::DebugFlags::PRIMITIVE_DBG)
|
||||
// Bit 18 is for the zoom display, which requires the mouse position and thus
|
||||
// currently only works in wrench.
|
||||
|
|
|
@ -620,7 +620,9 @@ void WebRenderAPI::WaitFlushed() {
|
|||
}
|
||||
|
||||
void WebRenderAPI::Capture() {
|
||||
uint8_t bits = 3; // TODO: get from JavaScript
|
||||
// see CaptureBits
|
||||
// SCENE | FRAME | TILE_CACHE
|
||||
uint8_t bits = 7; // TODO: get from JavaScript
|
||||
const char* path = "wr-capture"; // TODO: get from JavaScript
|
||||
wr_api_capture(mDocHandle, path, bits);
|
||||
}
|
||||
|
|
|
@ -1630,6 +1630,16 @@ dependencies = [
|
|||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tileview"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender 0.61.0",
|
||||
"webrender_api 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.40"
|
||||
|
|
|
@ -6,6 +6,7 @@ members = [
|
|||
"webrender_api",
|
||||
"wrench",
|
||||
"example-compositor/compositor",
|
||||
"tileview",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "tileview"
|
||||
version = "0.1.0"
|
||||
authors = ["Bert Peers <bpeers@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ron = "0.1.7"
|
||||
serde = {version = "1.0.88", features = ["derive"] }
|
||||
webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler","no_static_freetype", "leak_checks"]}
|
||||
webrender_api = {path = "../webrender_api", features=["serialize","deserialize"]}
|
|
@ -0,0 +1,404 @@
|
|||
/* 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 webrender::{TileNode, TileNodeKind, InvalidationReason, TileOffset};
|
||||
use webrender::{TileSerializer, TileCacheInstanceSerializer};
|
||||
use serde::Deserialize;
|
||||
//use ron::de::from_reader;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
use std::ffi::OsString;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Slice {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub tile_cache: TileCacheInstanceSerializer
|
||||
}
|
||||
|
||||
// invalidation reason CSS colors
|
||||
static CSS_FRACTIONAL_OFFSET: &str = "fill:#4040c0;fill-opacity:0.1;";
|
||||
static CSS_BACKGROUND_COLOR: &str = "fill:#10c070;fill-opacity:0.1;";
|
||||
static CSS_SURFACE_OPACITY_CHANNEL: &str = "fill:#c040c0;fill-opacity:0.1;";
|
||||
static CSS_NO_TEXTURE: &str = "fill:#c04040;fill-opacity:0.1;";
|
||||
static CSS_NO_SURFACE: &str = "fill:#40c040;fill-opacity:0.1;";
|
||||
static CSS_PRIM_COUNT: &str = "fill:#40f0f0;fill-opacity:0.1;";
|
||||
static CSS_CONTENT: &str = "fill:#f04040;fill-opacity:0.1;";
|
||||
|
||||
fn tile_node_to_svg(node: &TileNode, x: f32, y: f32) -> String
|
||||
{
|
||||
match &node.kind {
|
||||
TileNodeKind::Leaf { .. } => {
|
||||
format!("<rect x=\"{}\" y=\"{}\" width=\"{}\" height=\"{}\" />\n",
|
||||
(node.rect.origin.x + x) as i32,
|
||||
(node.rect.origin.y + y) as i32,
|
||||
node.rect.size.width, node.rect.size.height)
|
||||
},
|
||||
TileNodeKind::Node { children } => {
|
||||
children.iter().fold(String::new(), |acc, child| acc + &tile_node_to_svg(child, x, y) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tile_to_svg(key: TileOffset,
|
||||
tile: &TileSerializer,
|
||||
slice: &Slice,
|
||||
prev_tile: Option<&TileSerializer>,
|
||||
tile_stroke: &str,
|
||||
prim_class: &str,
|
||||
invalidation_report: &mut String,
|
||||
svg_width: &mut i32, svg_height: &mut i32 ) -> String
|
||||
{
|
||||
let mut svg = format!("\n<!-- tile key {},{} ; slice x {} y {}-->\n", key.x, key.y, slice.x, slice.y);
|
||||
|
||||
|
||||
let tile_fill =
|
||||
match tile.invalidation_reason {
|
||||
Some(InvalidationReason::FractionalOffset) => CSS_FRACTIONAL_OFFSET.to_string(),
|
||||
Some(InvalidationReason::BackgroundColor) => CSS_BACKGROUND_COLOR.to_string(),
|
||||
Some(InvalidationReason::SurfaceOpacityChanged) => CSS_SURFACE_OPACITY_CHANNEL.to_string(),
|
||||
Some(InvalidationReason::NoTexture) => CSS_NO_TEXTURE.to_string(),
|
||||
Some(InvalidationReason::NoSurface) => CSS_NO_SURFACE.to_string(),
|
||||
Some(InvalidationReason::PrimCount) => CSS_PRIM_COUNT.to_string(),
|
||||
Some(InvalidationReason::Content { prim_compare_result } ) => {
|
||||
let _foo = prim_compare_result;
|
||||
CSS_CONTENT.to_string() //TODO do something with the compare result
|
||||
}
|
||||
None => {
|
||||
let mut background = tile.background_color;
|
||||
if background.is_none() {
|
||||
background = slice.tile_cache.background_color
|
||||
}
|
||||
match background {
|
||||
Some(color) => {
|
||||
let rgb = ( (color.r * 255.0) as u8,
|
||||
(color.g * 255.0) as u8,
|
||||
(color.b * 255.0) as u8 );
|
||||
format!("fill:rgb({},{},{});fill-opacity:0.3;", rgb.0, rgb.1, rgb.2)
|
||||
}
|
||||
None => "fill:none;".to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//let tile_style = format!("{}{}", tile_fill, tile_stroke);
|
||||
let tile_style = format!("{}stroke:none;", tile_fill);
|
||||
|
||||
let title = match tile.invalidation_reason {
|
||||
Some(_) => format!("<title>slice {} tile ({},{}) - {:?}</title>",
|
||||
slice.tile_cache.slice, key.x, key.y,
|
||||
tile.invalidation_reason),
|
||||
None => String::new()
|
||||
};
|
||||
|
||||
if let Some(reason) = tile.invalidation_reason {
|
||||
invalidation_report.push_str(
|
||||
&format!("\n<tspan x=\"0\" dy=\"16px\">slice {} tile key ({},{}) invalidated: {:?}</tspan>\n",
|
||||
slice.tile_cache.slice, key.x, key.y, reason));
|
||||
}
|
||||
|
||||
svg = format!(r#"{}<rect x="{}" y="{}" width="{}" height="{}" style="{}" ></rect>"#,
|
||||
svg,
|
||||
//TODO --bpe are these in local space or screen space?
|
||||
tile.rect.origin.x,
|
||||
tile.rect.origin.y,
|
||||
tile.rect.size.width,
|
||||
tile.rect.size.height,
|
||||
tile_style);
|
||||
|
||||
svg = format!("{}\n\n<g class=\"svg_quadtree\">\n{}</g>\n",
|
||||
svg,
|
||||
//tile_node_to_svg(&tile.root, tile.rect.origin.x, tile.rect.origin.y));
|
||||
tile_node_to_svg(&tile.root, 0.0, 0.0));
|
||||
|
||||
let right = (tile.rect.origin.x + tile.rect.size.width) as i32;
|
||||
let bottom = (tile.rect.origin.y + tile.rect.size.height) as i32;
|
||||
|
||||
*svg_width = if right > *svg_width { right } else { *svg_width };
|
||||
*svg_height = if bottom > *svg_height { bottom } else { *svg_height };
|
||||
|
||||
svg += "\n<!-- primitives -->\n";
|
||||
|
||||
svg = format!("{}<g id=\"{}\">\n\t",
|
||||
svg,
|
||||
prim_class);
|
||||
|
||||
for prim in &tile.current_descriptor.prims {
|
||||
let rect = prim.prim_clip_rect;
|
||||
//TODO proper positioning of prims, especially when scrolling
|
||||
// this version seems closest, but introduces gaps (eg in about:config)
|
||||
let x = (rect.x + slice.x) as i32;
|
||||
let y = (rect.y + slice.y) as i32;
|
||||
// this version is .. interesting: when scrolling, nothing moves in about:config,
|
||||
// instead the searchbox shifts down.. hmm..
|
||||
//let x = rect.x as i32;
|
||||
//let y = rect.y as i32;
|
||||
let w = rect.w as i32;
|
||||
let h = rect.h as i32;
|
||||
|
||||
let style =
|
||||
if let Some(prev_tile) = prev_tile {
|
||||
// when this O(n^2) gets too slow, stop brute-forcing and use a set or something
|
||||
if prev_tile.current_descriptor.prims.iter().find(|&prim| prim.prim_clip_rect == rect).is_some() {
|
||||
""
|
||||
} else {
|
||||
"class=\"svg_changed_prim\" "
|
||||
}
|
||||
} else {
|
||||
"class=\"svg_changed_prim\" "
|
||||
};
|
||||
|
||||
svg = format!(r#"{}<rect x="{}" y="{}" width="{}" height="{}" {}/>"#,
|
||||
svg,
|
||||
x, y, w, h,
|
||||
style);
|
||||
|
||||
svg += "\n\t";
|
||||
}
|
||||
|
||||
svg += "\n</g>\n";
|
||||
|
||||
// nearly invisible, all we want is the toolip really
|
||||
let style = "style=\"fill-opacity:0.001;";
|
||||
svg += &format!("<rect x=\"{}\" y=\"{}\" width=\"{}\" height=\"{}\" {}{}\" >{}<\u{2f}rect>",
|
||||
tile.rect.origin.x,
|
||||
tile.rect.origin.y,
|
||||
tile.rect.size.width,
|
||||
tile.rect.size.height,
|
||||
style,
|
||||
tile_stroke,
|
||||
title);
|
||||
|
||||
svg
|
||||
}
|
||||
|
||||
fn slices_to_svg(slices: &[Slice], prev_slices: Option<Vec<Slice>>,
|
||||
svg_width: &mut i32, svg_height: &mut i32,
|
||||
max_slice_index: &mut usize) -> String
|
||||
{
|
||||
let svg_begin = "<?xml\u{2d}stylesheet type\u{3d}\"text/css\" href\u{3d}\"tilecache.css\" ?>\n";
|
||||
|
||||
let mut svg = String::new();
|
||||
let mut invalidation_report = String::new();
|
||||
|
||||
for slice in slices {
|
||||
let tile_cache = &slice.tile_cache;
|
||||
*max_slice_index = if tile_cache.slice > *max_slice_index { tile_cache.slice } else { *max_slice_index };
|
||||
|
||||
let prim_class = format!("tile_slice{}", tile_cache.slice);
|
||||
|
||||
//println!("slice {}", tile_cache.slice);
|
||||
svg.push_str(&format!("\n<!-- tile_cache slice {} -->\n",
|
||||
tile_cache.slice));
|
||||
|
||||
//let tile_stroke = "stroke:grey;stroke-width:1;".to_string();
|
||||
let tile_stroke = "stroke:none;".to_string();
|
||||
|
||||
let mut prev_slice = None;
|
||||
if let Some(prev) = &prev_slices {
|
||||
for prev_search in prev {
|
||||
if prev_search.tile_cache.slice == tile_cache.slice {
|
||||
prev_slice = Some(prev_search);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (key, tile) in &tile_cache.tiles {
|
||||
let mut prev_tile = None;
|
||||
if let Some(prev) = prev_slice {
|
||||
prev_tile = prev.tile_cache.tiles.get(key);
|
||||
}
|
||||
|
||||
//println!("fofs {:?}", tile.fract_offset);
|
||||
//println!("id {:?}", tile.id);
|
||||
//println!("invr {:?}", tile.invalidation_reason);
|
||||
svg.push_str(&tile_to_svg(*key, &tile, &slice, prev_tile, &tile_stroke, &prim_class,
|
||||
&mut invalidation_report, svg_width, svg_height));
|
||||
}
|
||||
}
|
||||
|
||||
svg.push_str(&format!("<text x=\"0\" y=\"-8px\" class=\"svg_invalidated\">{}</text>\n", invalidation_report));
|
||||
|
||||
format!(r#"{}<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" width="{}" height="{}" >"#,
|
||||
svg_begin,
|
||||
svg_width,
|
||||
svg_height)
|
||||
+ "\n"
|
||||
+ "<rect fill=\"black\" width=\"100%\" height=\"100%\"/>\n"
|
||||
+ &svg
|
||||
+ "\n</svg>\n"
|
||||
}
|
||||
|
||||
fn write_html(output_dir: &Path, svg_files: &[String]) {
|
||||
let html_head = "<!DOCTYPE html>\n\
|
||||
<html>\n\
|
||||
<head>\n\
|
||||
<meta charset=\"UTF-8\">\n\
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"tilecache.css\"></link>\n\
|
||||
</head>\n"
|
||||
.to_string();
|
||||
|
||||
let html_body = "<body bgcolor=\"#000000\" onload=\"load()\">\n"
|
||||
.to_string();
|
||||
|
||||
|
||||
let mut script = "\n<script>\n".to_string();
|
||||
|
||||
script = format!("{}var svg_files = [\n", script);
|
||||
for svg_file in svg_files {
|
||||
script = format!("{} \"{}\",\n", script, svg_file);
|
||||
}
|
||||
script = format!("{}];\n</script>\n\n", script);
|
||||
//TODO this requires copying the js file from somewhere?
|
||||
script = format!("{}<script src=\"tilecache.js\" type=\"text/javascript\"></script>\n\n", script);
|
||||
|
||||
|
||||
let html_end = "</body>\n\
|
||||
</html>\n"
|
||||
.to_string();
|
||||
|
||||
let html_body = format!(
|
||||
"{}\n\
|
||||
<object id=\"svg_container0\" type=\"image/svg+xml\" data=\"{}\" class=\"tile_svg\" ></object>\n\
|
||||
<object id=\"svg_container1\" type=\"image/svg+xml\" data=\"{}\" class=\"tile_svg\" ></object>\n\
|
||||
<div id=\"svg_ui_overlay\">\n\
|
||||
<div id=\"text_frame_counter\">{}</div>\n\
|
||||
<div id=\"text_spacebar\">Spacebar to Play</div>\n\
|
||||
<div>Use Left/Right to Step</div>\n\
|
||||
<input id=\"frame_slider\" type=\"range\" min=\"0\" max=\"{}\" value=\"0\" class=\"svg_ui_slider\" />
|
||||
</div>",
|
||||
html_body,
|
||||
svg_files[0],
|
||||
svg_files[0],
|
||||
svg_files[0],
|
||||
svg_files.len() );
|
||||
|
||||
let html = format!("{}{}{}{}", html_head, html_body, script, html_end);
|
||||
|
||||
let output_file = output_dir.join("index.html");
|
||||
let mut html_output = File::create(output_file).unwrap();
|
||||
html_output.write_all(html.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_css(output_dir: &Path, max_slice_index: usize) {
|
||||
let mut css = ".tile_svg {\n\
|
||||
position: fixed;\n\
|
||||
}\n\n\n\
|
||||
.svg_invalidated {\n\
|
||||
fill: white;\n\
|
||||
font-family:monospace;\n\
|
||||
}\n\n\n\
|
||||
#svg_ui_overlay {\n\
|
||||
position:absolute;\n\
|
||||
right:0; \n\
|
||||
top:0; \n\
|
||||
z-index:70; \n\
|
||||
color: rgb(255,255,100);\n\
|
||||
font-family:monospace;\n\
|
||||
background-color: #404040a0;\n\
|
||||
}\n\n\n\
|
||||
.svg_quadtree {\n\
|
||||
fill: none;\n\
|
||||
stroke-width: 1;\n\
|
||||
stroke: orange;\n\
|
||||
}\n\n\n\
|
||||
.svg_changed_prim {\n\
|
||||
stroke: red;\n\
|
||||
stroke-width: 2.0;\n\
|
||||
}\n\n\n\
|
||||
#svg_ui_slider {\n\
|
||||
width:90%;\n\
|
||||
}\n\n".to_string();
|
||||
|
||||
for ix in 0..max_slice_index + 1 {
|
||||
let color = ( ix % 7 ) + 1;
|
||||
let rgb = format!("rgb({},{},{})",
|
||||
if color & 2 != 0 { 205 } else { 90 },
|
||||
if color & 4 != 0 { 205 } else { 90 },
|
||||
if color & 1 != 0 { 225 } else { 90 });
|
||||
|
||||
let prim_class = format!("tile_slice{}", ix);
|
||||
|
||||
css = format!("{}\n\
|
||||
#{} {{\n\
|
||||
fill: {};\n\
|
||||
fill-opacity: 0.03;\n\
|
||||
stroke-width: 0.8;\n\
|
||||
stroke: {};\n\
|
||||
}}\n\n",
|
||||
css,
|
||||
prim_class,
|
||||
//rgb,
|
||||
"none",
|
||||
rgb);
|
||||
}
|
||||
|
||||
let output_file = output_dir.join("tilecache.css");
|
||||
let mut css_output = File::create(output_file).unwrap();
|
||||
css_output.write_all(css.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
println!("Usage: tileview input_dir output_dir");
|
||||
println!(" where input_dir is a tile_cache folder inside a wr-capture.");
|
||||
println!("\nexample: cargo run c:/Users/me/AppData/Local/wr-capture.6/tile_cache/ c:/temp/tilecache/");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let input_dir = Path::new(&args[1]);
|
||||
let output_dir = Path::new(&args[2]);
|
||||
std::fs::create_dir_all(output_dir).unwrap();
|
||||
|
||||
let mut svg_width = 100i32;
|
||||
let mut svg_height = 100i32;
|
||||
let mut max_slice_index = 0;
|
||||
|
||||
let mut entries: Vec<_> = std::fs::read_dir(input_dir).unwrap()
|
||||
//.map(|r| r.unwrap())
|
||||
.filter_map(|r| r.ok())
|
||||
.collect();
|
||||
entries.sort_by_key(|dir| dir.path());
|
||||
|
||||
let mut svg_files: Vec::<String> = Vec::new();
|
||||
let mut prev_slices = None;
|
||||
|
||||
for entry in &entries {
|
||||
if entry.path().is_dir() {
|
||||
continue;
|
||||
}
|
||||
print!("processing {:?}\t", entry.path());
|
||||
let f = File::open(entry.path()).unwrap();
|
||||
let slices: Vec<Slice> = match ron::de::from_reader(f) {
|
||||
Ok(data) => { data }
|
||||
Err(e) => {
|
||||
println!("ERROR: failed to deserialize {:?}\n{:?}", entry.path(), e);
|
||||
prev_slices = None;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let svg = slices_to_svg(&slices, prev_slices, &mut svg_width, &mut svg_height, &mut max_slice_index);
|
||||
|
||||
let mut output_filename = OsString::from(entry.path().file_name().unwrap());
|
||||
output_filename.push(".svg");
|
||||
svg_files.push(output_filename.to_string_lossy().to_string());
|
||||
|
||||
output_filename = output_dir.join(output_filename).into_os_string();
|
||||
let mut svg_output = File::create(output_filename).unwrap();
|
||||
svg_output.write_all(svg.as_bytes()).unwrap();
|
||||
|
||||
print!("\r");
|
||||
prev_slices = Some(slices);
|
||||
}
|
||||
|
||||
write_html(output_dir, &svg_files);
|
||||
write_css(output_dir, max_slice_index);
|
||||
|
||||
println!("OK. For now, manually copy tilecache.js to the output folder please. ");
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// current SVG file for scrubbing and playback
|
||||
var svg_index = 0;
|
||||
|
||||
// double buffered <object>s each holding an SVG file
|
||||
var backbuffer;
|
||||
var frontbuffer;
|
||||
|
||||
|
||||
// timer for animation
|
||||
var svg_timer;
|
||||
var is_playing = false;
|
||||
|
||||
function toggle_play() {
|
||||
if( is_playing ) {
|
||||
is_playing = false;
|
||||
clearInterval(svg_timer);
|
||||
document.getElementById("text_spacebar").innerHTML =
|
||||
'Spacebar to play';
|
||||
} else {
|
||||
is_playing = true;
|
||||
svg_timer = setInterval(on_tick, 100);
|
||||
document.getElementById("text_spacebar").innerHTML =
|
||||
'Playing (Spacebar to stop)';
|
||||
function on_tick() {
|
||||
if( svg_index + 1 == svg_files.length ) {
|
||||
toggle_play();
|
||||
} else {
|
||||
go_to_svg(svg_index+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggle_quadtree() {
|
||||
var quad_groups = document.getElementsByClassName("svg_quadtree")
|
||||
var i;
|
||||
for (i = 0; i < quad_groups.length; i++) {
|
||||
if( quad_groups[i].style.display == "none" )
|
||||
quad_groups[i].style.display = "block";
|
||||
else
|
||||
quad_groups[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// try to block repeated keypressed from causing flickering
|
||||
// when they land between go_to_svg returning and onload
|
||||
// firing.
|
||||
var is_loading = false;
|
||||
|
||||
function go_to_svg(index) {
|
||||
if( index >= svg_files.length ||
|
||||
index < 0 ||
|
||||
is_loading ) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_loading = true;
|
||||
svg_index = index;
|
||||
|
||||
var slider = document.getElementById('frame_slider');
|
||||
// won't recurse thanks to is_loading
|
||||
slider.value = svg_index;
|
||||
|
||||
backbuffer.onload = function(){
|
||||
|
||||
document.getElementById("text_frame_counter").innerHTML =
|
||||
svg_files[svg_index];
|
||||
|
||||
backbuffer.style.display = '';
|
||||
frontbuffer.style.display = 'none';
|
||||
|
||||
var t = frontbuffer;
|
||||
frontbuffer = backbuffer;
|
||||
backbuffer = t;
|
||||
is_loading = false;
|
||||
}
|
||||
backbuffer.setAttribute('data', svg_files[svg_index]);
|
||||
|
||||
// also see https://stackoverflow.com/a/29915275
|
||||
}
|
||||
|
||||
function load() {
|
||||
window.addEventListener('keypress', handle_keyboard_shortcut);
|
||||
window.addEventListener('keydown', handle_keydown);
|
||||
|
||||
frontbuffer = document.getElementById("svg_container0");
|
||||
backbuffer = document.getElementById("svg_container1");
|
||||
backbuffer.style.display='none';
|
||||
|
||||
var slider = document.getElementById('frame_slider');
|
||||
slider.oninput = function() {
|
||||
if( is_playing ) {
|
||||
toggle_play();
|
||||
}
|
||||
go_to_svg(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
function handle_keyboard_shortcut(event) {
|
||||
switch (event.charCode) {
|
||||
case 32: // ' '
|
||||
toggle_play();
|
||||
break;
|
||||
case 113: // 'q'
|
||||
toggle_quadtree();
|
||||
break;
|
||||
/*
|
||||
case 49: // "1" key
|
||||
document.getElementById("radio1").checked = true;
|
||||
show_image(1);
|
||||
break;
|
||||
case 50: // "2" key
|
||||
document.getElementById("radio2").checked = true;
|
||||
show_image(2);
|
||||
break;
|
||||
case 100: // "d" key
|
||||
document.getElementById("differences").click();
|
||||
break;
|
||||
case 112: // "p" key
|
||||
shift_images(-1);
|
||||
break;
|
||||
case 110: // "n" key
|
||||
shift_images(1);
|
||||
break;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
function handle_keydown(event) {
|
||||
switch (event.keyCode) {
|
||||
case 37: // left arrow
|
||||
go_to_svg(svg_index-1);
|
||||
event.preventDefault();
|
||||
break;
|
||||
case 38: // up arrow
|
||||
break;
|
||||
case 39: // right arrow
|
||||
go_to_svg(svg_index+1);
|
||||
event.preventDefault();
|
||||
break;
|
||||
case 40: // down arrow
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ use crate::gpu_types::TransformData;
|
|||
use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
|
||||
use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
|
||||
use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode};
|
||||
use crate::picture::{TileCacheLogger};
|
||||
use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
|
||||
use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask};
|
||||
use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters};
|
||||
|
@ -240,6 +241,7 @@ impl FrameBuilder {
|
|||
debug_flags: DebugFlags,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters,
|
||||
composite_state: &mut CompositeState,
|
||||
tile_cache_logger: &mut TileCacheLogger,
|
||||
) -> Option<RenderTaskId> {
|
||||
profile_scope!("cull");
|
||||
|
||||
|
@ -411,9 +413,12 @@ impl FrameBuilder {
|
|||
&mut frame_state,
|
||||
&frame_context,
|
||||
scratch,
|
||||
tile_cache_logger
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
tile_cache_logger.advance();
|
||||
|
||||
{
|
||||
profile_marker!("PreparePrims");
|
||||
|
||||
|
@ -425,6 +430,7 @@ impl FrameBuilder {
|
|||
&mut frame_state,
|
||||
data_stores,
|
||||
scratch,
|
||||
tile_cache_logger,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -465,6 +471,7 @@ impl FrameBuilder {
|
|||
scratch: &mut PrimitiveScratchBuffer,
|
||||
render_task_counters: &mut RenderTaskGraphCounters,
|
||||
debug_flags: DebugFlags,
|
||||
tile_cache_logger: &mut TileCacheLogger,
|
||||
) -> Frame {
|
||||
profile_scope!("build");
|
||||
profile_marker!("BuildFrame");
|
||||
|
@ -534,6 +541,7 @@ impl FrameBuilder {
|
|||
debug_flags,
|
||||
&mut resource_profile.texture_cache,
|
||||
&mut composite_state,
|
||||
tile_cache_logger,
|
||||
);
|
||||
|
||||
let mut passes;
|
||||
|
|
|
@ -221,3 +221,5 @@ pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle};
|
|||
pub use crate::shade::{Shaders, WrShaders};
|
||||
pub use api as webrender_api;
|
||||
pub use webrender_build::shader::ProgramSourceDigest;
|
||||
pub use crate::picture::{TileDescriptor, TileId, InvalidationReason, PrimitiveCompareResult};
|
||||
pub use crate::picture::{TileNode, TileNodeKind, TileSerializer, TileCacheInstanceSerializer, TileOffset};
|
||||
|
|
|
@ -107,6 +107,15 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
use crate::texture_cache::TextureCacheHandle;
|
||||
use crate::util::{TransformedRectKind, MatrixHelpers, MaxRect, scale_factors, VecHelper, RectHelpers};
|
||||
use crate::filterdata::{FilterDataHandle};
|
||||
#[cfg(feature = "capture")]
|
||||
use ron;
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
use std::fs::File;
|
||||
#[cfg(feature = "capture")]
|
||||
use std::io::prelude::*;
|
||||
#[cfg(feature = "capture")]
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Specify whether a surface allows subpixel AA text rendering.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
|
@ -300,7 +309,7 @@ fn clampf(value: f32, low: f32, high: f32) -> f32 {
|
|||
|
||||
/// An index into the prims array in a TileDescriptor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct PrimitiveDependencyIndex(u32);
|
||||
pub struct PrimitiveDependencyIndex(u32);
|
||||
|
||||
/// Information about the state of an opacity binding.
|
||||
#[derive(Debug)]
|
||||
|
@ -313,6 +322,8 @@ pub struct OpacityBindingInfo {
|
|||
|
||||
/// Information stored in a tile descriptor for an opacity binding.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum OpacityBinding {
|
||||
Value(f32),
|
||||
Binding(PropertyBindingId),
|
||||
|
@ -532,8 +543,10 @@ impl TileSurface {
|
|||
/// since this is a hot path in the code, and keeping the data small
|
||||
/// is a performance win.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[repr(u8)]
|
||||
enum PrimitiveCompareResult {
|
||||
pub enum PrimitiveCompareResult {
|
||||
/// Primitives match
|
||||
Equal,
|
||||
/// Something in the PrimitiveDescriptor was different
|
||||
|
@ -549,8 +562,10 @@ enum PrimitiveCompareResult {
|
|||
}
|
||||
|
||||
/// Debugging information about why a tile was invalidated
|
||||
#[derive(Debug)]
|
||||
enum InvalidationReason {
|
||||
#[derive(Debug,Copy,Clone)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum InvalidationReason {
|
||||
/// The fractional offset changed
|
||||
FractionalOffset,
|
||||
/// The background color changed
|
||||
|
@ -570,6 +585,29 @@ enum InvalidationReason {
|
|||
},
|
||||
}
|
||||
|
||||
/// A minimal subset of Tile for debug capturing
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct TileSerializer {
|
||||
pub rect: PictureRect,
|
||||
pub current_descriptor: TileDescriptor,
|
||||
pub fract_offset: PictureVector2D,
|
||||
pub id: TileId,
|
||||
pub root: TileNode,
|
||||
pub background_color: Option<ColorF>,
|
||||
pub invalidation_reason: Option<InvalidationReason>
|
||||
}
|
||||
|
||||
/// A minimal subset of TileCacheInstance for debug capturing
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct TileCacheInstanceSerializer {
|
||||
pub slice: usize,
|
||||
pub tiles: FastHashMap<TileOffset, TileSerializer>,
|
||||
pub background_color: Option<ColorF>,
|
||||
pub fract_offset: PictureVector2D,
|
||||
}
|
||||
|
||||
/// Information about a cached tile.
|
||||
pub struct Tile {
|
||||
/// The current world rect of this tile.
|
||||
|
@ -1023,6 +1061,8 @@ impl Tile {
|
|||
|
||||
/// Defines a key that uniquely identifies a primitive instance.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct PrimitiveDescriptor {
|
||||
/// Uniquely identifies the content of the primitive template.
|
||||
prim_uid: ItemUid,
|
||||
|
@ -1031,7 +1071,7 @@ pub struct PrimitiveDescriptor {
|
|||
/// The clip rect for this primitive. Included here in
|
||||
/// dependencies since there is no entry in the clip chain
|
||||
/// dependencies for the local clip rect.
|
||||
prim_clip_rect: RectangleKey,
|
||||
pub prim_clip_rect: RectangleKey,
|
||||
/// The number of extra dependencies that this primitive has.
|
||||
transform_dep_count: u8,
|
||||
image_dep_count: u8,
|
||||
|
@ -1153,6 +1193,9 @@ impl<'a, T> CompareHelper<'a, T> where T: PartialEq {
|
|||
|
||||
/// Uniquely describes the content of this tile, in a way that can be
|
||||
/// (reasonably) efficiently hashed and compared.
|
||||
#[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct TileDescriptor {
|
||||
/// List of primitive instance unique identifiers. The uid is guaranteed
|
||||
/// to uniquely describe the content of the primitive template, while
|
||||
|
@ -1222,7 +1265,7 @@ impl TileDescriptor {
|
|||
pt.new_level("images".to_string());
|
||||
for info in &self.images {
|
||||
pt.new_level(format!("key={:?}", info.key));
|
||||
pt.new_level(format!("generation={:?}", info.generation));
|
||||
pt.add_item(format!("generation={:?}", info.generation));
|
||||
pt.end_level();
|
||||
}
|
||||
pt.end_level();
|
||||
|
@ -1419,6 +1462,107 @@ impl BackdropInfo {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TileCacheLoggerSlice {
|
||||
pub serialized_slice: String,
|
||||
pub local_to_world_transform: DeviceRect
|
||||
}
|
||||
|
||||
/// Log tile cache activity whenever anything happens in take_context.
|
||||
pub struct TileCacheLogger {
|
||||
/// next write pointer
|
||||
pub write_index : usize,
|
||||
/// ron serialization of tile caches;
|
||||
/// each frame consists of slices, one per take_context call
|
||||
pub frames: Vec<Vec<TileCacheLoggerSlice>>
|
||||
}
|
||||
|
||||
impl TileCacheLogger {
|
||||
pub fn new(
|
||||
num_frames: usize
|
||||
) -> Self {
|
||||
let mut frames = Vec::with_capacity(num_frames);
|
||||
let empty_element = Vec::new();
|
||||
frames.resize(num_frames, empty_element);
|
||||
TileCacheLogger {
|
||||
write_index: 0,
|
||||
frames
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
!self.frames.is_empty()
|
||||
}
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
pub fn add(&mut self, serialized_slice: String, local_to_world_transform: DeviceRect) {
|
||||
if !self.is_enabled() {
|
||||
return;
|
||||
}
|
||||
self.frames[self.write_index].push(
|
||||
TileCacheLoggerSlice {
|
||||
serialized_slice,
|
||||
local_to_world_transform });
|
||||
}
|
||||
|
||||
/// see if anything was written in this frame, and if so,
|
||||
/// advance the write index in a circular way and clear the
|
||||
/// recorded string.
|
||||
pub fn advance(&mut self) {
|
||||
if !self.is_enabled() || self.frames[self.write_index].is_empty() {
|
||||
return;
|
||||
}
|
||||
self.write_index = self.write_index + 1;
|
||||
if self.write_index >= self.frames.len() {
|
||||
self.write_index = 0;
|
||||
}
|
||||
self.frames[self.write_index] = Vec::new();
|
||||
}
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
pub fn save_capture(
|
||||
&self, root: &PathBuf
|
||||
) {
|
||||
if !self.is_enabled() {
|
||||
return;
|
||||
}
|
||||
use std::fs;
|
||||
|
||||
info!("saving tile cache log");
|
||||
let path_tile_cache = root.join("tile_cache");
|
||||
if !path_tile_cache.is_dir() {
|
||||
fs::create_dir(&path_tile_cache).unwrap();
|
||||
}
|
||||
|
||||
let mut files_written = 0;
|
||||
for ix in 0..self.frames.len() {
|
||||
// ...and start with write_index, since that's the oldest entry
|
||||
// that we're about to overwrite. However when we get to
|
||||
// save_capture, we've add()ed entries but haven't advance()d yet,
|
||||
// so the actual oldest entry is write_index + 1
|
||||
let index = (self.write_index + 1 + ix) % self.frames.len();
|
||||
if self.frames[index].is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let filename = path_tile_cache.join(format!("frame{:05}.ron", files_written));
|
||||
files_written = files_written + 1;
|
||||
let mut output = File::create(filename).unwrap();
|
||||
output.write_all(b"[\n").unwrap();
|
||||
for item in &self.frames[index] {
|
||||
output.write_all(format!("( x: {}, y: {},\n",
|
||||
item.local_to_world_transform.origin.x,
|
||||
item.local_to_world_transform.origin.y)
|
||||
.as_bytes()).unwrap();
|
||||
output.write_all(b"tile_cache:\n").unwrap();
|
||||
output.write_all(item.serialized_slice.as_bytes()).unwrap();
|
||||
output.write_all(b"\n),\n").unwrap();
|
||||
}
|
||||
output.write_all(b"]\n").unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a cache of tiles that make up a picture primitives.
|
||||
pub struct TileCacheInstance {
|
||||
/// Index of the tile cache / slice for this frame builder. It's determined
|
||||
|
@ -3275,6 +3419,7 @@ impl PicturePrimitive {
|
|||
frame_state: &mut FrameBuildingState,
|
||||
frame_context: &FrameBuildingContext,
|
||||
scratch: &mut PrimitiveScratchBuffer,
|
||||
tile_cache_logger: &mut TileCacheLogger,
|
||||
) -> Option<(PictureContext, PictureState, PrimitiveList)> {
|
||||
if !self.is_visible() {
|
||||
return None;
|
||||
|
@ -3338,6 +3483,7 @@ impl PicturePrimitive {
|
|||
}
|
||||
};
|
||||
|
||||
let unclipped =
|
||||
match self.raster_config {
|
||||
Some(ref raster_config) => {
|
||||
let pic_rect = PictureRect::from_untyped(&self.precise_local_rect.to_untyped());
|
||||
|
@ -3926,10 +4072,46 @@ impl PicturePrimitive {
|
|||
root,
|
||||
);
|
||||
}
|
||||
|
||||
unclipped
|
||||
}
|
||||
None => {}
|
||||
None => DeviceRect::zero()
|
||||
};
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
{
|
||||
if frame_context.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) {
|
||||
if let Some(ref tile_cache) = self.tile_cache
|
||||
{
|
||||
// extract just the fields that we're interested in
|
||||
let mut tile_cache_tiny = TileCacheInstanceSerializer {
|
||||
slice: tile_cache.slice,
|
||||
tiles: FastHashMap::default(),
|
||||
background_color: tile_cache.background_color,
|
||||
fract_offset: tile_cache.fract_offset
|
||||
};
|
||||
for (key, tile) in &tile_cache.tiles {
|
||||
tile_cache_tiny.tiles.insert(*key, TileSerializer {
|
||||
rect: tile.rect,
|
||||
current_descriptor: tile.current_descriptor.clone(),
|
||||
fract_offset: tile.fract_offset,
|
||||
id: tile.id,
|
||||
root: tile.root.clone(),
|
||||
background_color: tile.background_color,
|
||||
invalidation_reason: tile.invalidation_reason.clone()
|
||||
});
|
||||
}
|
||||
let text = ron::ser::to_string_pretty(&tile_cache_tiny, Default::default()).unwrap();
|
||||
tile_cache_logger.add(text, unclipped);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "capture"))]
|
||||
{
|
||||
let _tile_cache_logger = tile_cache_logger; // unused variable fix
|
||||
let _unclipped = unclipped;
|
||||
}
|
||||
|
||||
let state = PictureState {
|
||||
//TODO: check for MAX_CACHE_SIZE here?
|
||||
map_local_to_pic,
|
||||
|
@ -4682,6 +4864,8 @@ struct PrimitiveComparisonKey {
|
|||
|
||||
/// Information stored an image dependency
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
struct ImageDependency {
|
||||
key: ImageKey,
|
||||
generation: ImageGeneration,
|
||||
|
@ -4830,15 +5014,22 @@ impl<'a> PrimitiveComparer<'a> {
|
|||
}
|
||||
|
||||
/// Details for a node in a quadtree that tracks dirty rects for a tile.
|
||||
enum TileNodeKind {
|
||||
#[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum TileNodeKind {
|
||||
Leaf {
|
||||
/// The index buffer of primitives that affected this tile previous frame
|
||||
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
|
||||
prev_indices: Vec<PrimitiveDependencyIndex>,
|
||||
/// The index buffer of primitives that affect this tile on this frame
|
||||
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
|
||||
curr_indices: Vec<PrimitiveDependencyIndex>,
|
||||
/// A bitset of which of the last 64 frames have been dirty for this leaf.
|
||||
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
|
||||
dirty_tracker: u64,
|
||||
/// The number of frames since this node split or merged.
|
||||
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
|
||||
frames_since_modified: usize,
|
||||
},
|
||||
Node {
|
||||
|
@ -4855,11 +5046,14 @@ enum TileModification {
|
|||
}
|
||||
|
||||
/// A node in the dirty rect tracking quadtree.
|
||||
struct TileNode {
|
||||
#[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct TileNode {
|
||||
/// Leaf or internal node
|
||||
kind: TileNodeKind,
|
||||
pub kind: TileNodeKind,
|
||||
/// Rect of this node in the same space as the tile cache picture
|
||||
rect: PictureRect,
|
||||
pub rect: PictureRect,
|
||||
}
|
||||
|
||||
impl TileNode {
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::image::{Repetition};
|
|||
use crate::intern;
|
||||
use crate::internal_types::PlaneSplitAnchor;
|
||||
use malloc_size_of::MallocSizeOf;
|
||||
use crate::picture::{PictureCompositeMode, PicturePrimitive, ClusterFlags};
|
||||
use crate::picture::{PictureCompositeMode, PicturePrimitive, ClusterFlags, TileCacheLogger};
|
||||
use crate::picture::{PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles, RasterConfig};
|
||||
use crate::prim_store::backdrop::BackdropDataHandle;
|
||||
use crate::prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle};
|
||||
|
@ -2656,6 +2656,7 @@ impl PrimitiveStore {
|
|||
plane_split_anchor: PlaneSplitAnchor,
|
||||
data_stores: &mut DataStores,
|
||||
scratch: &mut PrimitiveScratchBuffer,
|
||||
tile_cache_log: &mut TileCacheLogger,
|
||||
) -> bool {
|
||||
// If we have dependencies, we need to prepare them first, in order
|
||||
// to know the actual rect of this primitive.
|
||||
|
@ -2681,6 +2682,7 @@ impl PrimitiveStore {
|
|||
frame_state,
|
||||
frame_context,
|
||||
scratch,
|
||||
tile_cache_log,
|
||||
) {
|
||||
Some(info) => Some(info),
|
||||
None => {
|
||||
|
@ -2724,6 +2726,7 @@ impl PrimitiveStore {
|
|||
frame_state,
|
||||
data_stores,
|
||||
scratch,
|
||||
tile_cache_log,
|
||||
);
|
||||
|
||||
// Restore the dependencies (borrow check dance)
|
||||
|
@ -2789,6 +2792,7 @@ impl PrimitiveStore {
|
|||
frame_state: &mut FrameBuildingState,
|
||||
data_stores: &mut DataStores,
|
||||
scratch: &mut PrimitiveScratchBuffer,
|
||||
tile_cache_log: &mut TileCacheLogger,
|
||||
) {
|
||||
for (cluster_index, cluster) in prim_list.clusters.iter_mut().enumerate() {
|
||||
pic_state.map_local_to_pic.set_target_spatial_node(
|
||||
|
@ -2834,6 +2838,7 @@ impl PrimitiveStore {
|
|||
plane_split_anchor,
|
||||
data_stores,
|
||||
scratch,
|
||||
tile_cache_log,
|
||||
) {
|
||||
frame_state.profile_counters.visible_primitives.inc();
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ use crate::hit_test::{HitTest, HitTester};
|
|||
use crate::intern::DataStore;
|
||||
use crate::internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use crate::picture::RetainedTiles;
|
||||
use crate::picture::{RetainedTiles, TileCacheLogger};
|
||||
use crate::prim_store::{PrimitiveScratchBuffer, PrimitiveInstance};
|
||||
use crate::prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
|
||||
use crate::prim_store::interned::*;
|
||||
|
@ -540,6 +540,7 @@ impl Document {
|
|||
gpu_cache: &mut GpuCache,
|
||||
resource_profile: &mut ResourceProfileCounters,
|
||||
debug_flags: DebugFlags,
|
||||
tile_cache_logger: &mut TileCacheLogger,
|
||||
) -> RenderedDocument {
|
||||
let accumulated_scale_factor = self.view.accumulated_scale_factor();
|
||||
let pan = self.view.pan.to_f32() / accumulated_scale_factor;
|
||||
|
@ -566,6 +567,7 @@ impl Document {
|
|||
&mut self.scratch,
|
||||
&mut self.render_task_counters,
|
||||
debug_flags,
|
||||
tile_cache_logger,
|
||||
);
|
||||
self.hit_tester = Some(self.scene.create_hit_tester(&self.data_stores.clip));
|
||||
frame
|
||||
|
@ -712,6 +714,7 @@ pub struct RenderBackend {
|
|||
notifier: Box<dyn RenderNotifier>,
|
||||
recorder: Option<Box<dyn ApiRecordingReceiver>>,
|
||||
logrecorder: Option<Box<LogRecorder>>,
|
||||
tile_cache_logger: TileCacheLogger,
|
||||
sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
|
||||
size_of_ops: Option<MallocSizeOfOps>,
|
||||
debug_flags: DebugFlags,
|
||||
|
@ -754,6 +757,7 @@ impl RenderBackend {
|
|||
notifier,
|
||||
recorder,
|
||||
logrecorder: None,
|
||||
tile_cache_logger: TileCacheLogger::new(500usize),
|
||||
sampler,
|
||||
size_of_ops,
|
||||
debug_flags,
|
||||
|
@ -1529,6 +1533,7 @@ impl RenderBackend {
|
|||
&mut self.gpu_cache,
|
||||
&mut profile_counters.resources,
|
||||
self.debug_flags,
|
||||
&mut self.tile_cache_logger,
|
||||
);
|
||||
|
||||
debug!("generated frame for document {:?} with {} passes",
|
||||
|
@ -1706,6 +1711,7 @@ impl RenderBackend {
|
|||
&mut self.gpu_cache,
|
||||
&mut profile_counters.resources,
|
||||
self.debug_flags,
|
||||
&mut self.tile_cache_logger,
|
||||
);
|
||||
// After we rendered the frames, there are pending updates to both
|
||||
// GPU cache and resources. Instead of serializing them, we are going to make sure
|
||||
|
@ -1753,6 +1759,11 @@ impl RenderBackend {
|
|||
debug!("\tresource cache");
|
||||
let (resources, deferred) = self.resource_cache.save_capture(&config.root);
|
||||
|
||||
if config.bits.contains(CaptureBits::TILE_CACHE) {
|
||||
debug!("\ttile cache");
|
||||
self.tile_cache_logger.save_capture(&config.root);
|
||||
}
|
||||
|
||||
info!("\tbackend");
|
||||
let backend = PlainRenderBackend {
|
||||
default_device_pixel_ratio: self.default_device_pixel_ratio,
|
||||
|
|
|
@ -910,6 +910,8 @@ bitflags!{
|
|||
const SCENE = 0x1;
|
||||
///
|
||||
const FRAME = 0x2;
|
||||
///
|
||||
const TILE_CACHE = 0x4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1411,6 +1413,8 @@ bitflags! {
|
|||
const DISABLE_PICTURE_CACHING = 1 << 27;
|
||||
/// If set, dump picture cache invalidation debug to console.
|
||||
const INVALIDATION_DBG = 1 << 28;
|
||||
/// Log tile cache to memory for later saving as part of wr-capture
|
||||
const TILE_CACHE_LOGGING_DBG = 1 << 29;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -684,6 +684,7 @@ pref("gfx.webrender.debug.new-scene-indicator", false);
|
|||
pref("gfx.webrender.debug.show-overdraw", false);
|
||||
pref("gfx.webrender.debug.slow-frame-indicator", false);
|
||||
pref("gfx.webrender.debug.picture-caching", false);
|
||||
pref("gfx.webrender.debug.tile-cache-logging", false);
|
||||
pref("gfx.webrender.debug.primitives", false);
|
||||
pref("gfx.webrender.debug.small-screen", false);
|
||||
pref("gfx.webrender.debug.obscure-images", false);
|
||||
|
|
Загрузка…
Ссылка в новой задаче