/* 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; 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, pub border_segments: Vec, 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; impl From 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; 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; 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, pub src_color: Option, 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; impl From 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; 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::(), 84, "NormalBorderPrim size changed"); assert_eq!(mem::size_of::(), 216, "NormalBorderTemplate size changed"); assert_eq!(mem::size_of::(), 104, "NormalBorderKey size changed"); assert_eq!(mem::size_of::(), 68, "ImageBorder size changed"); assert_eq!(mem::size_of::(), 104, "ImageBorderTemplate size changed"); assert_eq!(mem::size_of::(), 88, "ImageBorderKey size changed"); }