diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index f4c1102aa4..1e6225babc 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -15,10 +15,12 @@ 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, PrimitiveTemplateKind, PrimitiveDataStore}; -use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind}; +use prim_store::{DeferredResolve, PrimitiveTemplateKind}; +use prim_store::{EdgeAaSegmentMask, PrimitiveInstanceKind}; use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex}; +use prim_store::image::ImageSource; +use render_backend::FrameResources; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree}; use renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; use renderer::BLOCKS_PER_UV_RECT; @@ -549,10 +551,10 @@ impl AlphaBatchBuilder { render_tasks, ).unwrap_or(OPAQUE_TASK_ADDRESS); - let prim_data = &ctx.resources.as_common_data(&prim_instance); + let prim_common_data = &ctx.resources.as_common_data(&prim_instance); let prim_rect = LayoutRect::new( prim_instance.prim_origin, - prim_data.prim_size, + prim_common_data.prim_size, ); match prim_instance.kind { @@ -1521,26 +1523,20 @@ impl AlphaBatchBuilder { ); } PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, .. } => { - let prim_data = &ctx.resources.prim_data_store[data_handle]; - let (format, yuv_key, image_rendering, color_depth, color_space) = match prim_data.kind { - PrimitiveTemplateKind::YuvImage { ref format, yuv_key, ref image_rendering, ref color_depth, ref color_space, .. } => { - (format, yuv_key, image_rendering, color_depth, color_space) - } - _ => unreachable!() - }; + let yuv_image_data = &ctx.resources.yuv_image_data_store[data_handle].kind; let mut textures = BatchTextures::no_texture(); let mut uv_rect_addresses = [0; 3]; //yuv channel - let channel_count = format.get_plane_num(); + let channel_count = yuv_image_data.format.get_plane_num(); debug_assert!(channel_count <= 3); for channel in 0 .. channel_count { - let image_key = yuv_key[channel]; + let image_key = yuv_image_data.yuv_key[channel]; let cache_item = resolve_image( ImageRequest { key: image_key, - rendering: *image_rendering, + rendering: yuv_image_data.image_rendering, tile: None, }, ctx.resource_cache, @@ -1560,16 +1556,16 @@ impl AlphaBatchBuilder { // All yuv textures should be the same type. let buffer_kind = get_buffer_kind(textures.colors[0]); assert!( - textures.colors[1 .. format.get_plane_num()] + textures.colors[1 .. yuv_image_data.format.get_plane_num()] .iter() .all(|&tid| buffer_kind == get_buffer_kind(tid)) ); let kind = BrushBatchKind::YuvImage( buffer_kind, - *format, - *color_depth, - *color_space, + yuv_image_data.format, + yuv_image_data.color_depth, + yuv_image_data.color_space, ); let batch_params = BrushBatchParameters::shared( @@ -1585,7 +1581,7 @@ impl AlphaBatchBuilder { let specified_blend_mode = BlendMode::PremultipliedAlpha; - let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || + let non_segmented_blend_mode = if !prim_common_data.opacity.is_opaque || prim_instance.clip_task_index != ClipTaskIndex::INVALID || transform_kind == TransformedRectKind::Complex { @@ -1596,7 +1592,7 @@ impl AlphaBatchBuilder { debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID); let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED { - (gpu_cache.get_address(&prim_data.gpu_cache_handle), None) + (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]); @@ -1620,7 +1616,7 @@ impl AlphaBatchBuilder { self.add_segmented_prim_to_batch( segments, - prim_data.opacity, + prim_common_data.opacity, &batch_params, specified_blend_mode, non_segmented_blend_mode, @@ -1635,27 +1631,22 @@ impl AlphaBatchBuilder { ); } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { - let prim_data = &ctx.resources.prim_data_store[data_handle]; - let (source, alpha_type, key, image_rendering) = match prim_data.kind { - PrimitiveTemplateKind::Image { ref source, alpha_type, key, image_rendering, .. } => { - (source, alpha_type, key, image_rendering) - } - _ => unreachable!() - }; + let image_data = &ctx.resources.image_data_store[data_handle].kind; + let common_data = &ctx.resources.image_data_store[data_handle].common; let image_instance = &ctx.prim_store.images[image_instance_index]; let opacity_binding = ctx.prim_store.get_opacity_binding(image_instance.opacity_binding_index); - let specified_blend_mode = match alpha_type { + let specified_blend_mode = match image_data.alpha_type { AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha, AlphaType::Alpha => BlendMode::Alpha, }; let request = ImageRequest { - key: key, - rendering: image_rendering, + key: image_data.key, + rendering: image_data.image_rendering, tile: None, }; if image_instance.visible_tiles.is_empty() { - let cache_item = match *source { + let cache_item = match image_data.source { ImageSource::Default => { resolve_image( request, @@ -1681,7 +1672,7 @@ impl AlphaBatchBuilder { let textures = BatchTextures::color(cache_item.texture_id); let opacity = PrimitiveOpacity::from_alpha(opacity_binding); - let opacity = opacity.combine(prim_data.opacity); + let opacity = opacity.combine(common_data.opacity); let non_segmented_blend_mode = if !opacity.is_opaque || prim_instance.clip_task_index != ClipTaskIndex::INVALID || @@ -1696,7 +1687,7 @@ impl AlphaBatchBuilder { BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, [ - ShaderColorMode::Image as i32 | ((alpha_type as i32) << 16), + ShaderColorMode::Image as i32 | ((image_data.alpha_type as i32) << 16), RasterizationSpace::Local as i32, get_shader_opacity(opacity_binding), ], @@ -1705,7 +1696,7 @@ impl AlphaBatchBuilder { debug_assert!(image_instance.segment_instance_index != SegmentInstanceIndex::INVALID); let (prim_cache_address, segments) = if image_instance.segment_instance_index == SegmentInstanceIndex::UNUSED { - (gpu_cache.get_address(&prim_data.gpu_cache_handle), None) + (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]); @@ -1749,7 +1740,7 @@ impl AlphaBatchBuilder { gpu_cache, deferred_resolves, request.with_tile(tile.tile_offset), - alpha_type, + image_data.alpha_type, get_shader_opacity(opacity_binding), ) { let prim_cache_address = gpu_cache.get_address(&tile.handle); @@ -2271,22 +2262,18 @@ impl BrushBatchParameters { impl PrimitiveInstance { pub fn is_cacheable( &self, - prim_data_store: &PrimitiveDataStore, + resources: &FrameResources, resource_cache: &ResourceCache, ) -> bool { let image_key = match self.kind { - PrimitiveInstanceKind::Image { data_handle, .. } | + PrimitiveInstanceKind::Image { data_handle, .. } => { + let image_data = &resources.image_data_store[data_handle].kind; + image_data.key + } PrimitiveInstanceKind::YuvImage { data_handle, .. } => { - let prim_data = &prim_data_store[data_handle]; - match prim_data.kind { - PrimitiveTemplateKind::YuvImage { ref yuv_key, .. } => { - yuv_key[0] - } - PrimitiveTemplateKind::Image { key, .. } => { - key - } - _ => unreachable!(), - } + let yuv_image_data = + &resources.yuv_image_data_store[data_handle].kind; + yuv_image_data.yuv_key[0] } PrimitiveInstanceKind::Picture { .. } | PrimitiveInstanceKind::TextRun { .. } | diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 2a7440a3c2..ccdac6e6ba 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -27,6 +27,7 @@ use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, NinePa use prim_store::{PrimitiveStore, PrimitiveStoreStats, LineDecorationCacheKey}; use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, get_line_decoration_sizes}; use prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams}; +use prim_store::image::{Image, YuvImage}; use prim_store::text_run::TextRun; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; @@ -1820,7 +1821,9 @@ impl<'a> DisplayListFlattener<'a> { for item in &items { match item { - // TODO(djg): ugh. de-duplicate this code. + ShadowItem::Image(ref pending_image) => { + self.add_shadow_prim(&pending_shadow, pending_image, &mut prims) + } ShadowItem::Primitive(ref pending_primitive) => { self.add_shadow_prim(&pending_shadow, pending_primitive, &mut prims) } @@ -1897,6 +1900,9 @@ impl<'a> DisplayListFlattener<'a> { self.add_primitive_to_draw_list(shadow_prim_instance); } } + ShadowItem::Image(pending_image) => { + self.add_shadow_prim_to_draw_list(pending_image) + }, ShadowItem::Primitive(pending_primitive) => { self.add_shadow_prim_to_draw_list(pending_primitive) }, @@ -2415,7 +2421,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &info, Vec::new(), - PrimitiveKeyKind::Image { + Image { key: image_key, tile_spacing: tile_spacing.into(), stretch_size: stretch_size.into(), @@ -2443,11 +2449,11 @@ impl<'a> DisplayListFlattener<'a> { YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY], }; - self.add_primitive( + self.add_nonshadowable_primitive( clip_and_scroll, info, Vec::new(), - PrimitiveKeyKind::YuvImage { + YuvImage { color_depth, yuv_key, format, @@ -2669,10 +2675,17 @@ pub struct PendingShadow { pub enum ShadowItem { Shadow(PendingShadow), + Image(PendingPrimitive), Primitive(PendingPrimitive), TextRun(PendingPrimitive), } +impl From> for ShadowItem { + fn from(image: PendingPrimitive) -> Self { + ShadowItem::Image(image) + } +} + impl From> for ShadowItem { fn from(container: PendingPrimitive) -> Self { ShadowItem::Primitive(container) diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 5655cbfefc..de351a8f9c 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -21,7 +21,7 @@ use internal_types::FastHashSet; use plane_split::{Clipper, Polygon, Splitter}; use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind}; use prim_store::{get_raster_rects, CoordinateSpaceMapping, PointKey}; -use prim_store::{OpacityBindingStorage, PrimitiveTemplateKind, ImageInstanceStorage, OpacityBindingIndex, SizeKey}; +use prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex, SizeKey}; use print_tree::PrintTreePrinter; use render_backend::FrameResources; use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit}; @@ -784,7 +784,7 @@ impl TileCache { // Some primitives can not be cached (e.g. external video images) let is_cacheable = prim_instance.is_cacheable( - &resources.prim_data_store, + &resources, resource_cache, ); @@ -809,7 +809,7 @@ impl TileCache { } } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { - let prim_data = &resources.prim_data_store[data_handle]; + let image_data = &resources.image_data_store[data_handle].kind; let image_instance = &image_instances[image_instance_index]; let opacity_binding_index = image_instance.opacity_binding_index; @@ -822,25 +822,11 @@ impl TileCache { } } - match prim_data.kind { - PrimitiveTemplateKind::Image { key, .. } => { - image_keys.push(key); - } - _ => { - unreachable!(); - } - } + image_keys.push(image_data.key); } PrimitiveInstanceKind::YuvImage { data_handle, .. } => { - let prim_data = &resources.prim_data_store[data_handle]; - match prim_data.kind { - PrimitiveTemplateKind::YuvImage { ref yuv_key, .. } => { - image_keys.extend_from_slice(yuv_key); - } - _ => { - unreachable!(); - } - } + let yuv_image_data = &resources.yuv_image_data_store[data_handle].kind; + image_keys.extend_from_slice(&yuv_image_data.yuv_key); } PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::LineDecoration { .. } | @@ -1410,11 +1396,12 @@ impl PrimitiveList { PrimitiveInstanceKind::NormalBorder { data_handle, .. } | PrimitiveInstanceKind::ImageBorder { data_handle, .. } | PrimitiveInstanceKind::Rectangle { data_handle, .. } | - PrimitiveInstanceKind::YuvImage { data_handle, .. } | - PrimitiveInstanceKind::Image { data_handle, .. } | PrimitiveInstanceKind::Clear { data_handle, .. } => { &resources.prim_interner[data_handle] } + PrimitiveInstanceKind::Image { data_handle, .. } => { + &resources.image_interner[data_handle] + } PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { &resources.linear_grad_interner[data_handle] } @@ -1424,6 +1411,9 @@ impl PrimitiveList { PrimitiveInstanceKind::TextRun { data_handle, .. } => { &resources.text_run_interner[data_handle] } + PrimitiveInstanceKind::YuvImage { data_handle, .. } => { + &resources.yuv_image_interner[data_handle] + } }; // Get the key for the cluster that this primitive should diff --git a/webrender/src/prim_store/image.rs b/webrender/src/prim_store/image.rs new file mode 100644 index 0000000000..883ed35faf --- /dev/null +++ b/webrender/src/prim_store/image.rs @@ -0,0 +1,581 @@ +/* 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, ColorDepth, ColorF, ColorU, DeviceIntRect, DeviceIntSideOffsets, + DeviceIntSize, ImageRendering, LayoutRect, LayoutSize, LayoutPrimitiveInfo, + PremultipliedColorF, Shadow, TileOffset, YuvColorSpace, YuvFormat +}; +use api::ImageKey as ApiImageKey; +use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible}; +use frame_builder::FrameBuildingState; +use gpu_cache::{GpuCacheHandle, GpuDataRequest}; +use intern::{DataStore, Handle, Internable, Interner, InternDebug, UpdateList}; +use picture::SurfaceIndex; +use prim_store::{ + EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind, + PrimitiveOpacity, PrimitiveSceneData, PrimKey, PrimKeyCommonData, + PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex, + SizeKey +}; +use render_task::{ + BlitSource, RenderTask, RenderTaskCacheEntryHandle, RenderTaskCacheKey, + RenderTaskCacheKeyKind +}; +use resource_cache::ImageRequest; +use util::pack_as_float; + +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct VisibleImageTile { + pub tile_offset: TileOffset, + pub handle: GpuCacheHandle, + pub edge_flags: EdgeAaSegmentMask, + pub local_rect: LayoutRect, + pub local_clip_rect: LayoutRect, +} + +// Key that identifies a unique (partial) image that is being +// stored in the render task cache. +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ImageCacheKey { + pub request: ImageRequest, + pub texel_rect: Option, +} + +/// Instance specific fields for an image primitive. These are +/// currently stored in a separate array to avoid bloating the +/// size of PrimitiveInstance. In the future, we should be able +/// to remove this and store the information inline, by: +/// (a) Removing opacity collapse / binding support completely. +/// Once we have general picture caching, we don't need this. +/// (b) Change visible_tiles to use Storage in the primitive +/// scratch buffer. This will reduce the size of the +/// visible_tiles field here, and save memory allocation +/// 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)] +pub struct ImageInstance { + pub opacity_binding_index: OpacityBindingIndex, + pub segment_instance_index: SegmentInstanceIndex, + pub visible_tiles: Vec, +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct Image { + pub key: ApiImageKey, + pub stretch_size: SizeKey, + pub tile_spacing: SizeKey, + pub color: ColorU, + pub sub_rect: Option, + pub image_rendering: ImageRendering, + pub alpha_type: AlphaType, +} + +pub type ImageKey = PrimKey; + +impl ImageKey { + pub fn new( + is_backface_visible: bool, + prim_size: LayoutSize, + prim_relative_clip_rect: LayoutRect, + image: Image, + ) -> Self { + + ImageKey { + common: PrimKeyCommonData { + is_backface_visible, + prim_size: prim_size.into(), + prim_relative_clip_rect: prim_relative_clip_rect.into(), + }, + kind: image, + } + } +} + +impl InternDebug for ImageKey {} + +impl AsInstanceKind for ImageKey { + /// Construct a primitive instance that matches the type + /// of primitive key. + fn as_instance_kind( + &self, + data_handle: ImageDataHandle, + prim_store: &mut PrimitiveStore, + ) -> 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, + visible_tiles: Vec::new(), + }); + + PrimitiveInstanceKind::Image { + data_handle, + image_instance_index, + } + } +} + +// Where to find the texture data for an image primitive. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug)] +pub enum ImageSource { + // A normal image - just reference the texture cache. + Default, + // An image that is pre-rendered into the texture cache + // via a render task. + Cache { + size: DeviceIntSize, + handle: Option, + }, +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ImageData { + pub key: ApiImageKey, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub color: ColorF, + pub source: ImageSource, + pub image_rendering: ImageRendering, + pub sub_rect: Option, + pub alpha_type: AlphaType, +} + +impl From for ImageData { + fn from(image: Image) -> Self { + ImageData { + key: image.key, + color: image.color.into(), + stretch_size: image.stretch_size.into(), + tile_spacing: image.tile_spacing.into(), + source: ImageSource::Default, + sub_rect: image.sub_rect, + image_rendering: image.image_rendering, + alpha_type: image.alpha_type, + } + } +} + +impl ImageData { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + // TODO(gw): Passing in surface_index here is not ideal. The primitive template + // code shouldn't depend on current surface state. This is due to a + // limitation in how render task caching works. We should fix this by + // allowing render task caching to assign to surfaces implicitly + // during pass allocation. + surface_index: SurfaceIndex, + common: &mut PrimTemplateCommonData, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { + self.write_prim_gpu_blocks(&mut request); + } + + common.opacity = { + let image_properties = frame_state + .resource_cache + .get_image_properties(self.key); + + match image_properties { + Some(image_properties) => { + let is_tiled = image_properties.tiling.is_some(); + + if self.tile_spacing != LayoutSize::zero() && !is_tiled { + self.source = ImageSource::Cache { + // Size in device-pixels we need to allocate in render task cache. + size: image_properties.descriptor.size.to_i32(), + handle: None, + }; + } + + // 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) = self.sub_rect { + // We don't properly support this right now. + debug_assert!(!is_tiled); + self.source = ImageSource::Cache { + // Size in device-pixels we need to allocate in render task cache. + size: rect.size, + handle: None, + }; + } + + let mut request_source_image = false; + let mut is_opaque = image_properties.descriptor.is_opaque; + let request = ImageRequest { + key: self.key, + rendering: self.image_rendering, + tile: None, + }; + + // 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 self.source { + ImageSource::Cache { ref mut size, ref mut handle } => { + let padding = DeviceIntSideOffsets::new( + 0, + (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32, + (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32, + 0, + ); + + let inner_size = *size; + size.width += padding.horizontal(); + size.height += padding.vertical(); + + is_opaque &= padding == DeviceIntSideOffsets::zero(); + + let image_cache_key = ImageCacheKey { + request, + texel_rect: self.sub_rect, + }; + let surfaces = &mut frame_state.surfaces; + + // Request a pre-rendered image task. + *handle = Some(frame_state.resource_cache.request_render_task( + RenderTaskCacheKey { + size: *size, + kind: RenderTaskCacheKeyKind::Image(image_cache_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_with_padding( + inner_size, + &padding, + BlitSource::Image { key: image_cache_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. + surfaces[surface_index.0].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; + } + } + + if request_source_image && !is_tiled { + frame_state.resource_cache.request_image( + request, + frame_state.gpu_cache, + ); + } + + if is_opaque { + PrimitiveOpacity::from_alpha(self.color.a) + } else { + PrimitiveOpacity::translucent() + } + } + None => { + PrimitiveOpacity::opaque() + } + } + }; + } + + 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. + 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, + ]); + } +} + +pub type ImageTemplate = PrimTemplate; + +impl From for ImageTemplate { + fn from(image: ImageKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(image.common); + + ImageTemplate { + common, + kind: image.kind.into(), + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct ImageDataMarker; + +pub type ImageDataStore = DataStore; +pub type ImageDataHandle = Handle; +pub type ImageDataUpdateList = UpdateList; +pub type ImageDataInterner = Interner; + +impl Internable for Image { + type Marker = ImageDataMarker; + type Source = ImageKey; + type StoreData = ImageTemplate; + type InternData = PrimitiveSceneData; + + /// Build a new key from self with `info`. + fn build_key( + self, + info: &LayoutPrimitiveInfo, + prim_relative_clip_rect: LayoutRect, + ) -> ImageKey { + ImageKey::new( + info.is_backface_visible, + info.rect.size, + prim_relative_clip_rect, + self + ) + } +} + +impl CreateShadow for Image { + fn create_shadow(&self, shadow: &Shadow) -> Self { + Image { + tile_spacing: self.tile_spacing, + stretch_size: self.stretch_size, + key: self.key, + sub_rect: self.sub_rect, + image_rendering: self.image_rendering, + alpha_type: self.alpha_type, + color: shadow.color.into(), + } + } +} + +impl IsVisible for Image { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct YuvImage { + pub color_depth: ColorDepth, + pub yuv_key: [ApiImageKey; 3], + pub format: YuvFormat, + pub color_space: YuvColorSpace, + pub image_rendering: ImageRendering, +} + +pub type YuvImageKey = PrimKey; + +impl YuvImageKey { + pub fn new( + is_backface_visible: bool, + prim_size: LayoutSize, + prim_relative_clip_rect: LayoutRect, + yuv_image: YuvImage, + ) -> Self { + + YuvImageKey { + common: PrimKeyCommonData { + is_backface_visible, + prim_size: prim_size.into(), + prim_relative_clip_rect: prim_relative_clip_rect.into(), + }, + kind: yuv_image, + } + } +} + +impl InternDebug for YuvImageKey {} + +impl AsInstanceKind for YuvImageKey { + /// Construct a primitive instance that matches the type + /// of primitive key. + fn as_instance_kind( + &self, + data_handle: YuvImageDataHandle, + _prim_store: &mut PrimitiveStore, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::YuvImage { + data_handle, + segment_instance_index: SegmentInstanceIndex::INVALID + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct YuvImageData { + pub color_depth: ColorDepth, + pub yuv_key: [ApiImageKey; 3], + pub format: YuvFormat, + pub color_space: YuvColorSpace, + pub image_rendering: ImageRendering, +} + +impl From for YuvImageData { + fn from(image: YuvImage) -> Self { + YuvImageData { + color_depth: image.color_depth, + yuv_key: image.yuv_key, + format: image.format, + color_space: image.color_space, + image_rendering: image.image_rendering, + } + } +} + +impl YuvImageData { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + common: &mut PrimTemplateCommonData, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { + self.write_prim_gpu_blocks(&mut request); + }; + + let channel_num = self.format.get_plane_num(); + debug_assert!(channel_num <= 3); + for channel in 0 .. channel_num { + frame_state.resource_cache.request_image( + ImageRequest { + key: self.yuv_key[channel], + rendering: self.image_rendering, + tile: None, + }, + frame_state.gpu_cache, + ); + } + + common.opacity = PrimitiveOpacity::translucent(); + } + + pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) { + request.push([ + self.color_depth.rescaling_factor(), + pack_as_float(self.color_space as u32), + pack_as_float(self.format as u32), + 0.0 + ]); + } +} + +pub type YuvImageTemplate = PrimTemplate; + +impl From for YuvImageTemplate { + fn from(image: YuvImageKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(image.common); + + YuvImageTemplate { + common, + kind: image.kind.into(), + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct YuvImageDataMarker; + +pub type YuvImageDataStore = DataStore; +pub type YuvImageDataHandle = Handle; +pub type YuvImageDataUpdateList = UpdateList; +pub type YuvImageDataInterner = Interner; + +impl Internable for YuvImage { + type Marker = YuvImageDataMarker; + type Source = YuvImageKey; + type StoreData = YuvImageTemplate; + type InternData = PrimitiveSceneData; + + /// Build a new key from self with `info`. + fn build_key( + self, + info: &LayoutPrimitiveInfo, + prim_relative_clip_rect: LayoutRect, + ) -> YuvImageKey { + YuvImageKey::new( + info.is_backface_visible, + info.rect.size, + prim_relative_clip_rect, + self + ) + } +} + +impl IsVisible for YuvImage { + fn is_visible(&self) -> bool { + true + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_struct_sizes() { + use std::mem; + // The sizes of these structures are critical for performance on a number of + // talos stress tests. If you get a failure here on CI, there's two possibilities: + // (a) You made a structure smaller than it currently is. Great work! Update the + // test expectations and move on. + // (b) You made a structure larger. This is not necessarily a problem, but should only + // be done with care, and after checking if talos performance regresses badly. + assert_eq!(mem::size_of::(), 56, "Image size changed"); + assert_eq!(mem::size_of::(), 144, "ImageTemplate size changed"); + assert_eq!(mem::size_of::(), 84, "ImageKey size changed"); + assert_eq!(mem::size_of::(), 36, "YuvImage size changed"); + assert_eq!(mem::size_of::(), 96, "YuvImageTemplate size changed"); + assert_eq!(mem::size_of::(), 64, "YuvImageKey size changed"); +} diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index f4d01d2be5..af5fc61a83 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -2,13 +2,13 @@ * 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, BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D}; -use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceRect, LayoutSideOffsetsAu}; -use api::{FilterOp, ImageKey, ImageRendering, TileOffset, RepeatMode, MixBlendMode}; +use api::{BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D}; +use api::{DeviceIntRect, DevicePixelScale, DeviceRect, LayoutSideOffsetsAu}; +use api::{FilterOp, ImageRendering, TileOffset, RepeatMode, MixBlendMode}; use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, PropertyBindingId}; -use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat}; -use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale}; -use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers}; +use api::{PremultipliedColorF, PropertyBinding, Shadow}; +use api::{WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale}; +use api::{PicturePixel, RasterPixel, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers}; use api::LayoutPrimitiveInfo; use app_units::Au; use border::{get_max_scale_for_border, build_border_instances, create_border_segments}; @@ -23,16 +23,17 @@ use frame_builder::PrimitiveContext; use glyph_rasterizer::GlyphKey; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use gpu_types::BrushFlags; -use image::{self, Repetition}; +use image::{Repetition}; use intern; use picture::{PictureCompositeMode, PicturePrimitive, PictureUpdateState, TileCacheUpdateState}; use picture::{ClusterRange, PrimitiveList, SurfaceIndex, SurfaceInfo, RetainedTiles, RasterConfig}; use prim_store::gradient::{LinearGradientDataHandle, RadialGradientDataHandle}; +use prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle}; use prim_store::text_run::{TextRunDataHandle, TextRunPrimitive}; #[cfg(debug_assertions)] use render_backend::{FrameId}; use render_backend::FrameResources; -use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, to_cache_size}; +use render_task::{RenderTask, RenderTaskCacheKey, to_cache_size}; use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle}; use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; @@ -47,6 +48,7 @@ use util::{pack_as_float, project_rect, raster_rect_to_device_pixels}; use smallvec::SmallVec; pub mod gradient; +pub mod image; pub mod text_run; /// Counter for unique primitive IDs for debug tracing. @@ -480,22 +482,6 @@ pub enum PrimitiveKeyKind { Rectangle { color: ColorU, }, - YuvImage { - color_depth: ColorDepth, - yuv_key: [ImageKey; 3], - format: YuvFormat, - color_space: YuvColorSpace, - image_rendering: ImageRendering, - }, - Image { - key: ImageKey, - stretch_size: SizeKey, - tile_spacing: SizeKey, - color: ColorU, - sub_rect: Option, - image_rendering: ImageRendering, - alpha_type: AlphaType, - }, Picture { composite_mode_key: PictureCompositeKey, }, @@ -727,6 +713,14 @@ impl PrimKeyCommonData { } } +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct PrimKey { + pub common: PrimKeyCommonData, + pub kind: T, +} + #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -761,7 +755,7 @@ impl AsInstanceKind for PrimitiveKey { fn as_instance_kind( &self, data_handle: PrimitiveDataHandle, - prim_store: &mut PrimitiveStore, + _: &mut PrimitiveStore, ) -> PrimitiveInstanceKind { match self.kind { PrimitiveKeyKind::LineDecoration { .. } => { @@ -793,26 +787,6 @@ impl AsInstanceKind for PrimitiveKey { segment_instance_index: SegmentInstanceIndex::INVALID, } } - PrimitiveKeyKind::YuvImage { .. } => { - PrimitiveInstanceKind::YuvImage { - data_handle, - segment_instance_index: SegmentInstanceIndex::INVALID - } - } - PrimitiveKeyKind::Image { .. } => { - // 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, - visible_tiles: Vec::new(), - }); - - PrimitiveInstanceKind::Image { - data_handle, - image_instance_index, - } - } PrimitiveKeyKind::Picture { .. } => { // Should never be hit as this method should not be // called for pictures. @@ -850,23 +824,6 @@ pub enum PrimitiveTemplateKind { Rectangle { color: ColorF, }, - YuvImage { - color_depth: ColorDepth, - yuv_key: [ImageKey; 3], - format: YuvFormat, - color_space: YuvColorSpace, - image_rendering: ImageRendering, - }, - Image { - key: ImageKey, - stretch_size: LayoutSize, - tile_spacing: LayoutSize, - color: ColorF, - source: ImageSource, - image_rendering: ImageRendering, - sub_rect: Option, - alpha_type: AlphaType, - }, Clear, Picture { @@ -934,27 +891,6 @@ impl PrimitiveKeyKind { color: color.into(), } } - PrimitiveKeyKind::YuvImage { color_depth, yuv_key, format, color_space, image_rendering, .. } => { - PrimitiveTemplateKind::YuvImage { - color_depth, - yuv_key, - format, - color_space, - image_rendering, - } - } - PrimitiveKeyKind::Image { alpha_type, key, color, stretch_size, tile_spacing, image_rendering, sub_rect, .. } => { - PrimitiveTemplateKind::Image { - key, - color: color.into(), - stretch_size: stretch_size.into(), - tile_spacing: tile_spacing.into(), - source: ImageSource::Default, - sub_rect, - image_rendering, - alpha_type, - } - } PrimitiveKeyKind::LineDecoration { cache_key, color } => { PrimitiveTemplateKind::LineDecoration { cache_key, @@ -991,6 +927,13 @@ impl PrimTemplateCommonData { } } +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct PrimTemplate { + pub common: PrimTemplateCommonData, + pub kind: T, +} + #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveTemplate { @@ -1078,26 +1021,6 @@ impl PrimitiveTemplateKind { } } } - PrimitiveTemplateKind::YuvImage { color_depth, format, color_space, .. } => { - request.push([ - color_depth.rescaling_factor(), - pack_as_float(color_space as u32), - pack_as_float(format as u32), - 0.0 - ]); - } - PrimitiveTemplateKind::Image { stretch_size, tile_spacing, color, .. } => { - // Images are drawn as a white color, modulated by the total - // opacity coming from any collapsed property bindings. - request.push(color.premultiplied()); - request.push(PremultipliedColorF::WHITE); - request.push([ - stretch_size.width + tile_spacing.width, - stretch_size.height + tile_spacing.height, - 0.0, - 0.0, - ]); - } PrimitiveTemplateKind::Picture { .. } => {} } } @@ -1127,9 +1050,7 @@ impl PrimitiveTemplateKind { } PrimitiveTemplateKind::Clear | PrimitiveTemplateKind::LineDecoration { .. } | - PrimitiveTemplateKind::Image { .. } | PrimitiveTemplateKind::Rectangle { .. } | - PrimitiveTemplateKind::YuvImage { .. } | PrimitiveTemplateKind::Picture { .. } => {} } } @@ -1142,12 +1063,6 @@ impl PrimitiveTemplate { /// done if the cache entry is invalid (due to first use or eviction). pub fn update( &mut self, - // TODO(gw): Passing in surface_index here is not ideal. The primitive template - // code shouldn't depend on current surface state. This is due to a - // limitation in how render task caching works. We should fix this by - // allowing render task caching to assign to surfaces implicitly - // during pass allocation. - surface_index: SurfaceIndex, frame_state: &mut FrameBuildingState, ) { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { @@ -1192,153 +1107,6 @@ impl PrimitiveTemplate { None => PrimitiveOpacity::from_alpha(color.a), } } - PrimitiveTemplateKind::YuvImage { format, yuv_key, image_rendering, .. } => { - let channel_num = format.get_plane_num(); - debug_assert!(channel_num <= 3); - for channel in 0 .. channel_num { - frame_state.resource_cache.request_image( - ImageRequest { - key: yuv_key[channel], - rendering: image_rendering, - tile: None, - }, - frame_state.gpu_cache, - ); - } - - PrimitiveOpacity::translucent() - } - PrimitiveTemplateKind::Image { key, stretch_size, ref color, tile_spacing, ref mut source, sub_rect, image_rendering, .. } => { - let image_properties = frame_state - .resource_cache - .get_image_properties(key); - - match image_properties { - Some(image_properties) => { - let is_tiled = image_properties.tiling.is_some(); - - if tile_spacing != LayoutSize::zero() && !is_tiled { - *source = ImageSource::Cache { - // Size in device-pixels we need to allocate in render task cache. - size: image_properties.descriptor.size.to_i32(), - handle: None, - }; - } - - // 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); - *source = ImageSource::Cache { - // Size in device-pixels we need to allocate in render task cache. - size: rect.size, - handle: None, - }; - } - - let mut request_source_image = false; - let mut is_opaque = image_properties.descriptor.is_opaque; - let request = ImageRequest { - key, - rendering: image_rendering, - tile: None, - }; - - // 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 *source { - ImageSource::Cache { ref mut size, ref mut handle } => { - let padding = DeviceIntSideOffsets::new( - 0, - (tile_spacing.width * size.width as f32 / stretch_size.width) as i32, - (tile_spacing.height * size.height as f32 / stretch_size.height) as i32, - 0, - ); - - let inner_size = *size; - size.width += padding.horizontal(); - size.height += padding.vertical(); - - is_opaque &= padding == DeviceIntSideOffsets::zero(); - - let image_cache_key = ImageCacheKey { - request, - texel_rect: sub_rect, - }; - let surfaces = &mut frame_state.surfaces; - - // Request a pre-rendered image task. - *handle = Some(frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: *size, - kind: RenderTaskCacheKeyKind::Image(image_cache_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_with_padding( - inner_size, - &padding, - BlitSource::Image { key: image_cache_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. - surfaces[surface_index.0].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; - } - } - - if request_source_image && !is_tiled { - frame_state.resource_cache.request_image( - request, - frame_state.gpu_cache, - ); - } - - if is_opaque { - PrimitiveOpacity::from_alpha(color.a) - } else { - PrimitiveOpacity::translucent() - } - } - None => { - PrimitiveOpacity::opaque() - } - } - } PrimitiveTemplateKind::Picture { .. } => { PrimitiveOpacity::translucent() } @@ -1415,17 +1183,6 @@ impl OpacityBinding { } } -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct VisibleImageTile { - pub tile_offset: TileOffset, - pub handle: GpuCacheHandle, - pub edge_flags: EdgeAaSegmentMask, - pub local_rect: LayoutRect, - pub local_clip_rect: LayoutRect, -} - #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -1562,16 +1319,6 @@ impl BrushSegment { } } -// Key that identifies a unique (partial) image that is being -// stored in the render task cache. -#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ImageCacheKey { - pub request: ImageRequest, - pub texel_rect: Option, -} - #[derive(Clone, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -1582,21 +1329,6 @@ pub struct LineDecorationCacheKey { pub size: LayoutSizeAu, } -// Where to find the texture data for an image primitive. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug)] -pub enum ImageSource { - // A normal image - just reference the texture cache. - Default, - // An image that is pre-rendered into the texture cache - // via a render task. - Cache { - size: DeviceIntSize, - handle: Option, - }, -} - #[derive(Debug)] #[repr(C)] struct ClipRect { @@ -1832,8 +1564,6 @@ impl IsVisible for PrimitiveKeyKind { match *self { PrimitiveKeyKind::NormalBorder { .. } | PrimitiveKeyKind::ImageBorder { .. } | - PrimitiveKeyKind::YuvImage { .. } | - PrimitiveKeyKind::Image { .. } | PrimitiveKeyKind::Clear | PrimitiveKeyKind::Picture { .. } => { true @@ -1873,19 +1603,7 @@ impl CreateShadow for PrimitiveKeyKind { widths, } } - PrimitiveKeyKind::Image { alpha_type, image_rendering, tile_spacing, stretch_size, key, sub_rect, .. } => { - PrimitiveKeyKind::Image { - tile_spacing, - stretch_size, - key, - sub_rect, - image_rendering, - alpha_type, - color: shadow.color.into(), - } - } PrimitiveKeyKind::ImageBorder { .. } | - PrimitiveKeyKind::YuvImage { .. } | PrimitiveKeyKind::Picture { .. } | PrimitiveKeyKind::Clear => { panic!("bug: this prim is not supported in shadow contexts"); @@ -1894,25 +1612,6 @@ impl CreateShadow for PrimitiveKeyKind { } } -/// Instance specific fields for an image primitive. These are -/// currently stored in a separate array to avoid bloating the -/// size of PrimitiveInstance. In the future, we should be able -/// to remove this and store the information inline, by: -/// (a) Removing opacity collapse / binding support completely. -/// Once we have general picture caching, we don't need this. -/// (b) Change visible_tiles to use Storage in the primitive -/// scratch buffer. This will reduce the size of the -/// visible_tiles field here, and save memory allocation -/// 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)] -pub struct ImageInstance { - pub opacity_binding_index: OpacityBindingIndex, - pub segment_instance_index: SegmentInstanceIndex, - pub visible_tiles: Vec, -} - #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -1965,12 +1664,12 @@ pub enum PrimitiveInstanceKind { }, YuvImage { /// Handle to the common interned data for this primitive. - data_handle: PrimitiveDataHandle, + data_handle: YuvImageDataHandle, segment_instance_index: SegmentInstanceIndex, }, Image { /// Handle to the common interned data for this primitive. - data_handle: PrimitiveDataHandle, + data_handle: ImageDataHandle, image_instance_index: ImageInstanceIndex, }, LinearGradient { @@ -2076,8 +1775,9 @@ impl PrimitiveInstance { PrimitiveInstanceKind::Clear { data_handle, .. } | PrimitiveInstanceKind::NormalBorder { data_handle, .. } | PrimitiveInstanceKind::ImageBorder { data_handle, .. } | - PrimitiveInstanceKind::Rectangle { data_handle, .. } | - PrimitiveInstanceKind::YuvImage { data_handle, .. } | + PrimitiveInstanceKind::Rectangle { data_handle, .. } => { + data_handle.uid() + } PrimitiveInstanceKind::Image { data_handle, .. } => { data_handle.uid() } @@ -2090,6 +1790,9 @@ impl PrimitiveInstance { PrimitiveInstanceKind::TextRun { data_handle, .. } => { data_handle.uid() } + PrimitiveInstanceKind::YuvImage { data_handle, .. } => { + data_handle.uid() + } } } } @@ -2766,7 +2469,7 @@ impl PrimitiveStore { } pic_state.is_cacheable &= prim_instance.is_cacheable( - &resources.prim_data_store, + &resources, frame_state.resource_cache, ); @@ -2944,10 +2647,7 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); + prim_data.update(frame_state); match &prim_data.kind { PrimitiveTemplateKind::LineDecoration { ref cache_key, .. } => { @@ -3032,20 +2732,14 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); + prim_data.update(frame_state); } PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { let prim_data = &mut resources.prim_data_store[*data_handle]; // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); + prim_data.update(frame_state); let template = match prim_data.kind { PrimitiveTemplateKind::NormalBorder { ref template, .. } => template, @@ -3109,20 +2803,14 @@ impl PrimitiveStore { // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); + prim_data.update(frame_state); } PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { let prim_data = &mut resources.prim_data_store[*data_handle]; // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); + prim_data.update(frame_state); update_opacity_binding( &mut self.opacity_bindings, @@ -3130,37 +2818,37 @@ impl PrimitiveStore { frame_context.scene_properties, ); - write_segment(prim_data, *segment_instance_index, frame_state, scratch); + write_segment(*segment_instance_index, frame_state, scratch, |request| { + prim_data.kind.write_prim_gpu_blocks( + request, + prim_data.prim_size, + ); + }); } PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, .. } => { - let prim_data = &mut resources.prim_data_store[*data_handle]; + let yuv_image_data = &mut resources.yuv_image_data_store[*data_handle]; // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); + yuv_image_data.kind.update(&mut yuv_image_data.common, frame_state); - write_segment(prim_data, *segment_instance_index, frame_state, scratch); + write_segment(*segment_instance_index, frame_state, scratch, |request| { + yuv_image_data.kind.write_prim_gpu_blocks(request); + }); } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { - let prim_data = &mut resources.prim_data_store[*data_handle]; + let prim_data = &mut resources.image_data_store[*data_handle]; + let common_data = &mut prim_data.common; + let image_data = &mut prim_data.kind; // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( + image_data.update( pic_context.surface_index, + common_data, frame_state, ); - let (key, stretch_size, tile_spacing, image_rendering) = match prim_data.kind { - PrimitiveTemplateKind::Image { ref key, ref stretch_size, ref tile_spacing, ref image_rendering, .. } => { - (key, stretch_size, tile_spacing, image_rendering) - } - _ => unreachable!() - }; - let image_instance = &mut self.images[*image_instance_index]; update_opacity_binding( @@ -3173,7 +2861,7 @@ impl PrimitiveStore { let image_properties = frame_state .resource_cache - .get_image_properties(*key); + .get_image_properties(image_data.key); if let Some(image_properties) = image_properties { if let Some(tile_size) = image_properties.tiling { @@ -3192,19 +2880,19 @@ impl PrimitiveStore { &tight_clip_rect ); - let base_edge_flags = edge_flags_for_tile_spacing(tile_spacing); + let base_edge_flags = edge_flags_for_tile_spacing(&image_data.tile_spacing); - let stride = *stretch_size + *tile_spacing; + let stride = image_data.stretch_size + image_data.tile_spacing; - let repetitions = image::repetitions( + let repetitions = ::image::repetitions( &prim_local_rect, &visible_rect, stride, ); let request = ImageRequest { - key: *key, - rendering: *image_rendering, + key: image_data.key, + rendering: image_data.image_rendering, tile: None, }; @@ -3213,10 +2901,10 @@ impl PrimitiveStore { let image_rect = LayoutRect { origin, - size: *stretch_size, + size: image_data.stretch_size, }; - let tiles = image::tiles( + let tiles = ::image::tiles( &image_rect, &visible_rect, &device_image_size, @@ -3257,7 +2945,9 @@ impl PrimitiveStore { } } - write_segment(prim_data, image_instance.segment_instance_index, frame_state, scratch); + 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, .. } => { let prim_data = &mut resources.linear_grad_data_store[*data_handle]; @@ -3348,12 +3038,12 @@ impl PrimitiveStore { } } -fn write_segment( - prim_data: &PrimitiveTemplate, +fn write_segment( 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); if segment_instance_index != SegmentInstanceIndex::UNUSED { let segment_instance = &mut scratch.segment_instances[segment_instance_index]; @@ -3361,10 +3051,7 @@ fn write_segment( if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) { let segments = &scratch.segments[segment_instance.segments_range]; - prim_data.kind.write_prim_gpu_blocks( - &mut request, - prim_data.prim_size, - ); + f(&mut request); for segment in segments { request.write_segment( @@ -3402,7 +3089,7 @@ fn decompose_repeated_primitive( ); let stride = *stretch_size + *tile_spacing; - let repetitions = image::repetitions(prim_local_rect, &visible_rect, stride); + let repetitions = ::image::repetitions(prim_local_rect, &visible_rect, stride); for Repetition { origin, .. } in repetitions { let mut handle = GpuCacheHandle::new(); let rect = LayoutRect { @@ -3622,22 +3309,17 @@ impl PrimitiveInstance { segment_instance_index } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { - let prim_data = &resources.prim_data_store[data_handle]; + let image_data = &resources.image_data_store[data_handle].kind; let image_instance = &mut prim_store.images[image_instance_index]; - match prim_data.kind { - PrimitiveTemplateKind::Image { key, .. } => { - // tiled images don't support segmentation - if frame_state - .resource_cache - .get_image_properties(key) - .and_then(|properties| properties.tiling) - .is_some() { - image_instance.segment_instance_index = SegmentInstanceIndex::UNUSED; - return; - } + // tiled images don't support segmentation + 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; } - _ => unreachable!(), - } &mut image_instance.segment_instance_index } PrimitiveInstanceKind::Picture { .. } | @@ -4056,8 +3738,8 @@ fn test_struct_sizes() { // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::(), 128, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 40, "PrimitiveInstanceKind size changed"); - assert_eq!(mem::size_of::(), 144, "PrimitiveTemplate size changed"); - assert_eq!(mem::size_of::(), 88, "PrimitiveTemplateKind size changed"); + assert_eq!(mem::size_of::(), 112, "PrimitiveTemplate size changed"); + assert_eq!(mem::size_of::(), 56, "PrimitiveTemplateKind size changed"); assert_eq!(mem::size_of::(), 124, "PrimitiveKey size changed"); assert_eq!(mem::size_of::(), 96, "PrimitiveKeyKind size changed"); } diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs index e8ad4e2cbd..d4767aeb6a 100644 --- a/webrender/src/profiler.rs +++ b/webrender/src/profiler.rs @@ -404,9 +404,11 @@ pub struct IpcProfileCounters { #[derive(Clone)] pub struct InternProfileCounters { pub prims: ResourceProfileCounter, + pub images: ResourceProfileCounter, pub linear_gradients: ResourceProfileCounter, pub radial_gradients: ResourceProfileCounter, pub text_runs: ResourceProfileCounter, + pub yuv_images: ResourceProfileCounter, pub clips: ResourceProfileCounter, } @@ -450,9 +452,11 @@ impl BackendProfileCounters { }, intern: InternProfileCounters { prims: ResourceProfileCounter::new("Interned primitives"), + images: ResourceProfileCounter::new("Interned images"), linear_gradients: ResourceProfileCounter::new("Interned linear gradients"), radial_gradients: ResourceProfileCounter::new("Interned radial gradients"), text_runs: ResourceProfileCounter::new("Interned text runs"), + yuv_images: ResourceProfileCounter::new("Interned YUV images"), clips: ResourceProfileCounter::new("Interned clips"), }, } @@ -1103,9 +1107,11 @@ impl Profiler { &[ &backend_profile.intern.clips, &backend_profile.intern.prims, + &backend_profile.intern.images, &backend_profile.intern.linear_gradients, &backend_profile.intern.radial_gradients, &backend_profile.intern.text_runs, + &backend_profile.intern.yuv_images, ], debug_renderer, true, diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 69e946288b..c01e2fbbda 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -34,6 +34,7 @@ use picture::RetainedTiles; use prim_store::{PrimitiveDataStore, PrimitiveScratchBuffer, PrimitiveInstance}; use prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData}; use prim_store::gradient::{LinearGradientDataStore, RadialGradientDataStore}; +use prim_store::image::{ImageDataStore, YuvImageDataStore}; use prim_store::text_run::TextRunDataStore; use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters}; use record::ApiRecordingReceiver; @@ -205,9 +206,11 @@ pub struct FrameResources { /// Currently active / available primitives. Kept in sync with the /// primitive interner in the scene builder, per document. pub prim_data_store: PrimitiveDataStore, + pub image_data_store: ImageDataStore, pub linear_grad_data_store: LinearGradientDataStore, pub radial_grad_data_store: RadialGradientDataStore, pub text_run_data_store: TextRunDataStore, + pub yuv_image_data_store: YuvImageDataStore, } impl FrameResources { @@ -221,12 +224,14 @@ impl FrameResources { PrimitiveInstanceKind::NormalBorder { data_handle, .. } | PrimitiveInstanceKind::ImageBorder { data_handle, .. } | PrimitiveInstanceKind::Rectangle { data_handle, .. } | - PrimitiveInstanceKind::YuvImage { data_handle, .. } | - PrimitiveInstanceKind::Image { data_handle, .. } | PrimitiveInstanceKind::Clear { data_handle, .. } => { let prim_data = &self.prim_data_store[data_handle]; &prim_data.common } + PrimitiveInstanceKind::Image { data_handle, .. } => { + let prim_data = &self.image_data_store[data_handle]; + &prim_data.common + } PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { let prim_data = &self.linear_grad_data_store[data_handle]; &prim_data.common @@ -239,6 +244,10 @@ impl FrameResources { let prim_data = &self.text_run_data_store[data_handle]; &prim_data.common } + PrimitiveInstanceKind::YuvImage { data_handle, .. } => { + let prim_data = &self.yuv_image_data_store[data_handle]; + &prim_data.common + } } } } @@ -1221,6 +1230,10 @@ impl RenderBackend { updates.prim_updates, &mut profile_counters.intern.prims, ); + doc.resources.image_data_store.apply_updates( + updates.image_updates, + &mut profile_counters.intern.images, + ); doc.resources.linear_grad_data_store.apply_updates( updates.linear_grad_updates, &mut profile_counters.intern.linear_gradients, @@ -1233,6 +1246,10 @@ impl RenderBackend { updates.text_run_updates, &mut profile_counters.intern.text_runs, ); + doc.resources.yuv_image_data_store.apply_updates( + updates.yuv_image_updates, + &mut profile_counters.intern.yuv_images, + ); } // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used @@ -1712,4 +1729,3 @@ impl RenderBackend { } } } - diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 433fc4ea0b..167eac94d8 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -21,7 +21,8 @@ use gpu_types::{BorderInstance, ImageSource, UvRectKind}; use internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex}; #[cfg(feature = "pathfinder")] use pathfinder_partitioner::mesh::Mesh; -use prim_store::{PictureIndex, ImageCacheKey, LineDecorationCacheKey}; +use prim_store::{PictureIndex, LineDecorationCacheKey}; +use prim_store::image::ImageCacheKey; #[cfg(feature = "debugger")] use print_tree::{PrintTreePrinter}; use render_backend::FrameId; diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index 13f0bd8b64..040a9943d8 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -20,6 +20,10 @@ use prim_store::gradient::{ LinearGradient, LinearGradientDataInterner, LinearGradientDataUpdateList, RadialGradient, RadialGradientDataInterner, RadialGradientDataUpdateList }; +use prim_store::image::{ + Image, ImageDataInterner, ImageDataUpdateList, + YuvImage, YuvImageDataInterner, YuvImageDataUpdateList, +}; use prim_store::text_run::{TextRunDataInterner, TextRun, TextRunDataUpdateList}; use resource_cache::{BlobImageRasterizerEpoch, FontInstanceMap}; use render_backend::DocumentView; @@ -35,9 +39,11 @@ use std::time::Duration; pub struct DocumentResourceUpdates { pub clip_updates: ClipDataUpdateList, pub prim_updates: PrimitiveDataUpdateList, + pub image_updates: ImageDataUpdateList, pub linear_grad_updates: LinearGradientDataUpdateList, pub radial_grad_updates: RadialGradientDataUpdateList, pub text_run_updates: TextRunDataUpdateList, + pub yuv_image_updates: YuvImageDataUpdateList, } /// Represents the work associated to a transaction before scene building. @@ -185,9 +191,11 @@ pub enum SceneSwapResult { pub struct DocumentResources { pub clip_interner: ClipDataInterner, pub prim_interner: PrimitiveDataInterner, + pub image_interner: ImageDataInterner, pub linear_grad_interner: LinearGradientDataInterner, pub radial_grad_interner: RadialGradientDataInterner, pub text_run_interner: TextRunDataInterner, + pub yuv_image_interner: YuvImageDataInterner, } // Access to `DocumentResources` interners by `Internable` @@ -196,31 +204,29 @@ pub trait InternerMut fn interner_mut(&mut self) -> &mut Interner; } -impl InternerMut for DocumentResources { - fn interner_mut(&mut self) -> &mut PrimitiveDataInterner { - &mut self.prim_interner - } -} - -impl InternerMut for DocumentResources { - fn interner_mut(&mut self) -> &mut LinearGradientDataInterner { - &mut self.linear_grad_interner - } -} - -impl InternerMut for DocumentResources { - fn interner_mut(&mut self) -> &mut RadialGradientDataInterner { - &mut self.radial_grad_interner +macro_rules! impl_internet_mut { + ($($ty:ident: $mem:ident,)*) => { + $(impl InternerMut<$ty> for DocumentResources { + fn interner_mut(&mut self) -> &mut Interner< + <$ty as Internable>::Source, + <$ty as Internable>::InternData, + <$ty as Internable>::Marker + > { + &mut self.$mem + } + })* } } -impl InternerMut for DocumentResources { - fn interner_mut(&mut self) -> &mut TextRunDataInterner { - &mut self.text_run_interner - } +impl_internet_mut! { + Image: image_interner, + LinearGradient: linear_grad_interner, + RadialGradient: radial_grad_interner, + TextRun: text_run_interner, + PrimitiveKeyKind: prim_interner, + YuvImage: yuv_image_interner, } - // A document in the scene builder contains the current scene, // as well as a persistent clip interner. This allows clips // to be de-duplicated, and persisted in the GPU cache between @@ -391,6 +397,11 @@ impl SceneBuilder { .prim_interner .end_frame_and_get_pending_updates(); + let image_updates = item + .doc_resources + .image_interner + .end_frame_and_get_pending_updates(); + let linear_grad_updates = item .doc_resources .linear_grad_interner @@ -406,13 +417,20 @@ impl SceneBuilder { .text_run_interner .end_frame_and_get_pending_updates(); + let yuv_image_updates = item + .doc_resources + .yuv_image_interner + .end_frame_and_get_pending_updates(); + doc_resource_updates = Some( DocumentResourceUpdates { clip_updates, prim_updates, + image_updates, linear_grad_updates, radial_grad_updates, text_run_updates, + yuv_image_updates, } ); @@ -521,6 +539,11 @@ impl SceneBuilder { .prim_interner .end_frame_and_get_pending_updates(); + let image_updates = doc + .resources + .image_interner + .end_frame_and_get_pending_updates(); + let linear_grad_updates = doc .resources .linear_grad_interner @@ -536,13 +559,20 @@ impl SceneBuilder { .text_run_interner .end_frame_and_get_pending_updates(); + let yuv_image_updates = doc + .resources + .yuv_image_interner + .end_frame_and_get_pending_updates(); + doc_resource_updates = Some( DocumentResourceUpdates { clip_updates, prim_updates, + image_updates, linear_grad_updates, radial_grad_updates, text_run_updates, + yuv_image_updates, } );