diff options
Diffstat (limited to 'gfx/wr/webrender/src/prim_store/borders.rs')
-rw-r--r-- | gfx/wr/webrender/src/prim_store/borders.rs | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/gfx/wr/webrender/src/prim_store/borders.rs b/gfx/wr/webrender/src/prim_store/borders.rs new file mode 100644 index 0000000000..f2f08e66fe --- /dev/null +++ b/gfx/wr/webrender/src/prim_store/borders.rs @@ -0,0 +1,387 @@ +/* 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::{NormalBorder, PremultipliedColorF, Shadow, RasterSpace}; +use api::units::*; +use crate::border::create_border_segments; +use crate::border::NormalBorderAu; +use crate::scene_building::{CreateShadow, IsVisible}; +use crate::frame_builder::{FrameBuildingState}; +use crate::gpu_cache::GpuDataRequest; +use crate::intern; +use crate::internal_types::{LayoutPrimitiveInfo, FrameId}; +use crate::prim_store::{ + BorderSegmentInfo, BrushSegment, NinePatchDescriptor, PrimKey, + PrimTemplate, PrimTemplateCommonData, + PrimitiveInstanceKind, PrimitiveOpacity, + PrimitiveStore, InternablePrimitive, +}; +use crate::resource_cache::ImageRequest; +use crate::render_task::RenderTask; +use crate::render_task_graph::RenderTaskId; + +use super::storage; + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)] +pub struct NormalBorderPrim { + pub border: NormalBorderAu, + pub widths: LayoutSideOffsetsAu, +} + +pub type NormalBorderKey = PrimKey<NormalBorderPrim>; + +impl NormalBorderKey { + pub fn new( + info: &LayoutPrimitiveInfo, + normal_border: NormalBorderPrim, + ) -> Self { + NormalBorderKey { + common: info.into(), + kind: normal_border, + } + } +} + +impl intern::InternDebug for NormalBorderKey {} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct NormalBorderData { + pub brush_segments: Vec<BrushSegment>, + pub border_segments: Vec<BorderSegmentInfo>, + pub border: NormalBorder, + pub widths: LayoutSideOffsets, +} + +impl NormalBorderData { + /// 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(ref mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { + self.write_prim_gpu_blocks(request, common.prim_rect.size()); + self.write_segment_gpu_blocks(request); + } + + common.opacity = PrimitiveOpacity::translucent(); + } + + fn write_prim_gpu_blocks( + &self, + request: &mut GpuDataRequest, + prim_size: LayoutSize + ) { + // Border primitives currently used for + // image borders, and run through the + // normal brush_image shader. + request.push(PremultipliedColorF::WHITE); + request.push(PremultipliedColorF::WHITE); + request.push([ + prim_size.width, + prim_size.height, + 0.0, + 0.0, + ]); + } + + fn write_segment_gpu_blocks( + &self, + request: &mut GpuDataRequest, + ) { + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } +} + +pub type NormalBorderTemplate = PrimTemplate<NormalBorderData>; + +impl From<NormalBorderKey> for NormalBorderTemplate { + fn from(key: NormalBorderKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(key.common); + + let mut border: NormalBorder = key.kind.border.into(); + let widths = LayoutSideOffsets::from_au(key.kind.widths); + + // FIXME(emilio): Is this the best place to do this? + border.normalize(&widths); + + let mut brush_segments = Vec::new(); + let mut border_segments = Vec::new(); + + create_border_segments( + common.prim_rect.size(), + &border, + &widths, + &mut border_segments, + &mut brush_segments, + ); + + NormalBorderTemplate { + common, + kind: NormalBorderData { + brush_segments, + border_segments, + border, + widths, + } + } + } +} + +pub type NormalBorderDataHandle = intern::Handle<NormalBorderPrim>; + +impl intern::Internable for NormalBorderPrim { + type Key = NormalBorderKey; + type StoreData = NormalBorderTemplate; + type InternData = (); + const PROFILE_COUNTER: usize = crate::profiler::INTERNED_NORMAL_BORDERS; +} + +impl InternablePrimitive for NormalBorderPrim { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> NormalBorderKey { + NormalBorderKey::new( + info, + self, + ) + } + + fn make_instance_kind( + _key: NormalBorderKey, + data_handle: NormalBorderDataHandle, + _: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::NormalBorder { + data_handle, + render_task_ids: storage::Range::empty(), + } + } +} + +impl CreateShadow for NormalBorderPrim { + fn create_shadow( + &self, + shadow: &Shadow, + _: bool, + _: RasterSpace, + ) -> Self { + let border = self.border.with_color(shadow.color.into()); + NormalBorderPrim { + border, + widths: self.widths, + } + } +} + +impl IsVisible for NormalBorderPrim { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)] +pub struct ImageBorder { + #[ignore_malloc_size_of = "Arc"] + pub request: ImageRequest, + pub nine_patch: NinePatchDescriptor, +} + +pub type ImageBorderKey = PrimKey<ImageBorder>; + +impl ImageBorderKey { + pub fn new( + info: &LayoutPrimitiveInfo, + image_border: ImageBorder, + ) -> Self { + ImageBorderKey { + common: info.into(), + kind: image_border, + } + } +} + +impl intern::InternDebug for ImageBorderKey {} + + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct ImageBorderData { + #[ignore_malloc_size_of = "Arc"] + pub request: ImageRequest, + pub brush_segments: Vec<BrushSegment>, + pub src_color: Option<RenderTaskId>, + pub frame_id: FrameId, + pub is_opaque: bool, +} + +impl ImageBorderData { + /// 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(ref mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { + self.write_prim_gpu_blocks(request, &common.prim_rect.size()); + self.write_segment_gpu_blocks(request); + } + + let frame_id = frame_state.rg_builder.frame_id(); + if self.frame_id != frame_id { + self.frame_id = frame_id; + + let size = frame_state.resource_cache.request_image( + self.request, + frame_state.gpu_cache, + ); + + let task_id = frame_state.rg_builder.add().init( + RenderTask::new_image(size, self.request) + ); + + self.src_color = Some(task_id); + + let image_properties = frame_state + .resource_cache + .get_image_properties(self.request.key); + + self.is_opaque = image_properties + .map(|properties| properties.descriptor.is_opaque()) + .unwrap_or(true); + } + + common.opacity = PrimitiveOpacity { is_opaque: self.is_opaque }; + } + + fn write_prim_gpu_blocks( + &self, + request: &mut GpuDataRequest, + prim_size: &LayoutSize, + ) { + // Border primitives currently used for + // image borders, and run through the + // normal brush_image shader. + request.push(PremultipliedColorF::WHITE); + request.push(PremultipliedColorF::WHITE); + request.push([ + prim_size.width, + prim_size.height, + 0.0, + 0.0, + ]); + } + + fn write_segment_gpu_blocks( + &self, + request: &mut GpuDataRequest, + ) { + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } +} + +pub type ImageBorderTemplate = PrimTemplate<ImageBorderData>; + +impl From<ImageBorderKey> for ImageBorderTemplate { + fn from(key: ImageBorderKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(key.common); + + let brush_segments = key.kind.nine_patch.create_segments(common.prim_rect.size()); + ImageBorderTemplate { + common, + kind: ImageBorderData { + request: key.kind.request, + brush_segments, + src_color: None, + frame_id: FrameId::INVALID, + is_opaque: false, + } + } + } +} + +pub type ImageBorderDataHandle = intern::Handle<ImageBorder>; + +impl intern::Internable for ImageBorder { + type Key = ImageBorderKey; + type StoreData = ImageBorderTemplate; + type InternData = (); + const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGE_BORDERS; +} + +impl InternablePrimitive for ImageBorder { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> ImageBorderKey { + ImageBorderKey::new( + info, + self, + ) + } + + fn make_instance_kind( + _key: ImageBorderKey, + data_handle: ImageBorderDataHandle, + _: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::ImageBorder { + data_handle + } + } +} + +impl IsVisible for ImageBorder { + fn is_visible(&self) -> bool { + true + } +} + +#[test] +#[cfg(target_pointer_width = "64")] +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::<NormalBorderPrim>(), 84, "NormalBorderPrim size changed"); + assert_eq!(mem::size_of::<NormalBorderTemplate>(), 216, "NormalBorderTemplate size changed"); + assert_eq!(mem::size_of::<NormalBorderKey>(), 104, "NormalBorderKey size changed"); + assert_eq!(mem::size_of::<ImageBorder>(), 68, "ImageBorder size changed"); + assert_eq!(mem::size_of::<ImageBorderTemplate>(), 104, "ImageBorderTemplate size changed"); + assert_eq!(mem::size_of::<ImageBorderKey>(), 88, "ImageBorderKey size changed"); +} |