diff --git a/webrender/res/ps_image.glsl b/webrender/res/ps_image.glsl deleted file mode 100644 index 3f0c14c335..0000000000 --- a/webrender/res/ps_image.glsl +++ /dev/null @@ -1,106 +0,0 @@ -/* 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/. */ - -#include shared,prim_shared - -// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use non-normalized -// texture coordinates. Otherwise, it uses normalized texture coordinates. Please -// check GL_TEXTURE_RECTANGLE. -flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas. -flat varying vec2 vTextureSize; // Size of the image in the texture atlas. -flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image. -flat varying vec4 vStRect; // Rectangle of valid texture rect. -flat varying float vLayer; - -#ifdef WR_FEATURE_TRANSFORM -flat varying vec4 vLocalRect; -#endif - -varying vec2 vLocalPos; -flat varying vec2 vStretchSize; - -#ifdef WR_VERTEX_SHADER -void main(void) { - Primitive prim = load_primitive(); - Image image = fetch_image(prim.specific_prim_address); - ImageResource res = fetch_image_resource(prim.user_data0); - -#ifdef WR_FEATURE_TRANSFORM - VertexInfo vi = write_transform_vertex_primitive(prim); - vLocalPos = vi.local_pos; - vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.p0 + prim.local_rect.size); -#else - VertexInfo vi = write_vertex(prim.local_rect, - prim.local_clip_rect, - prim.z, - prim.scroll_node, - prim.task, - prim.local_rect); - vLocalPos = vi.local_pos - prim.local_rect.p0; -#endif - - write_clip(vi.screen_pos, prim.clip_area); - - // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use - // non-normalized texture coordinates. -#ifdef WR_FEATURE_TEXTURE_RECT - vec2 texture_size_normalization_factor = vec2(1, 1); -#else - vec2 texture_size_normalization_factor = vec2(textureSize(sColor0, 0)); -#endif - - vec2 uv0 = res.uv_rect.p0; - vec2 uv1 = res.uv_rect.p1; - - // vUv will contain how many times this image has wrapped around the image size. - vec2 st0 = uv0 / texture_size_normalization_factor; - vec2 st1 = uv1 / texture_size_normalization_factor; - - vLayer = res.layer; - vTextureSize = st1 - st0; - vTextureOffset = st0; - vTileSpacing = image.stretch_size_and_tile_spacing.zw; - vStretchSize = image.stretch_size_and_tile_spacing.xy; - - // We clamp the texture coordinates to the half-pixel offset from the borders - // in order to avoid sampling outside of the texture area. - vec2 half_texel = vec2(0.5) / texture_size_normalization_factor; - vStRect = vec4(min(st0, st1) + half_texel, max(st0, st1) - half_texel); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { -#ifdef WR_FEATURE_TRANSFORM - float alpha = init_transform_fs(vLocalPos); - - // We clamp the texture coordinate calculation here to the local rectangle boundaries, - // which makes the edge of the texture stretch instead of repeat. - vec2 upper_bound_mask = step(vLocalRect.zw, vLocalPos); - vec2 relative_pos_in_rect = clamp(vLocalPos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy; -#else - float alpha = 1.0; - vec2 relative_pos_in_rect = vLocalPos; - vec2 upper_bound_mask = vec2(0.0); -#endif - - alpha *= do_clip(); - - // We calculate the particular tile this fragment belongs to, taking into - // account the spacing in between tiles. We only paint if our fragment does - // not fall into that spacing. - // If the pixel is at the local rectangle upper bound, we force the current - // tile upper bound in order to avoid wrapping. - vec2 position_in_tile = mix( - mod(relative_pos_in_rect, vStretchSize + vTileSpacing), - vStretchSize, - upper_bound_mask); - vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize); - st = clamp(st, vStRect.xy, vStRect.zw); - - alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize)))); - - oFragColor = vec4(alpha) * TEX_SAMPLE(sColor0, vec3(st, vLayer)); -} -#endif diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 4d160c0040..eb5e116332 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -40,7 +40,6 @@ const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff); #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum TransformBatchKind { TextRun(GlyphFormat), - Image(ImageBufferKind), BorderCorner, BorderEdge, } @@ -608,7 +607,26 @@ impl AlphaBatchBuilder { screen_rect.unclipped.size, ); - let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location); + // If the primitive is internally decomposed into multiple sub-primitives we may not + // use some of the per-primitive data typically stored in PrimitiveMetadata and get + // it from each sub-primitive instead. + let is_multiple_primitives = match prim_metadata.prim_kind { + PrimitiveKind::Brush => { + let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0]; + match brush.kind { + BrushKind::Image { ref visible_tiles, .. } => !visible_tiles.is_empty(), + _ => false, + } + } + _ => false, + }; + + let prim_cache_address = if is_multiple_primitives { + GpuCacheAddress::invalid() + } else { + gpu_cache.get_address(&prim_metadata.gpu_location) + }; + let no_textures = BatchTextures::no_texture(); let clip_task_address = prim_metadata .clip_task_id @@ -974,6 +992,32 @@ impl AlphaBatchBuilder { ); } } + BrushKind::Image { request, ref visible_tiles, .. } if !visible_tiles.is_empty() => { + for tile in visible_tiles { + if let Some((batch_kind, textures, user_data)) = get_image_tile_params( + ctx.resource_cache, + gpu_cache, + deferred_resolves, + request.with_tile(tile.tile_offset), + ) { + let prim_cache_address = gpu_cache.get_address(&tile.handle); + self.add_image_tile_to_batch( + batch_kind, + specified_blend_mode, + textures, + clip_chain_rect_index, + clip_task_address, + &task_relative_bounding_rect, + prim_cache_address, + scroll_id, + task_address, + z, + user_data, + tile.edge_flags + ); + } + } + } _ => { if let Some((batch_kind, textures, user_data)) = brush.get_batch_params( ctx.resource_cache, @@ -1064,50 +1108,6 @@ impl AlphaBatchBuilder { } } } - PrimitiveKind::Image => { - let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0]; - - let cache_item = match image_cpu.source { - ImageSource::Default => { - resolve_image( - image_cpu.key.request, - ctx.resource_cache, - gpu_cache, - deferred_resolves, - ) - } - ImageSource::Cache { ref handle, .. } => { - let rt_handle = handle - .as_ref() - .expect("bug: render task handle not allocated"); - let rt_cache_entry = ctx - .resource_cache - .get_cached_render_task(rt_handle); - ctx.resource_cache.get_texture_cache_item(&rt_cache_entry.handle) - } - }; - - if cache_item.texture_id == SourceTexture::Invalid { - warn!("Warnings: skip a PrimitiveKind::Image"); - debug!("at {:?}.", task_relative_bounding_rect); - return; - } - - let batch_kind = TransformBatchKind::Image(get_buffer_kind(cache_item.texture_id)); - let key = BatchKey::new( - BatchKind::Transformable(transform_kind, batch_kind), - non_segmented_blend_mode, - BatchTextures { - colors: [ - cache_item.texture_id, - SourceTexture::Invalid, - SourceTexture::Invalid, - ], - }, - ); - let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); - batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0)); - } PrimitiveKind::TextRun => { let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0]; @@ -1210,6 +1210,45 @@ impl AlphaBatchBuilder { } } + fn add_image_tile_to_batch( + &mut self, + batch_kind: BrushBatchKind, + blend_mode: BlendMode, + textures: BatchTextures, + clip_chain_rect_index: ClipChainRectIndex, + clip_task_address: RenderTaskAddress, + task_relative_bounding_rect: &DeviceIntRect, + prim_cache_address: GpuCacheAddress, + scroll_id: ClipScrollNodeIndex, + task_address: RenderTaskAddress, + z: ZBufferId, + user_data: [i32; 3], + edge_flags: EdgeAaSegmentMask, + ) { + let base_instance = BrushInstance { + picture_address: task_address, + prim_address: prim_cache_address, + clip_chain_rect_index, + scroll_id, + clip_task_address, + z, + segment_index: 0, + edge_flags, + brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, + user_data, + }; + + self.batch_list.add_bounding_rect(task_relative_bounding_rect); + + let batch_key = BatchKey { + blend_mode, + kind: BatchKind::Brush(batch_kind), + textures, + }; + let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect); + batch.push(PrimitiveInstance::from(base_instance)); + } + fn add_brush_to_batch( &mut self, brush: &BrushPrimitive, @@ -1309,6 +1348,37 @@ impl AlphaBatchBuilder { } } +fn get_image_tile_params( + resource_cache: &ResourceCache, + gpu_cache: &mut GpuCache, + deferred_resolves: &mut Vec, + request: ImageRequest, +) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> { + + let cache_item = resolve_image( + request, + resource_cache, + gpu_cache, + deferred_resolves, + ); + + if cache_item.texture_id == SourceTexture::Invalid { + None + } else { + let textures = BatchTextures::color(cache_item.texture_id); + Some(( + BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), + textures, + [ + cache_item.uv_rect_handle.as_int(gpu_cache), + (ShaderColorMode::ColorBitmap as i32) << 16 | + RasterizationSpace::Local as i32, + 0, + ], + )) + } +} + impl BrushPrimitive { pub fn get_picture_index(&self) -> PictureIndex { match self.kind { @@ -1528,13 +1598,6 @@ impl AlphaBatchHelpers for PrimitiveStore { } } } - PrimitiveKind::Image => { - let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0]; - match image_cpu.alpha_type { - AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha, - AlphaType::Alpha => BlendMode::Alpha, - } - } } } } diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 4b52252f79..b7b0e3985e 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -8,10 +8,10 @@ use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, Devi use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, Epoch, ExtendMode, ExternalScrollId}; use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop}; use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint}; -use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; +use api::{LayoutPrimitiveInfo, LayoutRect, LayoutVector2D, LayoutSize, LayoutTransform}; use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId}; use api::{PropertyBinding, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity, Shadow}; -use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect, TileOffset}; +use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect}; use api::{TransformStyle, YuvColorSpace, YuvData}; use app_units::Au; use clip::{ClipRegion, ClipSource, ClipSources, ClipStore}; @@ -22,15 +22,15 @@ use frame_builder::{FrameBuilder, FrameBuilderConfig}; use glyph_rasterizer::FontInstance; use gpu_types::BrushFlags; use hit_test::{HitTestingItem, HitTestingRun}; -use image::{decompose_image, TiledImageInfo, simplify_repeated_primitive}; +use image::simplify_repeated_primitive; use internal_types::{FastHashMap, FastHashSet}; use picture::PictureCompositeMode; use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient}; -use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageCacheKey, ImagePrimitiveCpu, ImageSource}; +use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageSource}; use prim_store::{BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore}; use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu}; use render_backend::{DocumentView}; -use resource_cache::{FontInstanceMap, ImageRequest, TiledImageMap}; +use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, ScenePipeline, StackingContextHelpers}; use scene_builder::{BuiltScene, SceneRequest}; use std::{f32, mem, usize}; @@ -150,9 +150,6 @@ pub struct DisplayListFlattener<'a> { /// The map of all font instances. font_instances: FontInstanceMap, - /// The map of tiled images. - tiled_image_map: TiledImageMap, - /// Used to track the latest flattened epoch for each pipeline. pipeline_epochs: Vec<(PipelineId, Epoch)>, @@ -207,7 +204,6 @@ impl<'a> DisplayListFlattener<'a> { scene: &Scene, clip_scroll_tree: &mut ClipScrollTree, font_instances: FontInstanceMap, - tiled_image_map: TiledImageMap, view: &DocumentView, output_pipelines: &FastHashSet, frame_builder_config: &FrameBuilderConfig, @@ -227,7 +223,6 @@ impl<'a> DisplayListFlattener<'a> { scene, clip_scroll_tree, font_instances, - tiled_image_map, config: *frame_builder_config, pipeline_epochs: Vec::new(), replacements: Vec::new(), @@ -623,49 +618,16 @@ impl<'a> DisplayListFlattener<'a> { let prim_info = item.get_layout_primitive_info(&reference_frame_relative_offset); match *item.item() { SpecificDisplayItem::Image(ref info) => { - match self.tiled_image_map.get(&info.image_key).cloned() { - Some(tiling) => { - // The image resource is tiled. We have to generate an image primitive - // for each tile. - decompose_image( - &TiledImageInfo { - rect: prim_info.rect, - tile_spacing: info.tile_spacing, - stretch_size: info.stretch_size, - device_image_size: tiling.image_size, - device_tile_size: tiling.tile_size as u32, - }, - &mut|tile| { - let mut prim_info = prim_info.clone(); - prim_info.rect = tile.rect; - self.add_image( - clip_and_scroll, - &prim_info, - tile.stretch_size, - info.tile_spacing, - None, - info.image_key, - info.image_rendering, - info.alpha_type, - Some(tile.tile_offset), - ); - } - ); - } - None => { - self.add_image( - clip_and_scroll, - &prim_info, - info.stretch_size, - info.tile_spacing, - None, - info.image_key, - info.image_rendering, - info.alpha_type, - None, - ); - } - } + self.add_image( + clip_and_scroll, + &prim_info, + info.stretch_size, + info.tile_spacing, + None, + info.image_key, + info.image_rendering, + info.alpha_type, + ); } SpecificDisplayItem::YuvImage(ref info) => { self.add_yuv_image( @@ -1773,7 +1735,6 @@ impl<'a> DisplayListFlattener<'a> { RepeatMode::Stretch, border.repeat_vertical, ); - let descriptor = BrushSegmentDescriptor { segments, clip_mask_kind: BrushClipMaskKind::Unknown, @@ -2141,7 +2102,6 @@ impl<'a> DisplayListFlattener<'a> { image_key: ImageKey, image_rendering: ImageRendering, alpha_type: AlphaType, - tile_offset: Option, ) { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); @@ -2150,12 +2110,6 @@ impl<'a> DisplayListFlattener<'a> { .. *info }; - let request = ImageRequest { - key: image_key, - rendering: image_rendering, - tile: tile_offset, - }; - let sub_rect = sub_rect.map(|texel_rect| { DeviceIntRect::new( DeviceIntPoint::new( @@ -2169,49 +2123,31 @@ impl<'a> DisplayListFlattener<'a> { ) }); - // See if conditions are met to run through the new - // image brush shader, which supports segments. - if tile_offset.is_none() { - let prim = BrushPrimitive::new( - BrushKind::Image { - request, - current_epoch: Epoch::invalid(), - alpha_type, - stretch_size, - tile_spacing, - source: ImageSource::Default, - sub_rect, - opacity_binding: OpacityBinding::new(), + let prim = BrushPrimitive::new( + BrushKind::Image { + request: ImageRequest { + key: image_key, + rendering: image_rendering, + tile: None, }, - None, - ); - - self.add_primitive( - clip_and_scroll, - &info, - Vec::new(), - PrimitiveContainer::Brush(prim), - ); - } else { - let prim_cpu = ImagePrimitiveCpu { - tile_spacing, + current_epoch: Epoch::invalid(), alpha_type, stretch_size, - current_epoch: Epoch::invalid(), + tile_spacing, source: ImageSource::Default, - key: ImageCacheKey { - request, - texel_rect: sub_rect, - }, - }; + sub_rect, + visible_tiles: Vec::new(), + opacity_binding: OpacityBinding::new(), + }, + None, + ); - self.add_primitive( - clip_and_scroll, - &info, - Vec::new(), - PrimitiveContainer::Image(prim_cpu), - ); - } + self.add_primitive( + clip_and_scroll, + &info, + Vec::new(), + PrimitiveContainer::Brush(prim), + ); } pub fn add_yuv_image( @@ -2258,7 +2194,6 @@ pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltS &request.scene, &mut clip_scroll_tree, request.font_instances, - request.tiled_image_map, &request.view, &request.output_pipelines, config, diff --git a/webrender/src/image.rs b/webrender/src/image.rs index dcf54877da..dcf9088211 100644 --- a/webrender/src/image.rs +++ b/webrender/src/image.rs @@ -2,8 +2,9 @@ * 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::{TileOffset, LayoutRect, LayoutSize, LayoutVector2D, DeviceUintSize}; -use euclid::rect; +use api::{TileOffset, LayoutRect, LayoutSize, LayoutPoint, DeviceUintSize}; +use euclid::vec2; +use prim_store::EdgeAaSegmentMask; /// If repetitions are far enough apart that only one is within /// the primitive rect, then we can simplify the parameters and @@ -27,102 +28,77 @@ pub fn simplify_repeated_primitive( } } -pub struct DecomposedTile { - pub rect: LayoutRect, - pub stretch_size: LayoutSize, - pub tile_offset: TileOffset, -} +pub fn for_each_repetition( + prim_rect: &LayoutRect, + visible_rect: &LayoutRect, + stride: &LayoutSize, + callback: &mut FnMut(&LayoutPoint, EdgeAaSegmentMask), +) { + assert!(stride.width > 0.0); + assert!(stride.height > 0.0); -pub struct TiledImageInfo { - /// The bounds of the item in layout space. - pub rect: LayoutRect, - /// The space between each repeated pattern in layout space. - pub tile_spacing: LayoutSize, - /// The size in layout space of each repetition of the image. - pub stretch_size: LayoutSize, - - /// The size the image occupies in the cache in device space. - pub device_image_size: DeviceUintSize, - /// The size of the tiles in the cache in device pixels. - pub device_tile_size: u32, -} + let visible_rect = match prim_rect.intersection(&visible_rect) { + Some(rect) => rect, + None => return, + }; -/// Decomposes an image that is repeated into an image per individual repetition. -/// We need to do this when we are unable to perform the repetition in the shader, -/// for example if the image is tiled. -/// -/// In all of the "decompose" methods below, we independently handle horizontal and vertical -/// decomposition. This lets us generate the minimum amount of primitives by, for example, -/// decomposing the repetition horizontally while repeating vertically in the shader (for -/// an image where the width is too bug but the height is not). -/// -/// decompose_image and decompose_row handle image repetitions while decompose_cache_tiles -/// takes care of the decomposition required by the internal tiling of the image in the cache. -/// -/// Note that the term tiling is overloaded: There is the tiling we get from repeating images -/// in layout space, and the tiling that we do in the texture cache (to avoid hitting texture -/// size limits). The latter is referred to as "device" tiling here to disambiguate. -pub fn decompose_image(info: &TiledImageInfo, callback: &mut FnMut(&DecomposedTile)) { - - let no_vertical_tiling = info.device_image_size.height <= info.device_tile_size; - let no_vertical_spacing = info.tile_spacing.height == 0.0; - - if no_vertical_tiling && no_vertical_spacing { - decompose_row(&info.rect, info, callback); - return; - } + let nx = if visible_rect.origin.x > prim_rect.origin.x { + f32::floor((visible_rect.origin.x - prim_rect.origin.x) / stride.width) + } else { + 0.0 + }; - // Decompose each vertical repetition into rows. - let layout_stride = info.stretch_size.height + info.tile_spacing.height; - let num_repetitions = (info.rect.size.height / layout_stride).ceil() as u32; + let ny = if visible_rect.origin.y > prim_rect.origin.y { + f32::floor((visible_rect.origin.y - prim_rect.origin.y) / stride.height) + } else { + 0.0 + }; - for i in 0 .. num_repetitions { - let row_rect = rect( - info.rect.origin.x, - info.rect.origin.y + (i as f32) * layout_stride, - info.rect.size.width, - info.stretch_size.height, - ).intersection(&info.rect); + let x0 = prim_rect.origin.x + nx * stride.width; + let y0 = prim_rect.origin.y + ny * stride.height; - if let Some(row_rect) = row_rect { - decompose_row(&row_rect, info, callback); - } - } -} + let mut p = LayoutPoint::new(x0, y0); + let x_most = visible_rect.max_x(); + let y_most = visible_rect.max_y(); -fn decompose_row(item_rect: &LayoutRect, info: &TiledImageInfo, callback: &mut FnMut(&DecomposedTile)) { + let x_count = f32::ceil((x_most - x0) / stride.width) as i32; + let y_count = f32::ceil((y_most - y0) / stride.height) as i32; - let no_horizontal_tiling = info.device_image_size.width <= info.device_tile_size; - let no_horizontal_spacing = info.tile_spacing.width == 0.0; - - if no_horizontal_tiling && no_horizontal_spacing { - decompose_cache_tiles(item_rect, info, callback); - return; - } + for y in 0..y_count { + let mut row_flags = EdgeAaSegmentMask::empty(); + if y == 0 { + row_flags |= EdgeAaSegmentMask::TOP; + } + if y == y_count - 1 { + row_flags |= EdgeAaSegmentMask::BOTTOM; + } - // Decompose each horizontal repetition. - let layout_stride = info.stretch_size.width + info.tile_spacing.width; - let num_repetitions = (item_rect.size.width / layout_stride).ceil() as u32; + for x in 0..x_count { + let mut edge_flags = row_flags; + if x == 0 { + edge_flags |= EdgeAaSegmentMask::LEFT; + } + if x == x_count - 1 { + edge_flags |= EdgeAaSegmentMask::RIGHT; + } - for i in 0 .. num_repetitions { - let decomposed_rect = rect( - item_rect.origin.x + (i as f32) * layout_stride, - item_rect.origin.y, - info.stretch_size.width, - item_rect.size.height, - ).intersection(item_rect); + callback(&p, edge_flags); - if let Some(decomposed_rect) = decomposed_rect { - decompose_cache_tiles(&decomposed_rect, info, callback); + p.x += stride.width; } + + p.x = x0; + p.y += stride.height; } } -fn decompose_cache_tiles( - item_rect: &LayoutRect, - info: &TiledImageInfo, - callback: &mut FnMut(&DecomposedTile), +pub fn for_each_tile( + prim_rect: &LayoutRect, + visible_rect: &LayoutRect, + device_image_size: &DeviceUintSize, + device_tile_size: u32, + callback: &mut FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask), ) { // The image resource is tiled. We have to generate an image primitive // for each tile. @@ -145,158 +121,169 @@ fn decompose_cache_tiles( // In the ascii diagram above, a large image is split into tiles of almost regular size. // The tiles on the right and bottom edges (hatched in the diagram) are smaller than // the regular tiles and are handled separately in the code see leftover_width/height. - // each generated image primitive corresponds to a tile in the texture cache, with the + // each generated segment corresponds to a tile in the texture cache, with the // assumption that the smaller tiles with leftover sizes are sized to fit their own // irregular size in the texture cache. - // - // For the case where we don't tile along an axis, we can still perform the repetition in - // the shader (for this particular axis), and it is worth special-casing for this to avoid - // generating many primitives. - // This can happen with very tall and thin images used as a repeating background. - // Apparently web authors do that... - - let needs_repeat_x = info.stretch_size.width < item_rect.size.width; - let needs_repeat_y = info.stretch_size.height < item_rect.size.height; - let tiled_in_x = info.device_image_size.width > info.device_tile_size; - let tiled_in_y = info.device_image_size.height > info.device_tile_size; + // Because we can have very large virtual images we iterate over the visible portion of + // the image in layer space intead of iterating over device tiles. - // If we don't actually tile in this dimension, repeating can be done in the shader. - let shader_repeat_x = needs_repeat_x && !tiled_in_x; - let shader_repeat_y = needs_repeat_y && !tiled_in_y; + let visible_rect = match prim_rect.intersection(&visible_rect) { + Some(rect) => rect, + None => return, + }; - let tile_size_f32 = info.device_tile_size as f32; + let device_tile_size_f32 = device_tile_size as f32; - // Note: this rounds down so it excludes the partially filled tiles on the right and - // bottom edges (we handle them separately below). - let num_tiles_x = (info.device_image_size.width / info.device_tile_size) as u16; - let num_tiles_y = (info.device_image_size.height / info.device_tile_size) as u16; + // Ratio between (image space) tile size and image size . + let tile_dw = device_tile_size_f32 / (device_image_size.width as f32); + let tile_dh = device_tile_size_f32 / (device_image_size.height as f32); - // Ratio between (image space) tile size and image size. - let img_dw = tile_size_f32 / (info.device_image_size.width as f32); - let img_dh = tile_size_f32 / (info.device_image_size.height as f32); - - // Stretched size of the tile in layout space. - let stretched_tile_size = LayoutSize::new( - img_dw * info.stretch_size.width, - img_dh * info.stretch_size.height, + // size of regular tiles in layout space. + let layer_tile_size = LayoutSize::new( + tile_dw * prim_rect.size.width, + tile_dh * prim_rect.size.height, ); // The size in pixels of the tiles on the right and bottom edges, smaller // than the regular tile size if the image is not a multiple of the tile size. // Zero means the image size is a multiple of the tile size. - let leftover = DeviceUintSize::new( - info.device_image_size.width % info.device_tile_size, - info.device_image_size.height % info.device_tile_size + let leftover_device_size = DeviceUintSize::new( + device_image_size.width % device_tile_size, + device_image_size.height % device_tile_size + ); + + // The size in layer space of the tiles on the right and bottom edges. + let leftover_layer_size = LayoutSize::new( + layer_tile_size.width * leftover_device_size.width as f32 / device_tile_size_f32, + layer_tile_size.height * leftover_device_size.height as f32 / device_tile_size_f32, + ); + + // Offset of the row and column of tiles with leftover size. + let leftover_offset = TileOffset::new( + (device_image_size.width / device_tile_size) as u16, + (device_image_size.height / device_tile_size) as u16, + ); + + // Number of culled out tiles to skip before the first visible tile. + let t0 = TileOffset::new( + if visible_rect.origin.x > prim_rect.origin.x { + f32::floor((visible_rect.origin.x - prim_rect.origin.x) / layer_tile_size.width) as u16 + } else { + 0 + }, + if visible_rect.origin.y > prim_rect.origin.y { + f32::floor((visible_rect.origin.y - prim_rect.origin.y) / layer_tile_size.height) as u16 + } else { + 0 + }, ); - for ty in 0 .. num_tiles_y { - for tx in 0 .. num_tiles_x { - add_device_tile( - item_rect, - stretched_tile_size, - TileOffset::new(tx, ty), - 1.0, - 1.0, - shader_repeat_x, - shader_repeat_y, - callback, - ); + let x_count = f32::ceil((visible_rect.max_x() - prim_rect.origin.x) / layer_tile_size.width) as u16 - t0.x; + let y_count = f32::ceil((visible_rect.max_y() - prim_rect.origin.y) / layer_tile_size.height) as u16 - t0.y; + + for y in 0..y_count { + + let mut row_flags = EdgeAaSegmentMask::empty(); + if y == 0 { + row_flags |= EdgeAaSegmentMask::TOP; } - if leftover.width != 0 { - // Tiles on the right edge that are smaller than the tile size. - add_device_tile( - item_rect, - stretched_tile_size, - TileOffset::new(num_tiles_x, ty), - (leftover.width as f32) / tile_size_f32, - 1.0, - shader_repeat_x, - shader_repeat_y, - callback, - ); + if y == y_count - 1 { + row_flags |= EdgeAaSegmentMask::BOTTOM; } - } - if leftover.height != 0 { - for tx in 0 .. num_tiles_x { - // Tiles on the bottom edge that are smaller than the tile size. - add_device_tile( - item_rect, - stretched_tile_size, - TileOffset::new(tx, num_tiles_y), - 1.0, - (leftover.height as f32) / tile_size_f32, - shader_repeat_x, - shader_repeat_y, - callback, - ); - } + for x in 0..x_count { + let tile_offset = t0 + vec2(x, y); + + + let mut segment_rect = LayoutRect { + origin: LayoutPoint::new( + prim_rect.origin.x + tile_offset.x as f32 * layer_tile_size.width, + prim_rect.origin.y + tile_offset.y as f32 * layer_tile_size.height, + ), + size: layer_tile_size, + }; - if leftover.width != 0 { - // Finally, the bottom-right tile with a "leftover" size. - add_device_tile( - item_rect, - stretched_tile_size, - TileOffset::new(num_tiles_x, num_tiles_y), - (leftover.width as f32) / tile_size_f32, - (leftover.height as f32) / tile_size_f32, - shader_repeat_x, - shader_repeat_y, - callback, - ); + if tile_offset.x == leftover_offset.x { + segment_rect.size.width = leftover_layer_size.width; + } + + if tile_offset.y == leftover_offset.y { + segment_rect.size.height = leftover_layer_size.height; + } + + let mut edge_flags = row_flags; + if x == 0 { + edge_flags |= EdgeAaSegmentMask::LEFT; + } + if x == x_count - 1 { + edge_flags |= EdgeAaSegmentMask::RIGHT; + } + + callback(&segment_rect, tile_offset, edge_flags); } } } -fn add_device_tile( - item_rect: &LayoutRect, - stretched_tile_size: LayoutSize, - tile_offset: TileOffset, - tile_ratio_width: f32, - tile_ratio_height: f32, - shader_repeat_x: bool, - shader_repeat_y: bool, - callback: &mut FnMut(&DecomposedTile), -) { - // If the image is tiled along a given axis, we can't have the shader compute - // the image repetition pattern. In this case we base the primitive's rectangle size - // on the stretched tile size which effectively cancels the repetition (and repetition - // has to be emulated by generating more primitives). - // If the image is not tiled along this axis, we can perform the repetition in the - // shader. In this case we use the item's size in the primitive (on that particular - // axis). - // See the shader_repeat_x/y code below. - - let stretch_size = LayoutSize::new( - stretched_tile_size.width * tile_ratio_width, - stretched_tile_size.height * tile_ratio_height, - ); - - let mut prim_rect = LayoutRect::new( - item_rect.origin + LayoutVector2D::new( - tile_offset.x as f32 * stretched_tile_size.width, - tile_offset.y as f32 * stretched_tile_size.height, - ), - stretch_size, - ); - - if shader_repeat_x { - assert_eq!(tile_offset.x, 0); - prim_rect.size.width = item_rect.size.width; +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashSet; + use api::{LayoutRect, DeviceUintSize}; + use euclid::{rect, size2}; + + // this checks some additional invariants + fn checked_for_each_tile( + prim_rect: &LayoutRect, + visible_rect: &LayoutRect, + device_image_size: &DeviceUintSize, + device_tile_size: u32, + callback: &mut FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask), + ) { + let mut coverage = LayoutRect::zero(); + let mut tiles = HashSet::new(); + for_each_tile(prim_rect, + visible_rect, + device_image_size, + device_tile_size, + &mut |tile_rect, tile_offset, tile_flags| { + // make sure we don't get sent duplicate tiles + assert!(!tiles.contains(&tile_offset)); + tiles.insert(tile_offset); + coverage = coverage.union(tile_rect); + assert!(prim_rect.contains_rect(&tile_rect)); + callback(tile_rect, tile_offset, tile_flags); + }, + ); + assert!(prim_rect.contains_rect(&coverage)); + assert!(coverage.contains_rect(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero()))); } - if shader_repeat_y { - assert_eq!(tile_offset.y, 0); - prim_rect.size.height = item_rect.size.height; + #[test] + fn basic() { + let mut count = 0; + checked_for_each_tile(&rect(0., 0., 1000., 1000.), + &rect(75., 75., 400., 400.), + &size2(400, 400), + 36, + &mut |_tile_rect, _tile_offset, _tile_flags| { + count += 1; + }, + ); + assert_eq!(count, 36); } - // Fix up the primitive's rect if it overflows the original item rect. - if let Some(rect) = prim_rect.intersection(item_rect) { - callback(&DecomposedTile { - tile_offset, - rect, - stretch_size, - }); + #[test] + fn empty() { + let mut count = 0; + checked_for_each_tile(&rect(0., 0., 74., 74.), + &rect(75., 75., 400., 400.), + &size2(400, 400), + 36, + &mut |_tile_rect, _tile_offset, _tile_flags| { + count += 1; + }, + ); + assert_eq!(count, 0); } -} +} \ No newline at end of file diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index b63f043a7a..a3756ccac2 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion}; -use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode}; -use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag}; +use api::{DeviceIntRect, DeviceIntSize, DeviceUintSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode}; +use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D}; use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets}; use border::{BorderCornerInstance, BorderEdgeKind}; @@ -19,6 +19,7 @@ use glyph_rasterizer::{FontInstance, FontTransform}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use gpu_types::{BrushFlags, ClipChainRectIndex}; +use image::{for_each_tile, for_each_repetition}; use picture::{PictureCompositeMode, PictureId, PicturePrimitive}; #[cfg(debug_assertions)] use render_backend::FrameId; @@ -146,7 +147,6 @@ pub struct PictureIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PrimitiveKind { TextRun, - Image, Border, Brush, } @@ -242,6 +242,13 @@ impl OpacityBinding { } } +#[derive(Debug)] +pub struct VisibleImageTile { + pub tile_offset: TileOffset, + pub handle: GpuCacheHandle, + pub edge_flags: EdgeAaSegmentMask, +} + #[derive(Debug)] pub enum BrushKind { Solid { @@ -261,6 +268,7 @@ pub enum BrushKind { source: ImageSource, sub_rect: Option, opacity_binding: OpacityBinding, + visible_tiles: Vec, }, YuvImage { yuv_key: [ImageKey; 3], @@ -1046,7 +1054,6 @@ impl ClipData { #[derive(Debug)] pub enum PrimitiveContainer { TextRun(TextRunPrimitiveCpu), - Image(ImagePrimitiveCpu), Border(BorderPrimitiveCpu), Brush(BrushPrimitive), } @@ -1080,7 +1087,6 @@ impl PrimitiveContainer { } } } - PrimitiveContainer::Image(..) | PrimitiveContainer::Border(..) => { true } @@ -1132,7 +1138,6 @@ impl PrimitiveContainer { } } } - PrimitiveContainer::Image(..) | PrimitiveContainer::Border(..) => { panic!("bug: other primitive containers not expected here"); } @@ -1267,17 +1272,6 @@ impl PrimitiveStore { self.cpu_text_runs.push(text_cpu); metadata } - PrimitiveContainer::Image(image_cpu) => { - let metadata = PrimitiveMetadata { - opacity: PrimitiveOpacity::translucent(), - prim_kind: PrimitiveKind::Image, - cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()), - ..base_metadata - }; - - self.cpu_images.push(image_cpu); - metadata - } PrimitiveContainer::Border(border_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), @@ -1347,7 +1341,6 @@ impl PrimitiveStore { } } PrimitiveKind::TextRun | - PrimitiveKind::Image | PrimitiveKind::Border => {} } @@ -1398,7 +1391,6 @@ impl PrimitiveStore { }; } PrimitiveKind::TextRun | - PrimitiveKind::Image | PrimitiveKind::Border => { unreachable!("bug: invalid prim type for opacity collapse"); } @@ -1431,6 +1423,7 @@ impl PrimitiveStore { frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) { + let mut is_tiled_image = false; let metadata = &mut self.cpu_metadata[prim_index.0]; #[cfg(debug_assertions)] { @@ -1451,117 +1444,6 @@ impl PrimitiveStore { frame_state, ); } - PrimitiveKind::Image => { - let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0]; - let image_properties = frame_state - .resource_cache - .get_image_properties(image_cpu.key.request.key); - - // TODO(gw): Add image.rs and move this code out to a separate - // source file as it gets more complicated, and we - // start pre-rendering images for other reasons. - - if let Some(image_properties) = image_properties { - // See if this image has been updated since we last hit this code path. - // If so, we need to (at least) update the opacity, and also rebuild - // and render task cached portions of this image. - if image_properties.epoch != image_cpu.current_epoch { - image_cpu.current_epoch = image_properties.epoch; - - // Update the opacity. - metadata.opacity.is_opaque = image_properties.descriptor.is_opaque && - image_cpu.tile_spacing.width == 0.0 && - image_cpu.tile_spacing.height == 0.0; - - // Work out whether this image is a normal / simple type, or if - // we need to pre-render it to the render task cache. - image_cpu.source = match image_cpu.key.texel_rect { - Some(texel_rect) => { - ImageSource::Cache { - // Size in device-pixels we need to allocate in render task cache. - size: texel_rect.size, - handle: None, - } - } - None => { - // Simple image - just use a normal texture cache entry. - ImageSource::Default - } - }; - } - - // Set if we need to request the source image from the cache this frame. - let mut request_source_image = false; - - // Every frame, for cached items, we need to request the render - // task cache item. The closure will be invoked on the first - // time through, and any time the render task output has been - // evicted from the texture cache. - match image_cpu.source { - ImageSource::Cache { size, ref mut handle } => { - let key = image_cpu.key; - - // Request a pre-rendered image task. - *handle = Some(frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size, - kind: RenderTaskCacheKeyKind::Image(key), - }, - frame_state.gpu_cache, - frame_state.render_tasks, - None, - image_properties.descriptor.is_opaque, - |render_tasks| { - // We need to render the image cache this frame, - // so will need access to the source texture. - request_source_image = true; - - // Create a task to blit from the texture cache to - // a normal transient render task surface. This will - // copy only the sub-rect, if specified. - let cache_to_target_task = RenderTask::new_blit( - size, - BlitSource::Image { - key, - }, - ); - let cache_to_target_task_id = render_tasks.add(cache_to_target_task); - - // Create a task to blit the rect from the child render - // task above back into the right spot in the persistent - // render target cache. - let target_to_cache_task = RenderTask::new_blit( - size, - BlitSource::RenderTask { - task_id: cache_to_target_task_id, - }, - ); - let target_to_cache_task_id = render_tasks.add(target_to_cache_task); - - // Hook this into the render task tree at the right spot. - pic_state.tasks.push(target_to_cache_task_id); - - // Pass the image opacity, so that the cached render task - // item inherits the same opacity properties. - target_to_cache_task_id - } - )); - } - ImageSource::Default => { - // Normal images just reference the source texture each frame. - request_source_image = true; - } - } - - // Request source image from the texture cache, if required. - if request_source_image { - frame_state.resource_cache.request_image( - image_cpu.key.request, - frame_state.gpu_cache, - ); - } - } - } PrimitiveKind::Brush => { let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0]; @@ -1574,15 +1456,18 @@ impl PrimitiveStore { ref mut current_epoch, ref mut source, ref mut opacity_binding, + ref mut visible_tiles, .. } => { let image_properties = frame_state .resource_cache .get_image_properties(request.key); + // Set if we need to request the source image from the cache this frame. if let Some(image_properties) = image_properties { *current_epoch = image_properties.epoch; + is_tiled_image = image_properties.tiling.is_some(); // If the opacity changed, invalidate the GPU cache so that // the new color for this primitive gets uploaded. @@ -1596,7 +1481,7 @@ impl PrimitiveStore { image_properties.descriptor.is_opaque && opacity_binding.current == 1.0; - if *tile_spacing != LayoutSize::zero() { + if *tile_spacing != LayoutSize::zero() && !is_tiled_image { *source = ImageSource::Cache { // Size in device-pixels we need to allocate in render task cache. size: DeviceIntSize::new( @@ -1610,6 +1495,8 @@ impl PrimitiveStore { // Work out whether this image is a normal / simple type, or if // we need to pre-render it to the render task cache. if let Some(rect) = sub_rect { + // We don't properly support this right now. + debug_assert!(!is_tiled_image); *source = ImageSource::Cache { // Size in device-pixels we need to allocate in render task cache. size: rect.size, @@ -1696,14 +1583,89 @@ impl PrimitiveStore { } } - if request_source_image { + if let Some(tile_size) = image_properties.tiling { + + let device_image_size = DeviceUintSize::new( + image_properties.descriptor.width, + image_properties.descriptor.height, + ); + + // 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 tight_clip_rect = metadata.local_clip_rect.intersection(&metadata.local_rect).unwrap(); + + let visible_rect = compute_conservative_visible_rect( + prim_run_context, + frame_context, + &tight_clip_rect + ); + + let base_edge_flags = edge_flags_for_tile_spacing(tile_spacing); + + let stride = stretch_size + *tile_spacing; + + visible_tiles.clear(); + + for_each_repetition( + &metadata.local_rect, + &visible_rect, + &stride, + &mut |origin, edge_flags| { + let edge_flags = base_edge_flags | edge_flags; + + let image_rect = LayoutRect { + origin: *origin, + size: stretch_size, + }; + + for_each_tile( + &image_rect, + &visible_rect, + &device_image_size, + tile_size as u32, + &mut |tile_rect, tile_offset, tile_flags| { + + frame_state.resource_cache.request_image( + request.with_tile(tile_offset), + frame_state.gpu_cache, + ); + + let mut handle = GpuCacheHandle::new(); + if let Some(mut request) = frame_state.gpu_cache.request(&mut handle) { + request.push(*tile_rect); + request.push(tight_clip_rect); + request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied()); + request.push(PremultipliedColorF::WHITE); + request.push([tile_rect.size.width, tile_rect.size.height, 0.0, 0.0]); + request.write_segment(*tile_rect, [0.0; 4]); + } + + visible_tiles.push(VisibleImageTile { + tile_offset, + handle, + edge_flags: tile_flags & edge_flags, + }); + } + ); + } + ); + + if 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. + metadata.screen_rect = None; + } + } else if request_source_image { frame_state.resource_cache.request_image( request, frame_state.gpu_cache, ); } } - } BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => { let channel_num = format.get_plane_num(); @@ -1789,6 +1751,11 @@ impl PrimitiveStore { } } + if is_tiled_image { + // we already requested each tile's gpu data. + return; + } + // Mark this GPU resource as required for this frame. if let Some(mut request) = frame_state.gpu_cache.request(&mut metadata.gpu_location) { // has to match VECS_PER_BRUSH_PRIM @@ -1800,10 +1767,6 @@ impl PrimitiveStore { let border = &self.cpu_borders[metadata.cpu_prim_index.0]; border.write_gpu_blocks(request); } - PrimitiveKind::Image => { - let image = &self.cpu_images[metadata.cpu_prim_index.0]; - image.write_gpu_blocks(request); - } PrimitiveKind::TextRun => { let text = &self.cpu_text_runs[metadata.cpu_prim_index.0]; text.write_gpu_blocks(&mut request); @@ -2517,6 +2480,39 @@ impl PrimitiveStore { } } +fn compute_conservative_visible_rect( + prim_run_context: &PrimitiveRunContext, + frame_context: &FrameBuildingContext, + local_clip_rect: &LayoutRect, +) -> LayoutRect { + let world_screen_rect = prim_run_context + .clip_chain.combined_outer_screen_rect + .to_f32() / frame_context.device_pixel_scale; + + if let Some(layer_screen_rect) = prim_run_context + .scroll_node + .world_content_transform + .unapply(&world_screen_rect) { + + return local_clip_rect.intersection(&layer_screen_rect).unwrap_or(LayoutRect::zero()); + } + + *local_clip_rect +} + +fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask { + let mut flags = EdgeAaSegmentMask::empty(); + + if tile_spacing.width > 0.0 { + flags |= EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT; + } + if tile_spacing.height > 0.0 { + flags |= EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM; + } + + flags +} + //Test for one clip region contains another trait InsideTest { fn might_contain(&self, clip: &T) -> bool; diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 3c00f1e0ee..f671474ec9 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -206,7 +206,6 @@ impl Document { &self.pending.scene, &mut self.clip_scroll_tree, resource_cache.get_font_instances(), - resource_cache.get_tiled_image_map(), &self.view, &self.output_pipelines, &self.frame_builder_config, @@ -251,7 +250,6 @@ impl Document { removed_pipelines: replace(&mut self.pending.removed_pipelines, Vec::new()), view: self.view.clone(), font_instances: resource_cache.get_font_instances(), - tiled_image_map: resource_cache.get_tiled_image_map(), output_pipelines: self.output_pipelines.clone(), }) } else { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index c7066108dc..badba837f0 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -136,10 +136,6 @@ const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag { label: "data init", color: debug_colors::LIGHTGREY, }; -const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag { - label: "Image", - color: debug_colors::GREEN, -}; const GPU_TAG_PRIM_SPLIT_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "SplitComposite", color: debug_colors::DARKBLUE, @@ -183,12 +179,6 @@ impl TransformBatchKind { fn debug_name(&self) -> &'static str { match *self { TransformBatchKind::TextRun(..) => "TextRun", - TransformBatchKind::Image(image_buffer_kind, ..) => match image_buffer_kind { - ImageBufferKind::Texture2D => "Image (2D)", - ImageBufferKind::TextureRect => "Image (Rect)", - ImageBufferKind::TextureExternal => "Image (External)", - ImageBufferKind::Texture2DArray => "Image (Array)", - }, TransformBatchKind::BorderCorner => "BorderCorner", TransformBatchKind::BorderEdge => "BorderEdge", } @@ -197,7 +187,6 @@ impl TransformBatchKind { fn sampler_tag(&self) -> GpuProfileTag { match *self { TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN, - TransformBatchKind::Image(..) => GPU_TAG_PRIM_IMAGE, TransformBatchKind::BorderCorner => GPU_TAG_PRIM_BORDER_CORNER, TransformBatchKind::BorderEdge => GPU_TAG_PRIM_BORDER_EDGE, } diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 1a5db01775..835eea163a 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -110,8 +110,6 @@ pub struct ImageTiling { pub tile_size: TileSize, } -pub type TiledImageMap = FastHashMap; - #[derive(Default)] struct ImageTemplates { images: FastHashMap, @@ -238,6 +236,16 @@ pub struct ImageRequest { pub tile: Option, } +impl ImageRequest { + pub fn with_tile(&self, offset: TileOffset) -> Self { + ImageRequest { + key: self.key, + rendering: self.rendering, + tile: Some(offset), + } + } +} + impl Into for ImageRequest { fn into(self) -> BlobImageRequest { BlobImageRequest { @@ -878,28 +886,6 @@ impl ResourceCache { }) } - pub fn get_tiled_image_map(&self) -> TiledImageMap { - self.resources - .image_templates - .images - .iter() - .filter_map(|(&key, template)| { - template.tiling.map(|tile_size| { - ( - key, - ImageTiling { - image_size: DeviceUintSize::new( - template.descriptor.width, - template.descriptor.height, - ), - tile_size, - }, - ) - }) - }) - .collect() - } - pub fn begin_frame(&mut self, frame_id: FrameId) { debug_assert_eq!(self.state, State::Idle); self.state = State::AddResources; diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index e9ded47a07..fcdc1bdef0 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -8,7 +8,7 @@ use display_list_flattener::build_scene; use frame_builder::{FrameBuilderConfig, FrameBuilder}; use clip_scroll_tree::ClipScrollTree; use internal_types::FastHashSet; -use resource_cache::{FontInstanceMap, TiledImageMap}; +use resource_cache::FontInstanceMap; use render_backend::DocumentView; use renderer::{PipelineInfo, SceneBuilderHooks}; use scene::Scene; @@ -55,7 +55,6 @@ pub struct SceneRequest { pub scene: Scene, pub view: DocumentView, pub font_instances: FontInstanceMap, - pub tiled_image_map: TiledImageMap, pub output_pipelines: FastHashSet, pub removed_pipelines: Vec, } diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index 1b9eae7ea9..36ce26dcb9 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -492,7 +492,6 @@ pub struct Shaders { // a cache shader (e.g. blur) to the screen. pub ps_text_run: TextShader, pub ps_text_run_dual_source: TextShader, - ps_image: Vec>, ps_border_corner: PrimitiveShader, ps_border_edge: PrimitiveShader, @@ -632,11 +631,9 @@ impl Shaders { // All image configuration. let mut image_features = Vec::new(); - let mut ps_image = Vec::new(); let mut brush_image = Vec::new(); // PrimitiveShader is not clonable. Use push() to initialize the vec. for _ in 0 .. IMAGE_BUFFER_KINDS.len() { - ps_image.push(None); brush_image.push(None); } for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() { @@ -645,12 +642,6 @@ impl Shaders { if feature_string != "" { image_features.push(feature_string); } - ps_image[buffer_kind] = Some(PrimitiveShader::new( - "ps_image", - device, - &image_features, - options.precache_shaders, - )?); brush_image[buffer_kind] = Some(BrushShader::new( "brush_image", device, @@ -749,7 +740,6 @@ impl Shaders { cs_clip_line, ps_text_run, ps_text_run_dual_source, - ps_image, ps_border_corner, ps_border_edge, ps_split_composite, @@ -815,11 +805,6 @@ impl Shaders { }; return text_shader.get(glyph_format, transform_kind); } - TransformBatchKind::Image(image_buffer_kind) => { - self.ps_image[image_buffer_kind as usize] - .as_mut() - .expect("Unsupported image shader kind") - } TransformBatchKind::BorderCorner => { &mut self.ps_border_corner } @@ -852,11 +837,6 @@ impl Shaders { shader.deinit(device); } } - for shader in self.ps_image { - if let Some(shader) = shader { - shader.deinit(device); - } - } for shader in self.brush_yuv_image { if let Some(shader) = shader { shader.deinit(device); diff --git a/webrender/tests/angle_shader_validation.rs b/webrender/tests/angle_shader_validation.rs index e214bede42..bd49dcef5b 100644 --- a/webrender/tests/angle_shader_validation.rs +++ b/webrender/tests/angle_shader_validation.rs @@ -62,10 +62,6 @@ const SHADERS: &[Shader] = &[ name: "ps_split_composite", features: PRIM_FEATURES, }, - Shader { - name: "ps_image", - features: PRIM_FEATURES, - }, Shader { name: "ps_text_run", features: PRIM_FEATURES,