diff options
Diffstat (limited to 'gfx/wr/webrender/src')
-rw-r--r-- | gfx/wr/webrender/src/batch.rs | 258 | ||||
-rw-r--r-- | gfx/wr/webrender/src/capture.rs | 2 | ||||
-rw-r--r-- | gfx/wr/webrender/src/command_buffer.rs | 19 | ||||
-rw-r--r-- | gfx/wr/webrender/src/device/gl.rs | 9 | ||||
-rw-r--r-- | gfx/wr/webrender/src/device/query_gl.rs | 2 | ||||
-rw-r--r-- | gfx/wr/webrender/src/internal_types.rs | 2 | ||||
-rw-r--r-- | gfx/wr/webrender/src/lib.rs | 2 | ||||
-rw-r--r-- | gfx/wr/webrender/src/pattern.rs | 68 | ||||
-rw-r--r-- | gfx/wr/webrender/src/prepare.rs | 594 | ||||
-rw-r--r-- | gfx/wr/webrender/src/prim_store/storage.rs | 2 | ||||
-rw-r--r-- | gfx/wr/webrender/src/profiler.rs | 128 | ||||
-rw-r--r-- | gfx/wr/webrender/src/quad.rs | 695 | ||||
-rw-r--r-- | gfx/wr/webrender/src/render_backend.rs | 3 | ||||
-rw-r--r-- | gfx/wr/webrender/src/render_target.rs | 50 | ||||
-rw-r--r-- | gfx/wr/webrender/src/render_task.rs | 11 | ||||
-rw-r--r-- | gfx/wr/webrender/src/renderer/mod.rs | 75 | ||||
-rw-r--r-- | gfx/wr/webrender/src/renderer/shade.rs | 16 | ||||
-rw-r--r-- | gfx/wr/webrender/src/resource_cache.rs | 6 | ||||
-rw-r--r-- | gfx/wr/webrender/src/texture_pack/mod.rs | 12 | ||||
-rw-r--r-- | gfx/wr/webrender/src/util.rs | 56 |
20 files changed, 1080 insertions, 930 deletions
diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs index 605283c58d..7cf9341515 100644 --- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -6,13 +6,14 @@ use api::{AlphaType, ClipMode, ImageBufferKind}; use api::{FontInstanceFlags, YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF}; use api::units::*; use crate::clip::{ClipNodeFlags, ClipNodeRange, ClipItemKind, ClipStore}; -use crate::command_buffer::{PrimitiveCommand, QuadFlags}; +use crate::command_buffer::PrimitiveCommand; use crate::composite::CompositorSurfaceKind; +use crate::pattern::PatternKind; use crate::spatial_tree::{SpatialTree, SpatialNodeIndex, CoordinateSystemId}; use glyph_rasterizer::{GlyphFormat, SubpixelDirection}; use crate::gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress}; use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator}; -use crate::gpu_types::{SplitCompositeInstance, QuadInstance}; +use crate::gpu_types::SplitCompositeInstance; use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use crate::gpu_types::{ImageBrushData, get_shader_opacity, BoxShadowData, MaskInstance}; @@ -22,12 +23,13 @@ use crate::picture::{Picture3DContext, PictureCompositeMode, calculate_screen_uv use crate::prim_store::{PrimitiveInstanceKind, ClipData}; use crate::prim_store::{PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex}; -use crate::prim_store::{VECS_PER_SEGMENT, PrimitiveInstanceIndex}; +use crate::prim_store::VECS_PER_SEGMENT; +use crate::quad; use crate::render_target::RenderTargetContext; use crate::render_task_graph::{RenderTaskId, RenderTaskGraph}; use crate::render_task::{RenderTaskAddress, RenderTaskKind, SubPass}; use crate::renderer::{BlendMode, GpuBufferBuilder, ShaderColorMode}; -use crate::renderer::{MAX_VERTEX_TEXTURE_WIDTH, GpuBufferAddress}; +use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH; use crate::resource_cache::{GlyphFetchResult, ImageProperties}; use crate::space::SpaceMapper; use crate::visibility::{PrimitiveVisibilityFlags, VisibilityState}; @@ -72,7 +74,7 @@ pub enum BatchKind { SplitComposite, TextRun(GlyphFormat), Brush(BrushBatchKind), - Primitive, + Quad(PatternKind), } /// Input textures for a primitive, without consideration of clip mask @@ -794,51 +796,6 @@ impl BatchBuilder { self.batcher.clear(); } - /// Add a quad primitive to the batch list, appllying edge AA and tiling - /// segments as required. - fn add_quad_to_batch( - &mut self, - prim_instance_index: PrimitiveInstanceIndex, - transform_id: TransformPaletteId, - prim_address_f: GpuBufferAddress, - quad_flags: QuadFlags, - edge_flags: EdgeAaSegmentMask, - segment_index: u8, - task_id: RenderTaskId, - z_generator: &mut ZBufferIdGenerator, - prim_instances: &[PrimitiveInstance], - render_tasks: &RenderTaskGraph, - gpu_buffer_builder: &mut GpuBufferBuilder, - ) { - let prim_instance = &prim_instances[prim_instance_index.0 as usize]; - let prim_info = &prim_instance.vis; - let bounding_rect = &prim_info.clip_chain.pic_coverage_rect; - let z_id = z_generator.next(); - - add_quad_to_batch( - self.batcher.render_task_address, - transform_id, - prim_address_f, - quad_flags, - edge_flags, - segment_index, - task_id, - z_id, - render_tasks, - gpu_buffer_builder, - |key, instance| { - let batch = self.batcher.set_params_and_get_batch( - key, - BatchFeatures::empty(), - bounding_rect, - z_id, - ); - - batch.push(instance); - } - ); - } - // Adds a primitive to a batch. // It can recursively call itself in some situations, for // example if it encounters a picture where the items @@ -869,38 +826,71 @@ impl BatchBuilder { PrimitiveCommand::Instance { prim_instance_index, gpu_buffer_address } => { (prim_instance_index, Some(gpu_buffer_address.as_int())) } - PrimitiveCommand::Quad { prim_instance_index, gpu_buffer_address, quad_flags, edge_flags, transform_id } => { + PrimitiveCommand::Quad { pattern, pattern_input, prim_instance_index, gpu_buffer_address, quad_flags, edge_flags, transform_id } => { + let prim_instance = &prim_instances[prim_instance_index.0 as usize]; + let prim_info = &prim_instance.vis; + let bounding_rect = &prim_info.clip_chain.pic_coverage_rect; + let render_task_address = self.batcher.render_task_address; + if segments.is_empty() { - self.add_quad_to_batch( - *prim_instance_index, + let z_id = z_generator.next(); + // TODO: Some pattern types will sample from render tasks. + // At the moment quads only use a render task as source for + // segments which have been pre-rendered and masked. + let src_color_task_id = RenderTaskId::INVALID; + + quad::add_to_batch( + *pattern, + *pattern_input, + render_task_address, *transform_id, *gpu_buffer_address, *quad_flags, *edge_flags, INVALID_SEGMENT_INDEX as u8, - RenderTaskId::INVALID, - z_generator, - prim_instances, + src_color_task_id, + z_id, render_tasks, gpu_buffer_builder, + |key, instance| { + let batch = self.batcher.set_params_and_get_batch( + key, + BatchFeatures::empty(), + bounding_rect, + z_id, + ); + batch.push(instance); + }, ); } else { for (i, task_id) in segments.iter().enumerate() { // TODO(gw): edge_flags should be per-segment, when used for more than composites debug_assert!(edge_flags.is_empty()); - self.add_quad_to_batch( - *prim_instance_index, + let z_id = z_generator.next(); + + quad::add_to_batch( + *pattern, + *pattern_input, + render_task_address, *transform_id, *gpu_buffer_address, *quad_flags, *edge_flags, i as u8, *task_id, - z_generator, - prim_instances, + z_id, render_tasks, gpu_buffer_builder, + |key, instance| { + let batch = self.batcher.set_params_and_get_batch( + key, + BatchFeatures::empty(), + bounding_rect, + z_id, + ); + batch.push(instance); + }, ); } } @@ -3892,154 +3882,6 @@ impl<'a, 'rc> RenderTargetContext<'a, 'rc> { } } -pub fn add_quad_to_batch<F>( - render_task_address: RenderTaskAddress, - transform_id: TransformPaletteId, - prim_address_f: GpuBufferAddress, - quad_flags: QuadFlags, - edge_flags: EdgeAaSegmentMask, - segment_index: u8, - task_id: RenderTaskId, - z_id: ZBufferId, - render_tasks: &RenderTaskGraph, - gpu_buffer_builder: &mut GpuBufferBuilder, - mut f: F, -) where F: FnMut(BatchKey, PrimitiveInstanceData) { - - #[repr(u8)] - enum PartIndex { - Center = 0, - Left = 1, - Top = 2, - Right = 3, - Bottom = 4, - All = 5, - } - - let mut writer = gpu_buffer_builder.i32.write_blocks(1); - writer.push_one([ - transform_id.0 as i32, - z_id.0, - 0, - 0, - ]); - let prim_address_i = writer.finish(); - - let texture = match task_id { - RenderTaskId::INVALID => { - TextureSource::Invalid - } - _ => { - let texture = render_tasks - .resolve_texture(task_id) - .expect("bug: valid task id must be resolvable"); - - texture - } - }; - - let textures = BatchTextures::prim_textured( - texture, - TextureSource::Invalid, - ); - - let default_blend_mode = if quad_flags.contains(QuadFlags::IS_OPAQUE) && task_id == RenderTaskId::INVALID { - BlendMode::None - } else { - BlendMode::PremultipliedAlpha - }; - - let edge_flags_bits = edge_flags.bits(); - - let prim_batch_key = BatchKey { - blend_mode: default_blend_mode, - kind: BatchKind::Primitive, - textures, - }; - - let edge_batch_key = BatchKey { - blend_mode: BlendMode::PremultipliedAlpha, - kind: BatchKind::Primitive, - textures, - }; - - if edge_flags.is_empty() { - let instance = QuadInstance { - render_task_address, - prim_address_i, - prim_address_f, - z_id, - transform_id, - edge_flags: edge_flags_bits, - quad_flags: quad_flags.bits(), - part_index: PartIndex::All as u8, - segment_index, - }; - - f(prim_batch_key, instance.into()); - } else if quad_flags.contains(QuadFlags::USE_AA_SEGMENTS) { - let main_instance = QuadInstance { - render_task_address, - prim_address_i, - prim_address_f, - z_id, - transform_id, - edge_flags: edge_flags_bits, - quad_flags: quad_flags.bits(), - part_index: PartIndex::Center as u8, - segment_index, - }; - - if edge_flags.contains(EdgeAaSegmentMask::LEFT) { - let instance = QuadInstance { - part_index: PartIndex::Left as u8, - ..main_instance - }; - f(edge_batch_key, instance.into()); - } - if edge_flags.contains(EdgeAaSegmentMask::RIGHT) { - let instance = QuadInstance { - part_index: PartIndex::Top as u8, - ..main_instance - }; - f(edge_batch_key, instance.into()); - } - if edge_flags.contains(EdgeAaSegmentMask::TOP) { - let instance = QuadInstance { - part_index: PartIndex::Right as u8, - ..main_instance - }; - f(edge_batch_key, instance.into()); - } - if edge_flags.contains(EdgeAaSegmentMask::BOTTOM) { - let instance = QuadInstance { - part_index: PartIndex::Bottom as u8, - ..main_instance - }; - f(edge_batch_key, instance.into()); - } - - let instance = QuadInstance { - ..main_instance - }; - f(prim_batch_key, instance.into()); - } else { - let instance = QuadInstance { - render_task_address, - prim_address_i, - prim_address_f, - z_id, - transform_id, - edge_flags: edge_flags_bits, - quad_flags: quad_flags.bits(), - part_index: PartIndex::All as u8, - segment_index, - }; - - f(edge_batch_key, instance.into()); - } -} - impl CompositorSurfaceKind { /// Returns true if the type of compositor surface needs an alpha cutout rendered fn needs_cutout(&self) -> bool { diff --git a/gfx/wr/webrender/src/capture.rs b/gfx/wr/webrender/src/capture.rs index 5cc1f90bab..7184f44b50 100644 --- a/gfx/wr/webrender/src/capture.rs +++ b/gfx/wr/webrender/src/capture.rs @@ -14,8 +14,6 @@ use api::units::DeviceIntSize; #[cfg(feature = "capture")] use crate::print_tree::{PrintableTree, PrintTree}; use crate::render_api::CaptureBits; -use ron; -use serde; #[derive(Clone)] diff --git a/gfx/wr/webrender/src/command_buffer.rs b/gfx/wr/webrender/src/command_buffer.rs index c0630db054..e630ceb628 100644 --- a/gfx/wr/webrender/src/command_buffer.rs +++ b/gfx/wr/webrender/src/command_buffer.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::units::PictureRect; +use crate::pattern::{PatternKind, PatternShaderInput}; use crate::{spatial_tree::SpatialNodeIndex, render_task_graph::RenderTaskId, surface::SurfaceTileDescriptor, picture::TileKey, renderer::GpuBufferAddress, FastHashMap, prim_store::PrimitiveInstanceIndex, gpu_cache::GpuCacheAddress}; use crate::gpu_types::{QuadSegment, TransformPaletteId}; use crate::segment::EdgeAaSegmentMask; @@ -115,6 +116,8 @@ pub enum PrimitiveCommand { gpu_buffer_address: GpuBufferAddress, }, Quad { + pattern: PatternKind, + pattern_input: PatternShaderInput, // TODO(gw): Used for bounding rect only, could possibly remove prim_instance_index: PrimitiveInstanceIndex, gpu_buffer_address: GpuBufferAddress, @@ -144,6 +147,8 @@ impl PrimitiveCommand { } pub fn quad( + pattern: PatternKind, + pattern_input: PatternShaderInput, prim_instance_index: PrimitiveInstanceIndex, gpu_buffer_address: GpuBufferAddress, transform_id: TransformPaletteId, @@ -151,6 +156,8 @@ impl PrimitiveCommand { edge_flags: EdgeAaSegmentMask, ) -> Self { PrimitiveCommand::Quad { + pattern, + pattern_input, prim_instance_index, gpu_buffer_address, transform_id, @@ -232,8 +239,11 @@ impl CommandBuffer { self.commands.push(Command::draw_instance(prim_instance_index)); self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32)); } - PrimitiveCommand::Quad { prim_instance_index, gpu_buffer_address, transform_id, quad_flags, edge_flags } => { + PrimitiveCommand::Quad { pattern, pattern_input, prim_instance_index, gpu_buffer_address, transform_id, quad_flags, edge_flags } => { self.commands.push(Command::draw_quad(prim_instance_index)); + self.commands.push(Command::data(pattern as u32)); + self.commands.push(Command::data(pattern_input.0 as u32)); + self.commands.push(Command::data(pattern_input.1 as u32)); self.commands.push(Command::data((gpu_buffer_address.u as u32) << 16 | gpu_buffer_address.v as u32)); self.commands.push(Command::data(transform_id.0)); self.commands.push(Command::data((quad_flags.bits() as u32) << 16 | edge_flags.bits() as u32)); @@ -279,6 +289,11 @@ impl CommandBuffer { } Command::CMD_DRAW_QUAD => { let prim_instance_index = PrimitiveInstanceIndex(param); + let pattern = PatternKind::from_u32(cmd_iter.next().unwrap().0); + let pattern_input = PatternShaderInput( + cmd_iter.next().unwrap().0 as i32, + cmd_iter.next().unwrap().0 as i32, + ); let data = cmd_iter.next().unwrap(); let transform_id = TransformPaletteId(cmd_iter.next().unwrap().0); let bits = cmd_iter.next().unwrap().0; @@ -289,6 +304,8 @@ impl CommandBuffer { v: (data.0 & 0xffff) as u16, }; let cmd = PrimitiveCommand::quad( + pattern, + pattern_input, prim_instance_index, gpu_buffer_address, transform_id, diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs index 04a7e13023..c17a16a757 100644 --- a/gfx/wr/webrender/src/device/gl.rs +++ b/gfx/wr/webrender/src/device/gl.rs @@ -199,10 +199,6 @@ pub fn get_unoptimized_shader_source(shader_name: &str, base_path: Option<&PathB } } -pub trait FileWatcherHandler: Send { - fn file_changed(&self, path: PathBuf); -} - impl VertexAttributeKind { fn size_in_bytes(&self) -> u32 { match *self { @@ -938,7 +934,7 @@ impl VertexUsageHint { } #[derive(Copy, Clone, Debug)] -pub struct UniformLocation(gl::GLint); +pub struct UniformLocation(#[allow(dead_code)] gl::GLint); impl UniformLocation { pub const INVALID: Self = UniformLocation(-1); @@ -1412,7 +1408,8 @@ fn parse_mali_version(version_string: &str) -> Option<(u32, u32, u32)> { let (r_str, version_string) = version_string.split_once("p")?; let r = r_str.parse().ok()?; - let (p_str, _) = version_string.split_once("-")?; + // Not all devices have the trailing string following the "p" number. + let (p_str, _) = version_string.split_once("-").unwrap_or((version_string, "")); let p = p_str.parse().ok()?; Some((v, r, p)) diff --git a/gfx/wr/webrender/src/device/query_gl.rs b/gfx/wr/webrender/src/device/query_gl.rs index c7fd9a9070..db3dfcd6cf 100644 --- a/gfx/wr/webrender/src/device/query_gl.rs +++ b/gfx/wr/webrender/src/device/query_gl.rs @@ -313,6 +313,6 @@ impl Drop for GpuMarker { } #[must_use] -pub struct GpuTimeQuery(GpuMarker); +pub struct GpuTimeQuery(#[allow(dead_code)] GpuMarker); #[must_use] pub struct GpuSampleQuery; diff --git a/gfx/wr/webrender/src/internal_types.rs b/gfx/wr/webrender/src/internal_types.rs index c76b7d362d..97827a98fe 100644 --- a/gfx/wr/webrender/src/internal_types.rs +++ b/gfx/wr/webrender/src/internal_types.rs @@ -6,7 +6,6 @@ use api::{ColorF, DocumentId, ExternalImageId, PrimitiveFlags, Parameter, Render use api::{ImageFormat, NotificationRequest, Shadow, FilterOp, ImageBufferKind}; use api::FramePublishId; use api::units::*; -use api; use crate::render_api::DebugCommand; use crate::composite::NativeSurfaceOperation; use crate::device::TextureFilter; @@ -692,7 +691,6 @@ impl ResourceUpdateList { /// Wraps a frame_builder::Frame, but conceptually could hold more information pub struct RenderedDocument { pub frame: Frame, - pub is_new_scene: bool, pub profile: TransactionProfile, pub render_reasons: RenderReasons, pub frame_stats: Option<FullFrameStats> diff --git a/gfx/wr/webrender/src/lib.rs b/gfx/wr/webrender/src/lib.rs index d319bc68bf..2699da7cfb 100644 --- a/gfx/wr/webrender/src/lib.rs +++ b/gfx/wr/webrender/src/lib.rs @@ -101,11 +101,13 @@ mod gpu_types; mod hit_test; mod internal_types; mod lru_cache; +mod pattern; mod picture; mod picture_graph; mod prepare; mod prim_store; mod print_tree; +mod quad; mod render_backend; mod render_target; mod render_task_graph; diff --git a/gfx/wr/webrender/src/pattern.rs b/gfx/wr/webrender/src/pattern.rs new file mode 100644 index 0000000000..36a06fa2b9 --- /dev/null +++ b/gfx/wr/webrender/src/pattern.rs @@ -0,0 +1,68 @@ +/* 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 api::{ColorF, PremultipliedColorF}; + +#[repr(u32)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum PatternKind { + ColorOrTexture = 0, + + Mask = 1, + // When adding patterns, don't forget to update the NUM_PATTERNS constant. +} + +pub const NUM_PATTERNS: u32 = 2; + +impl PatternKind { + pub fn from_u32(val: u32) -> Self { + assert!(val < NUM_PATTERNS); + unsafe { std::mem::transmute(val) } + } +} + +/// A 32bit payload used as input for the pattern-specific logic in the shader. +/// +/// Patterns typically use it as a GpuBuffer offset to fetch their data. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PatternShaderInput(pub i32, pub i32); + +impl Default for PatternShaderInput { + fn default() -> Self { + PatternShaderInput(0, 0) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct Pattern { + pub kind: PatternKind, + pub shader_input: PatternShaderInput, + pub base_color: PremultipliedColorF, + pub is_opaque: bool, +} + +impl Pattern { + pub fn color(color: ColorF) -> Self { + Pattern { + kind: PatternKind::ColorOrTexture, + shader_input: PatternShaderInput::default(), + base_color: color.premultiplied(), + is_opaque: color.a >= 1.0, + } + } + + pub fn clear() -> Self { + // Opaque black with operator dest out + Pattern { + kind: PatternKind::ColorOrTexture, + shader_input: PatternShaderInput::default(), + base_color: PremultipliedColorF::BLACK, + is_opaque: false, + } + } +} diff --git a/gfx/wr/webrender/src/prepare.rs b/gfx/wr/webrender/src/prepare.rs index a59eca0670..d9b4521cfc 100644 --- a/gfx/wr/webrender/src/prepare.rs +++ b/gfx/wr/webrender/src/prepare.rs @@ -6,13 +6,13 @@ //! //! TODO: document this! -use api::{ColorF, PremultipliedColorF, PropertyBinding}; +use api::{PremultipliedColorF, PropertyBinding}; use api::{BoxShadowClipMode, BorderStyle, ClipMode}; use api::units::*; use euclid::Scale; use smallvec::SmallVec; use crate::composite::CompositorSurfaceKind; -use crate::command_buffer::{PrimitiveCommand, QuadFlags, CommandBufferIndex}; +use crate::command_buffer::{PrimitiveCommand, CommandBufferIndex}; use crate::image_tiling::{self, Repetition}; use crate::border::{get_max_scale_for_border, build_border_instances}; use crate::clip::{ClipStore, ClipNodeRange}; @@ -20,32 +20,29 @@ use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainInstance, ClipItemKind}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest}; -use crate::gpu_types::{BrushFlags, TransformPaletteId, QuadSegment}; +use crate::gpu_types::BrushFlags; use crate::internal_types::{FastHashMap, PlaneSplitAnchor, Filter}; use crate::picture::{PicturePrimitive, SliceId, ClusterFlags, PictureCompositeMode}; use crate::picture::{PrimitiveList, PrimitiveCluster, SurfaceIndex, TileCacheInstance, SubpixelMode, Picture3DContext}; use crate::prim_store::line_dec::MAX_LINE_DECORATION_RESOLUTION; use crate::prim_store::*; +use crate::quad; +use crate::pattern::Pattern; use crate::prim_store::gradient::GradientGpuBlockBuilder; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; use crate::render_task_cache::RenderTaskCacheKeyKind; use crate::render_task_cache::{RenderTaskCacheKey, to_cache_size, RenderTaskParent}; use crate::render_task::{RenderTaskKind, RenderTask, SubPass, MaskSubPass, EmptyTask}; -use crate::renderer::{GpuBufferBuilderF, GpuBufferAddress}; -use crate::segment::{EdgeAaSegmentMask, SegmentBuilder}; -use crate::space::SpaceMapper; -use crate::util::{clamp_to_scale_factor, pack_as_float, MaxRect}; +use crate::segment::SegmentBuilder; +use crate::util::{clamp_to_scale_factor, pack_as_float}; use crate::visibility::{compute_conservative_visible_rect, PrimitiveVisibility, VisibilityState}; const MAX_MASK_SIZE: i32 = 4096; -const MIN_BRUSH_SPLIT_SIZE: f32 = 256.0; const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0; -const MIN_AA_SEGMENTS_SIZE: f32 = 4.0; - pub fn prepare_primitives( store: &mut PrimitiveStore, prim_list: &mut PrimitiveList, @@ -141,110 +138,6 @@ fn can_use_clip_chain_for_quad_path( true } -/// Describes how clipping affects the rendering of a quad primitive. -/// -/// As a general rule, parts of the quad that require masking are prerendered in an -/// intermediate target and the mask is applied using multiplicative blending to -/// the intermediate result before compositing it into the destination target. -/// -/// Each segment can opt in or out of masking independently. -#[derive(Debug, Copy, Clone)] -pub enum QuadRenderStrategy { - /// The quad is not affected by any mask and is drawn directly in the destination - /// target. - Direct, - /// The quad is drawn entirely in an intermediate target and a mask is applied - /// before compositing in the destination target. - Indirect, - /// A rounded rectangle clip is applied to the quad primitive via a nine-patch. - /// The segments of the nine-patch that require a mask are rendered and masked in - /// an intermediate target, while other segments are drawn directly in the destination - /// target. - NinePatch { - radius: LayoutVector2D, - clip_rect: LayoutRect, - }, - /// Split the primitive into coarse tiles so that each tile independently - /// has the opportunity to be drawn directly in the destination target or - /// via an intermediate target if it is affected by a mask. - Tiled { - x_tiles: u16, - y_tiles: u16, - } -} - -fn get_prim_render_strategy( - prim_spatial_node_index: SpatialNodeIndex, - clip_chain: &ClipChainInstance, - clip_store: &ClipStore, - data_stores: &DataStores, - can_use_nine_patch: bool, - spatial_tree: &SpatialTree, -) -> QuadRenderStrategy { - if !clip_chain.needs_mask { - return QuadRenderStrategy::Direct - } - - fn tile_count_for_size(size: f32) -> u16 { - (size / MIN_BRUSH_SPLIT_SIZE).min(4.0).max(1.0).ceil() as u16 - } - - let prim_coverage_size = clip_chain.pic_coverage_rect.size(); - let x_tiles = tile_count_for_size(prim_coverage_size.width); - let y_tiles = tile_count_for_size(prim_coverage_size.height); - let try_split_prim = x_tiles > 1 || y_tiles > 1; - - if !try_split_prim { - return QuadRenderStrategy::Indirect; - } - - if can_use_nine_patch && clip_chain.clips_range.count == 1 { - let clip_instance = clip_store.get_instance_from_range(&clip_chain.clips_range, 0); - let clip_node = &data_stores.clip[clip_instance.handle]; - - if let ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip, rect, .. } = clip_node.item.kind { - let max_corner_width = radius.top_left.width - .max(radius.bottom_left.width) - .max(radius.top_right.width) - .max(radius.bottom_right.width); - let max_corner_height = radius.top_left.height - .max(radius.bottom_left.height) - .max(radius.top_right.height) - .max(radius.bottom_right.height); - - if max_corner_width <= 0.5 * rect.size().width && - max_corner_height <= 0.5 * rect.size().height { - - let clip_prim_coords_match = spatial_tree.is_matching_coord_system( - prim_spatial_node_index, - clip_node.item.spatial_node_index, - ); - - if clip_prim_coords_match { - let map_clip_to_prim = SpaceMapper::new_with_target( - prim_spatial_node_index, - clip_node.item.spatial_node_index, - LayoutRect::max_rect(), - spatial_tree, - ); - - if let Some(rect) = map_clip_to_prim.map(&rect) { - return QuadRenderStrategy::NinePatch { - radius: LayoutVector2D::new(max_corner_width, max_corner_height), - clip_rect: rect, - }; - } - } - } - } - } - - QuadRenderStrategy::Tiled { - x_tiles, - y_tiles, - } -} - fn prepare_prim_for_render( store: &mut PrimitiveStore, prim_instance_index: usize, @@ -712,334 +605,32 @@ fn prepare_interned_prim_for_render( } ); } else { - let map_prim_to_surface = frame_context.spatial_tree.get_relative_transform( - prim_spatial_node_index, - pic_context.raster_spatial_node_index, - ); - let prim_is_2d_scale_translation = map_prim_to_surface.is_2d_scale_translation(); - let prim_is_2d_axis_aligned = map_prim_to_surface.is_2d_axis_aligned(); - - let strategy = get_prim_render_strategy( - prim_spatial_node_index, - &prim_instance.vis.clip_chain, - frame_state.clip_store, - data_stores, - prim_is_2d_scale_translation, - frame_context.spatial_tree, - ); - let prim_data = &data_stores.prim[*data_handle]; - let (color, is_opaque) = match prim_data.kind { - PrimitiveTemplateKind::Clear => { - // Opaque black with operator dest out - (ColorF::BLACK, false) - } + let pattern = match prim_data.kind { + PrimitiveTemplateKind::Clear => Pattern::clear(), PrimitiveTemplateKind::Rectangle { ref color, .. } => { let color = frame_context.scene_properties.resolve_color(color); - - (color, color.a >= 1.0) + Pattern::color(color) } }; - let premul_color = color.premultiplied(); - - let mut quad_flags = QuadFlags::empty(); - - // Only use AA edge instances if the primitive is large enough to require it - let prim_size = prim_data.common.prim_rect.size(); - if prim_size.width > MIN_AA_SEGMENTS_SIZE && prim_size.height > MIN_AA_SEGMENTS_SIZE { - quad_flags |= QuadFlags::USE_AA_SEGMENTS; - } - - if is_opaque { - quad_flags |= QuadFlags::IS_OPAQUE; - } - let needs_scissor = !prim_is_2d_scale_translation; - if !needs_scissor { - quad_flags |= QuadFlags::APPLY_DEVICE_CLIP; - } - - // TODO(gw): For now, we don't select per-edge AA at all if the primitive - // has a 2d transform, which matches existing behavior. However, - // as a follow up, we can now easily check if we have a 2d-aligned - // primitive on a subpixel boundary, and enable AA along those edge(s). - let aa_flags = if prim_is_2d_axis_aligned { - EdgeAaSegmentMask::empty() - } else { - EdgeAaSegmentMask::all() - }; - - let transform_id = frame_state.transforms.get_id( + quad::push_quad( + &pattern, + &prim_data.common.prim_rect, + prim_instance_index, prim_spatial_node_index, - pic_context.raster_spatial_node_index, - frame_context.spatial_tree, - ); - - // TODO(gw): Perhaps rather than writing untyped data here (we at least do validate - // the written block count) to gpu-buffer, we could add a trait for - // writing typed data? - let main_prim_address = write_prim_blocks( - &mut frame_state.frame_gpu_data.f32, - prim_data.common.prim_rect, - prim_instance.vis.clip_chain.local_clip_rect, - premul_color, - &[], + &prim_instance.vis.clip_chain, + device_pixel_scale, + frame_context, + pic_context, + targets, + &data_stores.clip, + frame_state, + pic_state, + scratch, ); - match strategy { - QuadRenderStrategy::Direct => { - frame_state.push_prim( - &PrimitiveCommand::quad( - prim_instance_index, - main_prim_address, - transform_id, - quad_flags, - aa_flags, - ), - prim_spatial_node_index, - targets, - ); - } - QuadRenderStrategy::Indirect => { - let surface = &frame_state.surfaces[pic_context.surface_index.0]; - let Some(clipped_surface_rect) = surface.get_surface_rect( - &prim_instance.vis.clip_chain.pic_coverage_rect, - frame_context.spatial_tree, - ) else { - return; - }; - - let p0 = clipped_surface_rect.min.to_f32(); - let p1 = clipped_surface_rect.max.to_f32(); - - let segment = add_segment( - p0.x, - p0.y, - p1.x, - p1.y, - true, - prim_instance, - prim_spatial_node_index, - pic_context.raster_spatial_node_index, - main_prim_address, - transform_id, - aa_flags, - quad_flags, - device_pixel_scale, - needs_scissor, - frame_state, - ); - - add_composite_prim( - prim_instance_index, - LayoutRect::new(p0.cast_unit(), p1.cast_unit()), - premul_color, - quad_flags, - frame_state, - targets, - &[segment], - ); - } - QuadRenderStrategy::Tiled { x_tiles, y_tiles } => { - let surface = &frame_state.surfaces[pic_context.surface_index.0]; - - let Some(clipped_surface_rect) = surface.get_surface_rect( - &prim_instance.vis.clip_chain.pic_coverage_rect, - frame_context.spatial_tree, - ) else { - return; - }; - - let unclipped_surface_rect = surface.map_to_device_rect( - &prim_instance.vis.clip_chain.pic_coverage_rect, - frame_context.spatial_tree, - ); - - scratch.quad_segments.clear(); - - let mut x_coords = vec![clipped_surface_rect.min.x]; - let mut y_coords = vec![clipped_surface_rect.min.y]; - - let dx = (clipped_surface_rect.max.x - clipped_surface_rect.min.x) as f32 / x_tiles as f32; - let dy = (clipped_surface_rect.max.y - clipped_surface_rect.min.y) as f32 / y_tiles as f32; - - for x in 1 .. (x_tiles as i32) { - x_coords.push((clipped_surface_rect.min.x as f32 + x as f32 * dx).round() as i32); - } - for y in 1 .. (y_tiles as i32) { - y_coords.push((clipped_surface_rect.min.y as f32 + y as f32 * dy).round() as i32); - } - - x_coords.push(clipped_surface_rect.max.x); - y_coords.push(clipped_surface_rect.max.y); - - for y in 0 .. y_coords.len()-1 { - let y0 = y_coords[y]; - let y1 = y_coords[y+1]; - - if y1 <= y0 { - continue; - } - - for x in 0 .. x_coords.len()-1 { - let x0 = x_coords[x]; - let x1 = x_coords[x+1]; - - if x1 <= x0 { - continue; - } - - let create_task = true; - - let segment = add_segment( - x0 as f32, - y0 as f32, - x1 as f32, - y1 as f32, - create_task, - prim_instance, - prim_spatial_node_index, - pic_context.raster_spatial_node_index, - main_prim_address, - transform_id, - aa_flags, - quad_flags, - device_pixel_scale, - needs_scissor, - frame_state, - ); - scratch.quad_segments.push(segment); - } - } - - add_composite_prim( - prim_instance_index, - unclipped_surface_rect.cast_unit(), - premul_color, - quad_flags, - frame_state, - targets, - &scratch.quad_segments, - ); - } - QuadRenderStrategy::NinePatch { clip_rect, radius } => { - let surface = &frame_state.surfaces[pic_context.surface_index.0]; - let Some(clipped_surface_rect) = surface.get_surface_rect( - &prim_instance.vis.clip_chain.pic_coverage_rect, - frame_context.spatial_tree, - ) else { - return; - }; - - let unclipped_surface_rect = surface.map_to_device_rect( - &prim_instance.vis.clip_chain.pic_coverage_rect, - frame_context.spatial_tree, - ); - - let local_corner_0 = LayoutRect::new( - clip_rect.min, - clip_rect.min + radius, - ); - - let local_corner_1 = LayoutRect::new( - clip_rect.max - radius, - clip_rect.max, - ); - - let pic_corner_0 = pic_state.map_local_to_pic.map(&local_corner_0).unwrap(); - let pic_corner_1 = pic_state.map_local_to_pic.map(&local_corner_1).unwrap(); - - let surface_rect_0 = surface.map_to_device_rect( - &pic_corner_0, - frame_context.spatial_tree, - ).round_out().to_i32(); - - let surface_rect_1 = surface.map_to_device_rect( - &pic_corner_1, - frame_context.spatial_tree, - ).round_out().to_i32(); - - let p0 = surface_rect_0.min; - let p1 = surface_rect_0.max; - let p2 = surface_rect_1.min; - let p3 = surface_rect_1.max; - - let mut x_coords = [p0.x, p1.x, p2.x, p3.x]; - let mut y_coords = [p0.y, p1.y, p2.y, p3.y]; - - x_coords.sort_by(|a, b| a.partial_cmp(b).unwrap()); - y_coords.sort_by(|a, b| a.partial_cmp(b).unwrap()); - - scratch.quad_segments.clear(); - - for y in 0 .. y_coords.len()-1 { - let y0 = y_coords[y]; - let y1 = y_coords[y+1]; - - if y1 <= y0 { - continue; - } - - for x in 0 .. x_coords.len()-1 { - let x0 = x_coords[x]; - let x1 = x_coords[x+1]; - - if x1 <= x0 { - continue; - } - - let create_task = if x == 1 || y == 1 { - false - } else { - true - }; - - let r = DeviceIntRect::new( - DeviceIntPoint::new(x0, y0), - DeviceIntPoint::new(x1, y1), - ); - - let r = match r.intersection(&clipped_surface_rect) { - Some(r) => r, - None => { - continue; - } - }; - - let segment = add_segment( - r.min.x as f32, - r.min.y as f32, - r.max.x as f32, - r.max.y as f32, - create_task, - prim_instance, - prim_spatial_node_index, - pic_context.raster_spatial_node_index, - main_prim_address, - transform_id, - aa_flags, - quad_flags, - device_pixel_scale, - false, - frame_state, - ); - scratch.quad_segments.push(segment); - } - } - - add_composite_prim( - prim_instance_index, - unclipped_surface_rect.cast_unit(), - premul_color, - quad_flags, - frame_state, - targets, - &scratch.quad_segments, - ); - } - } - return; } } @@ -1304,7 +895,7 @@ fn prepare_interned_prim_for_render( .clipped_local_rect .cast_unit(); - let prim_address_f = write_prim_blocks( + let prim_address_f = quad::write_prim_blocks( &mut frame_state.frame_gpu_data.f32, prim_local_rect, prim_instance.vis.clip_chain.local_clip_rect, @@ -2129,143 +1720,6 @@ fn adjust_mask_scale_for_max_size(device_rect: DeviceIntRect, device_pixel_scale } } -pub fn write_prim_blocks( - builder: &mut GpuBufferBuilderF, - prim_rect: LayoutRect, - clip_rect: LayoutRect, - color: PremultipliedColorF, - segments: &[QuadSegment], -) -> GpuBufferAddress { - let mut writer = builder.write_blocks(3 + segments.len() * 2); - - writer.push_one(prim_rect); - writer.push_one(clip_rect); - writer.push_one(color); - - for segment in segments { - writer.push_one(segment.rect); - match segment.task_id { - RenderTaskId::INVALID => { - writer.push_one([0.0; 4]); - } - task_id => { - writer.push_render_task(task_id); - } - } - } - - writer.finish() -} - -fn add_segment( - x0: f32, - y0: f32, - x1: f32, - y1: f32, - create_task: bool, - prim_instance: &PrimitiveInstance, - prim_spatial_node_index: SpatialNodeIndex, - raster_spatial_node_index: SpatialNodeIndex, - prim_address_f: GpuBufferAddress, - transform_id: TransformPaletteId, - aa_flags: EdgeAaSegmentMask, - quad_flags: QuadFlags, - device_pixel_scale: DevicePixelScale, - needs_scissor_rect: bool, - frame_state: &mut FrameBuildingState, -) -> QuadSegment { - let task_size = DeviceSize::new(x1 - x0, y1 - y0).round().to_i32(); - let content_origin = DevicePoint::new(x0, y0); - - let rect = LayoutRect::new( - LayoutPoint::new(x0, y0), - LayoutPoint::new(x1, y1), - ); - - let task_id = if create_task { - let task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_prim( - prim_spatial_node_index, - raster_spatial_node_index, - device_pixel_scale, - content_origin, - prim_address_f, - transform_id, - aa_flags, - quad_flags, - prim_instance.vis.clip_chain.clips_range, - needs_scissor_rect, - ), - )); - - let masks = MaskSubPass { - clip_node_range: prim_instance.vis.clip_chain.clips_range, - prim_spatial_node_index, - prim_address_f, - }; - - let task = frame_state.rg_builder.get_task_mut(task_id); - task.add_sub_pass(SubPass::Masks { - masks, - }); - - frame_state.surface_builder.add_child_render_task( - task_id, - frame_state.rg_builder, - ); - - task_id - } else { - RenderTaskId::INVALID - }; - - QuadSegment { - rect, - task_id, - } -} - -fn add_composite_prim( - prim_instance_index: PrimitiveInstanceIndex, - rect: LayoutRect, - color: PremultipliedColorF, - quad_flags: QuadFlags, - frame_state: &mut FrameBuildingState, - targets: &[CommandBufferIndex], - segments: &[QuadSegment], -) { - let composite_prim_address = write_prim_blocks( - &mut frame_state.frame_gpu_data.f32, - rect, - rect, - color, - segments, - ); - - frame_state.set_segments( - segments, - targets, - ); - - let mut composite_quad_flags = QuadFlags::IGNORE_DEVICE_PIXEL_SCALE | QuadFlags::APPLY_DEVICE_CLIP; - if quad_flags.contains(QuadFlags::IS_OPAQUE) { - composite_quad_flags |= QuadFlags::IS_OPAQUE; - } - - frame_state.push_cmd( - &PrimitiveCommand::quad( - prim_instance_index, - composite_prim_address, - TransformPaletteId::IDENTITY, - composite_quad_flags, - // TODO(gw): No AA on composite, unless we use it to apply 2d clips - EdgeAaSegmentMask::empty(), - ), - targets, - ); -} - impl CompositorSurfaceKind { /// Returns true if the compositor surface strategy supports segment rendering fn supports_segments(&self) -> bool { diff --git a/gfx/wr/webrender/src/prim_store/storage.rs b/gfx/wr/webrender/src/prim_store/storage.rs index 4b99d87556..cc8997097c 100644 --- a/gfx/wr/webrender/src/prim_store/storage.rs +++ b/gfx/wr/webrender/src/prim_store/storage.rs @@ -2,7 +2,7 @@ * 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::{iter::Extend, ops, marker::PhantomData, u32}; +use std::{ops, marker::PhantomData, u32}; use crate::util::Recycler; #[derive(Debug, Hash)] diff --git a/gfx/wr/webrender/src/profiler.rs b/gfx/wr/webrender/src/profiler.rs index ccf86e8647..6d63e10294 100644 --- a/gfx/wr/webrender/src/profiler.rs +++ b/gfx/wr/webrender/src/profiler.rs @@ -95,6 +95,8 @@ static PROFILER_PRESETS: &'static[(&'static str, &'static str)] = &[ (&"GPU samplers", &"Alpha targets samplers,Transparent pass samplers,Opaque pass samplers,Total samplers"), (&"Render reasons", &"Reason scene, Reason animated property, Reason resource update, Reason async image, Reason clear resources, Reason APZ, Reason resize, Reason widget, Reason cache flush, Reason snapshot, Reason resource hook, Reason config change, Reason content sync, Reason flush, On vsync, Reason testing, Reason other"), + + (&"Slow frame breakdown", &"Total slow frames CPU, Total slow frames GPU, Slow: frame build, Slow: upload, Slow: render, Slow: draw calls, Slow: targets, Slow: blobs"), ]; fn find_preset(name: &str) -> Option<&'static str> { @@ -256,7 +258,16 @@ pub const RENDER_REASON_VSYNC: usize = 119; pub const TEXTURES_CREATED: usize = 120; pub const TEXTURES_DELETED: usize = 121; -pub const NUM_PROFILER_EVENTS: usize = 122; +pub const SLOW_FRAME_CPU_COUNT: usize = 122; +pub const SLOW_FRAME_GPU_COUNT: usize = 123; +pub const SLOW_FRAME_BUILD_COUNT: usize = 124; +pub const SLOW_UPLOAD_COUNT: usize = 125; +pub const SLOW_RENDER_COUNT: usize = 126; +pub const SLOW_DRAW_CALLS_COUNT: usize = 127; +pub const SLOW_TARGETS_COUNT: usize = 128; +pub const SLOW_BLOB_COUNT: usize = 129; + +pub const NUM_PROFILER_EVENTS: usize = 130; pub struct Profiler { counters: Vec<Counter>, @@ -270,6 +281,23 @@ pub struct Profiler { // For FPS computation. Updated in update(). frame_timestamps_within_last_second: Vec<u64>, + /// Total number of slow frames on the CPU. + slow_frame_cpu_count: u64, + /// Total number of slow frames on the GPU. + slow_frame_gpu_count: u64, + /// Slow frames dominated by frame building. + slow_frame_build_count: u64, + /// Slow frames dominated by draw call submission. + slow_render_count: u64, + /// Slow frames dominated by texture uploads. + slow_upload_count: u64, + /// Slow renders with a high number of draw calls. + slow_draw_calls_count: u64, + /// Slow renders with a high number of render targets. + slow_targets_count: u64, + /// Slow uploads with a high number of blob tiles. + slow_blob_count: u64, + ui: Vec<Item>, } @@ -433,6 +461,15 @@ impl Profiler { int("Textures created", "", TEXTURES_CREATED, expected(0..5)), int("Textures deleted", "", TEXTURES_DELETED, Expected::none()), + + int("Total slow frames CPU", "", SLOW_FRAME_CPU_COUNT, Expected::none()), + int("Total slow frames GPU", "", SLOW_FRAME_GPU_COUNT, Expected::none()), + int("Slow: frame build", "%", SLOW_FRAME_BUILD_COUNT, Expected::none()), + int("Slow: upload", "%", SLOW_UPLOAD_COUNT, Expected::none()), + int("Slow: render", "%", SLOW_RENDER_COUNT, Expected::none()), + int("Slow: draw calls", "%", SLOW_DRAW_CALLS_COUNT, Expected::none()), + int("Slow: targets", "%", SLOW_TARGETS_COUNT, Expected::none()), + int("Slow: blobs", "%", SLOW_BLOB_COUNT, Expected::none()), ]; let mut counters = Vec::with_capacity(profile_counters.len()); @@ -452,6 +489,16 @@ impl Profiler { num_graph_samples: 500, // Would it be useful to control this via a pref? frame_timestamps_within_last_second: Vec::new(), + + slow_frame_cpu_count: 0, + slow_frame_gpu_count: 0, + slow_frame_build_count: 0, + slow_render_count: 0, + slow_upload_count: 0, + slow_draw_calls_count: 0, + slow_targets_count: 0, + slow_blob_count: 0, + ui: Vec::new(), } } @@ -460,7 +507,7 @@ impl Profiler { /// a specific counter. /// /// This is useful to monitor slow frame and slow transactions. - fn update_slow_event(&mut self, dst_counter: usize, counters: &[usize], threshold: f64) { + fn update_slow_event(&mut self, dst_counter: usize, counters: &[usize], threshold: f64) -> bool { let mut total = 0.0; for &counter in counters { if self.counters[counter].value.is_finite() { @@ -470,6 +517,61 @@ impl Profiler { if total > threshold { self.counters[dst_counter].set(total); + return true; + } + + false + } + + fn classify_slow_cpu_frame(&mut self) { + let is_apz = self.counters[RENDER_REASON_ANIMATED_PROPERTY].value > 0.5 + || self.counters[RENDER_REASON_APZ].value > 0.5; + + if !is_apz { + // Only consider slow frames affecting scrolling for now. + return; + } + + let frame_build = self.counters[FRAME_BUILDING_TIME].value; + let uploads = self.counters[TEXTURE_CACHE_UPDATE_TIME].value; + let renderer = self.counters[RENDERER_TIME].value - uploads; + let mut reasons = [ + (frame_build, &mut self.slow_frame_build_count, SLOW_FRAME_BUILD_COUNT,), + (renderer, &mut self.slow_render_count, SLOW_RENDER_COUNT,), + (uploads, &mut self.slow_upload_count, SLOW_UPLOAD_COUNT,), + ]; + + reasons.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); + + *reasons[0].1 += 1; + let reason = reasons[0].2; + std::mem::drop(reasons); + + self.slow_frame_cpu_count += 1; + + if reason == SLOW_RENDER_COUNT { + let draw_calls = self.counters[DRAW_CALLS].value; + if draw_calls > 200.0 { + self.slow_draw_calls_count += 1; + } + + let render_passes = self.counters[COLOR_PASSES].value + self.counters[ALPHA_PASSES].value; + if render_passes > 20.0 { + self.slow_targets_count += 1; + } + } + + if reason == SLOW_UPLOAD_COUNT { + let count = self.counters[TEXTURE_UPLOADS].value; + let blob_tiles = self.counters[RASTERIZED_BLOB_TILES].value; + // This is an approximation: we rasterize blobs for the whole displayport and + // only upload blob tiles for the current viewport. That said, the presence of + // a high number of blob tiles compared to the total number of uploads is still + // a good indication that blob images are the likely cause of the slow upload + // time, or at least contributing to it to a large extent. + if blob_tiles > count * 0.5 { + self.slow_blob_count += 1; + } } } @@ -484,7 +586,7 @@ impl Profiler { self.frame_timestamps_within_last_second.retain(|t| *t > one_second_ago); self.frame_timestamps_within_last_second.push(now); - self.update_slow_event( + let slow_cpu = self.update_slow_event( SLOW_FRAME, &[TOTAL_FRAME_CPU_TIME], 15.0, @@ -495,6 +597,20 @@ impl Profiler { 80.0 ); + if slow_cpu { + self.classify_slow_cpu_frame(); + } + + let div = 100.0 / self.slow_frame_cpu_count as f64; + self.counters[SLOW_FRAME_CPU_COUNT].set(self.slow_frame_cpu_count as f64); + self.counters[SLOW_FRAME_GPU_COUNT].set(self.slow_frame_gpu_count as f64); + self.counters[SLOW_FRAME_BUILD_COUNT].set(self.slow_frame_build_count as f64 * div); + self.counters[SLOW_RENDER_COUNT].set(self.slow_render_count as f64 * div); + self.counters[SLOW_UPLOAD_COUNT].set(self.slow_upload_count as f64 * div); + self.counters[SLOW_DRAW_CALLS_COUNT].set(self.slow_draw_calls_count as f64 * div); + self.counters[SLOW_TARGETS_COUNT].set(self.slow_targets_count as f64 * div); + self.counters[SLOW_BLOB_COUNT].set(self.slow_blob_count as f64 * div); + for counter in &mut self.counters { counter.update(update_avg); } @@ -517,7 +633,11 @@ impl Profiler { samples: gpu_queries }); - self.counters[GPU_TIME].set_f64(ns_to_ms(gpu_time_ns)); + let gpu_time = ns_to_ms(gpu_time_ns); + self.counters[GPU_TIME].set_f64(gpu_time); + if gpu_time > 12.0 { + self.slow_frame_gpu_count += 1; + } } // Find the index of a counter by its name. diff --git a/gfx/wr/webrender/src/quad.rs b/gfx/wr/webrender/src/quad.rs new file mode 100644 index 0000000000..5455611f3f --- /dev/null +++ b/gfx/wr/webrender/src/quad.rs @@ -0,0 +1,695 @@ +/* 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 api::{units::*, PremultipliedColorF, ClipMode}; +use euclid::point2; + +use crate::batch::{BatchKey, BatchKind, BatchTextures}; +use crate::clip::{ClipChainInstance, ClipIntern, ClipItemKind, ClipStore}; +use crate::command_buffer::{CommandBufferIndex, PrimitiveCommand, QuadFlags}; +use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; +use crate::gpu_types::{PrimitiveInstanceData, QuadInstance, QuadSegment, TransformPaletteId, ZBufferId}; +use crate::intern::DataStore; +use crate::internal_types::TextureSource; +use crate::pattern::{Pattern, PatternKind, PatternShaderInput}; +use crate::prim_store::{PrimitiveInstanceIndex, PrimitiveScratchBuffer}; +use crate::render_task::{MaskSubPass, RenderTask, RenderTaskAddress, RenderTaskKind, SubPass}; +use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; +use crate::renderer::{BlendMode, GpuBufferAddress, GpuBufferBuilder, GpuBufferBuilderF}; +use crate::segment::EdgeAaSegmentMask; +use crate::space::SpaceMapper; +use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; +use crate::util::MaxRect; + +const MIN_AA_SEGMENTS_SIZE: f32 = 4.0; +const MIN_QUAD_SPLIT_SIZE: f32 = 256.0; + +/// Describes how clipping affects the rendering of a quad primitive. +/// +/// As a general rule, parts of the quad that require masking are prerendered in an +/// intermediate target and the mask is applied using multiplicative blending to +/// the intermediate result before compositing it into the destination target. +/// +/// Each segment can opt in or out of masking independently. +#[derive(Debug, Copy, Clone)] +enum QuadRenderStrategy { + /// The quad is not affected by any mask and is drawn directly in the destination + /// target. + Direct, + /// The quad is drawn entirely in an intermediate target and a mask is applied + /// before compositing in the destination target. + Indirect, + /// A rounded rectangle clip is applied to the quad primitive via a nine-patch. + /// The segments of the nine-patch that require a mask are rendered and masked in + /// an intermediate target, while other segments are drawn directly in the destination + /// target. + NinePatch { + radius: LayoutVector2D, + clip_rect: LayoutRect, + }, + /// Split the primitive into coarse tiles so that each tile independently + /// has the opportunity to be drawn directly in the destination target or + /// via an intermediate target if it is affected by a mask. + Tiled { + x_tiles: u16, + y_tiles: u16, + } +} + +pub fn push_quad( + pattern: &Pattern, + local_rect: &LayoutRect, + prim_instance_index: PrimitiveInstanceIndex, + prim_spatial_node_index: SpatialNodeIndex, + clip_chain: &ClipChainInstance, + device_pixel_scale: DevicePixelScale, + + frame_context: &FrameBuildingContext, + pic_context: &PictureContext, + targets: &[CommandBufferIndex], + interned_clips: &DataStore<ClipIntern>, + + frame_state: &mut FrameBuildingState, + pic_state: &mut PictureState, + scratch: &mut PrimitiveScratchBuffer, + +) { + let map_prim_to_surface = frame_context.spatial_tree.get_relative_transform( + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + ); + let prim_is_2d_scale_translation = map_prim_to_surface.is_2d_scale_translation(); + let prim_is_2d_axis_aligned = map_prim_to_surface.is_2d_axis_aligned(); + + let strategy = get_prim_render_strategy( + prim_spatial_node_index, + clip_chain, + frame_state.clip_store, + interned_clips, + prim_is_2d_scale_translation, + frame_context.spatial_tree, + ); + + let mut quad_flags = QuadFlags::empty(); + + // Only use AA edge instances if the primitive is large enough to require it + let prim_size = local_rect.size(); + if prim_size.width > MIN_AA_SEGMENTS_SIZE && prim_size.height > MIN_AA_SEGMENTS_SIZE { + quad_flags |= QuadFlags::USE_AA_SEGMENTS; + } + + if pattern.is_opaque { + quad_flags |= QuadFlags::IS_OPAQUE; + } + let needs_scissor = !prim_is_2d_scale_translation; + if !needs_scissor { + quad_flags |= QuadFlags::APPLY_DEVICE_CLIP; + } + + // TODO(gw): For now, we don't select per-edge AA at all if the primitive + // has a 2d transform, which matches existing behavior. However, + // as a follow up, we can now easily check if we have a 2d-aligned + // primitive on a subpixel boundary, and enable AA along those edge(s). + let aa_flags = if prim_is_2d_axis_aligned { + EdgeAaSegmentMask::empty() + } else { + EdgeAaSegmentMask::all() + }; + + let transform_id = frame_state.transforms.get_id( + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + frame_context.spatial_tree, + ); + + // TODO(gw): Perhaps rather than writing untyped data here (we at least do validate + // the written block count) to gpu-buffer, we could add a trait for + // writing typed data? + let main_prim_address = write_prim_blocks( + &mut frame_state.frame_gpu_data.f32, + *local_rect, + clip_chain.local_clip_rect, + pattern.base_color, + &[], + ); + + if let QuadRenderStrategy::Direct = strategy { + frame_state.push_prim( + &PrimitiveCommand::quad( + pattern.kind, + pattern.shader_input, + prim_instance_index, + main_prim_address, + transform_id, + quad_flags, + aa_flags, + ), + prim_spatial_node_index, + targets, + ); + return; + } + + let surface = &frame_state.surfaces[pic_context.surface_index.0]; + let Some(clipped_surface_rect) = surface.get_surface_rect( + &clip_chain.pic_coverage_rect, frame_context.spatial_tree + ) else { + return; + }; + + match strategy { + QuadRenderStrategy::Direct => {} + QuadRenderStrategy::Indirect => { + let segment = add_segment( + pattern, + &clipped_surface_rect, + true, + clip_chain, + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + main_prim_address, + transform_id, + aa_flags, + quad_flags, + device_pixel_scale, + needs_scissor, + frame_state, + ); + + add_composite_prim( + pattern, + prim_instance_index, + segment.rect, + quad_flags, + frame_state, + targets, + &[segment], + ); + } + QuadRenderStrategy::Tiled { x_tiles, y_tiles } => { + let unclipped_surface_rect = surface + .map_to_device_rect(&clip_chain.pic_coverage_rect, frame_context.spatial_tree); + + scratch.quad_segments.clear(); + + let mut x_coords = vec![clipped_surface_rect.min.x]; + let mut y_coords = vec![clipped_surface_rect.min.y]; + + let dx = (clipped_surface_rect.max.x - clipped_surface_rect.min.x) as f32 / x_tiles as f32; + let dy = (clipped_surface_rect.max.y - clipped_surface_rect.min.y) as f32 / y_tiles as f32; + + for x in 1 .. (x_tiles as i32) { + x_coords.push((clipped_surface_rect.min.x as f32 + x as f32 * dx).round() as i32); + } + for y in 1 .. (y_tiles as i32) { + y_coords.push((clipped_surface_rect.min.y as f32 + y as f32 * dy).round() as i32); + } + + x_coords.push(clipped_surface_rect.max.x); + y_coords.push(clipped_surface_rect.max.y); + + for y in 0 .. y_coords.len()-1 { + let y0 = y_coords[y]; + let y1 = y_coords[y+1]; + + if y1 <= y0 { + continue; + } + + for x in 0 .. x_coords.len()-1 { + let x0 = x_coords[x]; + let x1 = x_coords[x+1]; + + if x1 <= x0 { + continue; + } + + let create_task = true; + let rect = DeviceIntRect { + min: point2(x0, y0), + max: point2(x1, y1), + }; + + let segment = add_segment( + pattern, + &rect, + create_task, + clip_chain, + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + main_prim_address, + transform_id, + aa_flags, + quad_flags, + device_pixel_scale, + needs_scissor, + frame_state, + ); + scratch.quad_segments.push(segment); + } + } + + add_composite_prim( + pattern, + prim_instance_index, + unclipped_surface_rect.cast_unit(), + quad_flags, + frame_state, + targets, + &scratch.quad_segments, + ); + } + QuadRenderStrategy::NinePatch { clip_rect, radius } => { + let unclipped_surface_rect = surface + .map_to_device_rect(&clip_chain.pic_coverage_rect, frame_context.spatial_tree); + + let local_corner_0 = LayoutRect::new( + clip_rect.min, + clip_rect.min + radius, + ); + + let local_corner_1 = LayoutRect::new( + clip_rect.max - radius, + clip_rect.max, + ); + + let pic_corner_0 = pic_state.map_local_to_pic.map(&local_corner_0).unwrap(); + let pic_corner_1 = pic_state.map_local_to_pic.map(&local_corner_1).unwrap(); + + let surface_rect_0 = surface.map_to_device_rect( + &pic_corner_0, + frame_context.spatial_tree, + ).round_out().to_i32(); + + let surface_rect_1 = surface.map_to_device_rect( + &pic_corner_1, + frame_context.spatial_tree, + ).round_out().to_i32(); + + let p0 = surface_rect_0.min; + let p1 = surface_rect_0.max; + let p2 = surface_rect_1.min; + let p3 = surface_rect_1.max; + + let mut x_coords = [p0.x, p1.x, p2.x, p3.x]; + let mut y_coords = [p0.y, p1.y, p2.y, p3.y]; + + x_coords.sort_by(|a, b| a.partial_cmp(b).unwrap()); + y_coords.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + scratch.quad_segments.clear(); + + for y in 0 .. y_coords.len()-1 { + let y0 = y_coords[y]; + let y1 = y_coords[y+1]; + + if y1 <= y0 { + continue; + } + + for x in 0 .. x_coords.len()-1 { + let x0 = x_coords[x]; + let x1 = x_coords[x+1]; + + if x1 <= x0 { + continue; + } + + // Only create render tasks for the corners. + let create_task = x != 1 && y != 1; + + let rect = DeviceIntRect::new(point2(x0, y0), point2(x1, y1)); + + let rect = match rect.intersection(&clipped_surface_rect) { + Some(rect) => rect, + None => { + continue; + } + }; + + let segment = add_segment( + pattern, + &rect, + create_task, + clip_chain, + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + main_prim_address, + transform_id, + aa_flags, + quad_flags, + device_pixel_scale, + false, + frame_state, + ); + scratch.quad_segments.push(segment); + } + } + + add_composite_prim( + pattern, + prim_instance_index, + unclipped_surface_rect.cast_unit(), + quad_flags, + frame_state, + targets, + &scratch.quad_segments, + ); + } + } +} + +fn get_prim_render_strategy( + prim_spatial_node_index: SpatialNodeIndex, + clip_chain: &ClipChainInstance, + clip_store: &ClipStore, + interned_clips: &DataStore<ClipIntern>, + can_use_nine_patch: bool, + spatial_tree: &SpatialTree, +) -> QuadRenderStrategy { + if !clip_chain.needs_mask { + return QuadRenderStrategy::Direct + } + + fn tile_count_for_size(size: f32) -> u16 { + (size / MIN_QUAD_SPLIT_SIZE).min(4.0).max(1.0).ceil() as u16 + } + + let prim_coverage_size = clip_chain.pic_coverage_rect.size(); + let x_tiles = tile_count_for_size(prim_coverage_size.width); + let y_tiles = tile_count_for_size(prim_coverage_size.height); + let try_split_prim = x_tiles > 1 || y_tiles > 1; + + if !try_split_prim { + return QuadRenderStrategy::Indirect; + } + + if can_use_nine_patch && clip_chain.clips_range.count == 1 { + let clip_instance = clip_store.get_instance_from_range(&clip_chain.clips_range, 0); + let clip_node = &interned_clips[clip_instance.handle]; + + if let ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip, rect, .. } = clip_node.item.kind { + let max_corner_width = radius.top_left.width + .max(radius.bottom_left.width) + .max(radius.top_right.width) + .max(radius.bottom_right.width); + let max_corner_height = radius.top_left.height + .max(radius.bottom_left.height) + .max(radius.top_right.height) + .max(radius.bottom_right.height); + + if max_corner_width <= 0.5 * rect.size().width && + max_corner_height <= 0.5 * rect.size().height { + + let clip_prim_coords_match = spatial_tree.is_matching_coord_system( + prim_spatial_node_index, + clip_node.item.spatial_node_index, + ); + + if clip_prim_coords_match { + let map_clip_to_prim = SpaceMapper::new_with_target( + prim_spatial_node_index, + clip_node.item.spatial_node_index, + LayoutRect::max_rect(), + spatial_tree, + ); + + if let Some(rect) = map_clip_to_prim.map(&rect) { + return QuadRenderStrategy::NinePatch { + radius: LayoutVector2D::new(max_corner_width, max_corner_height), + clip_rect: rect, + }; + } + } + } + } + } + + QuadRenderStrategy::Tiled { + x_tiles, + y_tiles, + } +} + +fn add_segment( + pattern: &Pattern, + rect: &DeviceIntRect, + create_task: bool, + clip_chain: &ClipChainInstance, + prim_spatial_node_index: SpatialNodeIndex, + raster_spatial_node_index: SpatialNodeIndex, + prim_address_f: GpuBufferAddress, + transform_id: TransformPaletteId, + aa_flags: EdgeAaSegmentMask, + quad_flags: QuadFlags, + device_pixel_scale: DevicePixelScale, + needs_scissor_rect: bool, + frame_state: &mut FrameBuildingState, +) -> QuadSegment { + let task_size = rect.size(); + let rect = rect.to_f32(); + let content_origin = rect.min; + + let task_id = if create_task { + let task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic( + task_size, + RenderTaskKind::new_prim( + pattern.kind, + pattern.shader_input, + prim_spatial_node_index, + raster_spatial_node_index, + device_pixel_scale, + content_origin, + prim_address_f, + transform_id, + aa_flags, + quad_flags, + clip_chain.clips_range, + needs_scissor_rect, + ), + )); + + let masks = MaskSubPass { + clip_node_range: clip_chain.clips_range, + prim_spatial_node_index, + prim_address_f, + }; + + let task = frame_state.rg_builder.get_task_mut(task_id); + task.add_sub_pass(SubPass::Masks { masks }); + + frame_state + .surface_builder + .add_child_render_task(task_id, frame_state.rg_builder); + + task_id + } else { + RenderTaskId::INVALID + }; + + QuadSegment { rect: rect.cast_unit(), task_id } +} + +fn add_composite_prim( + pattern: &Pattern, + prim_instance_index: PrimitiveInstanceIndex, + rect: LayoutRect, + quad_flags: QuadFlags, + frame_state: &mut FrameBuildingState, + targets: &[CommandBufferIndex], + segments: &[QuadSegment], +) { + let composite_prim_address = write_prim_blocks( + &mut frame_state.frame_gpu_data.f32, + rect, + rect, + pattern.base_color, + segments, + ); + + frame_state.set_segments(segments, targets); + + let mut composite_quad_flags = + QuadFlags::IGNORE_DEVICE_PIXEL_SCALE | QuadFlags::APPLY_DEVICE_CLIP; + if quad_flags.contains(QuadFlags::IS_OPAQUE) { + composite_quad_flags |= QuadFlags::IS_OPAQUE; + } + + frame_state.push_cmd( + &PrimitiveCommand::quad( + PatternKind::ColorOrTexture, + pattern.shader_input, + prim_instance_index, + composite_prim_address, + TransformPaletteId::IDENTITY, + composite_quad_flags, + // TODO(gw): No AA on composite, unless we use it to apply 2d clips + EdgeAaSegmentMask::empty(), + ), + targets, + ); +} + +pub fn write_prim_blocks( + builder: &mut GpuBufferBuilderF, + prim_rect: LayoutRect, + clip_rect: LayoutRect, + color: PremultipliedColorF, + segments: &[QuadSegment], +) -> GpuBufferAddress { + let mut writer = builder.write_blocks(3 + segments.len() * 2); + + writer.push_one(prim_rect); + writer.push_one(clip_rect); + writer.push_one(color); + + for segment in segments { + writer.push_one(segment.rect); + match segment.task_id { + RenderTaskId::INVALID => { + writer.push_one([0.0; 4]); + } + task_id => { + writer.push_render_task(task_id); + } + } + } + + writer.finish() +} + +pub fn add_to_batch<F>( + kind: PatternKind, + pattern_input: PatternShaderInput, + render_task_address: RenderTaskAddress, + transform_id: TransformPaletteId, + prim_address_f: GpuBufferAddress, + quad_flags: QuadFlags, + edge_flags: EdgeAaSegmentMask, + segment_index: u8, + task_id: RenderTaskId, + z_id: ZBufferId, + render_tasks: &RenderTaskGraph, + gpu_buffer_builder: &mut GpuBufferBuilder, + mut f: F, +) where F: FnMut(BatchKey, PrimitiveInstanceData) { + + // See the corresponfing #defines in ps_quad.glsl + #[repr(u8)] + enum PartIndex { + Center = 0, + Left = 1, + Top = 2, + Right = 3, + Bottom = 4, + All = 5, + } + + // See QuadHeader in ps_quad.glsl + let mut writer = gpu_buffer_builder.i32.write_blocks(1); + writer.push_one([ + transform_id.0 as i32, + z_id.0, + pattern_input.0, + pattern_input.1, + ]); + let prim_address_i = writer.finish(); + + let texture = match task_id { + RenderTaskId::INVALID => { + TextureSource::Invalid + } + _ => { + let texture = render_tasks + .resolve_texture(task_id) + .expect("bug: valid task id must be resolvable"); + + texture + } + }; + + let textures = BatchTextures::prim_textured( + texture, + TextureSource::Invalid, + ); + + let default_blend_mode = if quad_flags.contains(QuadFlags::IS_OPAQUE) && task_id == RenderTaskId::INVALID { + BlendMode::None + } else { + BlendMode::PremultipliedAlpha + }; + + let edge_flags_bits = edge_flags.bits(); + + let prim_batch_key = BatchKey { + blend_mode: default_blend_mode, + kind: BatchKind::Quad(kind), + textures, + }; + + let aa_batch_key = BatchKey { + blend_mode: BlendMode::PremultipliedAlpha, + kind: BatchKind::Quad(kind), + textures, + }; + + let mut instance = QuadInstance { + render_task_address, + prim_address_i, + prim_address_f, + z_id, + transform_id, + edge_flags: edge_flags_bits, + quad_flags: quad_flags.bits(), + part_index: PartIndex::All as u8, + segment_index, + }; + + if edge_flags.is_empty() { + // No antialisaing. + f(prim_batch_key, instance.into()); + } else if quad_flags.contains(QuadFlags::USE_AA_SEGMENTS) { + // Add instances for the antialisaing. This gives the center part + // an opportunity to stay in the opaque pass. + if edge_flags.contains(EdgeAaSegmentMask::LEFT) { + let instance = QuadInstance { + part_index: PartIndex::Left as u8, + ..instance + }; + f(aa_batch_key, instance.into()); + } + if edge_flags.contains(EdgeAaSegmentMask::RIGHT) { + let instance = QuadInstance { + part_index: PartIndex::Top as u8, + ..instance + }; + f(aa_batch_key, instance.into()); + } + if edge_flags.contains(EdgeAaSegmentMask::TOP) { + let instance = QuadInstance { + part_index: PartIndex::Right as u8, + ..instance + }; + f(aa_batch_key, instance.into()); + } + if edge_flags.contains(EdgeAaSegmentMask::BOTTOM) { + let instance = QuadInstance { + part_index: PartIndex::Bottom as u8, + ..instance + }; + f(aa_batch_key, instance.into()); + } + + instance = QuadInstance { + part_index: PartIndex::Center as u8, + ..instance + }; + + f(prim_batch_key, instance.into()); + } else { + // Render the anti-aliased quad with a single primitive. + f(aa_batch_key, instance.into()); + } +} + diff --git a/gfx/wr/webrender/src/render_backend.rs b/gfx/wr/webrender/src/render_backend.rs index ff4de9e82e..45ca2179d7 100644 --- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -545,7 +545,6 @@ impl Document { self.frame_is_valid = true; self.dirty_rects_are_valid = true; - let is_new_scene = self.has_built_scene; self.has_built_scene = false; let frame_build_time_ms = @@ -559,7 +558,6 @@ impl Document { RenderedDocument { frame, - is_new_scene, profile: self.profile.take_and_reset(), frame_stats: frame_stats, render_reasons, @@ -1918,7 +1916,6 @@ impl RenderBackend { id, RenderedDocument { frame, - is_new_scene: true, profile: TransactionProfile::new(), render_reasons: RenderReasons::empty(), frame_stats: None, diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs index f2d1c24c10..f53b5dd4f8 100644 --- a/gfx/wr/webrender/src/render_target.rs +++ b/gfx/wr/webrender/src/render_target.rs @@ -5,9 +5,10 @@ use api::{units::*, PremultipliedColorF, ClipMode}; use api::{ColorF, ImageFormat, LineOrientation, BorderStyle}; -use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, add_quad_to_batch}; +use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures}; use crate::batch::{ClipBatcher, BatchBuilder, INVALID_SEGMENT_INDEX, ClipMaskInstanceList}; use crate::command_buffer::{CommandBufferList, QuadFlags}; +use crate::pattern::{PatternKind, PatternShaderInput}; use crate::segment::EdgeAaSegmentMask; use crate::spatial_tree::SpatialTree; use crate::clip::{ClipStore, ClipItemKind}; @@ -18,7 +19,7 @@ use crate::gpu_types::{TransformPalette, ZBufferIdGenerator, MaskInstance, ClipS use crate::gpu_types::{ZBufferId, QuadSegment, PrimitiveInstanceData, TransformPaletteId}; use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId}; use crate::picture::{SliceId, SurfaceInfo, ResolvedSurfaceTexture, TileCacheInstance}; -use crate::prepare::write_prim_blocks; +use crate::quad; use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PrimitiveScratchBuffer}; use crate::prim_store::gradient::{ FastLinearGradientInstance, LinearGradientInstance, RadialGradientInstance, @@ -210,6 +211,7 @@ impl<T: RenderTarget> RenderTargetList<T> { } } +const NUM_PATTERNS: usize = crate::pattern::NUM_PATTERNS as usize; /// Contains the work (in the form of instance arrays) needed to fill a color /// color output surface (RGBA8). @@ -235,10 +237,10 @@ pub struct ColorRenderTarget { pub resolve_ops: Vec<ResolveOp>, pub clear_color: Option<ColorF>, - pub prim_instances: Vec<PrimitiveInstanceData>, - pub prim_instances_with_scissor: FastHashMap<DeviceIntRect, Vec<PrimitiveInstanceData>>, - - pub clip_masks: Vec<ClipMaskInstanceList>, + pub prim_instances: [Vec<PrimitiveInstanceData>; NUM_PATTERNS], + pub prim_instances_with_scissor: FastHashMap<(DeviceIntRect, PatternKind), Vec<PrimitiveInstanceData>>, + + pub clip_masks: ClipMaskInstanceList, } impl RenderTarget for ColorRenderTarget { @@ -261,9 +263,9 @@ impl RenderTarget for ColorRenderTarget { used_rect, resolve_ops: Vec::new(), clear_color: Some(ColorF::TRANSPARENT), - prim_instances: Vec::new(), + prim_instances: [Vec::new(), Vec::new()], prim_instances_with_scissor: FastHashMap::default(), - clip_masks: Vec::new(), + clip_masks: ClipMaskInstanceList::new(), } } @@ -376,7 +378,9 @@ impl RenderTarget for ColorRenderTarget { let render_task_address = task_id.into(); let target_rect = task.get_target_rect(); - add_quad_to_batch( + quad::add_to_batch( + info.pattern, + info.pattern_input, render_task_address, info.transform_id, info.prim_address_f, @@ -390,11 +394,11 @@ impl RenderTarget for ColorRenderTarget { |_, instance| { if info.prim_needs_scissor_rect { self.prim_instances_with_scissor - .entry(target_rect) + .entry((target_rect, info.pattern)) .or_insert(Vec::new()) .push(instance); } else { - self.prim_instances.push(instance); + self.prim_instances[info.pattern as usize].push(instance); } } ); @@ -501,7 +505,7 @@ pub struct AlphaRenderTarget { pub zero_clears: Vec<RenderTaskId>, pub one_clears: Vec<RenderTaskId>, pub texture_id: CacheTextureId, - pub clip_masks: Vec<ClipMaskInstanceList>, + pub clip_masks: ClipMaskInstanceList, } impl RenderTarget for AlphaRenderTarget { @@ -519,7 +523,7 @@ impl RenderTarget for AlphaRenderTarget { zero_clears: Vec::new(), one_clears: Vec::new(), texture_id, - clip_masks: Vec::new(), + clip_masks: ClipMaskInstanceList::new(), } } @@ -1047,7 +1051,7 @@ fn build_mask_tasks( } for tile in clip_store.visible_mask_tiles(&clip_instance) { - let clip_prim_address = write_prim_blocks( + let clip_prim_address = quad::write_prim_blocks( &mut gpu_buffer_builder.f32, rect, rect, @@ -1062,7 +1066,9 @@ fn build_mask_tasks( .resolve_texture(tile.task_id) .expect("bug: texture not found for tile"); - add_quad_to_batch( + quad::add_to_batch( + PatternKind::ColorOrTexture, + PatternShaderInput::default(), render_task_address, clip_transform_id, clip_prim_address, @@ -1112,7 +1118,7 @@ fn build_mask_tasks( spatial_tree, ); - let main_prim_address = write_prim_blocks( + let main_prim_address = quad::write_prim_blocks( &mut gpu_buffer_builder.f32, task_world_rect.cast_unit(), task_world_rect.cast_unit(), @@ -1158,7 +1164,9 @@ fn build_mask_tasks( QuadFlags::empty() }; - add_quad_to_batch( + quad::add_to_batch( + PatternKind::Mask, + PatternShaderInput::default(), render_task_address, prim_transform_id, main_prim_address, @@ -1209,7 +1217,7 @@ fn build_sub_pass( render_tasks: &RenderTaskGraph, transforms: &mut TransformPalette, ctx: &RenderTargetContext, - output: &mut Vec<ClipMaskInstanceList>, + output: &mut ClipMaskInstanceList, ) { if let Some(ref sub_pass) = task.sub_pass { match sub_pass { @@ -1235,8 +1243,6 @@ fn build_sub_pass( content_origin + target_rect.size().to_f32(), ); - let mut clip_masks = ClipMaskInstanceList::new(); - build_mask_tasks( masks, render_task_address, @@ -1251,10 +1257,8 @@ fn build_sub_pass( gpu_buffer_builder, transforms, render_tasks, - &mut clip_masks, + output, ); - - output.push(clip_masks); } } } diff --git a/gfx/wr/webrender/src/render_task.rs b/gfx/wr/webrender/src/render_task.rs index 8889ae1ea6..bf9050712c 100644 --- a/gfx/wr/webrender/src/render_task.rs +++ b/gfx/wr/webrender/src/render_task.rs @@ -8,6 +8,7 @@ use api::MAX_RENDER_TASK_SIZE; use api::units::*; use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange}; use crate::command_buffer::{CommandBufferIndex, QuadFlags}; +use crate::pattern::{PatternKind, PatternShaderInput}; use crate::spatial_tree::SpatialNodeIndex; use crate::filterdata::SFilterData; use crate::frame_builder::FrameBuilderConfig; @@ -184,6 +185,8 @@ pub struct EmptyTask { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimTask { + pub pattern: PatternKind, + pub pattern_input: PatternShaderInput, pub device_pixel_scale: DevicePixelScale, pub content_origin: DevicePoint, pub prim_address_f: GpuBufferAddress, @@ -516,6 +519,8 @@ impl RenderTaskKind { } pub fn new_prim( + pattern: PatternKind, + pattern_input: PatternShaderInput, prim_spatial_node_index: SpatialNodeIndex, raster_spatial_node_index: SpatialNodeIndex, device_pixel_scale: DevicePixelScale, @@ -528,6 +533,8 @@ impl RenderTaskKind { prim_needs_scissor_rect: bool, ) -> Self { RenderTaskKind::Prim(PrimTask { + pattern, + pattern_input, prim_spatial_node_index, raster_spatial_node_index, device_pixel_scale, @@ -940,9 +947,7 @@ impl RenderTask { size: DeviceIntSize, kind: RenderTaskKind, ) -> Self { - if size.is_empty() { - log::warn!("Bad {} render task size: {:?}", kind.as_str(), size); - } + assert!(!size.is_empty(), "Bad {} render task size: {:?}", kind.as_str(), size); RenderTask::new( RenderTaskLocation::Unallocated { size }, kind, diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs index 3a058ae8f4..a70d3eca18 100644 --- a/gfx/wr/webrender/src/renderer/mod.rs +++ b/gfx/wr/webrender/src/renderer/mod.rs @@ -48,6 +48,7 @@ use api::channel::{Sender, Receiver}; pub use api::DebugFlags; use core::time::Duration; +use crate::pattern::PatternKind; use crate::render_api::{DebugCommand, ApiMsg, MemoryReport}; use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList}; use crate::batch::{ClipMaskInstanceList}; @@ -286,7 +287,8 @@ impl BatchKind { } } BatchKind::TextRun(_) => GPU_TAG_PRIM_TEXT_RUN, - BatchKind::Primitive => GPU_TAG_PRIMITIVE, + BatchKind::Quad(PatternKind::ColorOrTexture) => GPU_TAG_PRIMITIVE, + BatchKind::Quad(PatternKind::Mask) => GPU_TAG_INDIRECT_MASK, } } } @@ -2171,8 +2173,8 @@ impl Renderer { fn handle_prims( &mut self, draw_target: &DrawTarget, - prim_instances: &[PrimitiveInstanceData], - prim_instances_with_scissor: &FastHashMap<DeviceIntRect, Vec<PrimitiveInstanceData>>, + prim_instances: &[Vec<PrimitiveInstanceData>], + prim_instances_with_scissor: &FastHashMap<(DeviceIntRect, PatternKind), Vec<PrimitiveInstanceData>>, projection: &default::Transform3D<f32>, stats: &mut RendererStats, ) { @@ -2181,10 +2183,17 @@ impl Renderer { { let _timer = self.gpu_profiler.start_timer(GPU_TAG_INDIRECT_PRIM); - if !prim_instances.is_empty() { + if prim_instances.iter().any(|instances| !instances.is_empty()) { self.set_blend(false, FramebufferKind::Other); + } - self.shaders.borrow_mut().ps_quad_textured.bind( + for (pattern_idx, prim_instances) in prim_instances.iter().enumerate() { + if prim_instances.is_empty() { + continue; + } + let pattern = PatternKind::from_u32(pattern_idx as u32); + + self.shaders.borrow_mut().get_quad_shader(pattern).bind( &mut self.device, projection, None, @@ -2192,10 +2201,13 @@ impl Renderer { &mut self.profile, ); + // TODO: Some patterns will need to be able to sample textures. + let texture_bindings = BatchTextures::empty(); + self.draw_instanced_batch( prim_instances, VertexArrayKind::Primitive, - &BatchTextures::empty(), + &texture_bindings, stats, ); } @@ -2205,17 +2217,22 @@ impl Renderer { self.device.set_blend_mode_premultiplied_alpha(); self.device.enable_scissor(); - self.shaders.borrow_mut().ps_quad_textured.bind( - &mut self.device, - projection, - None, - &mut self.renderer_errors, - &mut self.profile, - ); + let mut prev_pattern = None; - for (scissor_rect, prim_instances) in prim_instances_with_scissor { - self.device.set_scissor_rect(draw_target.to_framebuffer_rect(*scissor_rect)); + for ((scissor_rect, pattern), prim_instances) in prim_instances_with_scissor { + if prev_pattern != Some(*pattern) { + prev_pattern = Some(*pattern); + self.shaders.borrow_mut().get_quad_shader(*pattern).bind( + &mut self.device, + projection, + None, + &mut self.renderer_errors, + &mut self.profile, + ); + } + self.device.set_scissor_rect(draw_target.to_framebuffer_rect(*scissor_rect)); + // TODO: hook up the right pattern. self.draw_instanced_batch( prim_instances, VertexArrayKind::Primitive, @@ -3578,14 +3595,12 @@ impl Renderer { stats, ); - for clip_masks in &target.clip_masks { - self.handle_clips( - &draw_target, - clip_masks, - projection, - stats, - ); - } + self.handle_clips( + &draw_target, + &target.clip_masks, + projection, + stats, + ); if clear_depth.is_some() { self.device.invalidate_depth_target(); @@ -3840,14 +3855,12 @@ impl Renderer { stats, ); - for clip_masks in &target.clip_masks { - self.handle_clips( - &draw_target, - clip_masks, - projection, - stats, - ); - } + self.handle_clips( + &draw_target, + &target.clip_masks, + projection, + stats, + ); } self.gpu_profiler.finish_sampler(alpha_sampler); diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs index 777bfab44a..96e8982aa0 100644 --- a/gfx/wr/webrender/src/renderer/shade.rs +++ b/gfx/wr/webrender/src/renderer/shade.rs @@ -6,6 +6,7 @@ use api::{ImageBufferKind, units::DeviceSize}; use crate::batch::{BatchKey, BatchKind, BrushBatchKind, BatchFeatures}; use crate::composite::{CompositeFeatures, CompositeSurfaceFormat}; use crate::device::{Device, Program, ShaderError}; +use crate::pattern::PatternKind; use euclid::default::Transform3D; use glyph_rasterizer::GlyphFormat; use crate::renderer::{ @@ -1153,6 +1154,16 @@ impl Shaders { .expect("bug: unsupported scale shader requested") } + pub fn get_quad_shader( + &mut self, + pattern: PatternKind + ) -> &mut LazilyCompiledShader { + match pattern { + PatternKind::ColorOrTexture => &mut self.ps_quad_textured, + PatternKind::Mask => unreachable!(), + } + } + pub fn get(& mut self, key: &BatchKey, @@ -1161,9 +1172,12 @@ impl Shaders { device: &Device, ) -> &mut LazilyCompiledShader { match key.kind { - BatchKind::Primitive => { + BatchKind::Quad(PatternKind::ColorOrTexture) => { &mut self.ps_quad_textured } + BatchKind::Quad(PatternKind::Mask) => { + unreachable!(); + } BatchKind::SplitComposite => { &mut self.ps_split_composite } diff --git a/gfx/wr/webrender/src/resource_cache.rs b/gfx/wr/webrender/src/resource_cache.rs index 349be25cb8..aee3cbe241 100644 --- a/gfx/wr/webrender/src/resource_cache.rs +++ b/gfx/wr/webrender/src/resource_cache.rs @@ -193,12 +193,6 @@ struct ImageResource { generation: ImageGeneration, } -#[derive(Clone, Debug)] -pub struct ImageTiling { - pub image_size: DeviceIntSize, - pub tile_size: TileSize, -} - #[derive(Default)] struct ImageTemplates { images: FastHashMap<ImageKey, ImageResource>, diff --git a/gfx/wr/webrender/src/texture_pack/mod.rs b/gfx/wr/webrender/src/texture_pack/mod.rs index f89a82b0a1..47de681cbe 100644 --- a/gfx/wr/webrender/src/texture_pack/mod.rs +++ b/gfx/wr/webrender/src/texture_pack/mod.rs @@ -57,8 +57,6 @@ pub trait AtlasAllocatorList<TextureParameters> { fn set_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId, handle: &TextureCacheHandle); - fn remove_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId); - /// Deallocate a rectangle and return its size. fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId); @@ -235,14 +233,6 @@ for AllocatorList<Allocator, TextureParameters> { unit.handles.insert(alloc_id, handle.clone()); } - fn remove_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) { - let unit = self.units - .iter_mut() - .find(|unit| unit.texture_id == texture_id) - .expect("Unable to find the associated texture array unit"); - unit.handles.remove(&alloc_id); - } - fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) { self.deallocate(texture_id, alloc_id); } @@ -314,7 +304,6 @@ impl AtlasAllocator for ShelfAllocator { pub struct CompactionChange { pub handle: TextureCacheHandle, - pub old_id: AllocId, pub old_tex: CacheTextureId, pub old_rect: DeviceIntRect, pub new_id: AllocId, @@ -372,7 +361,6 @@ impl<P> AllocatorList<ShelfAllocator, P> { // Record the change so that the texture cache can do additional bookkeeping. changes.push(CompactionChange { handle, - old_id: AllocId(alloc.id.serialize()), old_tex: self.units[last_unit].texture_id, old_rect: alloc.rectangle.cast_unit(), new_id: AllocId(new_alloc.id.serialize()), diff --git a/gfx/wr/webrender/src/util.rs b/gfx/wr/webrender/src/util.rs index 9b16131327..fa02d87b91 100644 --- a/gfx/wr/webrender/src/util.rs +++ b/gfx/wr/webrender/src/util.rs @@ -66,9 +66,6 @@ pub trait VecHelper<T> { /// Equivalent to `mem::replace(&mut vec, Vec::new())` fn take(&mut self) -> Self; - /// Call clear and return self (useful for chaining with calls that move the vector). - fn cleared(self) -> Self; - /// Functionally equivalent to `mem::replace(&mut vec, Vec::new())` but tries /// to keep the allocation in the caller if it is empty or replace it with a /// pre-allocated vector. @@ -102,12 +99,6 @@ impl<T> VecHelper<T> for Vec<T> { replace(self, Vec::new()) } - fn cleared(mut self) -> Self { - self.clear(); - - self - } - fn take_and_preallocate(&mut self) -> Self { let len = self.len(); if len == 0 { @@ -399,10 +390,6 @@ pub trait MatrixHelpers<Src, Dst> { fn is_2d_scale_translation(&self) -> bool; /// Return the determinant of the 2D part of the matrix. fn determinant_2d(&self) -> f32; - /// This function returns a point in the `Src` space that projects into zero XY. - /// It ignores the Z coordinate and is usable for "flattened" transformations, - /// since they are not generally inversible. - fn inverse_project_2d_origin(&self) -> Option<Point2D<f32, Src>>; /// Turn Z transformation into identity. This is useful when crossing "flat" /// transform styled stacking contexts upon traversing the coordinate systems. fn flatten_z_output(&mut self); @@ -534,17 +521,6 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { self.m11 * self.m22 - self.m12 * self.m21 } - fn inverse_project_2d_origin(&self) -> Option<Point2D<f32, Src>> { - let det = self.determinant_2d(); - if det != 0.0 { - let x = (self.m21 * self.m42 - self.m41 * self.m22) / det; - let y = (self.m12 * self.m41 - self.m11 * self.m42) / det; - Some(Point2D::new(x, y)) - } else { - None - } - } - fn flatten_z_output(&mut self) { self.m13 = 0.0; self.m23 = 0.0; @@ -623,22 +599,6 @@ impl<U> RectHelpers<U> for Box2D<f32, U> { } } -pub trait VectorHelpers<U> -where - Self: Sized, -{ - fn snap(&self) -> Self; -} - -impl<U> VectorHelpers<U> for Vector2D<f32, U> { - fn snap(&self) -> Self { - Vector2D::new( - (self.x + 0.5).floor(), - (self.y + 0.5).floor(), - ) - } -} - pub fn lerp(a: f32, b: f32, t: f32) -> f32 { (b - a) * t + a } @@ -874,22 +834,6 @@ pub mod test { } #[test] - fn inverse_project_2d_origin() { - let mut m = Transform3D::identity(); - assert_eq!(m.inverse_project_2d_origin(), Some(Point2D::zero())); - m.m11 = 0.0; - assert_eq!(m.inverse_project_2d_origin(), None); - m.m21 = -2.0; - m.m22 = 0.0; - m.m12 = -0.5; - m.m41 = 1.0; - m.m42 = 0.5; - let origin = m.inverse_project_2d_origin().unwrap(); - assert_eq!(origin, Point2D::new(1.0, 0.5)); - assert_eq!(m.transform_point2d(origin), Some(Point2D::zero())); - } - - #[test] fn polygon_clip_is_left_of_point() { // Define points of a line through (1, -3) and (-2, 6) to test against. // If the triplet consisting of these two points and the test point |