From e0357ade5ccc411a859cc5ccabefb12a2fc0e362 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 10 Jan 2018 16:04:55 -0500 Subject: [PATCH 01/16] Capture: PlainFrame serialization under capture2 feature --- webrender/Cargo.toml | 1 + webrender/src/batch.rs | 13 +++++++++++++ webrender/src/box_shadow.rs | 1 + webrender/src/clip_scroll_tree.rs | 1 + webrender/src/device.rs | 5 +++++ webrender/src/freelist.rs | 2 ++ webrender/src/glyph_rasterizer.rs | 1 + webrender/src/gpu_cache.rs | 1 + webrender/src/gpu_types.rs | 7 +++++++ webrender/src/internal_types.rs | 4 ++++ webrender/src/picture.rs | 1 + webrender/src/prim_store.rs | 1 + webrender/src/render_backend.rs | 1 + webrender/src/render_task.rs | 12 ++++++++++++ webrender/src/renderer.rs | 2 ++ webrender/src/resource_cache.rs | 1 + webrender/src/texture_allocator.rs | 4 +++- webrender/src/tiling.rs | 21 +++++++++++++++++++++ webrender/src/util.rs | 3 ++- wrench/Cargo.toml | 2 +- 20 files changed, 81 insertions(+), 3 deletions(-) diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index e3e36bdf94..5135ca1ae6 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -12,6 +12,7 @@ freetype-lib = ["freetype/servo-freetype-sys"] profiler = ["thread_profiler/thread_profiler"] debugger = ["ws", "serde_json", "serde", "image", "base64"] capture = ["webrender_api/debug-serialization", "ron", "serde"] +capture2 = ["capture"] [dependencies] app_units = "0.6" diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index be7c10dbe3..07c48d6848 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -35,6 +35,7 @@ use util::{MatrixHelpers, TransformedRectKind}; const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32); #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum TransformBatchKind { TextRun(GlyphFormat), Image(ImageBufferKind), @@ -47,6 +48,7 @@ pub enum TransformBatchKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum BrushImageSourceKind { Alpha, Color, @@ -63,6 +65,7 @@ impl BrushImageSourceKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum BrushBatchKind { Image(BrushImageSourceKind), Solid, @@ -70,6 +73,7 @@ pub enum BrushBatchKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum BatchKind { Composite { task_id: RenderTaskId, @@ -86,6 +90,7 @@ pub enum BatchKind { /// Optional textures that can be used as a source in the shaders. /// Textures that are not used by the batch are equal to TextureId::invalid(). #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct BatchTextures { pub colors: [SourceTexture; 3], } @@ -115,6 +120,7 @@ impl BatchTextures { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct AlphaPrimitiveBatch { pub key: BatchKey, pub instances: Vec, @@ -132,6 +138,7 @@ impl AlphaPrimitiveBatch { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct OpaquePrimitiveBatch { pub key: BatchKey, pub instances: Vec, @@ -147,6 +154,7 @@ impl OpaquePrimitiveBatch { } #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct BatchKey { pub kind: BatchKind, pub blend_mode: BlendMode, @@ -175,6 +183,7 @@ fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool { t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2 } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct AlphaBatchList { pub batches: Vec, } @@ -252,6 +261,7 @@ impl AlphaBatchList { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct OpaqueBatchList { pub pixel_area_threshold_for_new_batch: i32, pub batches: Vec, @@ -317,6 +327,7 @@ impl OpaqueBatchList { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct BatchList { pub alpha_batch_list: AlphaBatchList, pub opaque_batch_list: OpaqueBatchList, @@ -363,6 +374,7 @@ impl BatchList { } /// Encapsulates the logic of building batches for items that are blended. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct AlphaBatcher { pub batch_list: BatchList, pub text_run_cache_prims: FastHashMap>, @@ -1435,6 +1447,7 @@ fn make_polygon( /// Batcher managing draw calls into the clip mask (in the RT cache). #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct ClipBatcher { /// Rectangle draws fill up the rectangles with rounded corners. pub rectangles: Vec, diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 4462d85c41..3cc0b503ac 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -29,6 +29,7 @@ pub const MAX_BLUR_RADIUS : f32 = 300.; pub const MASK_CORNER_PADDING: f32 = 4.0; #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct BoxShadowCacheKey { pub width: Au, pub height: Au, diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index 058dfc5a91..9907075bf5 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -23,6 +23,7 @@ pub type ScrollStates = FastHashMap; /// system are the same or are in the same axis-aligned space. This allows /// for optimizing mask generation. #[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct CoordinateSystemId(pub u32); impl CoordinateSystemId { diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 4cc28c121c..bfe81026b2 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -85,6 +85,7 @@ impl TextureTarget { } #[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum TextureFilter { Nearest, Linear, @@ -423,7 +424,9 @@ impl ExternalTexture { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct Texture { + #[serde(skip)] id: gl::GLuint, target: gl::GLuint, layer_count: i32, @@ -432,7 +435,9 @@ pub struct Texture { height: u32, filter: TextureFilter, render_target: Option, + #[serde(skip)] fbo_ids: Vec, + #[serde(skip)] depth_rb: Option, } diff --git a/webrender/src/freelist.rs b/webrender/src/freelist.rs index 17e3b07fa7..fef03f2b94 100644 --- a/webrender/src/freelist.rs +++ b/webrender/src/freelist.rs @@ -10,6 +10,7 @@ use util::recycle_vec; // retain() style functionality. #[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct Epoch(u32); #[derive(Debug)] @@ -40,6 +41,7 @@ impl Clone for WeakFreeListHandle { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct WeakFreeListHandle { index: u32, epoch: Epoch, diff --git a/webrender/src/glyph_rasterizer.rs b/webrender/src/glyph_rasterizer.rs index 2d34698cb9..f47cfd4a72 100644 --- a/webrender/src/glyph_rasterizer.rs +++ b/webrender/src/glyph_rasterizer.rs @@ -219,6 +219,7 @@ impl FontInstance { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] #[allow(dead_code)] pub enum GlyphFormat { Alpha, diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index 35af83c0d0..79a034cc22 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -123,6 +123,7 @@ impl GpuCacheHandle { // as part of the primitive instances, to allow the vertex // shader to fetch the specific data. #[derive(Copy, Debug, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GpuCacheAddress { pub u: u16, pub v: u16, diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index b7cd97f024..19f812aae6 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -11,6 +11,7 @@ use render_task::RenderTaskAddress; #[repr(i32)] #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum BlurDirection { Horizontal = 0, Vertical, @@ -18,6 +19,7 @@ pub enum BlurDirection { #[derive(Debug)] #[repr(C)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct BlurInstance { pub task_address: RenderTaskAddress, pub src_task_address: RenderTaskAddress, @@ -28,6 +30,7 @@ pub struct BlurInstance { /// Could be an image or a rectangle, which defines the /// way `address` is treated. #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] #[repr(C)] pub struct ClipMaskInstance { pub render_task_address: RenderTaskAddress, @@ -39,6 +42,7 @@ pub struct ClipMaskInstance { // 32 bytes per instance should be enough for anyone! #[derive(Debug, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct PrimitiveInstance { data: [i32; 8], } @@ -189,10 +193,12 @@ pub enum BrushImageKind { } #[derive(Copy, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] #[repr(C)] pub struct ClipScrollNodeIndex(pub u32); #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] #[repr(C)] pub struct ClipScrollNodeData { pub transform: LayerToWorldTransform, @@ -215,6 +221,7 @@ impl ClipScrollNodeData { pub struct ClipChainRectIndex(pub usize); #[derive(Copy, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] #[repr(C)] pub enum PictureType { Image = 1, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 3fe6498dac..32784e64f1 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -32,9 +32,11 @@ pub type FastHashSet = HashSet>; // map from cache texture ID to native texture. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct CacheTextureId(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderPassIndex(pub usize); // Represents the source for a texture. @@ -44,6 +46,7 @@ pub struct RenderPassIndex(pub usize); // native texture ID. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum SourceTexture { Invalid, TextureCache(CacheTextureId), @@ -60,6 +63,7 @@ pub const ORTHO_NEAR_PLANE: f32 = -1000000.0; pub const ORTHO_FAR_PLANE: f32 = 1000000.0; #[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTargetInfo { pub has_depth: bool, } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 099fc6af41..974005c1a0 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -43,6 +43,7 @@ pub enum PictureCompositeMode { /// Configure whether the content to be drawn by a picture /// in local space rasterization or the screen space. #[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum ContentOrigin { Local(LayerPoint), Screen(DeviceIntPoint), diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 60879c37e9..c4c753d40c 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -140,6 +140,7 @@ pub struct DeferredResolve { pub struct SpecificPrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct PrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index baa1dac1dc..5214259523 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -813,6 +813,7 @@ impl ToDebugString for SpecificDisplayItem { } + #[cfg(feature = "capture")] impl RenderBackend { // Note: the mutable `self` is only needed here for resolving blob images diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 32a4322e6a..524fc2f477 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -27,13 +27,16 @@ pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTaskId(pub u32); // TODO(gw): Make private when using GPU cache! #[derive(Debug, Copy, Clone)] #[repr(C)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTaskAddress(pub u32); #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTaskTree { pub tasks: Vec, pub task_data: Vec, @@ -209,6 +212,7 @@ impl ops::IndexMut for RenderTaskTree { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum RenderTaskLocation { Fixed, Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), @@ -216,6 +220,7 @@ pub enum RenderTaskLocation { } #[derive(Debug, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct ClipWorkItem { pub scroll_node_data_index: ClipScrollNodeIndex, pub clip_sources: ClipSourcesWeakHandle, @@ -223,6 +228,7 @@ pub struct ClipWorkItem { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct CacheMaskTask { actual_rect: DeviceIntRect, pub clips: Vec, @@ -230,6 +236,7 @@ pub struct CacheMaskTask { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct PictureTask { pub prim_index: PrimitiveIndex, pub target_kind: RenderTargetKind, @@ -239,6 +246,7 @@ pub struct PictureTask { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct BlurTask { pub blur_std_deviation: f32, pub target_kind: RenderTargetKind, @@ -256,11 +264,13 @@ impl BlurTask { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTaskData { pub data: [f32; FLOATS_PER_RENDER_TASK_INFO], } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum RenderTaskKind { Picture(PictureTask), CacheMask(CacheMaskTask), @@ -271,6 +281,7 @@ pub enum RenderTaskKind { } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum ClearMode { // Applicable to color and alpha targets. Zero, @@ -281,6 +292,7 @@ pub enum ClearMode { } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTask { pub location: RenderTaskLocation, pub children: Vec, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 834c7e770b..506fe9f9f1 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -480,6 +480,7 @@ pub struct GraphicsApiInfo { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum ImageBufferKind { Texture2D = 0, TextureRect = 1, @@ -757,6 +758,7 @@ impl SourceTextureResolver { } #[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] #[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment. pub enum BlendMode { None, diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index a9cd0ab451..76120bea69 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -38,6 +38,7 @@ use texture_cache::{TextureCache, TextureCacheHandle}; const DEFAULT_TILE_SIZE: TileSize = 512; +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GlyphFetchResult { pub index_in_text_run: i32, pub uv_rect_address: GpuCacheAddress, diff --git a/webrender/src/texture_allocator.rs b/webrender/src/texture_allocator.rs index 771ab33376..5bd3f75dc7 100644 --- a/webrender/src/texture_allocator.rs +++ b/webrender/src/texture_allocator.rs @@ -22,6 +22,7 @@ const MINIMUM_LARGE_RECT_SIZE: u32 = 32; /// /// This approach was chosen because of its simplicity, good performance, and easy support for /// dynamic texture deallocation. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GuillotineAllocator { texture_size: DeviceUintSize, free_list: FreeRectList, @@ -170,6 +171,7 @@ impl GuillotineAllocator { /// A binning free list. Binning is important to avoid sifting through lots of small strips when /// allocating many texture items. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct FreeRectList { small: Vec, medium: Vec, @@ -177,7 +179,7 @@ struct FreeRectList { } impl FreeRectList { - fn new() -> FreeRectList { + fn new() -> Self { FreeRectList { small: vec![], medium: vec![], diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index b07760d538..70c007c0ba 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -35,6 +35,7 @@ pub struct ScrollbarPrimitive { } #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTargetIndex(pub usize); pub struct RenderTargetContext<'a> { @@ -46,6 +47,7 @@ pub struct RenderTargetContext<'a> { pub use_dual_source_blending: bool, } +#[cfg_attr(feature = "capture2", derive(Serialize))] struct TextureAllocator { // TODO(gw): Replace this with a simpler allocator for // render target allocation - this use case doesn't need @@ -111,11 +113,13 @@ pub trait RenderTarget { } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum RenderTargetKind { Color, // RGBA32 Alpha, // R8 } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderTargetList { screen_size: DeviceIntSize, pub format: ImageFormat, @@ -216,17 +220,20 @@ impl RenderTargetList { /// Storing the task ID allows the renderer to find /// the target rect within the render target that this /// pipeline exists at. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct FrameOutput { pub task_id: RenderTaskId, pub pipeline_id: PipelineId, } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct ScalingInfo { pub src_task_id: RenderTaskId, pub dest_task_id: RenderTaskId, } /// A render target represents a number of rendering operations on a surface. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct ColorRenderTarget { pub alpha_batcher: AlphaBatcher, // List of blur operations to apply for this render target. @@ -361,6 +368,7 @@ impl RenderTarget for ColorRenderTarget { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, pub brush_mask_corners: Vec, @@ -530,6 +538,7 @@ impl RenderTarget for AlphaRenderTarget { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct TextureCacheRenderTarget { pub horizontal_blurs: Vec, } @@ -572,6 +581,7 @@ impl TextureCacheRenderTarget { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum RenderPassKind { MainFramebuffer(ColorRenderTarget), OffScreen { @@ -586,6 +596,7 @@ pub enum RenderPassKind { /// /// A render pass can have several render targets if there wasn't enough space in one /// target to do all of the rendering for that pass. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct RenderPass { pub kind: RenderPassKind, tasks: Vec, @@ -755,6 +766,16 @@ pub struct Frame { pub deferred_resolves: Vec, } +#[cfg(feature = "capture2")] +#[derive(Serialize)] +pub struct PlainFrame { + background_color: Option, + passes: Vec, + node_data: Vec, + clip_chain_local_clip_rects: Vec, + render_tasks: RenderTaskTree, +} + impl BlurTask { fn add_instances( &self, diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 758291256d..7c677506eb 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -214,8 +214,9 @@ pub fn _subtract_rect( } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[repr(u32)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum TransformedRectKind { AxisAligned = 0, Complex = 1, diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index da9a196689..e27e0097a4 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -25,7 +25,7 @@ time = "0.1" crossbeam = "0.2" osmesa-sys = { version = "0.1.2", optional = true } osmesa-src = { git = "https://github.com/servo/osmesa-src", optional = true } -webrender = {path = "../webrender", features=["capture","debugger","profiler"]} +webrender = {path = "../webrender", features=["capture2","debugger","profiler"]} webrender_api = {path = "../webrender_api", features=["debug-serialization"]} serde = {version = "1.0", features = ["derive"] } From 8e3d65fe25b5587e79067e2a3d35562ad526bfc7 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 10 Jan 2018 18:33:04 -0500 Subject: [PATCH 02/16] Capture: separate module --- webrender/src/capture.rs | 86 +++++++++++++++++++++++++++++++++ webrender/src/gpu_types.rs | 2 +- webrender/src/internal_types.rs | 14 ++---- webrender/src/lib.rs | 2 + webrender/src/render_backend.rs | 70 ++++++++------------------- webrender/src/renderer.rs | 4 +- webrender/src/resource_cache.rs | 4 +- webrender/src/tiling.rs | 10 ---- 8 files changed, 116 insertions(+), 76 deletions(-) create mode 100644 webrender/src/capture.rs diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs new file mode 100644 index 0000000000..49481e5e4c --- /dev/null +++ b/webrender/src/capture.rs @@ -0,0 +1,86 @@ +/* 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::io::{Read, Write}; +use std::path::{Path, PathBuf}; + +use api::{ColorF, ExternalImageData, ImageDescriptor, LayerRect}; +use ron::{de, ser}; +use serde::{Deserialize, Serialize}; + +use gpu_types::{ClipScrollNodeData}; +use render_task::RenderTaskTree; +use tiling::{RenderPass}; + +bitflags!{ + pub struct CaptureBits: u8 { + const SCENE = 0x1; + const FRAME = 0x2; + } +} + +pub struct CaptureConfig { + pub root: PathBuf, + pub bits: CaptureBits, + pretty: ser::PrettyConfig, +} + +impl CaptureConfig { + pub fn new(root: PathBuf, bits: CaptureBits) -> Self { + CaptureConfig { + root, + bits, + pretty: ser::PrettyConfig::default(), + } + } + + pub fn serialize(&self, data: &T, name: P) + where + T: Serialize, + P: AsRef, + { + let ron = ser::to_string_pretty(data, self.pretty.clone()) + .unwrap(); + let path = self.root + .join(name) + .with_extension("ron"); + let mut file = File::create(path) + .unwrap(); + write!(file, "{}\n", ron) + .unwrap(); + } + + pub fn deserialize(&self, name: P) -> T + where + T: for<'a> Deserialize<'a>, + P: AsRef, + { + let mut string = String::new(); + let path = self.root + .join(name) + .with_extension("ron"); + File::open(path) + .unwrap() + .read_to_string(&mut string) + .unwrap(); + de::from_str(&string) + .unwrap() + } +} + +pub struct ExternalCaptureImage { + pub short_path: String, + pub descriptor: ImageDescriptor, + pub external: ExternalImageData, +} + +#[derive(Serialize)] +pub struct PlainFrame { + background_color: Option, + passes: Vec, + node_data: Vec, + clip_chain_local_clip_rects: Vec, + render_tasks: RenderTaskTree, +} diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 19f812aae6..db586848de 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -207,7 +207,7 @@ pub struct ClipScrollNodeData { } impl ClipScrollNodeData { - pub fn invalid() -> ClipScrollNodeData { + pub fn invalid() -> Self { ClipScrollNodeData { transform: LayerToWorldTransform::identity(), transform_kind: 0.0, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 32784e64f1..085abc599c 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -5,8 +5,6 @@ use api::{ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch}; use api::{ExternalImageData, ExternalImageId}; use api::{ImageFormat, PipelineId}; -#[cfg(feature = "capture")] -use api::ImageDescriptor; use api::DebugCommand; use device::TextureFilter; use fxhash::FxHasher; @@ -17,6 +15,9 @@ use std::f32; use std::hash::BuildHasherDefault; use std::path::PathBuf; use std::sync::Arc; + +#[cfg(feature = "capture")] +use capture::{ExternalCaptureImage}; use tiling; pub type FastHashMap = HashMap>; @@ -108,7 +109,7 @@ pub struct TextureUpdateList { } impl TextureUpdateList { - pub fn new() -> TextureUpdateList { + pub fn new() -> Self { TextureUpdateList { updates: Vec::new(), } @@ -146,13 +147,6 @@ impl RenderedDocument { } } -#[cfg(feature = "capture")] -pub struct ExternalCaptureImage { - pub short_path: String, - pub descriptor: ImageDescriptor, - pub external: ExternalImageData, -} - pub enum DebugOutput { FetchDocuments(String), FetchClipScrollTree(String), diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 0ade76834a..386501c5b6 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -55,6 +55,8 @@ extern crate serde; mod batch; mod border; mod box_shadow; +#[cfg(feature = "capture")] +mod capture; mod clip; mod clip_scroll_node; mod clip_scroll_tree; diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 5214259523..c81f72fe39 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -10,14 +10,14 @@ use api::{DocumentId, DocumentLayer, DocumentMsg}; use api::{IdNamespace, PipelineId, RenderNotifier}; use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods}; use api::channel::{PayloadSender, PayloadSenderHelperMethods}; +#[cfg(feature = "capture")] +use capture::{CaptureBits, CaptureConfig, ExternalCaptureImage}; #[cfg(feature = "debugger")] use debug_server; use frame::FrameContext; use frame_builder::{FrameBuilder, FrameBuilderConfig}; use gpu_cache::GpuCache; use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg}; -#[cfg(feature = "capture")] -use internal_types::ExternalCaptureImage; use profiler::{BackendProfileCounters, ResourceProfileCounters}; use rayon::ThreadPool; use record::ApiRecordingReceiver; @@ -26,11 +26,9 @@ use resource_cache::ResourceCache; use resource_cache::PlainResources; use scene::Scene; #[cfg(feature = "serialize")] -use serde::{Serialize, Serializer}; +use serde::{Serialize, Deserialize}; #[cfg(feature = "debugger")] use serde_json; -#[cfg(feature = "capture")] -use std::path::PathBuf; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; use std::sync::Arc; use std::sync::mpsc::Sender; @@ -588,14 +586,16 @@ impl RenderBackend { } #[cfg(feature = "capture")] DebugCommand::SaveCapture(root) => { - let deferred = self.save_capture(&root); - ResultMsg::DebugOutput(DebugOutput::SaveCapture(root, deferred)) + let config = CaptureConfig::new(root, CaptureBits::SCENE); + let deferred = self.save_capture(&config); + ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.root, deferred)) }, #[cfg(feature = "capture")] DebugCommand::LoadCapture(root) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; - self.load_capture(&root, &mut profile_counters); + let config = CaptureConfig::new(root, CaptureBits::SCENE); + self.load_capture(&config, &mut profile_counters); ResultMsg::DebugOutput(DebugOutput::LoadCapture) }, DebugCommand::EnableDualSourceBlending(enable) => { @@ -812,27 +812,17 @@ impl ToDebugString for SpecificDisplayItem { } } - - #[cfg(feature = "capture")] impl RenderBackend { // Note: the mutable `self` is only needed here for resolving blob images - fn save_capture(&mut self, root: &PathBuf) -> Vec { - use ron::ser::{to_string_pretty, PrettyConfig}; - use std::fs; - use std::io::Write; - - info!("capture: saving {}", root.to_string_lossy()); - let ron_config = PrettyConfig::default(); - let (resources, deferred) = self.resource_cache.save_capture(root); + fn save_capture(&mut self, config: &CaptureConfig) -> Vec { + info!("capture: saving {:?}", config.root); + let (resources, deferred) = self.resource_cache.save_capture(&config.root); for (&id, doc) in &self.documents { info!("\tdocument {:?}", id); - let ron = to_string_pretty(&doc.scene, ron_config.clone()).unwrap(); - let file_name = format!("scene-{}-{}.ron", (id.0).0, id.1); - let ron_path = root.clone().join(file_name); - let mut file = fs::File::create(ron_path).unwrap(); - write!(file, "{}\n", ron).unwrap(); + let file_name = format!("scene-{}-{}", (id.0).0, id.1); + config.serialize(&doc.scene, file_name); } info!("\tbackend"); @@ -847,38 +837,24 @@ impl RenderBackend { resources, }; - let ron = to_string_pretty(&serial, ron_config).unwrap(); - let ron_path = root.clone().join("backend.ron"); - let mut file = fs::File::create(ron_path).unwrap(); - write!(file, "{}\n", ron).unwrap(); + config.serialize(&serial, "backend"); deferred } fn load_capture( &mut self, - root: &PathBuf, + config: &CaptureConfig, profile_counters: &mut BackendProfileCounters, ) { - use ron::de; - use std::fs::File; - use std::io::Read; - - let mut string = String::new(); - info!("capture: loading {}", root.to_string_lossy()); - - File::open(root.join("backend.ron")) - .unwrap() - .read_to_string(&mut string) - .unwrap(); - let backend: PlainRenderBackend = de::from_str(&string) - .unwrap(); + info!("capture: loading {:?}", config.root); + let backend: PlainRenderBackend = config.deserialize("backend"); // Note: it would be great to have RenderBackend to be split // rather explicitly on what's used before and after scene building // so that, for example, we never miss anything in the code below: - self.resource_cache.load_capture(backend.resources, root); + self.resource_cache.load_capture(backend.resources, &config.root); self.gpu_cache = GpuCache::new(); self.documents.clear(); self.default_device_pixel_ratio = backend.default_device_pixel_ratio; @@ -887,14 +863,8 @@ impl RenderBackend { for (id, view) in backend.documents { info!("\tdocument {:?}", id); - string.clear(); - let file_name = format!("scene-{}-{}.ron", (id.0).0, id.1); - File::open(root.join(file_name)) - .expect(&format!("Unable to open scene {:?}", id)) - .read_to_string(&mut string) - .unwrap(); - let scene: Scene = de::from_str(&string) - .unwrap(); + let file_name = format!("scene-{}-{}", (id.0).0, id.1); + let scene: Scene = config.deserialize(file_name); let mut doc = Document { scene, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 506fe9f9f1..799801d93a 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -2417,9 +2417,7 @@ impl Renderer { }; handler.unlock(id, channel_index); - let full_path = format!("{}/{}", - path.to_string_lossy(), def.short_path); - File::create(full_path) + File::create(path.join(&def.short_path)) .expect(&format!("Unable to create {}", def.short_path)) .write_all(&data) .unwrap(); diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 76120bea69..e9a2ce5b34 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -14,14 +14,14 @@ use api::{TileOffset, TileSize}; #[cfg(feature = "capture")] use api::{NativeFontHandle}; use app_units::Au; +#[cfg(feature = "capture")] +use capture::ExternalCaptureImage; use device::TextureFilter; use frame::FrameId; use glyph_cache::GlyphCache; use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest}; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList}; -#[cfg(feature = "capture")] -use internal_types::ExternalCaptureImage; use profiler::{ResourceProfileCounters, TextureCacheProfileCounters}; use rayon::ThreadPool; use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId, RenderTaskTree}; diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 70c007c0ba..1438bdc7a4 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -766,16 +766,6 @@ pub struct Frame { pub deferred_resolves: Vec, } -#[cfg(feature = "capture2")] -#[derive(Serialize)] -pub struct PlainFrame { - background_color: Option, - passes: Vec, - node_data: Vec, - clip_chain_local_clip_rects: Vec, - render_tasks: RenderTaskTree, -} - impl BlurTask { fn add_instances( &self, From 1392cc39120ad38d9eed886aaf6229808a291d26 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 10 Jan 2018 22:06:27 -0500 Subject: [PATCH 03/16] Capture: resource cache serialization --- webrender/src/capture.rs | 14 +---------- webrender/src/device.rs | 7 +++--- webrender/src/frame.rs | 1 + webrender/src/freelist.rs | 7 ++++-- webrender/src/glyph_cache.rs | 2 ++ webrender/src/glyph_rasterizer.rs | 1 + webrender/src/gpu_cache.rs | 13 +++++++++- webrender/src/internal_types.rs | 1 + webrender/src/render_backend.rs | 38 ++++++++++++++++++++++------ webrender/src/resource_cache.rs | 41 +++++++++++++++++++++++++++++-- webrender/src/texture_cache.rs | 17 ++++++++++--- webrender/src/tiling.rs | 4 +++ 12 files changed, 113 insertions(+), 33 deletions(-) diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index 49481e5e4c..61a227e5ce 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -6,13 +6,10 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use api::{ColorF, ExternalImageData, ImageDescriptor, LayerRect}; +use api::{ExternalImageData, ImageDescriptor}; use ron::{de, ser}; use serde::{Deserialize, Serialize}; -use gpu_types::{ClipScrollNodeData}; -use render_task::RenderTaskTree; -use tiling::{RenderPass}; bitflags!{ pub struct CaptureBits: u8 { @@ -75,12 +72,3 @@ pub struct ExternalCaptureImage { pub descriptor: ImageDescriptor, pub external: ExternalImageData, } - -#[derive(Serialize)] -pub struct PlainFrame { - background_color: Option, - passes: Vec, - node_data: Vec, - clip_chain_local_clip_rects: Vec, - render_tasks: RenderTaskTree, -} diff --git a/webrender/src/device.rs b/webrender/src/device.rs index bfe81026b2..e5e86985aa 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -26,6 +26,7 @@ use std::thread; const WORK_AROUND_TEX_IMAGE: bool = cfg!(windows); #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct FrameId(usize); impl FrameId { @@ -426,7 +427,7 @@ impl ExternalTexture { #[cfg_attr(feature = "capture2", derive(Serialize))] pub struct Texture { - #[serde(skip)] + #[cfg_attr(feature = "capture2", serde(skip))] id: gl::GLuint, target: gl::GLuint, layer_count: i32, @@ -435,9 +436,9 @@ pub struct Texture { height: u32, filter: TextureFilter, render_target: Option, - #[serde(skip)] + #[cfg_attr(feature = "capture2", serde(skip))] fbo_ids: Vec, - #[serde(skip)] + #[cfg_attr(feature = "capture2", serde(skip))] depth_rb: Option, } diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index ab45111db9..b2ef7ac031 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -24,6 +24,7 @@ use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties}; use tiling::CompositeOps; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct FrameId(pub u32); static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { diff --git a/webrender/src/freelist.rs b/webrender/src/freelist.rs index fef03f2b94..9ce9a3807d 100644 --- a/webrender/src/freelist.rs +++ b/webrender/src/freelist.rs @@ -14,6 +14,7 @@ use util::recycle_vec; struct Epoch(u32); #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct FreeListHandle { index: u32, epoch: Epoch, @@ -31,7 +32,7 @@ impl FreeListHandle { } impl Clone for WeakFreeListHandle { - fn clone(&self) -> WeakFreeListHandle { + fn clone(&self) -> Self { WeakFreeListHandle { index: self.index, epoch: self.epoch, @@ -48,12 +49,14 @@ pub struct WeakFreeListHandle { _marker: PhantomData, } +#[cfg_attr(feature = "capture2", derive(Serialize))] struct Slot { next: Option, epoch: Epoch, value: Option, } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct FreeList { slots: Vec>, free_list_head: Option, @@ -65,7 +68,7 @@ pub enum UpsertResult { } impl FreeList { - pub fn new() -> FreeList { + pub fn new() -> Self { FreeList { slots: Vec::new(), free_list_head: None, diff --git a/webrender/src/glyph_cache.rs b/webrender/src/glyph_cache.rs index bf0b45eac3..646a214b25 100644 --- a/webrender/src/glyph_cache.rs +++ b/webrender/src/glyph_cache.rs @@ -9,6 +9,7 @@ use resource_cache::ResourceClassCache; use std::sync::Arc; use texture_cache::TextureCacheHandle; +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct CachedGlyphInfo { pub texture_cache_handle: TextureCacheHandle, pub glyph_bytes: Arc>, @@ -20,6 +21,7 @@ pub struct CachedGlyphInfo { pub type GlyphKeyCache = ResourceClassCache>; +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GlyphCache { pub glyph_key_caches: FastHashMap, } diff --git a/webrender/src/glyph_rasterizer.rs b/webrender/src/glyph_rasterizer.rs index f47cfd4a72..7ea644b283 100644 --- a/webrender/src/glyph_rasterizer.rs +++ b/webrender/src/glyph_rasterizer.rs @@ -597,6 +597,7 @@ impl FontContext { } #[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GlyphRequest { pub key: GlyphKey, pub font: FontInstance, diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index 79a034cc22..aa83858acf 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -38,6 +38,7 @@ const FRAMES_BEFORE_EVICTION: usize = 10; const NEW_ROWS_PER_RESIZE: u32 = 512; #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct Epoch(u32); impl Epoch { @@ -47,6 +48,7 @@ impl Epoch { } #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct CacheLocation { block_index: BlockIndex, epoch: Epoch, @@ -54,12 +56,13 @@ struct CacheLocation { /// A single texel in RGBAF32 texture - 16 bytes. #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GpuBlockData { pub data: [f32; 4], } impl GpuBlockData { - pub fn empty() -> GpuBlockData { + pub fn empty() -> Self { GpuBlockData { data: [0.0; 4] } } } @@ -109,6 +112,7 @@ pub trait ToGpuBlocks { // A handle to a GPU resource. #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GpuCacheHandle { location: Option, } @@ -158,6 +162,7 @@ impl Add for GpuCacheAddress { // An entry in a free-list of blocks in the GPU cache. #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct Block { // The location in the cache of this block. address: GpuCacheAddress, @@ -183,9 +188,11 @@ impl Block { } #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct BlockIndex(usize); // A row in the cache texture. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct Row { // The fixed size of blocks that this row supports. // Each row becomes a slab allocator for a fixed block size. @@ -206,6 +213,7 @@ impl Row { // this frame. The list of updates is created by the render backend // during frame construction. It's passed to the render thread // where GL commands can be applied. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum GpuCacheUpdate { Copy { block_index: usize, @@ -227,6 +235,7 @@ pub struct GpuCacheUpdateList { // Holds the free lists of fixed size blocks. Mostly // just serves to work around the borrow checker. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct FreeBlockLists { free_list_1: Option, free_list_2: Option, @@ -277,6 +286,7 @@ impl FreeBlockLists { } // CPU-side representation of the GPU resource cache texture. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct Texture { // Current texture height height: u32, @@ -491,6 +501,7 @@ impl<'a> Drop for GpuDataRequest<'a> { /// The main LRU cache interface. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct GpuCache { /// Current frame ID. frame_id: FrameId, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 085abc599c..fe4a6d153f 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -104,6 +104,7 @@ pub struct TextureUpdate { pub op: TextureUpdateOp, } +#[derive(Default)] pub struct TextureUpdateList { pub updates: Vec, } diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index c81f72fe39..65d0e83b68 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -23,7 +23,7 @@ use rayon::ThreadPool; use record::ApiRecordingReceiver; use resource_cache::ResourceCache; #[cfg(feature = "capture")] -use resource_cache::PlainResources; +use resource_cache::{PlainCacheOwn, PlainResources}; use scene::Scene; #[cfg(feature = "serialize")] use serde::{Serialize, Deserialize}; @@ -177,12 +177,13 @@ static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT; #[cfg(feature = "capture")] #[derive(Serialize, Deserialize)] -struct PlainRenderBackend { +struct PlainRenderBackend { default_device_pixel_ratio: f32, enable_render_on_scroll: bool, frame_config: FrameBuilderConfig, documents: FastHashMap, resources: PlainResources, + cache: Option, } /// The render backend is responsible for transforming high level display lists into @@ -587,7 +588,7 @@ impl RenderBackend { #[cfg(feature = "capture")] DebugCommand::SaveCapture(root) => { let config = CaptureConfig::new(root, CaptureBits::SCENE); - let deferred = self.save_capture(&config); + let deferred = self.save_capture(&config, &mut profile_counters); ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.root, deferred)) }, #[cfg(feature = "capture")] @@ -815,14 +816,30 @@ impl ToDebugString for SpecificDisplayItem { #[cfg(feature = "capture")] impl RenderBackend { // Note: the mutable `self` is only needed here for resolving blob images - fn save_capture(&mut self, config: &CaptureConfig) -> Vec { + fn save_capture( + &mut self, + config: &CaptureConfig, + profile_counters: &mut BackendProfileCounters, + ) -> Vec { info!("capture: saving {:?}", config.root); let (resources, deferred) = self.resource_cache.save_capture(&config.root); - for (&id, doc) in &self.documents { + for (&id, doc) in &mut self.documents { info!("\tdocument {:?}", id); - let file_name = format!("scene-{}-{}", (id.0).0, id.1); - config.serialize(&doc.scene, file_name); + if config.bits.contains(CaptureBits::SCENE) { + let file_name = format!("scene-{}-{}", (id.0).0, id.1); + config.serialize(&doc.scene, file_name); + } + if config.bits.contains(CaptureBits::FRAME) { + let rendered_document = doc.render( + &mut self.resource_cache, + &mut self.gpu_cache, + &mut profile_counters.resources, + ); + //TODO: write down full `RenderedDocument`? + let file_name = format!("frame-{}-{}", (id.0).0, id.1); + config.serialize(&rendered_document.frame, file_name); + } } info!("\tbackend"); @@ -835,6 +852,11 @@ impl RenderBackend { .map(|(id, doc)| (*id, doc.view.clone())) .collect(), resources, + cache: if config.bits.contains(CaptureBits::FRAME) { + Some(self.resource_cache.to_cache()) + } else { + None + }, }; config.serialize(&serial, "backend"); @@ -848,7 +870,7 @@ impl RenderBackend { profile_counters: &mut BackendProfileCounters, ) { info!("capture: loading {:?}", config.root); - let backend: PlainRenderBackend = config.deserialize("backend"); + let backend: PlainRenderBackend = config.deserialize("backend"); // Note: it would be great to have RenderBackend to be split // rather explicitly on what's used before and after scene building diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index e9a2ce5b34..d69c4c6aaf 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -118,19 +118,22 @@ impl ImageTemplates { } } +#[cfg_attr(feature = "capture2", derive(Serialize))] struct CachedImageInfo { texture_cache_handle: TextureCacheHandle, epoch: Epoch, } #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub enum ResourceClassCacheError { OverLimitSize, } pub type ResourceCacheResult = Result; -pub struct ResourceClassCache { +#[cfg_attr(feature = "capture2", derive(Serialize))] +pub struct ResourceClassCache { resources: FastHashMap>, } @@ -183,6 +186,7 @@ where #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct ImageRequest { key: ImageKey, rendering: ImageRendering, @@ -218,6 +222,8 @@ impl BlobImageResources for Resources { } } +pub type GlyphDimensionsCache = FastHashMap>; + pub struct ResourceCache { cached_glyphs: GlyphCache, cached_images: ImageCache, @@ -230,7 +236,7 @@ pub struct ResourceCache { texture_cache: TextureCache, // TODO(gw): We should expire (parts of) this cache semi-regularly! - cached_glyph_dimensions: FastHashMap>, + cached_glyph_dimensions: GlyphDimensionsCache, glyph_rasterizer: GlyphRasterizer, // The set of images that aren't present or valid in the texture cache, @@ -993,8 +999,39 @@ pub struct PlainResources { image_templates: FastHashMap, } +#[cfg(feature = "capture")] +#[derive(Serialize)] +pub struct PlainCacheRef<'a> { + cached_glyphs: &'a GlyphCache, + cached_images: &'a ImageCache, + current_frame_id: FrameId, + texture_cache: &'a TextureCache, + cached_glyph_dimensions: &'a GlyphDimensionsCache, +} + +#[cfg(feature = "capture")] +#[derive(Deserialize)] +pub struct PlainCacheOwn { + //TODO + //cached_glyphs: GlyphCache, + //cached_images: ImageCache, + //current_frame_id: FrameId, + //texture_cache: TextureCache, + //cached_glyph_dimensions: GlyphDimensionsCache, +} + #[cfg(feature = "capture")] impl ResourceCache { + pub fn to_cache(&self) -> PlainCacheRef { + PlainCacheRef { + cached_glyphs: &self.cached_glyphs, + cached_images: &self.cached_images, + current_frame_id: self.current_frame_id, + texture_cache: &self.texture_cache, + cached_glyph_dimensions: &self.cached_glyph_dimensions, + } + } + pub fn save_capture( &mut self, root: &PathBuf ) -> (PlainResources, Vec) { diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index cc40e7e9d0..2768a94623 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -29,13 +29,14 @@ const TEXTURE_REGION_DIMENSIONS: u32 = 512; // Maintains a simple freelist of texture IDs that are mapped // to real API-specific texture IDs in the renderer. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct CacheTextureIdList { free_list: Vec, next_id: usize, } impl CacheTextureIdList { - fn new() -> CacheTextureIdList { + fn new() -> Self { CacheTextureIdList { next_id: 0, free_list: Vec::new(), @@ -63,6 +64,7 @@ impl CacheTextureIdList { // Items in the texture cache can either be standalone textures, // or a sub-rect inside the shared cache. #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] enum EntryKind { Standalone, Cache { @@ -79,6 +81,7 @@ enum EntryKind { // cache. This is stored for each item whether it's in the shared // cache or a standalone texture. #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] struct CacheEntry { // Size the requested item, in device pixels. size: DeviceUintSize, @@ -153,16 +156,18 @@ type WeakCacheEntryHandle = WeakFreeListHandle; // In this case, the cache handle needs to re-upload this item // to the texture cache (see request() below). #[derive(Debug)] +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct TextureCacheHandle { entry: Option, } impl TextureCacheHandle { - pub fn new() -> TextureCacheHandle { + pub fn new() -> Self { TextureCacheHandle { entry: None } } } +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct TextureCache { // A lazily allocated, fixed size, texture array for // each format the texture cache supports. @@ -181,6 +186,7 @@ pub struct TextureCache { // A list of updates that need to be applied to the // texture cache in the rendering thread this frame. + #[cfg_attr(feature = "capture2", serde(skip))] pending_updates: TextureUpdateList, // The current frame ID. Used for cache eviction policies. @@ -801,13 +807,14 @@ impl SlabSize { } // The x/y location within a texture region of an allocation. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct TextureLocation { x: u8, y: u8, } impl TextureLocation { - fn new(x: u32, y: u32) -> TextureLocation { + fn new(x: u32, y: u32) -> Self { debug_assert!(x < 256 && y < 256); TextureLocation { x: x as u8, @@ -818,6 +825,7 @@ impl TextureLocation { // A region is a sub-rect of a texture array layer. // All allocations within a region are of the same size. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct TextureRegion { layer_index: i32, region_size: u32, @@ -829,7 +837,7 @@ struct TextureRegion { } impl TextureRegion { - fn new(region_size: u32, layer_index: i32, origin: DeviceUintPoint) -> TextureRegion { + fn new(region_size: u32, layer_index: i32, origin: DeviceUintPoint) -> Self { TextureRegion { layer_index, region_size, @@ -900,6 +908,7 @@ impl TextureRegion { // A texture array contains a number of texture layers, where // each layer contains one or more regions that can act // as slab allocators. +#[cfg_attr(feature = "capture2", derive(Serialize))] struct TextureArray { filter: TextureFilter, layer_count: usize, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 1438bdc7a4..6280377fef 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -742,6 +742,7 @@ impl CompositeOps { /// A rendering-oriented representation of frame::Frame built by the render backend /// and presented to the renderer. +#[cfg_attr(feature = "capture2", derive(Serialize))] pub struct Frame { pub window_size: DeviceUintSize, pub inner_rect: DeviceUintRect, @@ -749,6 +750,7 @@ pub struct Frame { pub layer: DocumentLayer, pub device_pixel_ratio: f32, pub passes: Vec, + #[cfg_attr(feature = "capture2", serde(default = "FrameProfileCounters::new", skip))] pub profile_counters: FrameProfileCounters, pub node_data: Vec, @@ -757,12 +759,14 @@ pub struct Frame { // List of updates that need to be pushed to the // gpu resource cache. + #[cfg_attr(feature = "capture2", serde(skip))] pub gpu_cache_updates: Option, // List of textures that we don't know about yet // from the backend thread. The render thread // will use a callback to resolve these and // patch the data structures. + #[cfg_attr(feature = "capture2", serde(skip))] pub deferred_resolves: Vec, } From 2f691b93c5a27ca0eda82ab09450848c9075a53c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 11 Jan 2018 22:17:16 -0500 Subject: [PATCH 04/16] Capture: refactored resource cache serialization, separate glyph blobs --- webrender/src/capture.rs | 1 + webrender/src/glyph_cache.rs | 15 +++-- webrender/src/render_backend.rs | 24 +++---- webrender/src/resource_cache.rs | 113 +++++++++++++++++++++++--------- webrender/src/texture_cache.rs | 2 +- 5 files changed, 108 insertions(+), 47 deletions(-) diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index 61a227e5ce..326b1f61af 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; bitflags!{ pub struct CaptureBits: u8 { const SCENE = 0x1; + #[cfg(feature = "capture2")] const FRAME = 0x2; } } diff --git a/webrender/src/glyph_cache.rs b/webrender/src/glyph_cache.rs index 646a214b25..9883bde231 100644 --- a/webrender/src/glyph_cache.rs +++ b/webrender/src/glyph_cache.rs @@ -10,24 +10,31 @@ use std::sync::Arc; use texture_cache::TextureCacheHandle; #[cfg_attr(feature = "capture2", derive(Serialize))] -pub struct CachedGlyphInfo { +pub struct GenericCachedGlyphInfo { pub texture_cache_handle: TextureCacheHandle, - pub glyph_bytes: Arc>, + pub glyph_bytes: D, pub size: DeviceUintSize, pub offset: DevicePoint, pub scale: f32, pub format: GlyphFormat, } +pub type CachedGlyphInfo = GenericCachedGlyphInfo>>; pub type GlyphKeyCache = ResourceClassCache>; -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg(feature = "capture2")] +pub type PlainCachedGlyphInfo = GenericCachedGlyphInfo; +#[cfg(feature = "capture2")] +pub type PlainGlyphKeyCache = ResourceClassCache>; +#[cfg(feature = "capture2")] +pub type PlainGlyphCache<'a> = FastHashMap<&'a FontInstance, PlainGlyphKeyCache>; + pub struct GlyphCache { pub glyph_key_caches: FastHashMap, } impl GlyphCache { - pub fn new() -> GlyphCache { + pub fn new() -> Self { GlyphCache { glyph_key_caches: FastHashMap::default(), } diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 65d0e83b68..3259a1af0a 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -177,13 +177,12 @@ static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT; #[cfg(feature = "capture")] #[derive(Serialize, Deserialize)] -struct PlainRenderBackend { +struct PlainRenderBackend { default_device_pixel_ratio: f32, enable_render_on_scroll: bool, frame_config: FrameBuilderConfig, documents: FastHashMap, resources: PlainResources, - cache: Option, } /// The render backend is responsible for transforming high level display lists into @@ -587,7 +586,7 @@ impl RenderBackend { } #[cfg(feature = "capture")] DebugCommand::SaveCapture(root) => { - let config = CaptureConfig::new(root, CaptureBits::SCENE); + let config = CaptureConfig::new(root, CaptureBits::FRAME); let deferred = self.save_capture(&config, &mut profile_counters); ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.root, deferred)) }, @@ -595,7 +594,7 @@ impl RenderBackend { DebugCommand::LoadCapture(root) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; - let config = CaptureConfig::new(root, CaptureBits::SCENE); + let config = CaptureConfig::new(root, CaptureBits::FRAME); self.load_capture(&config, &mut profile_counters); ResultMsg::DebugOutput(DebugOutput::LoadCapture) }, @@ -843,7 +842,7 @@ impl RenderBackend { } info!("\tbackend"); - let serial = PlainRenderBackend { + let backend = PlainRenderBackend { default_device_pixel_ratio: self.default_device_pixel_ratio, enable_render_on_scroll: self.enable_render_on_scroll, frame_config: self.frame_config.clone(), @@ -852,14 +851,15 @@ impl RenderBackend { .map(|(id, doc)| (*id, doc.view.clone())) .collect(), resources, - cache: if config.bits.contains(CaptureBits::FRAME) { - Some(self.resource_cache.to_cache()) - } else { - None - }, }; - config.serialize(&serial, "backend"); + config.serialize(&backend, "backend"); + + if config.bits.contains(CaptureBits::FRAME) { + info!("\tcache"); + let caches = self.resource_cache.save_caches(&config.root); + config.serialize(&caches, "cache"); + } deferred } @@ -870,7 +870,7 @@ impl RenderBackend { profile_counters: &mut BackendProfileCounters, ) { info!("capture: loading {:?}", config.root); - let backend: PlainRenderBackend = config.deserialize("backend"); + let backend: PlainRenderBackend = config.deserialize("backend"); // Note: it would be great to have RenderBackend to be split // rather explicitly on what's used before and after scene building diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index d69c4c6aaf..ae90d1ed92 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -15,10 +15,12 @@ use api::{TileOffset, TileSize}; use api::{NativeFontHandle}; use app_units::Au; #[cfg(feature = "capture")] -use capture::ExternalCaptureImage; +use capture::{ExternalCaptureImage}; use device::TextureFilter; use frame::FrameId; use glyph_cache::GlyphCache; +#[cfg(feature = "capture")] +use glyph_cache::{PlainGlyphCache, PlainCachedGlyphInfo}; use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest}; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList}; @@ -125,7 +127,7 @@ struct CachedImageInfo { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture2", derive(Clone, Serialize))] pub enum ResourceClassCacheError { OverLimitSize, } @@ -1002,11 +1004,11 @@ pub struct PlainResources { #[cfg(feature = "capture")] #[derive(Serialize)] pub struct PlainCacheRef<'a> { - cached_glyphs: &'a GlyphCache, - cached_images: &'a ImageCache, current_frame_id: FrameId, - texture_cache: &'a TextureCache, - cached_glyph_dimensions: &'a GlyphDimensionsCache, + glyphs: PlainGlyphCache<'a>, + glyph_dimensions: &'a GlyphDimensionsCache, + images: &'a ImageCache, + textures: &'a TextureCache, } #[cfg(feature = "capture")] @@ -1022,16 +1024,6 @@ pub struct PlainCacheOwn { #[cfg(feature = "capture")] impl ResourceCache { - pub fn to_cache(&self) -> PlainCacheRef { - PlainCacheRef { - cached_glyphs: &self.cached_glyphs, - cached_images: &self.cached_images, - current_frame_id: self.current_frame_id, - texture_cache: &self.texture_cache, - cached_glyph_dimensions: &self.cached_glyph_dimensions, - } - } - pub fn save_capture( &mut self, root: &PathBuf ) -> (PlainResources, Vec) { @@ -1041,38 +1033,36 @@ impl ResourceCache { info!("saving resource cache"); let res = &self.resources; if !root.is_dir() { - fs::create_dir_all(&root).unwrap() + fs::create_dir_all(root).unwrap() } - let path_fonts = root.clone().join("fonts"); + let path_fonts = root.join("fonts"); if !path_fonts.is_dir() { fs::create_dir(&path_fonts).unwrap(); } - let path_images = root.clone().join("images"); + let path_images = root.join("images"); if !path_images.is_dir() { fs::create_dir(&path_images).unwrap(); } - let path_blobs = root.clone().join("blobs"); + let path_blobs = root.join("blobs"); if !path_blobs.is_dir() { fs::create_dir(&path_blobs).unwrap(); } info!("\tfont templates"); let mut font_paths = FastHashMap::default(); - let mut num_fonts = 0; for template in res.font_templates.values() { let data: &[u8] = match *template { FontTemplate::Raw(ref arc, _) => arc, FontTemplate::Native(_) => continue, }; + let font_id = res.font_templates.len() + 1; let entry = match font_paths.entry(data.as_ptr()) { Entry::Occupied(_) => continue, Entry::Vacant(e) => e, }; - num_fonts += 1; - let file_name = format!("{}.raw", num_fonts); + let file_name = format!("{}.raw", font_id); let short_path = format!("fonts/{}", file_name); - let full_path = path_fonts.clone().join(&file_name); - fs::File::create(full_path) + fs::File::create(path_fonts.join(file_name)) .expect(&format!("Unable to create {}", short_path)) .write_all(data) .unwrap(); @@ -1082,23 +1072,21 @@ impl ResourceCache { info!("\timage templates"); let mut image_paths = FastHashMap::default(); let mut other_paths = FastHashMap::default(); - let mut num_images = 0; let mut external_images = Vec::new(); for (&key, template) in res.image_templates.images.iter() { let desc = &template.descriptor; match template.data { ImageData::Raw(ref arc) => { + let image_id = image_paths.len() + 1; let entry = match image_paths.entry(arc.as_ptr()) { Entry::Occupied(_) => continue, Entry::Vacant(e) => e, }; //TODO: option to save as PNG - num_images += 1; - let file_name = format!("{}.raw", num_images); + let file_name = format!("{}.raw", image_id); let short_path = format!("images/{}", file_name); - let full_path = path_images.clone().join(&file_name); - fs::File::create(full_path) + fs::File::create(path_images.join(file_name)) .expect(&format!("Unable to create {}", short_path)) .write_all(&*arc) .unwrap(); @@ -1184,6 +1172,71 @@ impl ResourceCache { (resources, external_images) } + pub fn save_caches(&self, root: &PathBuf) -> PlainCacheRef { + use std::io::Write; + use std::fs; + + let path_glyphs = root.join("glyphs"); + if !path_glyphs.is_dir() { + fs::create_dir(&path_glyphs).unwrap(); + } + + info!("\tcached glyphs"); + let mut glyph_paths = FastHashMap::default(); + for cache in self.cached_glyphs.glyph_key_caches.values() { + for result in cache.resources.values() { + let arc = match *result { + Ok(Some(ref info)) => &info.glyph_bytes, + Ok(None) | Err(_) => continue, + }; + let glyph_id = glyph_paths.len() + 1; + let entry = match glyph_paths.entry(arc.as_ptr()) { + Entry::Occupied(_) => continue, + Entry::Vacant(e) => e, + }; + + let file_name = format!("{}.raw", glyph_id); + let short_path = format!("glyphs/{}", file_name); + fs::File::create(path_glyphs.join(&file_name)) + .expect(&format!("Unable to create {}", short_path)) + .write_all(&*arc) + .unwrap(); + entry.insert(short_path); + } + } + + PlainCacheRef { + current_frame_id: self.current_frame_id, + glyphs: self.cached_glyphs.glyph_key_caches + .iter() + .map(|(font_instance, cache)| { + (font_instance, ResourceClassCache { + resources: cache.resources + .iter() + .map(|(key, result)| + (key.clone(), match *result { + Ok(Some(ref info)) => Ok(Some(PlainCachedGlyphInfo { + texture_cache_handle: info.texture_cache_handle.clone(), + glyph_bytes: glyph_paths[&info.glyph_bytes.as_ptr()].clone(), + size: info.size, + offset: info.offset, + scale: info.scale, + format: info.format, + })), + Ok(None) => Ok(None), + Err(ref e) => Err(e.clone()), + }) + ) + .collect() + }) + }) + .collect(), + glyph_dimensions: &self.cached_glyph_dimensions, + images: &self.cached_images, + textures: &self.texture_cache, + } + } + pub fn load_capture(&mut self, resources: PlainResources, root: &PathBuf) { use std::fs::File; use std::io::Read; diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 2768a94623..b15472e89b 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -156,7 +156,7 @@ type WeakCacheEntryHandle = WeakFreeListHandle; // In this case, the cache handle needs to re-upload this item // to the texture cache (see request() below). #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture2", derive(Clone, Serialize))] pub struct TextureCacheHandle { entry: Option, } From 02bc8895dcfc0f97dd48e193ed487bf0df92657a Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 12 Jan 2018 08:59:50 -0500 Subject: [PATCH 05/16] Capture: deserialization derives, glyphs support for load_capture --- webrender/Cargo.toml | 1 - webrender/src/batch.rs | 26 +++--- webrender/src/box_shadow.rs | 2 +- webrender/src/capture.rs | 8 +- webrender/src/clip_scroll_tree.rs | 2 +- webrender/src/device.rs | 12 +-- webrender/src/frame.rs | 2 +- webrender/src/freelist.rs | 10 +- webrender/src/glyph_cache.rs | 12 ++- webrender/src/glyph_rasterizer.rs | 4 +- webrender/src/gpu_cache.rs | 24 ++--- webrender/src/gpu_types.rs | 14 +-- webrender/src/internal_types.rs | 8 +- webrender/src/picture.rs | 2 +- webrender/src/prim_store.rs | 2 +- webrender/src/render_backend.rs | 18 ++-- webrender/src/render_task.rs | 24 ++--- webrender/src/renderer.rs | 4 +- webrender/src/resource_cache.rs | 143 +++++++++++++++++++---------- webrender/src/texture_allocator.rs | 4 +- webrender/src/texture_cache.rs | 20 ++-- webrender/src/tiling.rs | 30 +++--- webrender/src/util.rs | 2 +- wrench/Cargo.toml | 2 +- 24 files changed, 215 insertions(+), 161 deletions(-) diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 5135ca1ae6..e3e36bdf94 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -12,7 +12,6 @@ freetype-lib = ["freetype/servo-freetype-sys"] profiler = ["thread_profiler/thread_profiler"] debugger = ["ws", "serde_json", "serde", "image", "base64"] capture = ["webrender_api/debug-serialization", "ron", "serde"] -capture2 = ["capture"] [dependencies] app_units = "0.6" diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 07c48d6848..df57aae3ba 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -35,7 +35,7 @@ use util::{MatrixHelpers, TransformedRectKind}; const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32); #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum TransformBatchKind { TextRun(GlyphFormat), Image(ImageBufferKind), @@ -48,7 +48,7 @@ pub enum TransformBatchKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum BrushImageSourceKind { Alpha, Color, @@ -65,7 +65,7 @@ impl BrushImageSourceKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum BrushBatchKind { Image(BrushImageSourceKind), Solid, @@ -73,7 +73,7 @@ pub enum BrushBatchKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum BatchKind { Composite { task_id: RenderTaskId, @@ -90,7 +90,7 @@ pub enum BatchKind { /// Optional textures that can be used as a source in the shaders. /// Textures that are not used by the batch are equal to TextureId::invalid(). #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct BatchTextures { pub colors: [SourceTexture; 3], } @@ -120,7 +120,7 @@ impl BatchTextures { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct AlphaPrimitiveBatch { pub key: BatchKey, pub instances: Vec, @@ -138,7 +138,7 @@ impl AlphaPrimitiveBatch { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct OpaquePrimitiveBatch { pub key: BatchKey, pub instances: Vec, @@ -154,7 +154,7 @@ impl OpaquePrimitiveBatch { } #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct BatchKey { pub kind: BatchKind, pub blend_mode: BlendMode, @@ -183,7 +183,7 @@ fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool { t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2 } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct AlphaBatchList { pub batches: Vec, } @@ -261,7 +261,7 @@ impl AlphaBatchList { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct OpaqueBatchList { pub pixel_area_threshold_for_new_batch: i32, pub batches: Vec, @@ -327,7 +327,7 @@ impl OpaqueBatchList { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct BatchList { pub alpha_batch_list: AlphaBatchList, pub opaque_batch_list: OpaqueBatchList, @@ -374,7 +374,7 @@ impl BatchList { } /// Encapsulates the logic of building batches for items that are blended. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct AlphaBatcher { pub batch_list: BatchList, pub text_run_cache_prims: FastHashMap>, @@ -1447,7 +1447,7 @@ fn make_polygon( /// Batcher managing draw calls into the clip mask (in the RT cache). #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct ClipBatcher { /// Rectangle draws fill up the rectangles with rounded corners. pub rectangles: Vec, diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 3cc0b503ac..a650f9fe74 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -29,7 +29,7 @@ pub const MAX_BLUR_RADIUS : f32 = 300.; pub const MASK_CORNER_PADDING: f32 = 4.0; #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct BoxShadowCacheKey { pub width: Au, pub height: Au, diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index 326b1f61af..e8a1f96719 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; bitflags!{ pub struct CaptureBits: u8 { const SCENE = 0x1; - #[cfg(feature = "capture2")] + #[cfg(feature = "capture")] const FRAME = 0x2; } } @@ -50,17 +50,17 @@ impl CaptureConfig { .unwrap(); } - pub fn deserialize(&self, name: P) -> T + pub fn deserialize(root: &PathBuf, name: P) -> Option where T: for<'a> Deserialize<'a>, P: AsRef, { let mut string = String::new(); - let path = self.root + let path = root .join(name) .with_extension("ron"); File::open(path) - .unwrap() + .ok()? .read_to_string(&mut string) .unwrap(); de::from_str(&string) diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index 9907075bf5..e11f47e00e 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -23,7 +23,7 @@ pub type ScrollStates = FastHashMap; /// system are the same or are in the same axis-aligned space. This allows /// for optimizing mask generation. #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct CoordinateSystemId(pub u32); impl CoordinateSystemId { diff --git a/webrender/src/device.rs b/webrender/src/device.rs index e5e86985aa..d3ddc85194 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -26,7 +26,7 @@ use std::thread; const WORK_AROUND_TEX_IMAGE: bool = cfg!(windows); #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct FrameId(usize); impl FrameId { @@ -86,7 +86,7 @@ impl TextureTarget { } #[derive(Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum TextureFilter { Nearest, Linear, @@ -425,9 +425,9 @@ impl ExternalTexture { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct Texture { - #[cfg_attr(feature = "capture2", serde(skip))] + #[cfg_attr(feature = "capture", serde(skip))] id: gl::GLuint, target: gl::GLuint, layer_count: i32, @@ -436,9 +436,9 @@ pub struct Texture { height: u32, filter: TextureFilter, render_target: Option, - #[cfg_attr(feature = "capture2", serde(skip))] + #[cfg_attr(feature = "capture", serde(skip))] fbo_ids: Vec, - #[cfg_attr(feature = "capture2", serde(skip))] + #[cfg_attr(feature = "capture", serde(skip))] depth_rb: Option, } diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index b2ef7ac031..e9d5098c3e 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -24,7 +24,7 @@ use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties}; use tiling::CompositeOps; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct FrameId(pub u32); static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { diff --git a/webrender/src/freelist.rs b/webrender/src/freelist.rs index 9ce9a3807d..f7c0c35acc 100644 --- a/webrender/src/freelist.rs +++ b/webrender/src/freelist.rs @@ -10,11 +10,11 @@ use util::recycle_vec; // retain() style functionality. #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct Epoch(u32); #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct FreeListHandle { index: u32, epoch: Epoch, @@ -42,21 +42,21 @@ impl Clone for WeakFreeListHandle { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct WeakFreeListHandle { index: u32, epoch: Epoch, _marker: PhantomData, } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct Slot { next: Option, epoch: Epoch, value: Option, } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct FreeList { slots: Vec>, free_list_head: Option, diff --git a/webrender/src/glyph_cache.rs b/webrender/src/glyph_cache.rs index 9883bde231..c28fc34e47 100644 --- a/webrender/src/glyph_cache.rs +++ b/webrender/src/glyph_cache.rs @@ -9,7 +9,7 @@ use resource_cache::ResourceClassCache; use std::sync::Arc; use texture_cache::TextureCacheHandle; -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GenericCachedGlyphInfo { pub texture_cache_handle: TextureCacheHandle, pub glyph_bytes: D, @@ -22,12 +22,14 @@ pub struct GenericCachedGlyphInfo { pub type CachedGlyphInfo = GenericCachedGlyphInfo>>; pub type GlyphKeyCache = ResourceClassCache>; -#[cfg(feature = "capture2")] +#[cfg(feature = "capture")] pub type PlainCachedGlyphInfo = GenericCachedGlyphInfo; -#[cfg(feature = "capture2")] +#[cfg(feature = "capture")] pub type PlainGlyphKeyCache = ResourceClassCache>; -#[cfg(feature = "capture2")] -pub type PlainGlyphCache<'a> = FastHashMap<&'a FontInstance, PlainGlyphKeyCache>; +#[cfg(feature = "capture")] +pub type PlainGlyphCacheRef<'a> = FastHashMap<&'a FontInstance, PlainGlyphKeyCache>; +#[cfg(feature = "capture")] +pub type PlainGlyphCacheOwn = FastHashMap; pub struct GlyphCache { pub glyph_key_caches: FastHashMap, diff --git a/webrender/src/glyph_rasterizer.rs b/webrender/src/glyph_rasterizer.rs index 7ea644b283..65b2b1cfee 100644 --- a/webrender/src/glyph_rasterizer.rs +++ b/webrender/src/glyph_rasterizer.rs @@ -219,7 +219,7 @@ impl FontInstance { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] #[allow(dead_code)] pub enum GlyphFormat { Alpha, @@ -597,7 +597,7 @@ impl FontContext { } #[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GlyphRequest { pub key: GlyphKey, pub font: FontInstance, diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index aa83858acf..c61939a459 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -38,7 +38,7 @@ const FRAMES_BEFORE_EVICTION: usize = 10; const NEW_ROWS_PER_RESIZE: u32 = 512; #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct Epoch(u32); impl Epoch { @@ -48,7 +48,7 @@ impl Epoch { } #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct CacheLocation { block_index: BlockIndex, epoch: Epoch, @@ -56,7 +56,7 @@ struct CacheLocation { /// A single texel in RGBAF32 texture - 16 bytes. #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct GpuBlockData { pub data: [f32; 4], } @@ -112,7 +112,7 @@ pub trait ToGpuBlocks { // A handle to a GPU resource. #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GpuCacheHandle { location: Option, } @@ -127,7 +127,7 @@ impl GpuCacheHandle { // as part of the primitive instances, to allow the vertex // shader to fetch the specific data. #[derive(Copy, Debug, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct GpuCacheAddress { pub u: u16, pub v: u16, @@ -162,7 +162,7 @@ impl Add for GpuCacheAddress { // An entry in a free-list of blocks in the GPU cache. #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] struct Block { // The location in the cache of this block. address: GpuCacheAddress, @@ -188,11 +188,11 @@ impl Block { } #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct BlockIndex(usize); // A row in the cache texture. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] struct Row { // The fixed size of blocks that this row supports. // Each row becomes a slab allocator for a fixed block size. @@ -213,7 +213,7 @@ impl Row { // this frame. The list of updates is created by the render backend // during frame construction. It's passed to the render thread // where GL commands can be applied. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum GpuCacheUpdate { Copy { block_index: usize, @@ -235,7 +235,7 @@ pub struct GpuCacheUpdateList { // Holds the free lists of fixed size blocks. Mostly // just serves to work around the borrow checker. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] struct FreeBlockLists { free_list_1: Option, free_list_2: Option, @@ -286,7 +286,7 @@ impl FreeBlockLists { } // CPU-side representation of the GPU resource cache texture. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] struct Texture { // Current texture height height: u32, @@ -501,7 +501,7 @@ impl<'a> Drop for GpuDataRequest<'a> { /// The main LRU cache interface. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct GpuCache { /// Current frame ID. frame_id: FrameId, diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index db586848de..3f4c0cf8ff 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -11,7 +11,7 @@ use render_task::RenderTaskAddress; #[repr(i32)] #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum BlurDirection { Horizontal = 0, Vertical, @@ -19,7 +19,7 @@ pub enum BlurDirection { #[derive(Debug)] #[repr(C)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct BlurInstance { pub task_address: RenderTaskAddress, pub src_task_address: RenderTaskAddress, @@ -30,7 +30,7 @@ pub struct BlurInstance { /// Could be an image or a rectangle, which defines the /// way `address` is treated. #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] #[repr(C)] pub struct ClipMaskInstance { pub render_task_address: RenderTaskAddress, @@ -42,7 +42,7 @@ pub struct ClipMaskInstance { // 32 bytes per instance should be enough for anyone! #[derive(Debug, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct PrimitiveInstance { data: [i32; 8], } @@ -193,12 +193,12 @@ pub enum BrushImageKind { } #[derive(Copy, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] #[repr(C)] pub struct ClipScrollNodeIndex(pub u32); #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] #[repr(C)] pub struct ClipScrollNodeData { pub transform: LayerToWorldTransform, @@ -221,7 +221,7 @@ impl ClipScrollNodeData { pub struct ClipChainRectIndex(pub usize); #[derive(Copy, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] #[repr(C)] pub enum PictureType { Image = 1, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index fe4a6d153f..e7515d32e3 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -33,11 +33,11 @@ pub type FastHashSet = HashSet>; // map from cache texture ID to native texture. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct CacheTextureId(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderPassIndex(pub usize); // Represents the source for a texture. @@ -47,7 +47,7 @@ pub struct RenderPassIndex(pub usize); // native texture ID. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum SourceTexture { Invalid, TextureCache(CacheTextureId), @@ -64,7 +64,7 @@ pub const ORTHO_NEAR_PLANE: f32 = -1000000.0; pub const ORTHO_FAR_PLANE: f32 = 1000000.0; #[derive(Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTargetInfo { pub has_depth: bool, } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 974005c1a0..83cfc0831a 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -43,7 +43,7 @@ pub enum PictureCompositeMode { /// Configure whether the content to be drawn by a picture /// in local space rasterization or the screen space. #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum ContentOrigin { Local(LayerPoint), Screen(DeviceIntPoint), diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index c4c753d40c..c31af9a6cb 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -140,7 +140,7 @@ pub struct DeferredResolve { pub struct SpecificPrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct PrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 3259a1af0a..978b3ef7a5 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -29,6 +29,8 @@ use scene::Scene; use serde::{Serialize, Deserialize}; #[cfg(feature = "debugger")] use serde_json; +#[cfg(feature = "capture")] +use std::path::PathBuf; use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; use std::sync::Arc; use std::sync::mpsc::Sender; @@ -594,8 +596,7 @@ impl RenderBackend { DebugCommand::LoadCapture(root) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; - let config = CaptureConfig::new(root, CaptureBits::FRAME); - self.load_capture(&config, &mut profile_counters); + self.load_capture(&root, &mut profile_counters); ResultMsg::DebugOutput(DebugOutput::LoadCapture) }, DebugCommand::EnableDualSourceBlending(enable) => { @@ -866,17 +867,19 @@ impl RenderBackend { fn load_capture( &mut self, - config: &CaptureConfig, + root: &PathBuf, profile_counters: &mut BackendProfileCounters, ) { - info!("capture: loading {:?}", config.root); - let backend: PlainRenderBackend = config.deserialize("backend"); + info!("capture: loading {:?}", root); + let backend = CaptureConfig::deserialize::(root, "backend") + .expect("Unable to open backend.ron"); + let caches_maybe = CaptureConfig::deserialize::(root, "cache"); // Note: it would be great to have RenderBackend to be split // rather explicitly on what's used before and after scene building // so that, for example, we never miss anything in the code below: - self.resource_cache.load_capture(backend.resources, &config.root); + self.resource_cache.load_capture(backend.resources, caches_maybe,root); self.gpu_cache = GpuCache::new(); self.documents.clear(); self.default_device_pixel_ratio = backend.default_device_pixel_ratio; @@ -886,7 +889,8 @@ impl RenderBackend { for (id, view) in backend.documents { info!("\tdocument {:?}", id); let file_name = format!("scene-{}-{}", (id.0).0, id.1); - let scene: Scene = config.deserialize(file_name); + let scene = CaptureConfig::deserialize::(root, &file_name) + .expect(&format!("Unable to open {}.ron", file_name)); let mut doc = Document { scene, diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 524fc2f477..2ded76d2c0 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -27,16 +27,16 @@ pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTaskId(pub u32); // TODO(gw): Make private when using GPU cache! #[derive(Debug, Copy, Clone)] #[repr(C)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTaskAddress(pub u32); #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTaskTree { pub tasks: Vec, pub task_data: Vec, @@ -212,7 +212,7 @@ impl ops::IndexMut for RenderTaskTree { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum RenderTaskLocation { Fixed, Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), @@ -220,7 +220,7 @@ pub enum RenderTaskLocation { } #[derive(Debug, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct ClipWorkItem { pub scroll_node_data_index: ClipScrollNodeIndex, pub clip_sources: ClipSourcesWeakHandle, @@ -228,7 +228,7 @@ pub struct ClipWorkItem { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct CacheMaskTask { actual_rect: DeviceIntRect, pub clips: Vec, @@ -236,7 +236,7 @@ pub struct CacheMaskTask { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct PictureTask { pub prim_index: PrimitiveIndex, pub target_kind: RenderTargetKind, @@ -246,7 +246,7 @@ pub struct PictureTask { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct BlurTask { pub blur_std_deviation: f32, pub target_kind: RenderTargetKind, @@ -264,13 +264,13 @@ impl BlurTask { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTaskData { pub data: [f32; FLOATS_PER_RENDER_TASK_INFO], } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum RenderTaskKind { Picture(PictureTask), CacheMask(CacheMaskTask), @@ -281,7 +281,7 @@ pub enum RenderTaskKind { } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum ClearMode { // Applicable to color and alpha targets. Zero, @@ -292,7 +292,7 @@ pub enum ClearMode { } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTask { pub location: RenderTaskLocation, pub children: Vec, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 799801d93a..e18d8f23b0 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -480,7 +480,7 @@ pub struct GraphicsApiInfo { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum ImageBufferKind { Texture2D = 0, TextureRect = 1, @@ -758,7 +758,7 @@ impl SourceTextureResolver { } #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] #[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment. pub enum BlendMode { None, diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index ae90d1ed92..02fb93e712 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -20,7 +20,7 @@ use device::TextureFilter; use frame::FrameId; use glyph_cache::GlyphCache; #[cfg(feature = "capture")] -use glyph_cache::{PlainGlyphCache, PlainCachedGlyphInfo}; +use glyph_cache::{CachedGlyphInfo, PlainGlyphCacheOwn, PlainGlyphCacheRef, PlainCachedGlyphInfo}; use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest}; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList}; @@ -40,7 +40,7 @@ use texture_cache::{TextureCache, TextureCacheHandle}; const DEFAULT_TILE_SIZE: TileSize = 512; -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct GlyphFetchResult { pub index_in_text_run: i32, pub uv_rect_address: GpuCacheAddress, @@ -120,21 +120,21 @@ impl ImageTemplates { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct CachedImageInfo { texture_cache_handle: TextureCacheHandle, epoch: Epoch, } #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Clone, Serialize))] +#[cfg_attr(feature = "capture", derive(Clone, Deserialize, Serialize))] pub enum ResourceClassCacheError { OverLimitSize, } pub type ResourceCacheResult = Result; -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct ResourceClassCache { resources: FastHashMap>, } @@ -188,7 +188,7 @@ where #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct ImageRequest { key: ImageKey, rendering: ImageRendering, @@ -1005,7 +1005,7 @@ pub struct PlainResources { #[derive(Serialize)] pub struct PlainCacheRef<'a> { current_frame_id: FrameId, - glyphs: PlainGlyphCache<'a>, + glyphs: PlainGlyphCacheRef<'a>, glyph_dimensions: &'a GlyphDimensionsCache, images: &'a ImageCache, textures: &'a TextureCache, @@ -1014,12 +1014,11 @@ pub struct PlainCacheRef<'a> { #[cfg(feature = "capture")] #[derive(Deserialize)] pub struct PlainCacheOwn { - //TODO - //cached_glyphs: GlyphCache, - //cached_images: ImageCache, - //current_frame_id: FrameId, - //texture_cache: TextureCache, - //cached_glyph_dimensions: GlyphDimensionsCache, + current_frame_id: FrameId, + glyphs: PlainGlyphCacheOwn, + glyph_dimensions: GlyphDimensionsCache, + images: ImageCache, + textures: TextureCache, } #[cfg(feature = "capture")] @@ -1210,25 +1209,24 @@ impl ResourceCache { glyphs: self.cached_glyphs.glyph_key_caches .iter() .map(|(font_instance, cache)| { - (font_instance, ResourceClassCache { - resources: cache.resources - .iter() - .map(|(key, result)| - (key.clone(), match *result { - Ok(Some(ref info)) => Ok(Some(PlainCachedGlyphInfo { - texture_cache_handle: info.texture_cache_handle.clone(), - glyph_bytes: glyph_paths[&info.glyph_bytes.as_ptr()].clone(), - size: info.size, - offset: info.offset, - scale: info.scale, - format: info.format, - })), - Ok(None) => Ok(None), - Err(ref e) => Err(e.clone()), - }) - ) - .collect() - }) + let resources = cache.resources + .iter() + .map(|(key, result)| { + (key.clone(), match *result { + Ok(Some(ref info)) => Ok(Some(PlainCachedGlyphInfo { + texture_cache_handle: info.texture_cache_handle.clone(), + glyph_bytes: glyph_paths[&info.glyph_bytes.as_ptr()].clone(), + size: info.size, + offset: info.offset, + scale: info.scale, + format: info.format, + })), + Ok(None) => Ok(None), + Err(ref e) => Err(e.clone()), + }) + }) + .collect(); + (font_instance, ResourceClassCache { resources }) }) .collect(), glyph_dimensions: &self.cached_glyph_dimensions, @@ -1237,22 +1235,76 @@ impl ResourceCache { } } - pub fn load_capture(&mut self, resources: PlainResources, root: &PathBuf) { + pub fn load_capture( + &mut self, + resources: PlainResources, + caches: Option, + root: &PathBuf, + ) { use std::fs::File; use std::io::Read; info!("loading resource cache"); - self.cached_glyphs.clear(); - self.cached_images.clear(); - self.cached_render_tasks.clear(); - - self.state = State::Idle; - self.current_frame_id = FrameId(0); + //TODO: pre-load the raw map + let mut raw_map = FastHashMap::>>::default(); - let max_texture_size = self.texture_cache.max_texture_size(); - self.texture_cache = TextureCache::new(max_texture_size); + match caches { + Some(cached) => { + let glyph_key_caches = cached.glyphs + .into_iter() + .map(|(font_instance, rcc)| { + let resources = rcc.resources + .into_iter() + .map(|(key, result)| { + (key, match result { + Ok(Some(info)) => { + let glyph_bytes = match raw_map.entry(info.glyph_bytes) { + Entry::Occupied(e) => { + e.get().clone() + } + Entry::Vacant(e) => { + let mut buffer = Vec::new(); + File::open(root.join(e.key())) + .expect(&format!("Unable to open {}", e.key())) + .read_to_end(&mut buffer) + .unwrap(); + e.insert(Arc::new(buffer)) + .clone() + } + }; + Ok(Some(CachedGlyphInfo { + texture_cache_handle: info.texture_cache_handle, + glyph_bytes, + size: info.size, + offset: info.offset, + scale: info.scale, + format: info.format, + })) + }, + Ok(None) => Ok(None), + Err(e) => Err(e), + }) + }) + .collect(); + (font_instance, ResourceClassCache { resources }) + }) + .collect(); + self.current_frame_id = cached.current_frame_id; + self.cached_glyphs = GlyphCache { glyph_key_caches }; + self.cached_glyph_dimensions = cached.glyph_dimensions; + self.texture_cache = cached.textures; + } + None => { + self.current_frame_id = FrameId(0); + self.cached_glyphs.clear(); + self.cached_glyph_dimensions.clear(); + self.cached_images.clear(); + let max_texture_size = self.texture_cache.max_texture_size(); + self.texture_cache = TextureCache::new(max_texture_size); + } + } - self.cached_glyph_dimensions.clear(); + self.state = State::Idle; self.glyph_rasterizer.reset(); self.pending_image_requests.clear(); @@ -1260,7 +1312,6 @@ impl ResourceCache { res.font_templates.clear(); *res.font_instances.write().unwrap() = resources.font_instances; res.image_templates.images.clear(); - let mut raw_map = FastHashMap::>>::default(); info!("\tfont templates..."); for (key, plain_template) in resources.font_templates { @@ -1271,9 +1322,8 @@ impl ResourceCache { e.get().clone() } Entry::Vacant(e) => { - let path = format!("{}/{}", root.to_string_lossy(), e.key()); let mut buffer = Vec::new(); - File::open(path) + File::open(root.join(e.key())) .expect(&format!("Unable to open {}", e.key())) .read_to_end(&mut buffer) .unwrap(); @@ -1300,9 +1350,8 @@ impl ResourceCache { } Entry::Vacant(e) => { //TODO: consider merging the code path with font loading - let path = format!("{}/{}", root.to_string_lossy(), e.key()); let mut buffer = Vec::new(); - File::open(path) + File::open(root.join(e.key())) .expect(&format!("Unable to open {}", e.key())) .read_to_end(&mut buffer) .unwrap(); diff --git a/webrender/src/texture_allocator.rs b/webrender/src/texture_allocator.rs index 5bd3f75dc7..7a9eeb10a6 100644 --- a/webrender/src/texture_allocator.rs +++ b/webrender/src/texture_allocator.rs @@ -22,7 +22,7 @@ const MINIMUM_LARGE_RECT_SIZE: u32 = 32; /// /// This approach was chosen because of its simplicity, good performance, and easy support for /// dynamic texture deallocation. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct GuillotineAllocator { texture_size: DeviceUintSize, free_list: FreeRectList, @@ -171,7 +171,7 @@ impl GuillotineAllocator { /// A binning free list. Binning is important to avoid sifting through lots of small strips when /// allocating many texture items. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] struct FreeRectList { small: Vec, medium: Vec, diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index b15472e89b..476dbb7ab1 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -29,7 +29,7 @@ const TEXTURE_REGION_DIMENSIONS: u32 = 512; // Maintains a simple freelist of texture IDs that are mapped // to real API-specific texture IDs in the renderer. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct CacheTextureIdList { free_list: Vec, next_id: usize, @@ -64,7 +64,7 @@ impl CacheTextureIdList { // Items in the texture cache can either be standalone textures, // or a sub-rect inside the shared cache. #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] enum EntryKind { Standalone, Cache { @@ -81,7 +81,7 @@ enum EntryKind { // cache. This is stored for each item whether it's in the shared // cache or a standalone texture. #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct CacheEntry { // Size the requested item, in device pixels. size: DeviceUintSize, @@ -156,7 +156,7 @@ type WeakCacheEntryHandle = WeakFreeListHandle; // In this case, the cache handle needs to re-upload this item // to the texture cache (see request() below). #[derive(Debug)] -#[cfg_attr(feature = "capture2", derive(Clone, Serialize))] +#[cfg_attr(feature = "capture", derive(Clone, Deserialize, Serialize))] pub struct TextureCacheHandle { entry: Option, } @@ -167,7 +167,7 @@ impl TextureCacheHandle { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct TextureCache { // A lazily allocated, fixed size, texture array for // each format the texture cache supports. @@ -186,7 +186,7 @@ pub struct TextureCache { // A list of updates that need to be applied to the // texture cache in the rendering thread this frame. - #[cfg_attr(feature = "capture2", serde(skip))] + #[cfg_attr(feature = "capture", serde(skip))] pending_updates: TextureUpdateList, // The current frame ID. Used for cache eviction policies. @@ -807,7 +807,7 @@ impl SlabSize { } // The x/y location within a texture region of an allocation. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct TextureLocation { x: u8, y: u8, @@ -825,7 +825,7 @@ impl TextureLocation { // A region is a sub-rect of a texture array layer. // All allocations within a region are of the same size. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct TextureRegion { layer_index: i32, region_size: u32, @@ -908,7 +908,7 @@ impl TextureRegion { // A texture array contains a number of texture layers, where // each layer contains one or more regions that can act // as slab allocators. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct TextureArray { filter: TextureFilter, layer_count: usize, @@ -923,7 +923,7 @@ impl TextureArray { format: ImageFormat, filter: TextureFilter, layer_count: usize - ) -> TextureArray { + ) -> Self { TextureArray { format, filter, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 6280377fef..dfa9f6f367 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -35,7 +35,7 @@ pub struct ScrollbarPrimitive { } #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTargetIndex(pub usize); pub struct RenderTargetContext<'a> { @@ -47,7 +47,7 @@ pub struct RenderTargetContext<'a> { pub use_dual_source_blending: bool, } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] struct TextureAllocator { // TODO(gw): Replace this with a simpler allocator for // render target allocation - this use case doesn't need @@ -113,13 +113,13 @@ pub trait RenderTarget { } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum RenderTargetKind { Color, // RGBA32 Alpha, // R8 } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderTargetList { screen_size: DeviceIntSize, pub format: ImageFormat, @@ -220,20 +220,20 @@ impl RenderTargetList { /// Storing the task ID allows the renderer to find /// the target rect within the render target that this /// pipeline exists at. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct FrameOutput { pub task_id: RenderTaskId, pub pipeline_id: PipelineId, } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct ScalingInfo { pub src_task_id: RenderTaskId, pub dest_task_id: RenderTaskId, } /// A render target represents a number of rendering operations on a surface. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct ColorRenderTarget { pub alpha_batcher: AlphaBatcher, // List of blur operations to apply for this render target. @@ -368,7 +368,7 @@ impl RenderTarget for ColorRenderTarget { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, pub brush_mask_corners: Vec, @@ -538,7 +538,7 @@ impl RenderTarget for AlphaRenderTarget { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct TextureCacheRenderTarget { pub horizontal_blurs: Vec, } @@ -581,7 +581,7 @@ impl TextureCacheRenderTarget { } } -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum RenderPassKind { MainFramebuffer(ColorRenderTarget), OffScreen { @@ -596,7 +596,7 @@ pub enum RenderPassKind { /// /// A render pass can have several render targets if there wasn't enough space in one /// target to do all of the rendering for that pass. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct RenderPass { pub kind: RenderPassKind, tasks: Vec, @@ -742,7 +742,7 @@ impl CompositeOps { /// A rendering-oriented representation of frame::Frame built by the render backend /// and presented to the renderer. -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub struct Frame { pub window_size: DeviceUintSize, pub inner_rect: DeviceUintRect, @@ -750,7 +750,7 @@ pub struct Frame { pub layer: DocumentLayer, pub device_pixel_ratio: f32, pub passes: Vec, - #[cfg_attr(feature = "capture2", serde(default = "FrameProfileCounters::new", skip))] + #[cfg_attr(feature = "capture", serde(default = "FrameProfileCounters::new", skip))] pub profile_counters: FrameProfileCounters, pub node_data: Vec, @@ -759,14 +759,14 @@ pub struct Frame { // List of updates that need to be pushed to the // gpu resource cache. - #[cfg_attr(feature = "capture2", serde(skip))] + #[cfg_attr(feature = "capture", serde(skip))] pub gpu_cache_updates: Option, // List of textures that we don't know about yet // from the backend thread. The render thread // will use a callback to resolve these and // patch the data structures. - #[cfg_attr(feature = "capture2", serde(skip))] + #[cfg_attr(feature = "capture", serde(skip))] pub deferred_resolves: Vec, } diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 7c677506eb..6c721a6d35 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -216,7 +216,7 @@ pub fn _subtract_rect( #[repr(u32)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture2", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Serialize))] pub enum TransformedRectKind { AxisAligned = 0, Complex = 1, diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index e27e0097a4..da9a196689 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -25,7 +25,7 @@ time = "0.1" crossbeam = "0.2" osmesa-sys = { version = "0.1.2", optional = true } osmesa-src = { git = "https://github.com/servo/osmesa-src", optional = true } -webrender = {path = "../webrender", features=["capture2","debugger","profiler"]} +webrender = {path = "../webrender", features=["capture","debugger","profiler"]} webrender_api = {path = "../webrender_api", features=["debug-serialization"]} serde = {version = "1.0", features = ["derive"] } From 42e8cb42ba4ee899d29525ac356921c22dc11d6f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 12 Jan 2018 09:20:26 -0500 Subject: [PATCH 06/16] Capture: tiling frame deserialization --- webrender/src/batch.rs | 26 +++++++++++----------- webrender/src/box_shadow.rs | 2 +- webrender/src/clip_scroll_tree.rs | 2 +- webrender/src/device.rs | 2 +- webrender/src/frame.rs | 11 ++++++---- webrender/src/gpu_cache.rs | 6 ++--- webrender/src/gpu_types.rs | 22 +++++++++---------- webrender/src/internal_types.rs | 6 ++--- webrender/src/picture.rs | 2 +- webrender/src/prim_store.rs | 2 +- webrender/src/render_backend.rs | 35 +++++++++++++++++++----------- webrender/src/render_task.rs | 24 ++++++++++---------- webrender/src/renderer.rs | 4 ++-- webrender/src/resource_cache.rs | 2 +- webrender/src/texture_allocator.rs | 4 ++-- webrender/src/tiling.rs | 24 ++++++++++---------- webrender/src/util.rs | 2 +- 17 files changed, 94 insertions(+), 82 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index df57aae3ba..342f1d655b 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -35,7 +35,7 @@ use util::{MatrixHelpers, TransformedRectKind}; const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32); #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum TransformBatchKind { TextRun(GlyphFormat), Image(ImageBufferKind), @@ -48,7 +48,7 @@ pub enum TransformBatchKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum BrushImageSourceKind { Alpha, Color, @@ -65,7 +65,7 @@ impl BrushImageSourceKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum BrushBatchKind { Image(BrushImageSourceKind), Solid, @@ -73,7 +73,7 @@ pub enum BrushBatchKind { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum BatchKind { Composite { task_id: RenderTaskId, @@ -90,7 +90,7 @@ pub enum BatchKind { /// Optional textures that can be used as a source in the shaders. /// Textures that are not used by the batch are equal to TextureId::invalid(). #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct BatchTextures { pub colors: [SourceTexture; 3], } @@ -120,7 +120,7 @@ impl BatchTextures { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct AlphaPrimitiveBatch { pub key: BatchKey, pub instances: Vec, @@ -138,7 +138,7 @@ impl AlphaPrimitiveBatch { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct OpaquePrimitiveBatch { pub key: BatchKey, pub instances: Vec, @@ -154,7 +154,7 @@ impl OpaquePrimitiveBatch { } #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct BatchKey { pub kind: BatchKind, pub blend_mode: BlendMode, @@ -183,7 +183,7 @@ fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool { t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2 } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct AlphaBatchList { pub batches: Vec, } @@ -261,7 +261,7 @@ impl AlphaBatchList { } } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct OpaqueBatchList { pub pixel_area_threshold_for_new_batch: i32, pub batches: Vec, @@ -327,7 +327,7 @@ impl OpaqueBatchList { } } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct BatchList { pub alpha_batch_list: AlphaBatchList, pub opaque_batch_list: OpaqueBatchList, @@ -374,7 +374,7 @@ impl BatchList { } /// Encapsulates the logic of building batches for items that are blended. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct AlphaBatcher { pub batch_list: BatchList, pub text_run_cache_prims: FastHashMap>, @@ -1447,7 +1447,7 @@ fn make_polygon( /// Batcher managing draw calls into the clip mask (in the RT cache). #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct ClipBatcher { /// Rectangle draws fill up the rectangles with rounded corners. pub rectangles: Vec, diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index a650f9fe74..f9552f13a0 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -29,7 +29,7 @@ pub const MAX_BLUR_RADIUS : f32 = 300.; pub const MASK_CORNER_PADDING: f32 = 4.0; #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct BoxShadowCacheKey { pub width: Au, pub height: Au, diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index e11f47e00e..f736fa6d1a 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -23,7 +23,7 @@ pub type ScrollStates = FastHashMap; /// system are the same or are in the same axis-aligned space. This allows /// for optimizing mask generation. #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct CoordinateSystemId(pub u32); impl CoordinateSystemId { diff --git a/webrender/src/device.rs b/webrender/src/device.rs index d3ddc85194..76f7674794 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -425,7 +425,7 @@ impl ExternalTexture { } } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct Texture { #[cfg_attr(feature = "capture", serde(skip))] id: gl::GLuint, diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index e9d5098c3e..481f54a9b6 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -21,7 +21,7 @@ use internal_types::{FastHashMap, FastHashSet, RenderedDocument}; use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters}; use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap}; use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties}; -use tiling::CompositeOps; +use tiling::{CompositeOps, Frame}; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] #[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] @@ -1096,6 +1096,11 @@ impl FrameContext { self.pipeline_epoch_map.insert(pipeline_id, epoch); } + pub fn make_rendered_document(&self, frame: Frame) -> RenderedDocument { + let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back(); + RenderedDocument::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame) + } + pub fn build_rendered_document( &mut self, frame_builder: &mut FrameBuilder, @@ -1123,8 +1128,6 @@ impl FrameContext { gpu_cache_profile, scene_properties, ); - - let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back(); - RenderedDocument::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame) + self.make_rendered_document(frame) } } diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index c61939a459..2accad56e6 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -127,21 +127,21 @@ impl GpuCacheHandle { // as part of the primitive instances, to allow the vertex // shader to fetch the specific data. #[derive(Copy, Debug, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GpuCacheAddress { pub u: u16, pub v: u16, } impl GpuCacheAddress { - fn new(u: usize, v: usize) -> GpuCacheAddress { + fn new(u: usize, v: usize) -> Self { GpuCacheAddress { u: u as u16, v: v as u16, } } - pub fn invalid() -> GpuCacheAddress { + pub fn invalid() -> Self { GpuCacheAddress { u: u16::MAX, v: u16::MAX, diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 3f4c0cf8ff..a736ffbf26 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -11,7 +11,7 @@ use render_task::RenderTaskAddress; #[repr(i32)] #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum BlurDirection { Horizontal = 0, Vertical, @@ -19,7 +19,7 @@ pub enum BlurDirection { #[derive(Debug)] #[repr(C)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct BlurInstance { pub task_address: RenderTaskAddress, pub src_task_address: RenderTaskAddress, @@ -30,7 +30,7 @@ pub struct BlurInstance { /// Could be an image or a rectangle, which defines the /// way `address` is treated. #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] #[repr(C)] pub struct ClipMaskInstance { pub render_task_address: RenderTaskAddress, @@ -42,7 +42,7 @@ pub struct ClipMaskInstance { // 32 bytes per instance should be enough for anyone! #[derive(Debug, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct PrimitiveInstance { data: [i32; 8], } @@ -64,7 +64,7 @@ impl SimplePrimitiveInstance { clip_chain_rect_index: ClipChainRectIndex, scroll_id: ClipScrollNodeIndex, z_sort_index: i32, - ) -> SimplePrimitiveInstance { + ) -> Self { SimplePrimitiveInstance { specific_prim_address, task_address, @@ -112,7 +112,7 @@ impl CompositePrimitiveInstance { z: i32, data2: i32, data3: i32, - ) -> CompositePrimitiveInstance { + ) -> Self { CompositePrimitiveInstance { task_address, src_task_address, @@ -127,7 +127,7 @@ impl CompositePrimitiveInstance { } impl From for PrimitiveInstance { - fn from(instance: CompositePrimitiveInstance) -> PrimitiveInstance { + fn from(instance: CompositePrimitiveInstance) -> Self { PrimitiveInstance { data: [ instance.task_address.0 as i32, @@ -165,7 +165,7 @@ pub struct BrushInstance { } impl From for PrimitiveInstance { - fn from(instance: BrushInstance) -> PrimitiveInstance { + fn from(instance: BrushInstance) -> Self { PrimitiveInstance { data: [ instance.picture_address.0 as i32, @@ -193,12 +193,12 @@ pub enum BrushImageKind { } #[derive(Copy, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] #[repr(C)] pub struct ClipScrollNodeIndex(pub u32); #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] #[repr(C)] pub struct ClipScrollNodeData { pub transform: LayerToWorldTransform, @@ -221,7 +221,7 @@ impl ClipScrollNodeData { pub struct ClipChainRectIndex(pub usize); #[derive(Copy, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] #[repr(C)] pub enum PictureType { Image = 1, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index e7515d32e3..0dc59c505f 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -37,7 +37,7 @@ pub type FastHashSet = HashSet>; pub struct CacheTextureId(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderPassIndex(pub usize); // Represents the source for a texture. @@ -47,7 +47,7 @@ pub struct RenderPassIndex(pub usize); // native texture ID. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum SourceTexture { Invalid, TextureCache(CacheTextureId), @@ -64,7 +64,7 @@ pub const ORTHO_NEAR_PLANE: f32 = -1000000.0; pub const ORTHO_FAR_PLANE: f32 = 1000000.0; #[derive(Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTargetInfo { pub has_depth: bool, } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 83cfc0831a..7bcd090e74 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -43,7 +43,7 @@ pub enum PictureCompositeMode { /// Configure whether the content to be drawn by a picture /// in local space rasterization or the screen space. #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum ContentOrigin { Local(LayerPoint), Screen(DeviceIntPoint), diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index c31af9a6cb..0567b03743 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -140,7 +140,7 @@ pub struct DeferredResolve { pub struct SpecificPrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct PrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 978b3ef7a5..28ad485728 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -870,6 +870,8 @@ impl RenderBackend { root: &PathBuf, profile_counters: &mut BackendProfileCounters, ) { + use tiling::Frame; + info!("capture: loading {:?}", root); let backend = CaptureConfig::deserialize::(root, "backend") .expect("Unable to open backend.ron"); @@ -888,9 +890,9 @@ impl RenderBackend { for (id, view) in backend.documents { info!("\tdocument {:?}", id); - let file_name = format!("scene-{}-{}", (id.0).0, id.1); - let scene = CaptureConfig::deserialize::(root, &file_name) - .expect(&format!("Unable to open {}.ron", file_name)); + let scene_name = format!("scene-{}-{}", (id.0).0, id.1); + let scene = CaptureConfig::deserialize::(root, &scene_name) + .expect(&format!("Unable to open {}.ron", scene_name)); let mut doc = Document { scene, @@ -901,25 +903,32 @@ impl RenderBackend { render_on_scroll: None, }; - doc.build_scene(&mut self.resource_cache); - let render_doc = doc.render( - &mut self.resource_cache, - &mut self.gpu_cache, - &mut profile_counters.resources, - ); + let frame_name = format!("frame-{}-{}", (id.0).0, id.1); + let render_doc = match CaptureConfig::deserialize::(root, frame_name) { + Some(frame) => { + info!("\tfound built frame"); + doc.frame_ctx.make_rendered_document(frame) + } + None => { + doc.build_scene(&mut self.resource_cache); + doc.render( + &mut self.resource_cache, + &mut self.gpu_cache, + &mut profile_counters.resources, + ) + } + }; - let pending_update = self.resource_cache.pending_updates(); let msg = ResultMsg::PublishDocument( id, render_doc, - pending_update, - profile_counters.clone() + self.resource_cache.pending_updates(), + profile_counters.clone(), ); self.result_tx.send(msg).unwrap(); profile_counters.reset(); self.notifier.new_document_ready(id, false, true); - self.documents.insert(id, doc); } } diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 2ded76d2c0..b5481df1da 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -27,16 +27,16 @@ pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTaskId(pub u32); // TODO(gw): Make private when using GPU cache! #[derive(Debug, Copy, Clone)] #[repr(C)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTaskAddress(pub u32); #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTaskTree { pub tasks: Vec, pub task_data: Vec, @@ -212,7 +212,7 @@ impl ops::IndexMut for RenderTaskTree { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum RenderTaskLocation { Fixed, Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), @@ -220,7 +220,7 @@ pub enum RenderTaskLocation { } #[derive(Debug, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct ClipWorkItem { pub scroll_node_data_index: ClipScrollNodeIndex, pub clip_sources: ClipSourcesWeakHandle, @@ -228,7 +228,7 @@ pub struct ClipWorkItem { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct CacheMaskTask { actual_rect: DeviceIntRect, pub clips: Vec, @@ -236,7 +236,7 @@ pub struct CacheMaskTask { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct PictureTask { pub prim_index: PrimitiveIndex, pub target_kind: RenderTargetKind, @@ -246,7 +246,7 @@ pub struct PictureTask { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct BlurTask { pub blur_std_deviation: f32, pub target_kind: RenderTargetKind, @@ -264,13 +264,13 @@ impl BlurTask { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTaskData { pub data: [f32; FLOATS_PER_RENDER_TASK_INFO], } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum RenderTaskKind { Picture(PictureTask), CacheMask(CacheMaskTask), @@ -281,7 +281,7 @@ pub enum RenderTaskKind { } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum ClearMode { // Applicable to color and alpha targets. Zero, @@ -292,7 +292,7 @@ pub enum ClearMode { } #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTask { pub location: RenderTaskLocation, pub children: Vec, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index e18d8f23b0..1b9dcd9fd2 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -480,7 +480,7 @@ pub struct GraphicsApiInfo { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum ImageBufferKind { Texture2D = 0, TextureRect = 1, @@ -758,7 +758,7 @@ impl SourceTextureResolver { } #[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] #[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment. pub enum BlendMode { None, diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 02fb93e712..2fefe5f360 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -40,7 +40,7 @@ use texture_cache::{TextureCache, TextureCacheHandle}; const DEFAULT_TILE_SIZE: TileSize = 512; -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GlyphFetchResult { pub index_in_text_run: i32, pub uv_rect_address: GpuCacheAddress, diff --git a/webrender/src/texture_allocator.rs b/webrender/src/texture_allocator.rs index 7a9eeb10a6..e5dd2d022a 100644 --- a/webrender/src/texture_allocator.rs +++ b/webrender/src/texture_allocator.rs @@ -22,7 +22,7 @@ const MINIMUM_LARGE_RECT_SIZE: u32 = 32; /// /// This approach was chosen because of its simplicity, good performance, and easy support for /// dynamic texture deallocation. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GuillotineAllocator { texture_size: DeviceUintSize, free_list: FreeRectList, @@ -171,7 +171,7 @@ impl GuillotineAllocator { /// A binning free list. Binning is important to avoid sifting through lots of small strips when /// allocating many texture items. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct FreeRectList { small: Vec, medium: Vec, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index dfa9f6f367..f62cecbd82 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -35,7 +35,7 @@ pub struct ScrollbarPrimitive { } #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTargetIndex(pub usize); pub struct RenderTargetContext<'a> { @@ -47,7 +47,7 @@ pub struct RenderTargetContext<'a> { pub use_dual_source_blending: bool, } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct TextureAllocator { // TODO(gw): Replace this with a simpler allocator for // render target allocation - this use case doesn't need @@ -113,13 +113,13 @@ pub trait RenderTarget { } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum RenderTargetKind { Color, // RGBA32 Alpha, // R8 } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderTargetList { screen_size: DeviceIntSize, pub format: ImageFormat, @@ -220,20 +220,20 @@ impl RenderTargetList { /// Storing the task ID allows the renderer to find /// the target rect within the render target that this /// pipeline exists at. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct FrameOutput { pub task_id: RenderTaskId, pub pipeline_id: PipelineId, } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct ScalingInfo { pub src_task_id: RenderTaskId, pub dest_task_id: RenderTaskId, } /// A render target represents a number of rendering operations on a surface. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct ColorRenderTarget { pub alpha_batcher: AlphaBatcher, // List of blur operations to apply for this render target. @@ -368,7 +368,7 @@ impl RenderTarget for ColorRenderTarget { } } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, pub brush_mask_corners: Vec, @@ -538,7 +538,7 @@ impl RenderTarget for AlphaRenderTarget { } } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct TextureCacheRenderTarget { pub horizontal_blurs: Vec, } @@ -581,7 +581,7 @@ impl TextureCacheRenderTarget { } } -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum RenderPassKind { MainFramebuffer(ColorRenderTarget), OffScreen { @@ -596,7 +596,7 @@ pub enum RenderPassKind { /// /// A render pass can have several render targets if there wasn't enough space in one /// target to do all of the rendering for that pass. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct RenderPass { pub kind: RenderPassKind, tasks: Vec, @@ -742,7 +742,7 @@ impl CompositeOps { /// A rendering-oriented representation of frame::Frame built by the render backend /// and presented to the renderer. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct Frame { pub window_size: DeviceUintSize, pub inner_rect: DeviceUintRect, diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 6c721a6d35..b21bd05edf 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -216,7 +216,7 @@ pub fn _subtract_rect( #[repr(u32)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum TransformedRectKind { AxisAligned = 0, Complex = 1, From f485d663a289bd021d8948331d74edd78cffb939 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 12 Jan 2018 17:05:17 -0500 Subject: [PATCH 07/16] Capture: cached textures on the Renderer --- webrender/examples/common/boilerplate.rs | 2 +- webrender/examples/frame_output.rs | 10 +- webrender/src/capture.rs | 5 +- webrender/src/device.rs | 91 ++++++++- webrender/src/internal_types.rs | 8 +- webrender/src/lib.rs | 4 +- webrender/src/render_backend.rs | 6 +- webrender/src/renderer.rs | 228 ++++++++++++++--------- webrender/src/texture_cache.rs | 18 +- webrender/src/tiling.rs | 1 + 10 files changed, 253 insertions(+), 120 deletions(-) diff --git a/webrender/examples/common/boilerplate.rs b/webrender/examples/common/boilerplate.rs index c0edce9f6f..41be05afd5 100644 --- a/webrender/examples/common/boilerplate.rs +++ b/webrender/examples/common/boilerplate.rs @@ -280,7 +280,7 @@ pub fn main_wrapper( _, Some(glutin::VirtualKeyCode::C), ) => { - let path: PathBuf = "captures/example".into(); + let path: PathBuf = "../captures/example".into(); if path.is_dir() { api.load_capture(path); } else { diff --git a/webrender/examples/frame_output.rs b/webrender/examples/frame_output.rs index 317ca1c77c..13f021aee2 100644 --- a/webrender/examples/frame_output.rs +++ b/webrender/examples/frame_output.rs @@ -77,7 +77,7 @@ impl App { ImageData::External(ExternalImageData { id: ExternalImageId(0), channel_index: 0, - image_type: ExternalImageType::Texture2DHandle + image_type: ExternalImageType::Texture2DHandle, }), None, ); @@ -126,7 +126,7 @@ impl App { true, resources, ); - + api.generate_frame(document.id, None); self.output_document = Some(document); } @@ -174,7 +174,7 @@ impl Example for App { fn get_image_handlers( &mut self, gl: &gl::Gl, - ) -> (Option>, + ) -> (Option>, Option>) { let texture_id = gl.gen_textures(1)[0]; @@ -212,8 +212,8 @@ impl Example for App { ); gl.bind_texture(gl::TEXTURE_2D, 0); - ( - Some(Box::new(ExternalHandler { texture_id })), + ( + Some(Box::new(ExternalHandler { texture_id })), Some(Box::new(OutputHandler { texture_id })) ) } diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index e8a1f96719..aee8262f70 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -14,7 +14,6 @@ use serde::{Deserialize, Serialize}; bitflags!{ pub struct CaptureBits: u8 { const SCENE = 0x1; - #[cfg(feature = "capture")] const FRAME = 0x2; } } @@ -63,8 +62,8 @@ impl CaptureConfig { .ok()? .read_to_string(&mut string) .unwrap(); - de::from_str(&string) - .unwrap() + Some(de::from_str(&string) + .unwrap()) } } diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 76f7674794..c0fa9e76ca 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -128,6 +128,14 @@ pub enum UploadMethod { PixelBuffer(VertexUsageHint), } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ReadPixelsFormat { + R8, + Rgba8, + Bgra8, + Rgba32F, +} + pub fn get_gl_format_bgra(gl: &gl::Gl) -> gl::GLuint { match gl.get_type() { gl::GlType::Gl => GL_FORMAT_BGRA_GL, @@ -425,9 +433,7 @@ impl ExternalTexture { } } -#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct Texture { - #[cfg_attr(feature = "capture", serde(skip))] id: gl::GLuint, target: gl::GLuint, layer_count: i32, @@ -436,9 +442,7 @@ pub struct Texture { height: u32, filter: TextureFilter, render_target: Option, - #[cfg_attr(feature = "capture", serde(skip))] fbo_ids: Vec, - #[cfg_attr(feature = "capture", serde(skip))] depth_rb: Option, } @@ -459,6 +463,14 @@ impl Texture { self.format } + pub fn get_filter(&self) -> TextureFilter { + self.filter + } + + pub fn get_render_target(&self) -> Option { + self.render_target.clone() + } + pub fn get_bpp(&self) -> u32 { match self.format { ImageFormat::R8 => 1, @@ -835,7 +847,7 @@ impl Device { self.bind_texture_impl(sampler.into(), external_texture.id, external_texture.target); } - fn bind_read_target_impl(&mut self, fbo_id: FBOId) { + pub fn bind_read_target_impl(&mut self, fbo_id: FBOId) { debug_assert!(self.inside_frame); if self.bound_read_fbo != fbo_id { @@ -884,7 +896,7 @@ impl Device { pub fn create_fbo_for_external_texture(&mut self, texture_id: u32) -> FBOId { let fbo = FBOId(self.gl.gen_framebuffers(1)[0]); - self.bind_external_draw_target(fbo); + fbo.bind(self.gl(), FBOTarget::Draw); self.gl.framebuffer_texture_2d( gl::DRAW_FRAMEBUFFER, gl::COLOR_ATTACHMENT0, @@ -892,6 +904,7 @@ impl Device { texture_id, 0, ); + self.bound_draw_fbo.bind(self.gl(), FBOTarget::Draw); fbo } @@ -1509,6 +1522,72 @@ impl Device { ) } + /// Read rectangle of RGBA8 or BGRA8 pixels into the specified output slice. + pub fn read_pixels_into( + &mut self, + rect: DeviceUintRect, + format: ReadPixelsFormat, + output: &mut Vec, + ) { + let (gl_format, gl_type, pixel_size) = match format { + ReadPixelsFormat::R8 => (gl::RED, gl::UNSIGNED_BYTE, 1), + ReadPixelsFormat::Rgba8 => (gl::RGBA, gl::UNSIGNED_BYTE, 4), + ReadPixelsFormat::Bgra8 => (get_gl_format_bgra(self.gl()), gl::UNSIGNED_BYTE, 4), + ReadPixelsFormat::Rgba32F => (gl::RGBA, gl::FLOAT, 16), + }; + let size = (pixel_size * rect.size.width * rect.size.height) as usize; + let base = output.len(); + output.extend((0..size).map(|_| 0)); + + self.gl.flush(); + self.gl.read_pixels_into_buffer( + rect.origin.x as _, + rect.origin.y as _, + rect.size.width as _, + rect.size.height as _, + gl_format, + gl_type, + &mut output[base ..], + ); + } + + /// Attaches the provided texture to the current Read FBO binding. + fn attach_read_texture_raw( + &mut self, texture_id: gl::GLuint, target: gl::GLuint, layer_id: i32 + ) { + match target { + gl::TEXTURE_2D_ARRAY => { + self.gl.framebuffer_texture_layer( + gl::READ_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture_id, + 0, + layer_id, + ) + } + _ => { + assert_eq!(layer_id, 0); + self.gl.framebuffer_texture_2d( + gl::READ_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + target, + texture_id, + 0, + ) + } + } + } + + pub fn attach_read_texture_external( + &mut self, texture_id: gl::GLuint, target: TextureTarget, layer_id: i32 + ) { + self.attach_read_texture_raw(texture_id, target.to_gl_target(), layer_id) + } + + pub fn attach_read_texture(&mut self, texture: &Texture, layer_id: i32) { + self.attach_read_texture_raw(texture.id, texture.target, layer_id) + } + fn bind_vao_impl(&mut self, id: gl::GLuint) { debug_assert!(self.inside_frame); diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 0dc59c505f..b48b9f4220 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -17,7 +17,7 @@ use std::path::PathBuf; use std::sync::Arc; #[cfg(feature = "capture")] -use capture::{ExternalCaptureImage}; +use capture::{CaptureConfig, ExternalCaptureImage}; use tiling; pub type FastHashMap = HashMap>; @@ -152,9 +152,11 @@ pub enum DebugOutput { FetchDocuments(String), FetchClipScrollTree(String), #[cfg(feature = "capture")] - SaveCapture(PathBuf, Vec), + SaveCapture(CaptureConfig, Vec), #[cfg(feature = "capture")] - LoadCapture, + LoadCapture { + reset_textures: bool, + } } pub enum ResultMsg { diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 386501c5b6..c5decf1879 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -162,10 +162,10 @@ extern crate base64; pub extern crate webrender_api; #[doc(hidden)] -pub use device::{build_shader_strings, ProgramCache, UploadMethod, VertexUsageHint}; +pub use device::{build_shader_strings, ProgramCache, ReadPixelsFormat, UploadMethod, VertexUsageHint}; pub use renderer::{CpuProfile, DebugFlags, GpuProfile, OutputImageHandler, RendererKind}; pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource}; -pub use renderer::{GraphicsApi, GraphicsApiInfo, ReadPixelsFormat, Renderer, RendererOptions}; +pub use renderer::{GraphicsApi, GraphicsApiInfo, Renderer, RendererOptions}; pub use renderer::{RendererStats, ThreadListener}; pub use renderer::MAX_VERTEX_TEXTURE_WIDTH; pub use webrender_api as api; diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 28ad485728..fd5e5cf08f 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -588,16 +588,16 @@ impl RenderBackend { } #[cfg(feature = "capture")] DebugCommand::SaveCapture(root) => { - let config = CaptureConfig::new(root, CaptureBits::FRAME); + let config = CaptureConfig::new(root, CaptureBits::all()); let deferred = self.save_capture(&config, &mut profile_counters); - ResultMsg::DebugOutput(DebugOutput::SaveCapture(config.root, deferred)) + ResultMsg::DebugOutput(DebugOutput::SaveCapture(config, deferred)) }, #[cfg(feature = "capture")] DebugCommand::LoadCapture(root) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; self.load_capture(&root, &mut profile_counters); - ResultMsg::DebugOutput(DebugOutput::LoadCapture) + ResultMsg::DebugOutput(DebugOutput::LoadCapture { reset_textures: true }) }, DebugCommand::EnableDualSourceBlending(enable) => { // Set in the config used for any future documents diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 1b9dcd9fd2..26b407f8fc 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -20,17 +20,18 @@ use api::DebugCommand; use api::channel::MsgSender; use batch::{BatchKey, BatchKind, BatchTextures, BrushBatchKind}; use batch::{BrushImageSourceKind, TransformBatchKind}; +#[cfg(feature = "capture")] +use capture::{CaptureBits, CaptureConfig, ExternalCaptureImage}; use debug_colors; use debug_render::DebugRenderer; #[cfg(feature = "debugger")] use debug_server::{self, DebugServer}; use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, VertexDescriptor, PBO}; -use device::{get_gl_format_bgra, ExternalTexture, FBOId, TextureSlot, VertexAttribute, - VertexAttributeKind}; +use device::{ExternalTexture, FBOId, TextureSlot, VertexAttribute, VertexAttributeKind}; use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget, VertexUsageHint, VAO, VBO, CustomVAO}; -use device::ProgramCache; +use device::{ProgramCache, ReadPixelsFormat}; use euclid::{rect, Transform3D}; use frame_builder::FrameBuilderConfig; use gleam::gl; @@ -1539,12 +1540,6 @@ impl FileWatcherHandler for FileWatcher { } } -#[derive(Clone, Debug, PartialEq)] -pub enum ReadPixelsFormat { - Rgba8, - Bgra8, -} - struct FrameOutput { last_access: FrameId, fbo_id: FBOId, @@ -1663,6 +1658,9 @@ pub struct Renderer { /// via get_frame_profiles(). cpu_profiles: VecDeque, gpu_profiles: VecDeque, + + #[cfg(feature = "capture")] + capture_read_fbo: FBOId, } #[derive(Debug)] @@ -2220,6 +2218,9 @@ impl Renderer { }) }; + #[cfg(feature = "capture")] + let capture_read_fbo = device.create_fbo_for_external_texture(0); + let gpu_profile = GpuProfiler::new(Rc::clone(device.rc_gl())); let mut renderer = Renderer { @@ -2284,6 +2285,8 @@ impl Renderer { texture_cache_upload_pbo, texture_resolver, renderer_errors: Vec::new(), + #[cfg(feature = "capture")] + capture_read_fbo, }; renderer.set_debug_flags(options.debug_flags); @@ -2386,45 +2389,19 @@ impl Renderer { self.debug_server.send(string); } #[cfg(feature = "capture")] - DebugOutput::SaveCapture(path, deferred)=> { - use std::fs::File; - use std::io::Write; - use api::ExternalImageData; - - if deferred.is_empty() { - continue - } - - info!("saving external images"); - let handler = self.external_image_handler - .as_mut() - .expect("Unable to lock the external image handler!"); - for def in deferred { - let ExternalImageData { id, channel_index, .. } = def.external; - let data = match handler.lock(id, channel_index).source { - ExternalImageSource::RawData(data) => data.to_vec(), - ExternalImageSource::NativeTexture(_gl_id) => { - //TODO: make a read FBO with this GL texture - //self.device.read_pixels(&def.descriptor); - unimplemented!() - } - ExternalImageSource::Invalid => { - // Create a dummy buffer... - let stride = def.descriptor.compute_stride(); - let total_size = def.descriptor.height * stride; - vec![0xFF; total_size as usize] - } - }; - handler.unlock(id, channel_index); - - File::create(path.join(&def.short_path)) - .expect(&format!("Unable to create {}", def.short_path)) - .write_all(&data) - .unwrap(); - } + DebugOutput::SaveCapture(config, deferred) => { + self.save_capture(config, deferred); } #[cfg(feature = "capture")] - DebugOutput::LoadCapture => {} + DebugOutput::LoadCapture { reset_textures } => { + if reset_textures { + self.device.begin_frame(); + for mut texture in self.texture_resolver.cache_texture_map.drain(..) { + self.device.free_texture_storage(&mut texture); + } + self.device.end_frame(); + } + } }, ResultMsg::DebugCommand(command) => { self.handle_debug_command(command); @@ -3736,10 +3713,9 @@ impl Renderer { .as_mut() .expect("Found output image, but no handler set!"); if let Some((texture_id, output_size)) = handler.lock(output.pipeline_id) { - let device = &mut self.device; let fbo_id = match self.output_targets.entry(texture_id) { Entry::Vacant(entry) => { - let fbo_id = device.create_fbo_for_external_texture(texture_id); + let fbo_id = self.device.create_fbo_for_external_texture(texture_id); entry.insert(FrameOutput { fbo_id, last_access: frame_id, @@ -3754,9 +3730,9 @@ impl Renderer { }; let (src_rect, _) = render_tasks[output.task_id].get_target_rect(); let dest_rect = DeviceIntRect::new(DeviceIntPoint::zero(), output_size); - device.bind_read_target(render_target); - device.bind_external_draw_target(fbo_id); - device.blit_render_target(src_rect, dest_rect); + self.device.bind_read_target(render_target); + self.device.bind_external_draw_target(fbo_id); + self.device.blit_render_target(src_rect, dest_rect); handler.unlock(output.pipeline_id); } } @@ -4511,20 +4487,20 @@ impl Renderer { ); } - pub fn read_pixels_rgba8(&self, rect: DeviceUintRect) -> Vec { - let mut pixels = vec![0u8; (4 * rect.size.width * rect.size.height) as usize]; - self.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut pixels); + pub fn read_pixels_rgba8(&mut self, rect: DeviceUintRect) -> Vec { + let mut pixels = Vec::new(); + self.device.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut pixels); pixels } pub fn read_gpu_cache(&mut self) -> (DeviceUintSize, Vec) { let size = self.gpu_cache_texture.texture.get_dimensions(); - let mut texels = vec![0u8; 4 * (size.width * size.height) as usize]; + let mut texels = Vec::new(); self.device.begin_frame(); self.device.bind_read_target(Some((&self.gpu_cache_texture.texture, 0))); - self.read_pixels_into( + self.device.read_pixels_into( DeviceUintRect::new(DeviceUintPoint::zero(), size), - ReadPixelsFormat::Rgba8, + ReadPixelsFormat::Rgba32F, &mut texels, ); self.device.bind_read_target(None); @@ -4532,32 +4508,6 @@ impl Renderer { (size, texels) } - pub fn read_pixels_into( - &self, - rect: DeviceUintRect, - format: ReadPixelsFormat, - output: &mut [u8], - ) { - let (gl_format, gl_type, size) = match format { - ReadPixelsFormat::Rgba8 => (gl::RGBA, gl::UNSIGNED_BYTE, 4), - ReadPixelsFormat::Bgra8 => (get_gl_format_bgra(self.device.gl()), gl::UNSIGNED_BYTE, 4), - }; - assert_eq!( - output.len(), - (size * rect.size.width * rect.size.height) as usize - ); - self.device.gl().flush(); - self.device.gl().read_pixels_into_buffer( - rect.origin.x as gl::GLint, - rect.origin.y as gl::GLint, - rect.size.width as gl::GLsizei, - rect.size.height as gl::GLsizei, - gl_format, - gl_type, - output, - ); - } - // De-initialize the Renderer safely, assuming the GL is still alive and active. pub fn deinit(mut self) { //Note: this is a fake frame, only needed because texture deletion is require to happen inside a frame @@ -4736,7 +4686,7 @@ pub struct DebugServer; #[cfg(not(feature = "debugger"))] impl DebugServer { - pub fn new(_: MsgSender) -> DebugServer { + pub fn new(_: MsgSender) -> Self { DebugServer } @@ -4754,7 +4704,7 @@ pub struct RendererStats { } impl RendererStats { - pub fn empty() -> RendererStats { + pub fn empty() -> Self { RendererStats { total_draw_calls: 0, alpha_target_count: 0, @@ -4762,3 +4712,111 @@ impl RendererStats { } } } + +#[cfg(feature = "capture")] +impl Renderer { + fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec) { + use std::fs; + use std::io::Write; + use api::ExternalImageData; + + #[derive(Serialize)] + struct PlainTexture { + data: String, + size: (u32, u32, i32), + format: ImageFormat, + filter: TextureFilter, + render_target: Option, + } + + self.device.begin_frame(); + self.device.bind_read_target_impl(self.capture_read_fbo); + + if !deferred_images.is_empty() { + info!("saving external images"); + //TODO: add a switch to check `self.texture_resolver.external_images` + // instead of locking with the actual handler. + let handler = self.external_image_handler + .as_mut() + .expect("Unable to lock the external image handler!"); + for def in deferred_images { + let ExternalImageData { id, channel_index, image_type } = def.external; + let data = match handler.lock(id, channel_index).source { + ExternalImageSource::RawData(data) => data.to_vec(), + ExternalImageSource::NativeTexture(gl_id) => { + let target = match image_type { + ExternalImageType::Texture2DHandle => TextureTarget::Default, + ExternalImageType::Texture2DArrayHandle => TextureTarget::Array, // layer is always 0? + ExternalImageType::TextureRectHandle => TextureTarget::Rect, + ExternalImageType::TextureExternalHandle => TextureTarget::External, + ExternalImageType::ExternalBuffer => unimplemented!(), + }; + self.device.attach_read_texture_external(gl_id, target, 0); + self.device.read_pixels(&def.descriptor) + } + ExternalImageSource::Invalid => { + // Create a dummy buffer... + let stride = def.descriptor.compute_stride(); + let total_size = def.descriptor.height * stride; + vec![0xFF; total_size as usize] + } + }; + handler.unlock(id, channel_index); + + fs::File::create(config.root.join(&def.short_path)) + .expect(&format!("Unable to create {}", def.short_path)) + .write_all(&data) + .unwrap(); + } + } + + if config.bits.contains(CaptureBits::FRAME) { + info!("saving cached textures"); + let mut data = Vec::new(); + let path_textures = config.root.join("textures"); + if !path_textures.is_dir() { + fs::create_dir(&path_textures).unwrap(); + } + let mut plain_textures = Vec::new(); + + for texture in &self.texture_resolver.cache_texture_map { + let file_name = format!("{}.raw", plain_textures.len() + 1); + info!("\t{}", file_name); + let short_path = format!("textures/{}", file_name); + + let read_format = match texture.get_format() { + ImageFormat::A8 => ReadPixelsFormat::R8, + ImageFormat::BGRA8 => ReadPixelsFormat::Bgra8, + ImageFormat::RGBAF32 => ReadPixelsFormat::Rgba32F, + _ => unimplemented!() + }; + let rect = DeviceUintRect::new( + DeviceUintPoint::zero(), + texture.get_dimensions(), + ); + + data.clear(); + for layer_id in 0 .. texture.get_layer_count() { + self.device.attach_read_texture(texture, layer_id); + self.device.read_pixels_into(rect, read_format, &mut data); + } + + fs::File::create(path_textures.join(file_name)) + .expect(&format!("Unable to create {}", short_path)) + .write_all(&data) + .unwrap(); + + plain_textures.push(PlainTexture { + data: short_path, + size: (rect.size.width, rect.size.height, texture.get_layer_count()), + format: texture.get_format(), + filter: texture.get_filter(), + render_target: texture.get_render_target(), + }); + } + config.serialize(&plain_textures, "textures"); + } + + self.device.end_frame(); + } +} diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 476dbb7ab1..9a98d9f8d0 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -224,7 +224,7 @@ impl TextureCache { array_rgba8_nearest: TextureArray::new( ImageFormat::BGRA8, TextureFilter::Nearest, - TEXTURE_ARRAY_LAYERS_NEAREST + TEXTURE_ARRAY_LAYERS_NEAREST, ), cache_textures: CacheTextureIdList::new(), pending_updates: TextureUpdateList::new(), @@ -808,18 +808,12 @@ impl SlabSize { // The x/y location within a texture region of an allocation. #[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] -struct TextureLocation { - x: u8, - y: u8, -} +struct TextureLocation(u8, u8); impl TextureLocation { fn new(x: u32, y: u32) -> Self { - debug_assert!(x < 256 && y < 256); - TextureLocation { - x: x as u8, - y: y as u8, - } + debug_assert!(x < 0x100 && y < 0x100); + TextureLocation(x as u8, y as u8) } } @@ -884,8 +878,8 @@ impl TextureRegion { fn alloc(&mut self) -> Option { self.free_slots.pop().map(|location| { DeviceUintPoint::new( - self.origin.x + self.slab_size * location.x as u32, - self.origin.y + self.slab_size * location.y as u32, + self.origin.x + self.slab_size * location.0 as u32, + self.origin.y + self.slab_size * location.1 as u32, ) }) } diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index f62cecbd82..b09d1d3f3e 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -125,6 +125,7 @@ pub struct RenderTargetList { pub format: ImageFormat, pub max_size: DeviceUintSize, pub targets: Vec, + #[cfg_attr(feature = "capture", serde(skip))] pub texture: Option, } From ec26b5209b1bf027184b0f9e682f1c8555c2756c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 12 Jan 2018 17:26:30 -0500 Subject: [PATCH 08/16] Capture: GPU cache --- webrender/src/renderer.rs | 111 +++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 26b407f8fc..7bee6ec91c 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -4713,20 +4713,67 @@ impl RendererStats { } } + +#[cfg(feature = "capture")] +#[derive(Serialize)] +struct PlainTexture { + data: String, + size: (u32, u32, i32), + format: ImageFormat, + filter: TextureFilter, + render_target: Option, +} + #[cfg(feature = "capture")] impl Renderer { + fn save_texture( + texture: &Texture, name: &str, root: &PathBuf, device: &mut Device + ) -> PlainTexture { + use std::fs; + use std::io::Write; + + let short_path = format!("textures/{}.raw", name); + + let read_format = match texture.get_format() { + ImageFormat::A8 => ReadPixelsFormat::R8, + ImageFormat::BGRA8 => ReadPixelsFormat::Bgra8, + ImageFormat::RGBAF32 => ReadPixelsFormat::Rgba32F, + _ => unimplemented!() + }; + let rect = DeviceUintRect::new( + DeviceUintPoint::zero(), + texture.get_dimensions(), + ); + + let mut data = Vec::new(); + for layer_id in 0 .. texture.get_layer_count() { + device.attach_read_texture(texture, layer_id); + device.read_pixels_into(rect, read_format, &mut data); + } + + fs::File::create(root.join(&short_path)) + .expect(&format!("Unable to create {}", short_path)) + .write_all(&data) + .unwrap(); + + PlainTexture { + data: short_path, + size: (rect.size.width, rect.size.height, texture.get_layer_count()), + format: texture.get_format(), + filter: texture.get_filter(), + render_target: texture.get_render_target(), + } + } + fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec) { use std::fs; use std::io::Write; use api::ExternalImageData; #[derive(Serialize)] - struct PlainTexture { - data: String, - size: (u32, u32, i32), - format: ImageFormat, - filter: TextureFilter, - render_target: Option, + struct PlainRenderer { + gpu_cache: PlainTexture, + textures: Vec, } self.device.begin_frame(); @@ -4771,52 +4818,32 @@ impl Renderer { } if config.bits.contains(CaptureBits::FRAME) { - info!("saving cached textures"); - let mut data = Vec::new(); let path_textures = config.root.join("textures"); if !path_textures.is_dir() { fs::create_dir(&path_textures).unwrap(); } - let mut plain_textures = Vec::new(); + info!("saving GPU cache"); + let mut plain_self = PlainRenderer { + gpu_cache: Self::save_texture( + &self.gpu_cache_texture.texture, + "gpu", &config.root, &mut self.device, + ), + textures: Vec::new(), + }; + + info!("saving cached textures"); for texture in &self.texture_resolver.cache_texture_map { - let file_name = format!("{}.raw", plain_textures.len() + 1); + let file_name = format!("cache-{}", plain_self.textures.len() + 1); info!("\t{}", file_name); - let short_path = format!("textures/{}", file_name); - - let read_format = match texture.get_format() { - ImageFormat::A8 => ReadPixelsFormat::R8, - ImageFormat::BGRA8 => ReadPixelsFormat::Bgra8, - ImageFormat::RGBAF32 => ReadPixelsFormat::Rgba32F, - _ => unimplemented!() - }; - let rect = DeviceUintRect::new( - DeviceUintPoint::zero(), - texture.get_dimensions(), - ); - - data.clear(); - for layer_id in 0 .. texture.get_layer_count() { - self.device.attach_read_texture(texture, layer_id); - self.device.read_pixels_into(rect, read_format, &mut data); - } - - fs::File::create(path_textures.join(file_name)) - .expect(&format!("Unable to create {}", short_path)) - .write_all(&data) - .unwrap(); - - plain_textures.push(PlainTexture { - data: short_path, - size: (rect.size.width, rect.size.height, texture.get_layer_count()), - format: texture.get_format(), - filter: texture.get_filter(), - render_target: texture.get_render_target(), - }); + let plain = Self::save_texture(texture, &file_name, &config.root, &mut self.device); + plain_self.textures.push(plain); } - config.serialize(&plain_textures, "textures"); + + config.serialize(&plain_self, "renderer"); } + self.device.bind_read_target(None); self.device.end_frame(); } } From 4b6a59255788fdd23273a8e5754700ecae0bbb1f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 15 Jan 2018 11:18:49 -0500 Subject: [PATCH 09/16] Capture: device texture init refactor, texture loading --- .gitignore | 2 +- webrender/src/device.rs | 184 +++++++++++++++++--------------- webrender/src/gpu_cache.rs | 3 - webrender/src/internal_types.rs | 4 +- webrender/src/render_backend.rs | 8 +- webrender/src/renderer.rs | 88 ++++++++++++--- 6 files changed, 176 insertions(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index 0b43b20312..a89b0cd9c3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ target/ *# # WR internals -webrender/captures +captures wrench/json_frames wrench/ron_frames diff --git a/webrender/src/device.rs b/webrender/src/device.rs index c0fa9e76ca..1ea99bf94b 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -983,7 +983,7 @@ impl Device { self.bind_texture(DEFAULT_TEXTURE, texture); self.set_texture_parameters(texture.target, texture.filter); - self.update_texture_storage(texture, &rt_info, true, false); + self.update_target_storage(texture, &rt_info, true, false, None); let rect = DeviceIntRect::new(DeviceIntPoint::zero(), old_size.to_i32()); for (read_fbo, &draw_fbo) in old_fbos.into_iter().zip(&texture.fbo_ids) { @@ -1025,65 +1025,31 @@ impl Device { match render_target { Some(info) => { - assert!(pixels.is_none()); - self.update_texture_storage(texture, &info, is_resized, is_format_changed); + self.update_target_storage(texture, &info, is_resized, is_format_changed, pixels); } None => { - let (internal_format, gl_format) = gl_texture_formats_for_image_format(self.gl(), format); - let type_ = gl_type_for_texture_format(format); - - match texture.target { - gl::TEXTURE_2D_ARRAY => { - self.gl.tex_image_3d( - gl::TEXTURE_2D_ARRAY, - 0, - internal_format as gl::GLint, - width as gl::GLint, - height as gl::GLint, - layer_count, - 0, - gl_format, - type_, - pixels, - ); - } - gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { - self.gl.tex_image_2d( - texture.target, - 0, - internal_format as gl::GLint, - width as gl::GLint, - height as gl::GLint, - 0, - gl_format, - type_, - pixels, - ); - } - _ => panic!("BUG: Unexpected texture target!"), - } + self.update_texture_storage(texture, pixels); } } } - /// Updates the texture storage for the texture, creating FBOs as required. - fn update_texture_storage( + /// Updates the render target storage for the texture, creating FBOs as required. + fn update_target_storage( &mut self, texture: &mut Texture, rt_info: &RenderTargetInfo, is_resized: bool, is_format_changed: bool, + pixels: Option<&[u8]>, ) { assert!(texture.layer_count > 0); let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32; - let allocate_color = needed_layer_count != 0 || is_resized || is_format_changed; + let allocate_color = needed_layer_count != 0 || + is_resized || is_format_changed || pixels.is_some(); if allocate_color { - let (internal_format, gl_format) = - gl_texture_formats_for_image_format(&*self.gl, texture.format); - let type_ = gl_type_for_texture_format(texture.format); - + let desc = gl_describe_format(self.gl(), texture.format); match texture.target { gl::TEXTURE_2D_ARRAY => { if WORK_AROUND_TEX_IMAGE { @@ -1102,14 +1068,14 @@ impl Device { self.gl.tex_image_3d( texture.target, 0, - internal_format as _, + desc.internal, texture.width as _, texture.height as _, texture.layer_count, 0, - gl_format, - type_, - None, + desc.external, + desc.pixel_type, + pixels, ) } _ => { @@ -1117,13 +1083,13 @@ impl Device { self.gl.tex_image_2d( texture.target, 0, - internal_format as _, + desc.internal, texture.width as _, texture.height as _, 0, - gl_format, - type_, - None, + desc.external, + desc.pixel_type, + pixels, ) } } @@ -1206,6 +1172,40 @@ impl Device { } } + fn update_texture_storage(&mut self, texture: &Texture, pixels: Option<&[u8]>) { + let desc = gl_describe_format(self.gl(), texture.format); + match texture.target { + gl::TEXTURE_2D_ARRAY => { + self.gl.tex_image_3d( + gl::TEXTURE_2D_ARRAY, + 0, + desc.internal, + texture.width as _, + texture.height as _, + texture.layer_count, + 0, + desc.external, + desc.pixel_type, + pixels, + ); + } + gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { + self.gl.tex_image_2d( + texture.target, + 0, + desc.internal, + texture.width as _, + texture.height as _, + 0, + desc.external, + desc.pixel_type, + pixels, + ); + } + _ => panic!("BUG: Unexpected texture target!"), + } + } + pub fn blit_render_target(&mut self, src_rect: DeviceIntRect, dest_rect: DeviceIntRect) { debug_assert!(self.inside_frame); @@ -1231,23 +1231,20 @@ impl Device { } self.bind_texture(DEFAULT_TEXTURE, texture); - - let (internal_format, gl_format) = - gl_texture_formats_for_image_format(&*self.gl, texture.format); - let type_ = gl_type_for_texture_format(texture.format); + let desc = gl_describe_format(self.gl(), texture.format); match texture.target { gl::TEXTURE_2D_ARRAY => { self.gl.tex_image_3d( gl::TEXTURE_2D_ARRAY, 0, - internal_format as gl::GLint, + desc.internal, 0, 0, 0, 0, - gl_format, - type_, + desc.external, + desc.pixel_type, None, ); } @@ -1255,12 +1252,12 @@ impl Device { self.gl.tex_image_2d( texture.target, 0, - internal_format, + desc.internal, 0, 0, 0, - gl_format, - type_, + desc.external, + desc.pixel_type, None, ); } @@ -1509,16 +1506,14 @@ impl Device { } } - pub fn read_pixels(&mut self, desc: &ImageDescriptor) -> Vec { - let (_, gl_format) = gl_texture_formats_for_image_format(self.gl(), desc.format); - let type_ = gl_type_for_texture_format(desc.format); - + pub fn read_pixels(&mut self, img_desc: &ImageDescriptor) -> Vec { + let desc = gl_describe_format(self.gl(), img_desc.format); self.gl.read_pixels( 0, 0, - desc.width as i32, - desc.height as i32, - gl_format, - type_, + img_desc.width as i32, + img_desc.height as i32, + desc.external, + desc.pixel_type, ) } @@ -2010,31 +2005,44 @@ impl Device { } } -/// return (gl_internal_format, gl_format) -fn gl_texture_formats_for_image_format( - gl: &gl::Gl, - format: ImageFormat, -) -> (gl::GLint, gl::GLuint) { - match format { - ImageFormat::R8 => (gl::RED as gl::GLint, gl::RED), - ImageFormat::BGRA8 => match gl.get_type() { - gl::GlType::Gl => (gl::RGBA as gl::GLint, get_gl_format_bgra(gl)), - gl::GlType::Gles => (get_gl_format_bgra(gl) as gl::GLint, get_gl_format_bgra(gl)), - }, - ImageFormat::RGBAF32 => (gl::RGBA32F as gl::GLint, gl::RGBA), - ImageFormat::RG8 => (gl::RG8 as gl::GLint, gl::RG), - ImageFormat::Invalid => unreachable!(), - } +struct FormatDesc { + internal: gl::GLint, + external: gl::GLuint, + pixel_type: gl::GLuint, } -fn gl_type_for_texture_format(format: ImageFormat) -> gl::GLuint { +fn gl_describe_format(gl: &gl::Gl, format: ImageFormat) -> FormatDesc { match format { - ImageFormat::RGBAF32 => gl::FLOAT, - _ => gl::UNSIGNED_BYTE, + ImageFormat::R8 => FormatDesc { + internal: gl::RED as _, + external: gl::RED, + pixel_type: gl::UNSIGNED_BYTE, + }, + ImageFormat::BGRA8 => { + let external = get_gl_format_bgra(gl); + FormatDesc { + internal: match gl.get_type() { + gl::GlType::Gl => gl::RGBA as _, + gl::GlType::Gles => external as _, + }, + external, + pixel_type: gl::UNSIGNED_BYTE, + } + }, + ImageFormat::RGBAF32 => FormatDesc { + internal: gl::RGBA32F as _, + external: gl::RGBA, + pixel_type: gl::FLOAT, + }, + ImageFormat::RG8 => FormatDesc { + internal: gl::RG8 as _, + external: gl::RG, + pixel_type: gl::UNSIGNED_BYTE, + }, + ImageFormat::Invalid => unreachable!(), } } - struct UploadChunk { rect: DeviceUintRect, layer_index: i32, diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index 2accad56e6..626186f362 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -213,7 +213,6 @@ impl Row { // this frame. The list of updates is created by the render backend // during frame construction. It's passed to the render thread // where GL commands can be applied. -#[cfg_attr(feature = "capture", derive(Serialize))] pub enum GpuCacheUpdate { Copy { block_index: usize, @@ -286,7 +285,6 @@ impl FreeBlockLists { } // CPU-side representation of the GPU resource cache texture. -#[cfg_attr(feature = "capture", derive(Serialize))] struct Texture { // Current texture height height: u32, @@ -501,7 +499,6 @@ impl<'a> Drop for GpuDataRequest<'a> { /// The main LRU cache interface. -#[cfg_attr(feature = "capture", derive(Serialize))] pub struct GpuCache { /// Current frame ID. frame_id: FrameId, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index b48b9f4220..e203f72cb3 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -154,9 +154,7 @@ pub enum DebugOutput { #[cfg(feature = "capture")] SaveCapture(CaptureConfig, Vec), #[cfg(feature = "capture")] - LoadCapture { - reset_textures: bool, - } + LoadCapture(PathBuf), } pub enum ResultMsg { diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index fd5e5cf08f..f499156e85 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -596,8 +596,14 @@ impl RenderBackend { DebugCommand::LoadCapture(root) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; + let msg = ResultMsg::DebugOutput( + DebugOutput::LoadCapture(root.clone()) + ); + self.result_tx.send(msg).unwrap(); self.load_capture(&root, &mut profile_counters); - ResultMsg::DebugOutput(DebugOutput::LoadCapture { reset_textures: true }) + // Note: we can't pass `LoadCapture` here since it needs to arrive + // before the `PublishDocument` messages sent by `load_capture`. + continue }, DebugCommand::EnableDualSourceBlending(enable) => { // Set in the config used for any future documents diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 7bee6ec91c..efa13203bb 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -778,7 +778,7 @@ struct CacheRow { } impl CacheRow { - fn new() -> CacheRow { + fn new() -> Self { CacheRow { is_dirty: false } } } @@ -2393,14 +2393,9 @@ impl Renderer { self.save_capture(config, deferred); } #[cfg(feature = "capture")] - DebugOutput::LoadCapture { reset_textures } => { - if reset_textures { - self.device.begin_frame(); - for mut texture in self.texture_resolver.cache_texture_map.drain(..) { - self.device.free_texture_storage(&mut texture); - } - self.device.end_frame(); - } + DebugOutput::LoadCapture(root) => { + self.active_documents.clear(); + self.load_capture(root); } }, ResultMsg::DebugCommand(command) => { @@ -4715,7 +4710,7 @@ impl RendererStats { #[cfg(feature = "capture")] -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] struct PlainTexture { data: String, size: (u32, u32, i32), @@ -4724,6 +4719,13 @@ struct PlainTexture { render_target: Option, } +#[cfg(feature = "capture")] +#[derive(Deserialize, Serialize)] +struct PlainRenderer { + gpu_cache: PlainTexture, + textures: Vec, +} + #[cfg(feature = "capture")] impl Renderer { fn save_texture( @@ -4735,7 +4737,7 @@ impl Renderer { let short_path = format!("textures/{}.raw", name); let read_format = match texture.get_format() { - ImageFormat::A8 => ReadPixelsFormat::R8, + ImageFormat::R8 => ReadPixelsFormat::R8, ImageFormat::BGRA8 => ReadPixelsFormat::Bgra8, ImageFormat::RGBAF32 => ReadPixelsFormat::Rgba32F, _ => unimplemented!() @@ -4765,17 +4767,28 @@ impl Renderer { } } + fn load_texture(texture: &mut Texture, plain: &PlainTexture, root: &PathBuf, device: &mut Device) { + use std::fs::File; + use std::io::Read; + + let mut texels = Vec::new(); + File::open(root.join(&plain.data)) + .unwrap() + .read_to_end(&mut texels) + .unwrap(); + + device.init_texture( + texture, plain.size.0, plain.size.1, + plain.format, plain.filter, plain.render_target, + plain.size.2, Some(texels.as_slice()), + ); + } + fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec) { use std::fs; use std::io::Write; use api::ExternalImageData; - #[derive(Serialize)] - struct PlainRenderer { - gpu_cache: PlainTexture, - textures: Vec, - } - self.device.begin_frame(); self.device.bind_read_target_impl(self.capture_read_fbo); @@ -4845,5 +4858,46 @@ impl Renderer { self.device.bind_read_target(None); self.device.end_frame(); + info!("done."); + } + + fn load_capture(&mut self, root: PathBuf) { + let renderer = match CaptureConfig::deserialize::(&root, "renderer") { + Some(r) => r, + None => return, + }; + + self.device.begin_frame(); + info!("loading cached textures"); + + for texture in self.texture_resolver.cache_texture_map.drain(..) { + self.device.delete_texture(texture); + } + for texture in renderer.textures { + info!("\t{}", texture.data); + let mut t = self.device.create_texture(TextureTarget::Array); + Self::load_texture(&mut t, &texture, &root, &mut self.device); + self.texture_resolver.cache_texture_map.push(t); + } + + info!("loading gpu cache"); + Self::load_texture( + &mut self.gpu_cache_texture.texture, + &renderer.gpu_cache, + &root, + &mut self.device, + ); + match self.gpu_cache_texture.bus { + CacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => { + rows.clear(); + cpu_blocks.clear(); + } + CacheBus::Scatter { ref mut count, .. } => { + *count = 0; //TODO? + } + } + + self.device.end_frame(); + info!("done."); } } From 33d812235f966c8fe4342903d0d26fe647e55fb6 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 15 Jan 2018 14:15:59 -0500 Subject: [PATCH 10/16] Gleam update --- Cargo.lock | 23 +++++++++++++++++------ webrender/Cargo.toml | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c8538699c..8cfb52b789 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,7 @@ name = "cgl" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -359,12 +359,22 @@ dependencies = [ "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gl_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gleam" -version = "0.4.15" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1011,7 +1021,7 @@ dependencies = [ "euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1074,7 +1084,7 @@ dependencies = [ "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "font-loader 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1192,7 +1202,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f" "checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d" -"checksum gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dff613336334932baaa2759d001f14e06ea1a08a247c05962d1423aa0e89ee99" +"checksum gl_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5c19cde55637681450c92f7a05ea16c78e2b6d0587e601ec1ebdab6960854b" +"checksum gleam 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "4f756699879522bc654ecc44ad42ad14c59803c2dacfa5a67a7fc27257a8b4e9" "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1576ffa01849c91b484b95c01d54dddc242b4d50923eaa2d4d74a58c4b9e8fd" diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index e3e36bdf94..d28c607710 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -19,7 +19,7 @@ bincode = "0.9" byteorder = "1.0" euclid = "0.16" fxhash = "0.2.1" -gleam = "0.4.15" +gleam = "0.4.19" lazy_static = "1" log = "0.3" num-traits = "0.1.32" From 207e39baf37c0187fb12c1e99128d16a437a63bb Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 15 Jan 2018 14:16:40 -0500 Subject: [PATCH 11/16] Capture: GPU cache --- webrender/examples/common/boilerplate.rs | 9 +++--- webrender/src/capture.rs | 9 +----- webrender/src/device.rs | 2 +- webrender/src/gpu_cache.rs | 13 +++++--- webrender/src/render_backend.rs | 40 ++++++++++++++++++------ webrender/src/renderer.rs | 10 +++--- webrender_api/src/api.rs | 39 ++++++++++++++++++----- 7 files changed, 82 insertions(+), 40 deletions(-) diff --git a/webrender/examples/common/boilerplate.rs b/webrender/examples/common/boilerplate.rs index 41be05afd5..4b88fc2137 100644 --- a/webrender/examples/common/boilerplate.rs +++ b/webrender/examples/common/boilerplate.rs @@ -281,11 +281,10 @@ pub fn main_wrapper( Some(glutin::VirtualKeyCode::C), ) => { let path: PathBuf = "../captures/example".into(); - if path.is_dir() { - api.load_capture(path); - } else { - api.save_capture(path); - } + //TODO: switch between SCENE/FRAME capture types + // based on "shift" modifier, when `glutin` is updated. + let bits = CaptureBits::all(); + api.save_capture(path, bits); } _ => if example.on_event(event, &api, document_id) { let mut builder = DisplayListBuilder::new(pipeline_id, layout_size); diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index aee8262f70..4d3c8b441a 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -6,18 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use api::{ExternalImageData, ImageDescriptor}; +use api::{CaptureBits, ExternalImageData, ImageDescriptor}; use ron::{de, ser}; use serde::{Deserialize, Serialize}; -bitflags!{ - pub struct CaptureBits: u8 { - const SCENE = 0x1; - const FRAME = 0x2; - } -} - pub struct CaptureConfig { pub root: PathBuf, pub bits: CaptureBits, diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 1ea99bf94b..a3adc9d31e 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -26,7 +26,7 @@ use std::thread; const WORK_AROUND_TEX_IMAGE: bool = cfg!(windows); #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct FrameId(usize); impl FrameId { diff --git a/webrender/src/gpu_cache.rs b/webrender/src/gpu_cache.rs index 626186f362..d59487d597 100644 --- a/webrender/src/gpu_cache.rs +++ b/webrender/src/gpu_cache.rs @@ -56,7 +56,7 @@ struct CacheLocation { /// A single texel in RGBAF32 texture - 16 bytes. #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub struct GpuBlockData { pub data: [f32; 4], } @@ -162,7 +162,7 @@ impl Add for GpuCacheAddress { // An entry in a free-list of blocks in the GPU cache. #[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct Block { // The location in the cache of this block. address: GpuCacheAddress, @@ -192,7 +192,7 @@ impl Block { struct BlockIndex(usize); // A row in the cache texture. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct Row { // The fixed size of blocks that this row supports. // Each row becomes a slab allocator for a fixed block size. @@ -213,6 +213,7 @@ impl Row { // this frame. The list of updates is created by the render backend // during frame construction. It's passed to the render thread // where GL commands can be applied. +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] pub enum GpuCacheUpdate { Copy { block_index: usize, @@ -234,7 +235,7 @@ pub struct GpuCacheUpdateList { // Holds the free lists of fixed size blocks. Mostly // just serves to work around the borrow checker. -#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))] struct FreeBlockLists { free_list_1: Option, free_list_2: Option, @@ -248,7 +249,7 @@ struct FreeBlockLists { } impl FreeBlockLists { - fn new() -> FreeBlockLists { + fn new() -> Self { FreeBlockLists { free_list_1: None, free_list_2: None, @@ -285,6 +286,7 @@ impl FreeBlockLists { } // CPU-side representation of the GPU resource cache texture. +#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))] struct Texture { // Current texture height height: u32, @@ -499,6 +501,7 @@ impl<'a> Drop for GpuDataRequest<'a> { /// The main LRU cache interface. +#[cfg_attr(feature = "capture", derive(Serialize, Deserialize))] pub struct GpuCache { /// Current frame ID. frame_id: FrameId, diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index f499156e85..845ee07b89 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -11,7 +11,9 @@ use api::{IdNamespace, PipelineId, RenderNotifier}; use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods}; use api::channel::{PayloadSender, PayloadSenderHelperMethods}; #[cfg(feature = "capture")] -use capture::{CaptureBits, CaptureConfig, ExternalCaptureImage}; +use api::CapturedDocument; +#[cfg(feature = "capture")] +use capture::{CaptureConfig, ExternalCaptureImage}; #[cfg(feature = "debugger")] use debug_server; use frame::FrameContext; @@ -587,13 +589,13 @@ impl RenderBackend { ResultMsg::DebugOutput(DebugOutput::FetchClipScrollTree(json)) } #[cfg(feature = "capture")] - DebugCommand::SaveCapture(root) => { - let config = CaptureConfig::new(root, CaptureBits::all()); + DebugCommand::SaveCapture(root, bits) => { + let config = CaptureConfig::new(root, bits); let deferred = self.save_capture(&config, &mut profile_counters); ResultMsg::DebugOutput(DebugOutput::SaveCapture(config, deferred)) }, #[cfg(feature = "capture")] - DebugCommand::LoadCapture(root) => { + DebugCommand::LoadCapture(root, tx) => { NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed); frame_counter += 1; let msg = ResultMsg::DebugOutput( @@ -601,6 +603,14 @@ impl RenderBackend { ); self.result_tx.send(msg).unwrap(); self.load_capture(&root, &mut profile_counters); + + for (id, doc) in &self.documents { + let captured = CapturedDocument { + document_id: *id, + root_pipeline_id: doc.scene.root_pipeline_id, + }; + tx.send(captured).unwrap(); + } // Note: we can't pass `LoadCapture` here since it needs to arrive // before the `PublishDocument` messages sent by `load_capture`. continue @@ -678,6 +688,9 @@ impl RenderBackend { &mut profile_counters.resources, ); + info!("generated frame for document {:?} with {} passes", + document_id, rendered_document.frame.passes.len()); + // Publish the frame let pending_update = self.resource_cache.pending_updates(); let msg = ResultMsg::PublishDocument( @@ -827,6 +840,8 @@ impl RenderBackend { config: &CaptureConfig, profile_counters: &mut BackendProfileCounters, ) -> Vec { + use api::CaptureBits; + info!("capture: saving {:?}", config.root); let (resources, deferred) = self.resource_cache.save_capture(&config.root); @@ -863,9 +878,11 @@ impl RenderBackend { config.serialize(&backend, "backend"); if config.bits.contains(CaptureBits::FRAME) { - info!("\tcache"); + info!("\tresource cache"); let caches = self.resource_cache.save_caches(&config.root); - config.serialize(&caches, "cache"); + config.serialize(&caches, "resource_cache"); + info!("\tgpu cache"); + config.serialize(&self.gpu_cache, "gpu_cache"); } deferred @@ -881,14 +898,19 @@ impl RenderBackend { info!("capture: loading {:?}", root); let backend = CaptureConfig::deserialize::(root, "backend") .expect("Unable to open backend.ron"); - let caches_maybe = CaptureConfig::deserialize::(root, "cache"); + let caches_maybe = CaptureConfig::deserialize::(root, "resource_cache"); // Note: it would be great to have RenderBackend to be split // rather explicitly on what's used before and after scene building // so that, for example, we never miss anything in the code below: self.resource_cache.load_capture(backend.resources, caches_maybe,root); - self.gpu_cache = GpuCache::new(); + + self.gpu_cache = match CaptureConfig::deserialize::(root, "gpu_cache") { + Some(gpu_cache) => gpu_cache, + None => GpuCache::new(), + }; + self.documents.clear(); self.default_device_pixel_ratio = backend.default_device_pixel_ratio; self.frame_config = backend.frame_config; @@ -912,7 +934,7 @@ impl RenderBackend { let frame_name = format!("frame-{}-{}", (id.0).0, id.1); let render_doc = match CaptureConfig::deserialize::(root, frame_name) { Some(frame) => { - info!("\tfound built frame"); + info!("\tloaded a built frame with {} passes", frame.passes.len()); doc.frame_ctx.make_rendered_document(frame) } None => { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index efa13203bb..7c692397c4 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -21,7 +21,7 @@ use api::channel::MsgSender; use batch::{BatchKey, BatchKind, BatchTextures, BrushBatchKind}; use batch::{BrushImageSourceKind, TransformBatchKind}; #[cfg(feature = "capture")] -use capture::{CaptureBits, CaptureConfig, ExternalCaptureImage}; +use capture::{CaptureConfig, ExternalCaptureImage}; use debug_colors; use debug_render::DebugRenderer; #[cfg(feature = "debugger")] @@ -2642,9 +2642,9 @@ impl Renderer { let json = self.get_screenshot_for_debugger(); self.debug_server.send(json); } - DebugCommand::SaveCapture(_) | - DebugCommand::LoadCapture(_) => { - panic!("Capture commands are not welcome here!") + DebugCommand::SaveCapture(..) | + DebugCommand::LoadCapture(..) => { + panic!("Capture commands are not welcome here! Did you build with 'capture' feature?") } DebugCommand::EnableDualSourceBlending(_) => { panic!("Should be handled by render backend"); @@ -4787,7 +4787,7 @@ impl Renderer { fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec) { use std::fs; use std::io::Write; - use api::ExternalImageData; + use api::{CaptureBits, ExternalImageData}; self.device.begin_frame(); self.device.bind_read_target_impl(self.capture_read_fbo); diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 0471032013..7ad46ce4ff 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -419,7 +419,25 @@ impl fmt::Debug for DocumentMsg { } } -#[derive(Debug, Clone, Deserialize, Serialize)] +bitflags!{ + /// Bit flags for WR stages to store in a capture. + // Note: capturing `FRAME` without `SCENE` is not currently supported. + #[derive(Deserialize, Serialize)] + pub struct CaptureBits: u8 { + const SCENE = 0x1; + const FRAME = 0x2; + } +} + +/// Information about a loaded capture of each document +/// that is returned by `RenderBackend`. +#[derive(Clone, Debug)] +pub struct CapturedDocument { + pub document_id: DocumentId, + pub root_pipeline_id: Option, +} + +#[derive(Clone, Deserialize, Serialize)] pub enum DebugCommand { /// Display the frame profiler on screen. EnableProfiler(bool), @@ -444,9 +462,9 @@ pub enum DebugCommand { /// Fetch screenshot. FetchScreenshot, /// Save a capture of all the documents state. - SaveCapture(PathBuf), + SaveCapture(PathBuf, CaptureBits), /// Load a capture of all the documents state. - LoadCapture(PathBuf), + LoadCapture(PathBuf, MsgSender), /// Configure if dual-source blending is used, if available. EnableDualSourceBlending(bool), } @@ -964,15 +982,22 @@ impl RenderApi { } /// Save a capture of the current frame state for debugging. - pub fn save_capture(&self, path: PathBuf) { - let msg = ApiMsg::DebugCommand(DebugCommand::SaveCapture(path)); + pub fn save_capture(&self, path: PathBuf, bits: CaptureBits) { + let msg = ApiMsg::DebugCommand(DebugCommand::SaveCapture(path, bits)); self.send_message(msg); } /// Load a capture of the current frame state for debugging. - pub fn load_capture(&self, path: PathBuf) { - let msg = ApiMsg::DebugCommand(DebugCommand::LoadCapture(path)); + pub fn load_capture(&self, path: PathBuf) -> Vec { + let (tx, rx) = channel::msg_channel().unwrap(); + let msg = ApiMsg::DebugCommand(DebugCommand::LoadCapture(path, tx)); self.send_message(msg); + + let mut documents = Vec::new(); + while let Ok(captured_doc) = rx.recv() { + documents.push(captured_doc); + } + documents } pub fn send_debug_cmd(&self, cmd: DebugCommand) { From 7c7c0611a33306b8c3a502106e27696bb94e814b Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 15 Jan 2018 14:16:53 -0500 Subject: [PATCH 12/16] Capture: Wrench loading support --- wrench/src/args.yaml | 8 ++++++++ wrench/src/main.rs | 18 +++++++++--------- wrench/src/rawtest.rs | 2 +- wrench/src/wrench.rs | 17 +++++++++++++++++ 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/wrench/src/args.yaml b/wrench/src/args.yaml index 9023ad5e26..11810fb888 100644 --- a/wrench/src/args.yaml +++ b/wrench/src/args.yaml @@ -149,3 +149,11 @@ subcommands: help: second benchmark file to compare required: true index: 2 + - load: + about: load a capture + args: + - path: + help: directory containing the capture + takes_value: true + required: false + index: 1 diff --git a/wrench/src/main.rs b/wrench/src/main.rs index c4f7205a8b..ae3278de1d 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -419,6 +419,13 @@ fn main() { let second_filename = subargs.value_of("second_filename").unwrap(); perf::compare(first_filename, second_filename); return; + } else if let Some(subargs) = args.subcommand_matches("load") { + let dir = subargs.value_of("path").unwrap_or("../captures/example"); + let mut documents = wrench.api.load_capture(PathBuf::from(dir)); + println!("loaded {:#?}", documents); + let captured = documents.swap_remove(0); + wrench.document_id = captured.document_id; + Box::new(captured) as Box } else { panic!("Should never have gotten here! {:?}", args); }; @@ -551,15 +558,8 @@ fn main() { } if is_headless { - let pixels = window.gl().read_pixels( - 0, - 0, - size.width as gl::GLsizei, - size.height as gl::GLsizei, - gl::RGBA, - gl::UNSIGNED_BYTE, - ); - + let rect = DeviceUintRect::new(DeviceUintPoint::zero(), size); + let pixels = wrench.renderer.read_pixels_rgba8(rect); save_flipped("screenshot.png", pixels, size); } diff --git a/wrench/src/rawtest.rs b/wrench/src/rawtest.rs index f61d441928..e7bb51177f 100644 --- a/wrench/src/rawtest.rs +++ b/wrench/src/rawtest.rs @@ -522,7 +522,7 @@ impl<'a> RawtestHarness<'a> { // 2. capture it - self.wrench.api.save_capture("capture".into()); + self.wrench.api.save_capture("capture".into(), CaptureBits::all()); self.rx.recv().unwrap(); // 3. set a different scene diff --git a/wrench/src/wrench.rs b/wrench/src/wrench.rs index 7714e3d7fc..58975cd238 100644 --- a/wrench/src/wrench.rs +++ b/wrench/src/wrench.rs @@ -121,6 +121,23 @@ pub trait WrenchThing { } } +impl WrenchThing for CapturedDocument { + fn next_frame(&mut self) {} + fn prev_frame(&mut self) {} + fn do_frame(&mut self, wrench: &mut Wrench) -> u32 { + match self.root_pipeline_id.take() { + Some(root_pipeline_id) => { + // skip the first frame - to not overwrite the loaded one + wrench.api.set_root_pipeline(self.document_id, root_pipeline_id); + } + None => { + wrench.refresh(); + } + } + 0 + } +} + pub struct Wrench { window_size: DeviceUintSize, device_pixel_ratio: f32, From ddde21fd42e4d6b55b8f0c550b7a99cb1ad9d5fe Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 15 Jan 2018 15:33:00 -0500 Subject: [PATCH 13/16] Capture: share the push_iter implementation --- webrender/examples/animation.rs | 2 +- webrender_api/src/display_list.rs | 65 +++++++++++++++---------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/webrender/examples/animation.rs b/webrender/examples/animation.rs index c78dbc4431..05298216e6 100644 --- a/webrender/examples/animation.rs +++ b/webrender/examples/animation.rs @@ -66,7 +66,7 @@ impl Example for App { ); // Fill it with a white rect - builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0)); + builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0)); builder.pop_stacking_context(); } diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 06ec03c1be..b907300163 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -491,17 +491,6 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { use display_item::CompletelySpecificDisplayItem::*; use display_item::{CompletelySpecificDisplayItem, GenericDisplayItem}; - // Push a vector of things into the DL according to the - // convention used by `skip_iter` and `push_iter` - fn push_vec(data: &mut Vec, vec: Vec) { - let vec_len = vec.len(); - let byte_size = mem::size_of::() * vec_len; - serialize_fast(data, &byte_size); - serialize_fast(data, &vec_len); - let count = serialize_iter_fast(data, vec.into_iter()); - assert_eq!(count, vec_len); - } - let list = Vec::> ::deserialize(deserializer)?; @@ -511,15 +500,15 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { let item = DisplayItem { item: match complete.item { Clip(specific_item, complex_clips) => { - push_vec(&mut temp, complex_clips); + DisplayListBuilder::push_iter_impl(&mut temp, complex_clips); SpecificDisplayItem::Clip(specific_item) }, ClipChain(specific_item, clip_chain_ids) => { - push_vec(&mut temp, clip_chain_ids); + DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids); SpecificDisplayItem::ClipChain(specific_item) } ScrollFrame(specific_item, complex_clips) => { - push_vec(&mut temp, complex_clips); + DisplayListBuilder::push_iter_impl(&mut temp, complex_clips); SpecificDisplayItem::ScrollFrame(specific_item) }, StickyFrame(specific_item) => SpecificDisplayItem::StickyFrame(specific_item), @@ -527,7 +516,7 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { ClearRectangle => SpecificDisplayItem::ClearRectangle, Line(specific_item) => SpecificDisplayItem::Line(specific_item), Text(specific_item, glyphs) => { - push_vec(&mut temp, glyphs); + DisplayListBuilder::push_iter_impl(&mut temp, glyphs); SpecificDisplayItem::Text(specific_item) }, Image(specific_item) => SpecificDisplayItem::Image(specific_item), @@ -539,12 +528,12 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { SpecificDisplayItem::RadialGradient(specific_item), Iframe(specific_item) => SpecificDisplayItem::Iframe(specific_item), PushStackingContext(specific_item, filters) => { - push_vec(&mut temp, filters); + DisplayListBuilder::push_iter_impl(&mut temp, filters); SpecificDisplayItem::PushStackingContext(specific_item) }, PopStackingContext => SpecificDisplayItem::PopStackingContext, SetGradientStops(stops) => { - push_vec(&mut temp, stops); + DisplayListBuilder::push_iter_impl(&mut temp, stops); SpecificDisplayItem::SetGradientStops }, PushShadow(specific_item) => SpecificDisplayItem::PushShadow(specific_item), @@ -927,38 +916,48 @@ impl DisplayListBuilder { ) } - fn push_iter(&mut self, iter: I) + fn push_iter_impl(data: &mut Vec, iter_source: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone, I::Item: Serialize, { - let iter = iter.into_iter(); + let iter = iter_source.into_iter(); let len = iter.len(); - // Format: // payload_byte_size: usize, item_count: usize, [I; item_count] // We write a dummy value so there's room for later - let byte_size_offset = self.data.len(); - serialize_fast(&mut self.data, &0usize); - serialize_fast(&mut self.data, &len); - let payload_offset = self.data.len(); + let byte_size_offset = data.len(); + serialize_fast(data, &0usize); + serialize_fast(data, &len); + let payload_offset = data.len(); - let count = serialize_iter_fast(&mut self.data, iter.into_iter()); + let count = serialize_iter_fast(data, iter); // Now write the actual byte_size - let final_offset = self.data.len(); + let final_offset = data.len(); let byte_size = final_offset - payload_offset; // Note we don't use serialize_fast because we don't want to change the Vec's len - bincode::serialize_into(&mut &mut self.data[byte_size_offset..], - &byte_size, - bincode::Infinite).unwrap(); + bincode::serialize_into( + &mut &mut data[byte_size_offset..], + &byte_size, + bincode::Infinite, + ).unwrap(); debug_assert_eq!(len, count); } + fn push_iter(&mut self, iter: I) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator + Clone, + I::Item: Serialize, + { + Self::push_iter_impl(&mut self.data, iter); + } + pub fn push_rect(&mut self, info: &LayoutPrimitiveInfo, color: ColorF) { let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem { color }); self.push_item(item, info); @@ -1254,7 +1253,7 @@ impl DisplayListBuilder { } /// Pushes a linear gradient to be displayed. - /// + /// /// The gradient itself is described in the /// `gradient` parameter. It is drawn on /// a "tile" with the dimensions from `tile_size`. @@ -1262,9 +1261,9 @@ impl DisplayListBuilder { /// to the bottom infinitly. If `tile_spacing` /// is not zero spacers with the given dimensions /// are inserted between the tiles as seams. - /// + /// /// The origin of the tiles is given in `info.rect.origin`. - /// If the gradient should only be displayed once limit + /// If the gradient should only be displayed once limit /// the `info.rect.size` to a single tile. /// The gradient is only visible within the local clip. pub fn push_gradient( @@ -1284,7 +1283,7 @@ impl DisplayListBuilder { } /// Pushes a radial gradient to be displayed. - /// + /// /// See [`push_gradient`](#method.push_gradient) for explanation. pub fn push_radial_gradient( &mut self, From f0d9865832cc5b77a33acf666437e2b09618d8df Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 15 Jan 2018 16:27:21 -0500 Subject: [PATCH 14/16] Capture: raw test update --- webrender_api/src/api.rs | 2 +- wrench/src/main.rs | 2 +- wrench/src/rawtest.rs | 26 ++++++++++++++++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 7ad46ce4ff..4e9a9f11b6 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -431,7 +431,7 @@ bitflags!{ /// Information about a loaded capture of each document /// that is returned by `RenderBackend`. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct CapturedDocument { pub document_id: DocumentId, pub root_pipeline_id: Option, diff --git a/wrench/src/main.rs b/wrench/src/main.rs index ae3278de1d..ce82010c2d 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -422,7 +422,7 @@ fn main() { } else if let Some(subargs) = args.subcommand_matches("load") { let dir = subargs.value_of("path").unwrap_or("../captures/example"); let mut documents = wrench.api.load_capture(PathBuf::from(dir)); - println!("loaded {:#?}", documents); + println!("loaded {:?}", documents.iter().map(|cd| cd.document_id).collect::>()); let captured = documents.swap_remove(0); wrench.document_id = captured.document_id; Box::new(captured) as Box diff --git a/wrench/src/rawtest.rs b/wrench/src/rawtest.rs index e7bb51177f..738702e1f3 100644 --- a/wrench/src/rawtest.rs +++ b/wrench/src/rawtest.rs @@ -76,6 +76,7 @@ impl<'a> RawtestHarness<'a> { } fn test_tile_decomposition(&mut self) { + println!("\ttile decomposition..."); // This exposes a crash in tile decomposition let layout_size = LayoutSize::new(800., 800.); let mut resources = ResourceUpdates::new(); @@ -117,6 +118,7 @@ impl<'a> RawtestHarness<'a> { } fn test_retained_blob_images_test(&mut self) { + println!("\tretained blob images test..."); let blob_img; let window_size = self.window.get_inner_size_pixels(); let window_size = DeviceUintSize::new(window_size.0, window_size.1); @@ -197,6 +199,7 @@ impl<'a> RawtestHarness<'a> { } fn test_blob_update_epoch_test(&mut self) { + println!("\tblob update epoch test..."); let (blob_img, blob_img2); let window_size = self.window.get_inner_size_pixels(); let window_size = DeviceUintSize::new(window_size.0, window_size.1); @@ -318,6 +321,7 @@ impl<'a> RawtestHarness<'a> { } fn test_blob_update_test(&mut self) { + println!("\tblob update test..."); let window_size = self.window.get_inner_size_pixels(); let window_size = DeviceUintSize::new(window_size.0, window_size.1); @@ -413,6 +417,7 @@ impl<'a> RawtestHarness<'a> { // Ensures that content doing a save-restore produces the same results as not fn test_save_restore(&mut self) { + println!("\tsave/restore..."); let window_size = self.window.get_inner_size_pixels(); let window_size = DeviceUintSize::new(window_size.0, window_size.1); @@ -478,6 +483,8 @@ impl<'a> RawtestHarness<'a> { } fn test_capture(&mut self) { + println!("\tcapture..."); + let path = "../captures/test"; let layout_size = LayoutSize::new(400., 400.); let (_, windows_height) = self.window.get_inner_size_pixels(); let window_rect = DeviceUintRect::new( @@ -522,7 +529,7 @@ impl<'a> RawtestHarness<'a> { // 2. capture it - self.wrench.api.save_capture("capture".into(), CaptureBits::all()); + self.wrench.api.save_capture(path.into(), CaptureBits::all()); self.rx.recv().unwrap(); // 3. set a different scene @@ -541,13 +548,20 @@ impl<'a> RawtestHarness<'a> { // 4. load the first one - self.wrench.api.load_capture("capture".into()); - self.rx.recv().unwrap(); - - // 5. render and compare + let mut documents = self.wrench.api.load_capture(path.into()); + let captured = documents.swap_remove(0); - self.wrench.api.generate_frame(self.wrench.document_id, None); + // 5. render the built frame and compare let pixels1 = self.render_and_get_pixels(window_rect); assert!(pixels0 == pixels1); + + // 6. rebuild the scene and compare again + self.wrench.api.set_root_pipeline( + captured.document_id, + captured.root_pipeline_id.unwrap() + ); + self.wrench.api.generate_frame(captured.document_id, None); + let pixels2 = self.render_and_get_pixels(window_rect); + assert!(pixels0 == pixels2); } } From cd01eaa08abfd0619364c1de95d28dd64587cf04 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 16 Jan 2018 14:36:42 -0500 Subject: [PATCH 15/16] Capture: proper handling of external images on the Renderer side --- webrender/src/capture.rs | 1 + webrender/src/device.rs | 54 ++++++++++++---- webrender/src/renderer.rs | 130 +++++++++++++++++++++++++++++--------- 3 files changed, 142 insertions(+), 43 deletions(-) diff --git a/webrender/src/capture.rs b/webrender/src/capture.rs index 4d3c8b441a..cedbe6b040 100644 --- a/webrender/src/capture.rs +++ b/webrender/src/capture.rs @@ -60,6 +60,7 @@ impl CaptureConfig { } } +#[derive(Deserialize, Serialize)] pub struct ExternalCaptureImage { pub short_path: String, pub descriptor: ImageDescriptor, diff --git a/webrender/src/device.rs b/webrender/src/device.rs index a3adc9d31e..f7df4a4d32 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -419,13 +419,14 @@ impl Drop for VBO { } } +#[cfg_attr(feature = "capture", derive(Clone))] pub struct ExternalTexture { id: gl::GLuint, target: gl::GLuint, } impl ExternalTexture { - pub fn new(id: u32, target: TextureTarget) -> ExternalTexture { + pub fn new(id: u32, target: TextureTarget) -> Self { ExternalTexture { id, target: target.to_gl_target(), @@ -488,6 +489,16 @@ impl Texture { pub fn get_rt_info(&self) -> Option<&RenderTargetInfo> { self.render_target.as_ref() } + + #[cfg(feature = "capture")] + pub fn into_external(mut self) -> ExternalTexture { + let ext = ExternalTexture { + id: self.id, + target: self.target, + }; + self.id = 0; // don't complain, moved out + ext + } } impl Drop for Texture { @@ -1223,17 +1234,8 @@ impl Device { ); } - pub fn free_texture_storage(&mut self, texture: &mut Texture) { - debug_assert!(self.inside_frame); - - if texture.format == ImageFormat::Invalid { - return; - } - - self.bind_texture(DEFAULT_TEXTURE, texture); - let desc = gl_describe_format(self.gl(), texture.format); - - match texture.target { + fn free_texture_storage_impl(&mut self, target: gl::GLenum, desc: FormatDesc) { + match target { gl::TEXTURE_2D_ARRAY => { self.gl.tex_image_3d( gl::TEXTURE_2D_ARRAY, @@ -1250,7 +1252,7 @@ impl Device { } _ => { self.gl.tex_image_2d( - texture.target, + target, 0, desc.internal, 0, @@ -1262,6 +1264,19 @@ impl Device { ); } } + } + + pub fn free_texture_storage(&mut self, texture: &mut Texture) { + debug_assert!(self.inside_frame); + + if texture.format == ImageFormat::Invalid { + return; + } + + self.bind_texture(DEFAULT_TEXTURE, texture); + let desc = gl_describe_format(self.gl(), texture.format); + + self.free_texture_storage_impl(texture.target, desc); if let Some(RBOId(depth_rb)) = texture.depth_rb.take() { self.gl.delete_renderbuffers(&[depth_rb]); @@ -1288,6 +1303,19 @@ impl Device { texture.id = 0; } + #[cfg(feature = "capture")] + pub fn delete_external_texture(&mut self, mut external: ExternalTexture) { + self.bind_external_texture(DEFAULT_TEXTURE, &external); + //Note: the format descriptor here doesn't really matter + self.free_texture_storage_impl(external.target, FormatDesc { + internal: gl::R8 as _, + external: gl::RED, + pixel_type: gl::UNSIGNED_BYTE, + }); + self.gl.delete_textures(&[external.id]); + external.id = 0; + } + pub fn delete_program(&mut self, mut program: Program) { self.gl.delete_program(program.id); program.id = 0; diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 7c692397c4..7a8f765ec0 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -42,6 +42,7 @@ use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE}; use internal_types::{CacheTextureId, FastHashMap, RenderedDocument, ResultMsg, TextureUpdateOp}; use internal_types::{DebugOutput, RenderPassIndex, RenderTargetInfo, TextureUpdateList, TextureUpdateSource}; use picture::ContentOrigin; +use prim_store::DeferredResolve; use profiler::{BackendProfileCounters, Profiler}; use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers}; use query::{GpuProfiler, GpuTimer}; @@ -700,7 +701,7 @@ impl SourceTextureResolver { SourceTexture::External(external_image) => { let texture = self.external_images .get(&(external_image.id, external_image.channel_index)) - .expect("BUG: External image should be resolved by now!"); + .expect(&format!("BUG: External image should be resolved by now: {:?}", external_image)); device.bind_external_texture(sampler, texture); } SourceTexture::TextureCache(index) => { @@ -1259,7 +1260,7 @@ impl BrushShader { device: &mut Device, features: &[&'static str], precache: bool, - ) -> Result { + ) -> Result { let opaque = try!{ LazilyCompiledShader::new(ShaderKind::Brush, name, @@ -1323,7 +1324,7 @@ impl PrimitiveShader { device: &mut Device, features: &[&'static str], precache: bool, - ) -> Result { + ) -> Result { let simple = try!{ LazilyCompiledShader::new(ShaderKind::Primitive, name, @@ -1382,7 +1383,7 @@ impl TextShader { device: &mut Device, features: &[&'static str], precache: bool, - ) -> Result { + ) -> Result { let simple = try!{ LazilyCompiledShader::new(ShaderKind::Text, name, @@ -1528,6 +1529,16 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result Option { + Some(match ext_type { + ExternalImageType::Texture2DHandle => TextureTarget::Default, + ExternalImageType::Texture2DArrayHandle => TextureTarget::Array, + ExternalImageType::TextureRectHandle => TextureTarget::Rect, + ExternalImageType::TextureExternalHandle => TextureTarget::External, + ExternalImageType::ExternalBuffer => return None, + }) +} + struct FileWatcher { notifier: Box, result_tx: Sender, @@ -1661,6 +1672,8 @@ pub struct Renderer { #[cfg(feature = "capture")] capture_read_fbo: FBOId, + #[cfg(feature = "capture")] + owned_external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>, } #[derive(Debug)] @@ -2287,6 +2300,8 @@ impl Renderer { renderer_errors: Vec::new(), #[cfg(feature = "capture")] capture_read_fbo, + #[cfg(feature = "capture")] + owned_external_images: FastHashMap::default(), }; renderer.set_debug_flags(options.debug_flags); @@ -2786,6 +2801,11 @@ impl Renderer { self.prepare_tile_frame(&mut doc_with_id.1.frame); } + #[cfg(feature = "capture")] + self.texture_resolver.external_images.extend( + self.owned_external_images.iter().map(|(key, value)| (*key, value.clone())) + ); + for &mut (_, RenderedDocument { ref mut frame, .. }) in &mut active_documents { self.prepare_gpu_cache(frame); @@ -2865,7 +2885,7 @@ impl Renderer { fn prepare_gpu_cache(&mut self, frame: &Frame) { let _gm = self.gpu_profile.start_marker("gpu cache update"); - let deferred_update_list = self.update_deferred_resolves(frame); + let deferred_update_list = self.update_deferred_resolves(&frame.deferred_resolves); self.pending_gpu_cache_updates.extend(deferred_update_list); // For an artificial stress test of GPU cache resizing, @@ -3966,12 +3986,12 @@ impl Renderer { } } - fn update_deferred_resolves(&mut self, frame: &Frame) -> Option { + fn update_deferred_resolves(&mut self, deferred_resolves: &[DeferredResolve]) -> Option { // The first thing we do is run through any pending deferred // resolves, and use a callback to get the UV rect for this // custom item. Then we patch the resource_rects structure // here before it's uploaded to the GPU. - if frame.deferred_resolves.is_empty() { + if deferred_resolves.is_empty() { return None; } @@ -3985,25 +4005,15 @@ impl Renderer { updates: Vec::new(), }; - for deferred_resolve in &frame.deferred_resolves { + for deferred_resolve in deferred_resolves { self.gpu_profile.place_marker("deferred resolve"); let props = &deferred_resolve.image_properties; let ext_image = props .external_image .expect("BUG: Deferred resolves must be external images!"); let image = handler.lock(ext_image.id, ext_image.channel_index); - let texture_target = match ext_image.image_type { - ExternalImageType::Texture2DHandle => TextureTarget::Default, - ExternalImageType::Texture2DArrayHandle => TextureTarget::Array, - ExternalImageType::TextureRectHandle => TextureTarget::Rect, - ExternalImageType::TextureExternalHandle => TextureTarget::External, - ExternalImageType::ExternalBuffer => { - panic!( - "{:?} is not a suitable image type in update_deferred_resolves().", - ext_image.image_type - ); - } - }; + let texture_target = get_external_image_target(ext_image.image_type) + .expect(&format!("{:?} is not a suitable image type in update_deferred_resolves()", ext_image.image_type)); // In order to produce the handle, the external image handler may call into // the GL context and change some states. @@ -4557,6 +4567,12 @@ impl Renderer { self.ps_hw_composite.deinit(&mut self.device); self.ps_split_composite.deinit(&mut self.device); self.ps_composite.deinit(&mut self.device); + #[cfg(feature = "capture")] + self.device.delete_fbo(self.capture_read_fbo); + #[cfg(feature = "capture")] + for (_, ext) in self.owned_external_images { + self.device.delete_external_texture(ext); + } self.device.end_frame(); } } @@ -4643,7 +4659,7 @@ pub struct RendererOptions { } impl Default for RendererOptions { - fn default() -> RendererOptions { + fn default() -> Self { RendererOptions { device_pixel_ratio: 1.0, resource_override_path: None, @@ -4724,6 +4740,37 @@ struct PlainTexture { struct PlainRenderer { gpu_cache: PlainTexture, textures: Vec, + external_images: Vec +} + +#[cfg(feature = "capture")] +struct DummyExternalImageHandler { + data: FastHashMap<(ExternalImageId, u8), Vec>, +} + +#[cfg(feature = "capture")] +impl ExternalImageHandler for DummyExternalImageHandler { + fn lock(&mut self, key: ExternalImageId, channel_index: u8) -> ExternalImage { + let slice = &self.data[&(key, channel_index)]; + ExternalImage { + u0: 0.0, + v0: 0.0, + u1: 1.0, + v1: 1.0, + source: ExternalImageSource::RawData(slice), + } + } + fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {} +} + +#[cfg(feature = "capture")] +impl OutputImageHandler for () { + fn lock(&mut self, _: PipelineId) -> Option<(u32, DeviceIntSize)> { + None + } + fn unlock(&mut self, _: PipelineId) { + unreachable!() + } } #[cfg(feature = "capture")] @@ -4767,7 +4814,7 @@ impl Renderer { } } - fn load_texture(texture: &mut Texture, plain: &PlainTexture, root: &PathBuf, device: &mut Device) { + fn load_texture(texture: &mut Texture, plain: &PlainTexture, root: &PathBuf, device: &mut Device) -> Vec { use std::fs::File; use std::io::Read; @@ -4782,6 +4829,8 @@ impl Renderer { plain.format, plain.filter, plain.render_target, plain.size.2, Some(texels.as_slice()), ); + + texels } fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec) { @@ -4799,18 +4848,12 @@ impl Renderer { let handler = self.external_image_handler .as_mut() .expect("Unable to lock the external image handler!"); - for def in deferred_images { + for def in &deferred_images { let ExternalImageData { id, channel_index, image_type } = def.external; let data = match handler.lock(id, channel_index).source { ExternalImageSource::RawData(data) => data.to_vec(), ExternalImageSource::NativeTexture(gl_id) => { - let target = match image_type { - ExternalImageType::Texture2DHandle => TextureTarget::Default, - ExternalImageType::Texture2DArrayHandle => TextureTarget::Array, // layer is always 0? - ExternalImageType::TextureRectHandle => TextureTarget::Rect, - ExternalImageType::TextureExternalHandle => TextureTarget::External, - ExternalImageType::ExternalBuffer => unimplemented!(), - }; + let target = get_external_image_target(image_type).unwrap(); self.device.attach_read_texture_external(gl_id, target, 0); self.device.read_pixels(&def.descriptor) } @@ -4843,6 +4886,7 @@ impl Renderer { "gpu", &config.root, &mut self.device, ), textures: Vec::new(), + external_images: deferred_images, }; info!("saving cached textures"); @@ -4897,7 +4941,33 @@ impl Renderer { } } + info!("loading external images"); + assert!(self.texture_resolver.external_images.is_empty()); + let mut image_handler = DummyExternalImageHandler { + data: FastHashMap::default(), + }; + + for ExternalCaptureImage { short_path, external, descriptor } in renderer.external_images { + let target = get_external_image_target(external.image_type).unwrap(); + let layer_count = 1; //TODO? + let plain = PlainTexture { + data: short_path, + size: (descriptor.width, descriptor.height, layer_count), + format: descriptor.format, + filter: TextureFilter::Linear, //TODO? + render_target: None, + }; + + let mut t = self.device.create_texture(target); + let data = Self::load_texture(&mut t, &plain, &root, &mut self.device); + let key = (external.id, external.channel_index); + self.owned_external_images.insert(key, t.into_external()); + image_handler.data.insert(key, data); + } + self.device.end_frame(); + self.external_image_handler = Some(Box::new(image_handler) as Box<_>); + self.output_image_handler = Some(Box::new(()) as Box<_>); info!("done."); } } From 712e62fc78da13e0abbb874aed90d3014e410e72 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 16 Jan 2018 16:16:05 -0500 Subject: [PATCH 16/16] Capture: refined and clarified the associated TODOs, re-exposed the read_pixels_into API --- webrender/src/device.rs | 9 ++++---- webrender/src/render_backend.rs | 2 ++ webrender/src/renderer.rs | 41 ++++++++++++++++++--------------- webrender/src/resource_cache.rs | 12 ++++++---- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/webrender/src/device.rs b/webrender/src/device.rs index f7df4a4d32..963981994f 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -1550,7 +1550,7 @@ impl Device { &mut self, rect: DeviceUintRect, format: ReadPixelsFormat, - output: &mut Vec, + output: &mut [u8], ) { let (gl_format, gl_type, pixel_size) = match format { ReadPixelsFormat::R8 => (gl::RED, gl::UNSIGNED_BYTE, 1), @@ -1558,9 +1558,8 @@ impl Device { ReadPixelsFormat::Bgra8 => (get_gl_format_bgra(self.gl()), gl::UNSIGNED_BYTE, 4), ReadPixelsFormat::Rgba32F => (gl::RGBA, gl::FLOAT, 16), }; - let size = (pixel_size * rect.size.width * rect.size.height) as usize; - let base = output.len(); - output.extend((0..size).map(|_| 0)); + let size_in_bytes = (pixel_size * rect.size.width * rect.size.height) as usize; + assert_eq!(output.len(), size_in_bytes); self.gl.flush(); self.gl.read_pixels_into_buffer( @@ -1570,7 +1569,7 @@ impl Device { rect.size.height as _, gl_format, gl_type, - &mut output[base ..], + output, ); } diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 845ee07b89..1d5589747a 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -858,6 +858,8 @@ impl RenderBackend { &mut profile_counters.resources, ); //TODO: write down full `RenderedDocument`? + // it has `pipeline_epoch_map` and `layers_bouncing_back`, + // which may capture necessary details for some cases. let file_name = format!("frame-{}-{}", (id.0).0, id.1); config.serialize(&rendered_document.frame, file_name); } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 7a8f765ec0..ae4a1ad6d0 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -4492,15 +4492,20 @@ impl Renderer { ); } + /// Pass-through to `Device::read_pixels_into`, used by Gecko's WR bindings. + pub fn read_pixels_into(&mut self, rect: DeviceUintRect, format: ReadPixelsFormat, output: &mut [u8]) { + self.device.read_pixels_into(rect, format, output); + } + pub fn read_pixels_rgba8(&mut self, rect: DeviceUintRect) -> Vec { - let mut pixels = Vec::new(); + let mut pixels = vec![0; (rect.size.width * rect.size.height * 4) as usize]; self.device.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut pixels); pixels } pub fn read_gpu_cache(&mut self) -> (DeviceUintSize, Vec) { let size = self.gpu_cache_texture.texture.get_dimensions(); - let mut texels = Vec::new(); + let mut texels = vec![0; (size.width * size.height * 16) as usize]; self.device.begin_frame(); self.device.bind_read_target(Some((&self.gpu_cache_texture.texture, 0))); self.device.read_pixels_into( @@ -4783,10 +4788,10 @@ impl Renderer { let short_path = format!("textures/{}.raw", name); - let read_format = match texture.get_format() { - ImageFormat::R8 => ReadPixelsFormat::R8, - ImageFormat::BGRA8 => ReadPixelsFormat::Bgra8, - ImageFormat::RGBAF32 => ReadPixelsFormat::Rgba32F, + let (read_format, bytes_per_pixel) = match texture.get_format() { + ImageFormat::R8 => (ReadPixelsFormat::R8, 1u32), + ImageFormat::BGRA8 => (ReadPixelsFormat::Bgra8, 4u32), + ImageFormat::RGBAF32 => (ReadPixelsFormat::Rgba32F, 16u32), _ => unimplemented!() }; let rect = DeviceUintRect::new( @@ -4794,17 +4799,18 @@ impl Renderer { texture.get_dimensions(), ); - let mut data = Vec::new(); + let mut file = fs::File::create(root.join(&short_path)) + .expect(&format!("Unable to create {}", short_path)); + let bytes_per_layer = (rect.size.width * rect.size.height * bytes_per_pixel) as usize; + let mut data = vec![0; bytes_per_layer]; + for layer_id in 0 .. texture.get_layer_count() { device.attach_read_texture(texture, layer_id); device.read_pixels_into(rect, read_format, &mut data); + file.write_all(&data) + .unwrap(); } - fs::File::create(root.join(&short_path)) - .expect(&format!("Unable to create {}", short_path)) - .write_all(&data) - .unwrap(); - PlainTexture { data: short_path, size: (rect.size.width, rect.size.height, texture.get_layer_count()), @@ -4843,8 +4849,6 @@ impl Renderer { if !deferred_images.is_empty() { info!("saving external images"); - //TODO: add a switch to check `self.texture_resolver.external_images` - // instead of locking with the actual handler. let handler = self.external_image_handler .as_mut() .expect("Unable to lock the external image handler!"); @@ -4936,9 +4940,7 @@ impl Renderer { rows.clear(); cpu_blocks.clear(); } - CacheBus::Scatter { ref mut count, .. } => { - *count = 0; //TODO? - } + CacheBus::Scatter { .. } => {} } info!("loading external images"); @@ -4949,12 +4951,13 @@ impl Renderer { for ExternalCaptureImage { short_path, external, descriptor } in renderer.external_images { let target = get_external_image_target(external.image_type).unwrap(); - let layer_count = 1; //TODO? + //TODO: provide a way to query both the layer count and the filter from external images + let (layer_count, filter) = (1, TextureFilter::Linear); let plain = PlainTexture { data: short_path, size: (descriptor.width, descriptor.height, layer_count), format: descriptor.format, - filter: TextureFilter::Linear, //TODO? + filter, render_target: None, }; diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 2fefe5f360..fcd957c04b 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -1082,7 +1082,8 @@ impl ResourceCache { Entry::Vacant(e) => e, }; - //TODO: option to save as PNG + //TODO: option to save as PNG: + // https://github.com/servo/webrender/issues/2234 let file_name = format!("{}.raw", image_id); let short_path = format!("images/{}", file_name); fs::File::create(path_images.join(file_name)) @@ -1095,7 +1096,9 @@ impl ResourceCache { assert_eq!(template.tiling, None); let request = BlobImageRequest { key, - tile: None, //TODO: tiled blob images + //TODO: support tiled blob images + // https://github.com/servo/webrender/issues/2236 + tile: None, }; let renderer = self.blob_image_renderer.as_mut().unwrap(); @@ -1245,7 +1248,9 @@ impl ResourceCache { use std::io::Read; info!("loading resource cache"); - //TODO: pre-load the raw map + //TODO: instead of filling the local path to Arc map as we process + // each of the resource types, we could go through all of the local paths + // and fill out the map as the first step. let mut raw_map = FastHashMap::>>::default(); match caches { @@ -1349,7 +1354,6 @@ impl ResourceCache { e.get().clone() } Entry::Vacant(e) => { - //TODO: consider merging the code path with font loading let mut buffer = Vec::new(); File::open(root.join(e.key())) .expect(&format!("Unable to open {}", e.key()))