| author | Dzmitry Malyshau <dmalyshau@mozilla.com> |
| Tue, 12 Feb 2019 15:38:12 +0000 | |
| changeset 458751 | e4db508a2d78aeabf5900e90f345acd083b3d290 |
| parent 458750 | bac61e8bf3a17cd4d0c64d0b7ecaa0731512dba8 |
| child 458752 | 7f9e50bfaad127c642d8537b1a4d4feda283048b |
| push id | 35548 |
| push user | opoprus@mozilla.com |
| push date | Wed, 13 Feb 2019 09:48:26 +0000 |
| treeherder | mozilla-central@93e37c529818 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | gw, nical |
| bugs | 1526235 |
| milestone | 67.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -1,33 +1,34 @@ /* 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::{AlphaType, ClipMode, DeviceIntRect, DeviceIntPoint, DeviceIntSize, WorldRect}; use api::{ExternalImageType, FilterOp, ImageRendering, LayoutRect, DeviceRect, DevicePixelScale}; use api::{YuvColorSpace, YuvFormat, PictureRect, ColorDepth, LayoutPoint, DevicePoint, LayoutSize}; +use api::{PremultipliedColorF}; use clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore, ClipNodeInstance}; use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId}; use glyph_rasterizer::GlyphFormat; -use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress}; +use gpu_cache::{GpuBlockData, GpuCache, GpuCacheHandle, GpuCacheAddress}; use gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator}; use gpu_types::{ClipMaskInstance, SplitCompositeInstance, SnapOffsets}; use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use internal_types::{FastHashMap, SavedTargetIndex, TextureSource}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface}; use prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex}; use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex}; use prim_store::image::ImageSource; use render_backend::DataStores; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree, TileBlit}; use renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; -use renderer::BLOCKS_PER_UV_RECT; +use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties}; use scene::FilterOpHelpers; use smallvec::SmallVec; use std::{f32, i32, usize}; use tiling::{RenderTargetContext}; use util::{project_rect, TransformedRectKind}; // Special sentinel value recognized by the shader. It is considered to be @@ -1748,17 +1749,17 @@ impl AlphaBatchBuilder { prim_info.clip_task_index != ClipTaskIndex::INVALID || transform_kind == TransformedRectKind::Complex { specified_blend_mode } else { BlendMode::None }; - debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID); + debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID); let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED { (gpu_cache.get_address(&prim_common_data.gpu_cache_handle), None) } else { let segment_instance = &ctx.scratch.segment_instances[segment_instance_index]; let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]); (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments) }; @@ -1802,16 +1803,21 @@ impl AlphaBatchBuilder { AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha, AlphaType::Alpha => BlendMode::Alpha, }; let request = ImageRequest { key: image_data.key, rendering: image_data.image_rendering, tile: None, }; + let prim_user_data = [ + ShaderColorMode::Image as i32 | ((image_data.alpha_type as i32) << 16), + RasterizationSpace::Local as i32, + get_shader_opacity(opacity_binding), + ]; if image_instance.visible_tiles.is_empty() { let cache_item = match image_data.source { ImageSource::Default => { resolve_image( request, ctx.resource_cache, gpu_cache, @@ -1844,25 +1850,21 @@ impl AlphaBatchBuilder { specified_blend_mode } else { BlendMode::None }; let batch_params = BrushBatchParameters::shared( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, - [ - ShaderColorMode::Image as i32 | ((image_data.alpha_type as i32) << 16), - RasterizationSpace::Local as i32, - get_shader_opacity(opacity_binding), - ], + prim_user_data, cache_item.uv_rect_handle.as_int(gpu_cache), ); - debug_assert!(image_instance.segment_instance_index != SegmentInstanceIndex::INVALID); + debug_assert_ne!(image_instance.segment_instance_index, SegmentInstanceIndex::INVALID); let (prim_cache_address, segments) = if image_instance.segment_instance_index == SegmentInstanceIndex::UNUSED { (gpu_cache.get_address(&common_data.gpu_cache_handle), None) } else { let segment_instance = &ctx.scratch.segment_instances[image_instance.segment_instance_index]; let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]); (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments) }; @@ -1892,47 +1894,72 @@ impl AlphaBatchBuilder { bounding_rect, transform_kind, render_tasks, z_id, prim_info.clip_task_index, ctx, ); } else { - for tile in &image_instance.visible_tiles { - if let Some((batch_kind, textures, user_data, uv_rect_address)) = get_image_tile_params( - ctx.resource_cache, - gpu_cache, - deferred_resolves, - request.with_tile(tile.tile_offset), - image_data.alpha_type, - get_shader_opacity(opacity_binding), - ) { - let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle); - let prim_header = PrimitiveHeader { - specific_prim_address: prim_cache_address, - local_rect: tile.local_rect, - local_clip_rect: tile.local_clip_rect, - task_address, - clip_task_address, - transform_id, - }; - let prim_header_index = prim_headers.push(&prim_header, z_id, user_data); + const VECS_PER_SPECIFIC_BRUSH: usize = 3; + const VECS_PER_SEGMENT: usize = 2; + let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT; + + // use temporary block storage since we don't know the number of visible tiles beforehand + let mut gpu_blocks = Vec::<GpuBlockData>::new(); + for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) { + gpu_blocks.clear(); + gpu_blocks.push(PremultipliedColorF::WHITE.into()); //color + gpu_blocks.push(PremultipliedColorF::WHITE.into()); //bg color + gpu_blocks.push([-1.0, 0.0, 0.0, 0.0].into()); //stretch size + // negative first value makes the shader code ignore it and use the local size instead + for tile in chunk { + let tile_rect = tile.local_rect.translate(&-prim_rect.origin.to_vector()); + gpu_blocks.push(tile_rect.into()); + gpu_blocks.push(GpuBlockData::EMPTY); + } - self.add_image_tile_to_batch( - batch_kind, - specified_blend_mode, - textures, - prim_header_index, - clip_task_address, - bounding_rect, - tile.edge_flags, - uv_rect_address, - z_id, - ); + let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks); + let prim_header = PrimitiveHeader { + local_rect: prim_rect, + local_clip_rect: image_instance.tight_local_clip_rect, + task_address, + specific_prim_address: gpu_cache.get_address(&gpu_handle), + clip_task_address, + transform_id, + }; + let prim_header_index = prim_headers.push(&prim_header, z_id, prim_user_data); + + for (i, tile) in chunk.iter().enumerate() { + if let Some((batch_kind, textures, uv_rect_address)) = get_image_tile_params( + ctx.resource_cache, + gpu_cache, + deferred_resolves, + request.with_tile(tile.tile_offset), + ) { + let base_instance = BrushInstance { + prim_header_index, + clip_task_address, + segment_index: i as i32, + edge_flags: tile.edge_flags, + brush_flags: BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION, + user_data: uv_rect_address.as_int(), + }; + let batch_key = BatchKey { + blend_mode: specified_blend_mode, + kind: BatchKind::Brush(batch_kind), + textures, + }; + self.current_batch_list().push_single_instance( + batch_key, + bounding_rect, + z_id, + base_instance.into(), + ); + } } } } } PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => { let prim_data = &ctx.data_stores.linear_grad[data_handle]; let specified_blend_mode = BlendMode::PremultipliedAlpha; @@ -2092,50 +2119,16 @@ impl AlphaBatchBuilder { prim_headers, z_id, ); } } } } - fn add_image_tile_to_batch( - &mut self, - batch_kind: BrushBatchKind, - blend_mode: BlendMode, - textures: BatchTextures, - prim_header_index: PrimitiveHeaderIndex, - clip_task_address: RenderTaskAddress, - bounding_rect: &PictureRect, - edge_flags: EdgeAaSegmentMask, - uv_rect_address: GpuCacheAddress, - z_id: ZBufferId, - ) { - let base_instance = BrushInstance { - prim_header_index, - clip_task_address, - segment_index: INVALID_SEGMENT_INDEX, - edge_flags, - brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, - user_data: uv_rect_address.as_int(), - }; - - let batch_key = BatchKey { - blend_mode, - kind: BatchKind::Brush(batch_kind), - textures, - }; - self.current_batch_list().push_single_instance( - batch_key, - bounding_rect, - z_id, - PrimitiveInstanceData::from(base_instance), - ); - } - /// Add a single segment instance to a batch. fn add_segment_to_batch( &mut self, segment: &BrushSegment, segment_data: &SegmentInstanceData, segment_index: i32, batch_kind: BrushBatchKind, prim_header_index: PrimitiveHeaderIndex, @@ -2338,39 +2331,32 @@ fn add_gradient_tiles( } } fn get_image_tile_params( resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec<DeferredResolve>, request: ImageRequest, - alpha_type: AlphaType, - shader_opacity: i32, -) -> Option<(BrushBatchKind, BatchTextures, [i32; 3], GpuCacheAddress)> { +) -> Option<(BrushBatchKind, BatchTextures, GpuCacheAddress)> { let cache_item = resolve_image( request, resource_cache, gpu_cache, deferred_resolves, ); if cache_item.texture_id == TextureSource::Invalid { None } else { let textures = BatchTextures::color(cache_item.texture_id); Some(( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, - [ - ShaderColorMode::Image as i32 | ((alpha_type as i32) << 16), - RasterizationSpace::Local as i32, - shader_opacity, - ], gpu_cache.get_address(&cache_item.uv_rect_handle), )) } } /// Either a single texture / user data for all segments, /// or a list of one per segment. enum SegmentDataKind {
--- a/gfx/wr/webrender/src/prim_store/image.rs +++ b/gfx/wr/webrender/src/prim_store/image.rs @@ -58,16 +58,17 @@ pub struct ImageCacheKey { /// when image tiling is used. I've left it as a Vec for /// now to reduce the number of changes, and because image /// tiling is very rare on real pages. #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] pub struct ImageInstance { pub opacity_binding_index: OpacityBindingIndex, pub segment_instance_index: SegmentInstanceIndex, + pub tight_local_clip_rect: LayoutRect, pub visible_tiles: Vec<VisibleImageTile>, } #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf, Hash)] pub struct Image { pub key: ApiImageKey, @@ -109,16 +110,17 @@ impl AsInstanceKind<ImageDataHandle> for prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { // TODO(gw): Refactor this to not need a separate image // instance (see ImageInstance struct). let image_instance_index = prim_store.images.push(ImageInstance { opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, + tight_local_clip_rect: LayoutRect::zero(), visible_tiles: Vec::new(), }); PrimitiveInstanceKind::Image { data_handle, image_instance_index, } } @@ -306,16 +308,17 @@ impl ImageData { } } }; } pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) { // Images are drawn as a white color, modulated by the total // opacity coming from any collapsed property bindings. + // Size has to match `VECS_PER_SPECIFIC_BRUSH` from `brush_image.glsl` exactly. request.push(self.color.premultiplied()); request.push(PremultipliedColorF::WHITE); request.push([ self.stretch_size.width + self.tile_spacing.width, self.stretch_size.height + self.tile_spacing.height, 0.0, 0.0, ]);
--- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -2660,92 +2660,87 @@ impl PrimitiveStore { ); image_instance.visible_tiles.clear(); let image_properties = frame_state .resource_cache .get_image_properties(image_data.key); - if let Some(image_properties) = image_properties { - if let Some(tile_size) = image_properties.tiling { - let device_image_size = image_properties.descriptor.size; - - // Tighten the clip rect because decomposing the repeated image can - // produce primitives that are partially covering the original image - // rect and we want to clip these extra parts out. - let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; - let prim_rect = LayoutRect::new( - prim_instance.prim_origin, - common_data.prim_size, - ); - let tight_clip_rect = prim_info - .combined_local_clip_rect - .intersection(&prim_rect).unwrap(); - - let visible_rect = compute_conservative_visible_rect( - prim_context, - &frame_state.current_dirty_region().combined.world_rect, - &tight_clip_rect - ); - - let base_edge_flags = edge_flags_for_tile_spacing(&image_data.tile_spacing); - - let stride = image_data.stretch_size + image_data.tile_spacing; - - let repetitions = ::image::repetitions( - &prim_rect, - &visible_rect, - stride, - ); - - let request = ImageRequest { - key: image_data.key, - rendering: image_data.image_rendering, - tile: None, + if let Some(ImageProperties { descriptor, tiling: Some(tile_size), .. }) = image_properties { + let device_image_size = descriptor.size; + + // Tighten the clip rect because decomposing the repeated image can + // produce primitives that are partially covering the original image + // rect and we want to clip these extra parts out. + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + let prim_rect = LayoutRect::new( + prim_instance.prim_origin, + common_data.prim_size, + ); + let tight_clip_rect = prim_info + .combined_local_clip_rect + .intersection(&prim_rect).unwrap(); + image_instance.tight_local_clip_rect = tight_clip_rect; + + let visible_rect = compute_conservative_visible_rect( + prim_context, + &frame_state.current_dirty_region().combined.world_rect, + &tight_clip_rect + ); + + let base_edge_flags = edge_flags_for_tile_spacing(&image_data.tile_spacing); + + let stride = image_data.stretch_size + image_data.tile_spacing; + + let repetitions = ::image::repetitions( + &prim_rect, + &visible_rect, + stride, + ); + + let request = ImageRequest { + key: image_data.key, + rendering: image_data.image_rendering, + tile: None, + }; + + for Repetition { origin, edge_flags } in repetitions { + let edge_flags = base_edge_flags | edge_flags; + + let image_rect = LayoutRect { + origin, + size: image_data.stretch_size, }; - for Repetition { origin, edge_flags } in repetitions { - let edge_flags = base_edge_flags | edge_flags; - - let image_rect = LayoutRect { - origin, - size: image_data.stretch_size, - }; - - let tiles = ::image::tiles( - &image_rect, - &visible_rect, - &device_image_size, - tile_size as i32, + let tiles = ::image::tiles( + &image_rect, + &visible_rect, + &device_image_size, + tile_size as i32, + ); + + for tile in tiles { + frame_state.resource_cache.request_image( + request.with_tile(tile.offset), + frame_state.gpu_cache, ); - for tile in tiles { - frame_state.resource_cache.request_image( - request.with_tile(tile.offset), - frame_state.gpu_cache, - ); - - image_instance.visible_tiles.push(VisibleImageTile { - tile_offset: tile.offset, - edge_flags: tile.edge_flags & edge_flags, - local_rect: tile.rect, - local_clip_rect: tight_clip_rect, - }); - } + image_instance.visible_tiles.push(VisibleImageTile { + tile_offset: tile.offset, + edge_flags: tile.edge_flags & edge_flags, + local_rect: tile.rect, + local_clip_rect: tight_clip_rect, + }); } - - if image_instance.visible_tiles.is_empty() { - // At this point if we don't have tiles to show it means we could probably - // have done a better a job at culling during an earlier stage. - // Clearing the screen rect has the effect of "culling out" the primitive - // from the point of view of the batch builder, and ensures we don't hit - // assertions later on because we didn't request any image. - prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; - } + } + + if image_instance.visible_tiles.is_empty() { + // Mark as invisible + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; } } write_segment(image_instance.segment_instance_index, frame_state, scratch, |request| { image_data.write_prim_gpu_blocks(request); }); } PrimitiveInstanceKind::LinearGradient { data_handle, ref mut visible_tiles_range, .. } => { @@ -2848,17 +2843,17 @@ impl PrimitiveStore { } fn write_segment<F>( segment_instance_index: SegmentInstanceIndex, frame_state: &mut FrameBuildingState, scratch: &mut PrimitiveScratchBuffer, f: F, ) where F: Fn(&mut GpuDataRequest) { - debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID); + debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID); if segment_instance_index != SegmentInstanceIndex::UNUSED { let segment_instance = &mut scratch.segment_instances[segment_instance_index]; if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) { let segments = &scratch.segments[segment_instance.segments_range]; f(&mut request); @@ -3120,25 +3115,27 @@ impl PrimitiveInstance { let segment_instance_index = match self.kind { PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } | PrimitiveInstanceKind::YuvImage { ref mut segment_instance_index, .. } => { segment_instance_index } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { let image_data = &data_stores.image[data_handle].kind; let image_instance = &mut prim_store.images[image_instance_index]; - // tiled images don't support segmentation + //Note: tiled images don't support automatic segmentation, + // they strictly produce one segment per visible tile instead. if frame_state .resource_cache .get_image_properties(image_data.key) .and_then(|properties| properties.tiling) - .is_some() { - image_instance.segment_instance_index = SegmentInstanceIndex::UNUSED; - return; - } + .is_some() + { + image_instance.segment_instance_index = SegmentInstanceIndex::UNUSED; + return; + } &mut image_instance.segment_instance_index } PrimitiveInstanceKind::Picture { .. } | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::Clear { .. } | PrimitiveInstanceKind::LinearGradient { .. } | @@ -3158,17 +3155,17 @@ impl PrimitiveInstance { prim_clip_chain, &mut frame_state.segment_builder, frame_state.clip_store, data_stores, ) { frame_state.segment_builder.build(|segment| { segments.push( BrushSegment::new( - segment.rect.translate(&LayoutVector2D::new(-prim_local_rect.origin.x, -prim_local_rect.origin.y)), + segment.rect.translate(&-prim_local_rect.origin.to_vector()), segment.has_mask, segment.edge_flags, [0.0; 4], BrushFlags::PERSPECTIVE_INTERPOLATION, ), ); }); }