Bug 1512287 - Expose dirty regions to wrench. r=gw

Depends on D17991

Differential Revision: https://phabricator.services.mozilla.com/D17992

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Bobby Holley 2019-01-30 08:57:37 +00:00
Родитель c7b68459e9
Коммит d9c60f139c
9 изменённых файлов: 91 добавлений и 44 удалений

Просмотреть файл

@ -659,8 +659,8 @@ pub extern "C" fn wr_renderer_render(renderer: &mut Renderer,
renderer.notify_slow_frame();
}
match renderer.render(DeviceIntSize::new(width, height)) {
Ok(stats) => {
*out_stats = stats;
Ok(results) => {
*out_stats = results.stats;
true
}
Err(errors) => {

Просмотреть файл

@ -596,6 +596,7 @@ impl FrameBuilder {
has_been_rendered: false,
has_texture_cache_tasks,
prim_headers,
recorded_dirty_regions: mem::replace(&mut scratch.recorded_dirty_regions, Vec::new()),
debug_items: mem::replace(&mut scratch.debug_items, Vec::new()),
}
}

Просмотреть файл

@ -209,7 +209,7 @@ pub use frame_builder::ChasePrimitive;
pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind};
pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile};
pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions};
pub use renderer::{RendererStats, SceneBuilderHooks, ThreadListener, ShaderPrecacheFlags};
pub use renderer::{RenderResults, RendererStats, SceneBuilderHooks, ThreadListener, ShaderPrecacheFlags};
pub use renderer::MAX_VERTEX_TEXTURE_WIDTH;
pub use shade::{Shaders, WrShaders};
pub use webrender_api as api;

Просмотреть файл

@ -420,6 +420,35 @@ impl DirtyRegion {
combined,
}
}
/// Creates a record of this dirty region for exporting to test infrastructure.
pub fn record(&self) -> RecordedDirtyRegion {
let mut rects: Vec<WorldRect> =
self.dirty_rects.iter().map(|r| r.world_rect.clone()).collect();
rects.sort_unstable_by_key(|r| (r.origin.y as usize, r.origin.x as usize));
RecordedDirtyRegion { rects }
}
}
/// A recorded copy of the dirty region for exporting to test infrastructure.
pub struct RecordedDirtyRegion {
pub rects: Vec<WorldRect>,
}
impl ::std::fmt::Display for RecordedDirtyRegion {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
for r in self.rects.iter() {
let (x, y, w, h) = (r.origin.x, r.origin.y, r.size.width, r.size.height);
write!(f, "[({},{}):{}x{}]", x, y, w, h)?;
}
Ok(())
}
}
impl ::std::fmt::Debug for RecordedDirtyRegion {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::std::fmt::Display::fmt(self, f)
}
}
/// A helper struct to build a (roughly) minimal set of dirty rectangles
@ -1490,6 +1519,12 @@ impl TileCache {
builder.build(&mut self.dirty_region);
// When under test, record a copy of the dirty region to support
// invalidation testing in wrench.
if frame_context.config.testing {
scratch.recorded_dirty_regions.push(self.dirty_region.record());
}
// If we end up with too many dirty rects, then it's going to be a lot
// of extra draw calls to submit (since we currently just submit every
// draw call for every dirty rect). In this case, bail out and work

Просмотреть файл

@ -29,7 +29,7 @@ use image::{Repetition};
use intern;
use malloc_size_of::MallocSizeOf;
use picture::{PictureCompositeMode, PicturePrimitive, PictureUpdateState};
use picture::{ClusterIndex, PrimitiveList, SurfaceIndex, RetainedTiles, RasterConfig};
use picture::{ClusterIndex, PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles, RasterConfig};
use prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle};
use prim_store::gradient::{LinearGradientDataHandle, RadialGradientDataHandle};
use prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle};
@ -1553,6 +1553,11 @@ pub struct PrimitiveScratchBuffer {
/// List of the visibility information for currently visible primitives.
pub prim_info: Vec<PrimitiveVisibility>,
/// List of dirty regions for the cached pictures in this document, used to
/// verify invalidation in wrench reftests. Only collected in testing.
pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
/// List of debug display items for rendering.
pub debug_items: Vec<DebugItem>,
}
@ -1565,6 +1570,7 @@ impl PrimitiveScratchBuffer {
segments: SegmentStorage::new(0),
segment_instances: SegmentInstanceStorage::new(0),
gradient_tiles: GradientTileStorage::new(0),
recorded_dirty_regions: Vec::new(),
debug_items: Vec::new(),
prim_info: Vec::new(),
}
@ -1599,6 +1605,8 @@ impl PrimitiveScratchBuffer {
self.prim_info.clear();
self.debug_items.clear();
assert!(self.recorded_dirty_regions.is_empty(), "Should have sent to Renderer");
}
#[allow(dead_code)]

Просмотреть файл

@ -58,6 +58,7 @@ use internal_types::{CacheTextureId, DebugOutput, FastHashMap, LayerIndex, Rende
use internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource};
use internal_types::{RenderTargetInfo, SavedTargetIndex};
use malloc_size_of::MallocSizeOfOps;
use picture::RecordedDirtyRegion;
use prim_store::DeferredResolve;
use profiler::{BackendProfileCounters, FrameProfileCounters, TimeProfileCounter,
GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
@ -2455,7 +2456,7 @@ impl Renderer {
pub fn render(
&mut self,
framebuffer_size: DeviceIntSize,
) -> Result<RendererStats, Vec<RendererError>> {
) -> Result<RenderResults, Vec<RendererError>> {
self.framebuffer_size = Some(framebuffer_size);
let result = self.render_impl(Some(framebuffer_size));
@ -2481,14 +2482,14 @@ impl Renderer {
fn render_impl(
&mut self,
framebuffer_size: Option<DeviceIntSize>,
) -> Result<RendererStats, Vec<RendererError>> {
) -> Result<RenderResults, Vec<RendererError>> {
profile_scope!("render");
let mut results = RenderResults::default();
if self.active_documents.is_empty() {
self.last_time = precise_time_ns();
return Ok(RendererStats::empty());
return Ok(results);
}
let mut stats = RendererStats::empty();
let mut frame_profiles = Vec::new();
let mut profile_timers = RendererProfileTimers::new();
@ -2577,12 +2578,16 @@ impl Renderer {
framebuffer_size,
clear_depth_value.is_some(),
cpu_frame_id,
&mut stats
&mut results.stats
);
if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
frame_profiles.push(frame.profile_counters.clone());
}
let dirty_regions =
mem::replace(&mut frame.recorded_dirty_regions, Vec::new());
results.recorded_dirty_regions.extend(dirty_regions);
}
self.unlock_external_images();
@ -2665,13 +2670,13 @@ impl Renderer {
self.device.echo_driver_messages();
}
stats.texture_upload_kb = self.profile_counters.texture_data_uploaded.get();
results.stats.texture_upload_kb = self.profile_counters.texture_data_uploaded.get();
self.backend_profile_counters.reset();
self.profile_counters.reset();
self.profile_counters.frame_counter.inc();
stats.resource_upload_time = self.resource_upload_time;
results.stats.resource_upload_time = self.resource_upload_time;
self.resource_upload_time = 0;
stats.gpu_cache_upload_time = self.gpu_cache_upload_time;
results.stats.gpu_cache_upload_time = self.gpu_cache_upload_time;
self.gpu_cache_upload_time = 0;
profile_timers.cpu_time.profile(|| {
@ -2687,7 +2692,7 @@ impl Renderer {
}
if self.renderer_errors.is_empty() {
Ok(stats)
Ok(results)
} else {
Err(mem::replace(&mut self.renderer_errors, Vec::new()))
}
@ -4845,11 +4850,11 @@ impl DebugServer {
pub fn send(&mut self, _: String) {}
}
// Some basic statistics about the rendered scene
// that we can use in wrench reftests to ensure that
// tests are batching and/or allocating on render
// targets as we expect them to.
/// Some basic statistics about the rendered scene, used in Gecko, as
/// well as in wrench reftests to ensure that tests are batching and/or
/// allocating on render targets as we expect them to.
#[repr(C)]
#[derive(Debug, Default)]
pub struct RendererStats {
pub total_draw_calls: usize,
pub alpha_target_count: usize,
@ -4859,21 +4864,14 @@ pub struct RendererStats {
pub gpu_cache_upload_time: u64,
}
impl RendererStats {
pub fn empty() -> Self {
RendererStats {
total_draw_calls: 0,
alpha_target_count: 0,
color_target_count: 0,
texture_upload_kb: 0,
resource_upload_time: 0,
gpu_cache_upload_time: 0,
}
}
/// Return type from render(), which contains some repr(C) statistics as well as
/// some non-repr(C) data.
#[derive(Debug, Default)]
pub struct RenderResults {
pub stats: RendererStats,
pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
}
#[cfg(any(feature = "capture", feature = "replay"))]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]

Просмотреть файл

@ -18,7 +18,7 @@ use gpu_types::{TransformData, TransformPalette, ZBufferIdGenerator};
use internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
use picture::SurfaceInfo;
use picture::{RecordedDirtyRegion, SurfaceInfo};
use prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer};
use profiler::FrameProfileCounters;
use render_backend::{DataStores, FrameId};
@ -1119,6 +1119,11 @@ pub struct Frame {
/// renderer.
pub has_been_rendered: bool,
/// Dirty regions recorded when generating this frame. Empty when not in
/// testing.
#[cfg_attr(feature = "serde", serde(skip))]
pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
/// Debugging information to overlay for this frame.
pub debug_items: Vec<DebugItem>,
}

Просмотреть файл

@ -15,7 +15,7 @@ use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::sync::mpsc::Receiver;
use webrender::RendererStats;
use webrender::RenderResults;
use webrender::api::*;
use wrench::{Wrench, WrenchThing};
use yaml_frame_reader::YamlFrameReader;
@ -376,7 +376,7 @@ impl<'a> ReftestHarness<'a> {
// the reference can be smaller than the window size,
// in which case we only compare the intersection
let (test, stats) = self.render_yaml(
let (test, results) = self.render_yaml(
t.test.as_path(),
reference.size,
t.font_render_mode,
@ -394,10 +394,10 @@ impl<'a> ReftestHarness<'a> {
let comparison = test.compare(&reference);
if let Some(expected_draw_calls) = t.expected_draw_calls {
if expected_draw_calls != stats.total_draw_calls {
if expected_draw_calls != results.stats.total_draw_calls {
println!("REFTEST TEST-UNEXPECTED-FAIL | {} | {}/{} | expected_draw_calls",
t,
stats.total_draw_calls,
results.stats.total_draw_calls,
expected_draw_calls
);
println!("REFTEST TEST-END | {}", t);
@ -405,10 +405,10 @@ impl<'a> ReftestHarness<'a> {
}
}
if let Some(expected_alpha_targets) = t.expected_alpha_targets {
if expected_alpha_targets != stats.alpha_target_count {
if expected_alpha_targets != results.stats.alpha_target_count {
println!("REFTEST TEST-UNEXPECTED-FAIL | {} | {}/{} | alpha_target_count",
t,
stats.alpha_target_count,
results.stats.alpha_target_count,
expected_alpha_targets
);
println!("REFTEST TEST-END | {}", t);
@ -416,10 +416,10 @@ impl<'a> ReftestHarness<'a> {
}
}
if let Some(expected_color_targets) = t.expected_color_targets {
if expected_color_targets != stats.color_target_count {
if expected_color_targets != results.stats.color_target_count {
println!("REFTEST TEST-UNEXPECTED-FAIL | {} | {}/{} | color_target_count",
t,
stats.color_target_count,
results.stats.color_target_count,
expected_color_targets
);
println!("REFTEST TEST-END | {}", t);
@ -483,7 +483,7 @@ impl<'a> ReftestHarness<'a> {
size: DeviceIntSize,
font_render_mode: Option<FontRenderMode>,
allow_mipmaps: bool,
) -> (ReftestImage, RendererStats) {
) -> (ReftestImage, RenderResults) {
let mut reader = YamlFrameReader::new(filename);
reader.set_font_render_mode(font_render_mode);
reader.allow_mipmaps(allow_mipmaps);
@ -493,7 +493,7 @@ impl<'a> ReftestHarness<'a> {
// wait for the frame
self.rx.recv().unwrap();
let stats = self.wrench.render();
let results = self.wrench.render();
let window_size = self.window.get_inner_size();
assert!(
@ -515,6 +515,6 @@ impl<'a> ReftestHarness<'a> {
reader.deinit(self.wrench);
(ReftestImage { data: pixels, size }, stats)
(ReftestImage { data: pixels, size }, results)
}
}

Просмотреть файл

@ -20,7 +20,7 @@ use std::sync::mpsc::Receiver;
use time;
use webrender;
use webrender::api::*;
use webrender::{DebugFlags, RendererStats, ShaderPrecacheFlags};
use webrender::{DebugFlags, RenderResults, ShaderPrecacheFlags};
use yaml_frame_writer::YamlFrameWriterReceiver;
use {WindowWrapper, NotifierEvent};
@ -555,7 +555,7 @@ impl Wrench {
self.renderer.get_frame_profiles()
}
pub fn render(&mut self) -> RendererStats {
pub fn render(&mut self) -> RenderResults {
self.renderer.update();
let _ = self.renderer.flush_pipeline_info();
self.renderer