Bug 1536021 - Shadow stack implementation. r=kvark

Differential Revision: https://phabricator.services.mozilla.com/D30620

--HG--
extra : source : e830b256419b53556e27aa2586f2ed8f715aa99e
extra : intermediate-source : c223987f3de744979a0b08b336b46ac9f199e9bd
This commit is contained in:
Nicolas Silva 2019-05-12 12:27:44 +02:00
parent 8308eb7ea1
commit c7a54e68a1
15 changed files with 620 additions and 245 deletions

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, ClipMode, ExternalImageType, FilterOp, ImageRendering};
use api::{AlphaType, ClipMode, ExternalImageType, ImageRendering};
use api::{YuvColorSpace, YuvFormat, ColorDepth, PremultipliedColorF, RasterSpace};
use api::units::*;
use crate::clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore, ClipNodeInstance};
@ -13,7 +13,7 @@ use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, Z
use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance, SnapOffsets};
use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
use crate::internal_types::{FastHashMap, SavedTargetIndex, TextureSource};
use crate::internal_types::{FastHashMap, SavedTargetIndex, TextureSource, Filter};
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex};
use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
@ -25,7 +25,6 @@ use crate::render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree, TileBl
use crate::renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
use crate::renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH};
use crate::resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
use crate::scene::FilterOpHelpers;
use smallvec::SmallVec;
use std::{f32, i32, usize};
use crate::tiling::{RenderTargetContext};
@ -636,6 +635,7 @@ impl BatchBuilder {
let transform_kind = transform_id.transform_kind();
let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize];
let bounding_rect = &prim_info.clip_chain.pic_clip_rect;
let z_id = z_generator.next();
let prim_common_data = &ctx.data_stores.as_common_data(&prim_instance);
@ -1287,7 +1287,7 @@ impl BatchBuilder {
PictureCompositeMode::Filter(ref filter) => {
assert!(filter.is_visible());
match filter {
FilterOp::Blur(..) => {
Filter::Blur(..) => {
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
);
@ -1323,7 +1323,105 @@ impl BatchBuilder {
PrimitiveInstanceData::from(instance),
);
}
FilterOp::DropShadow(offset, ..) => {
Filter::DropShadowStack(shadows) => {
// Draw an instance per shadow first, following by the content.
// The shadows and the content get drawn as a brush image.
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
);
// Gets the saved render task ID of the content, which is
// deeper in the render task tree than the direct child.
let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
// Build BatchTextures for shadow/content
let shadow_textures = BatchTextures::render_target_cache();
let content_textures = BatchTextures {
colors: [
TextureSource::RenderTaskCache(saved_index),
TextureSource::Invalid,
TextureSource::Invalid,
],
};
// Build batch keys for shadow/content
let shadow_key = BatchKey::new(kind, non_segmented_blend_mode, shadow_textures);
let content_key = BatchKey::new(kind, non_segmented_blend_mode, content_textures);
// Retrieve the UV rect addresses for shadow/content.
let cache_task_id = surface
.expect("bug: surface must be allocated by now");
let shadow_uv_rect_address = render_tasks[cache_task_id]
.get_texture_address(gpu_cache)
.as_int();
let content_uv_rect_address = render_tasks[secondary_id]
.get_texture_address(gpu_cache)
.as_int();
for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) {
// Get the GPU cache address of the extra data handle.
let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data);
let shadow_rect = prim_header.local_rect.translate(&shadow.offset);
let shadow_prim_header = PrimitiveHeader {
local_rect: shadow_rect,
specific_prim_address: shadow_prim_address,
..prim_header
};
let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, z_id, [
ShaderColorMode::Alpha as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
RasterizationSpace::Screen as i32,
get_shader_opacity(1.0),
0,
]);
let shadow_instance = BrushInstance {
prim_header_index: shadow_prim_header_index,
clip_task_address,
segment_index: INVALID_SEGMENT_INDEX,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags,
user_data: shadow_uv_rect_address,
};
batcher.current_batch_list().push_single_instance(
shadow_key,
bounding_rect,
z_id,
PrimitiveInstanceData::from(shadow_instance),
);
}
let z_id_content = z_generator.next();
let content_prim_header_index = prim_headers.push(&prim_header, z_id_content, [
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
RasterizationSpace::Screen as i32,
get_shader_opacity(1.0),
0,
]);
let content_instance = BrushInstance {
prim_header_index: content_prim_header_index,
clip_task_address,
segment_index: INVALID_SEGMENT_INDEX,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags,
user_data: content_uv_rect_address,
};
batcher.current_batch_list().push_single_instance(
content_key,
bounding_rect,
z_id_content,
PrimitiveInstanceData::from(content_instance),
);
}
Filter::DropShadow(shadow) => {
// Draw an instance of the shadow first, following by the content.
// Both the shadow and the content get drawn as a brush image.
@ -1361,7 +1459,7 @@ impl BatchBuilder {
.as_int();
// Get the GPU cache address of the extra data handle.
let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle);
let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handles[0]);
let z_id_shadow = z_id;
let z_id_content = z_generator.next();
@ -1373,7 +1471,7 @@ impl BatchBuilder {
0,
]);
let shadow_rect = prim_header.local_rect.translate(&offset);
let shadow_rect = prim_header.local_rect.translate(&shadow.offset);
let shadow_prim_header = PrimitiveHeader {
local_rect: shadow_rect,
@ -1423,47 +1521,49 @@ impl BatchBuilder {
}
_ => {
let filter_mode = match filter {
FilterOp::Identity => 1, // matches `Contrast(1)`
FilterOp::Blur(..) => 0,
FilterOp::Contrast(..) => 1,
FilterOp::Grayscale(..) => 2,
FilterOp::HueRotate(..) => 3,
FilterOp::Invert(..) => 4,
FilterOp::Saturate(..) => 5,
FilterOp::Sepia(..) => 6,
FilterOp::Brightness(..) => 7,
FilterOp::Opacity(..) => 8,
FilterOp::DropShadow(..) => 9,
FilterOp::ColorMatrix(..) => 10,
FilterOp::SrgbToLinear => 11,
FilterOp::LinearToSrgb => 12,
FilterOp::ComponentTransfer => unreachable!(),
Filter::Identity => 1, // matches `Contrast(1)`
Filter::Blur(..) => 0,
Filter::Contrast(..) => 1,
Filter::Grayscale(..) => 2,
Filter::HueRotate(..) => 3,
Filter::Invert(..) => 4,
Filter::Saturate(..) => 5,
Filter::Sepia(..) => 6,
Filter::Brightness(..) => 7,
Filter::Opacity(..) => 8,
Filter::DropShadow(..) |
Filter::DropShadowStack(..) => 9,
Filter::ColorMatrix(..) => 10,
Filter::SrgbToLinear => 11,
Filter::LinearToSrgb => 12,
Filter::ComponentTransfer => unreachable!(),
};
let user_data = match filter {
FilterOp::Identity => 0x10000i32, // matches `Contrast(1)`
FilterOp::Contrast(amount) |
FilterOp::Grayscale(amount) |
FilterOp::Invert(amount) |
FilterOp::Saturate(amount) |
FilterOp::Sepia(amount) |
FilterOp::Brightness(amount) |
FilterOp::Opacity(_, amount) => {
Filter::Identity => 0x10000i32, // matches `Contrast(1)`
Filter::Contrast(amount) |
Filter::Grayscale(amount) |
Filter::Invert(amount) |
Filter::Saturate(amount) |
Filter::Sepia(amount) |
Filter::Brightness(amount) |
Filter::Opacity(_, amount) => {
(amount * 65536.0) as i32
}
FilterOp::SrgbToLinear | FilterOp::LinearToSrgb => 0,
FilterOp::HueRotate(angle) => {
Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
Filter::HueRotate(angle) => {
(0.01745329251 * angle * 65536.0) as i32
}
// Go through different paths
FilterOp::Blur(..) |
FilterOp::DropShadow(..) => {
Filter::Blur(..) |
Filter::DropShadowStack(..) |
Filter::DropShadow(..) => {
unreachable!();
}
FilterOp::ColorMatrix(_) => {
picture.extra_gpu_data_handle.as_int(gpu_cache)
Filter::ColorMatrix(_) => {
picture.extra_gpu_data_handles[0].as_int(gpu_cache)
}
FilterOp::ComponentTransfer => unreachable!(),
Filter::ComponentTransfer => unreachable!(),
};
let (uv_rect_address, textures) = render_tasks.resolve_surface(

View File

@ -19,7 +19,7 @@ use crate::glyph_rasterizer::FontInstance;
use crate::hit_test::{HitTestingItem, HitTestingScene};
use crate::image::simplify_repeated_primitive;
use crate::intern::Interner;
use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo};
use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo, Filter};
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureOptions};
use crate::picture::{BlitReason, PrimitiveList, TileCache};
use crate::prim_store::{PrimitiveInstance, PrimitiveSceneData};
@ -1280,7 +1280,7 @@ impl<'a> DisplayListFlattener<'a> {
apply_pipeline_clip
);
self.push_shadow(info.shadow, clip_and_scroll);
self.push_shadow(info.shadow, clip_and_scroll, info.should_inflate);
}
DisplayItem::PopAllShadows => {
self.pop_all_shadows();
@ -1760,7 +1760,7 @@ impl<'a> DisplayListFlattener<'a> {
let filter = filter.sanitize();
let composite_mode = Some(match filter {
FilterOp::ComponentTransfer => {
Filter::ComponentTransfer => {
let filter_data =
&stacking_context.composite_ops.filter_datas[current_filter_data_index];
let filter_data = filter_data.sanitize();
@ -2098,12 +2098,14 @@ impl<'a> DisplayListFlattener<'a> {
&mut self,
shadow: Shadow,
clip_and_scroll: ScrollNodeAndClipChain,
should_inflate: bool,
) {
// Store this shadow in the pending list, for processing
// during pop_all_shadows.
self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
shadow,
clip_and_scroll,
should_inflate,
}));
}
@ -2202,7 +2204,7 @@ impl<'a> DisplayListFlattener<'a> {
// blur radius is 0, the code in Picture::prepare_for_render will
// detect this and mark the picture to be drawn directly into the
// parent picture, which avoids an intermediate surface and blur.
let blur_filter = FilterOp::Blur(std_deviation).sanitize();
let blur_filter = Filter::Blur(std_deviation).sanitize();
let composite_mode = PictureCompositeMode::Filter(blur_filter);
let composite_mode_key = Some(composite_mode.clone()).into();
let is_backface_visible = true; //TODO: double check this
@ -2210,7 +2212,7 @@ impl<'a> DisplayListFlattener<'a> {
// Pass through configuration information about whether WR should
// do the bounding rect inflation for text shadows.
let options = PictureOptions {
inflate_if_required: pending_shadow.shadow.should_inflate,
inflate_if_required: pending_shadow.should_inflate,
};
// Create the primitive to draw the shadow picture into the scene.
@ -2312,16 +2314,16 @@ impl<'a> DisplayListFlattener<'a> {
// Offset the local rect and clip rect by the shadow offset.
let mut info = pending_primitive.info.clone();
info.rect = info.rect.translate(&pending_shadow.shadow.offset);
info.clip_rect = info.clip_rect.translate(&pending_shadow.shadow.offset);
info.clip_rect = info.clip_rect.translate(
&pending_shadow.shadow.offset
);
// Construct and add a primitive for the given shadow.
let shadow_prim_instance = self.create_primitive(
&info,
pending_primitive.clip_and_scroll.clip_chain_id,
pending_primitive.clip_and_scroll.spatial_node_index,
pending_primitive.prim.create_shadow(
&pending_shadow.shadow,
),
pending_primitive.prim.create_shadow(&pending_shadow.shadow),
);
// Add the new primitive to the shadow picture.
@ -3031,6 +3033,7 @@ pub struct PendingPrimitive<T> {
/// shadows, and handled at once during pop_all_shadows.
pub struct PendingShadow {
shadow: Shadow,
should_inflate: bool,
clip_and_scroll: ScrollNodeAndClipChain,
}

View File

@ -3,8 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DebugCommand, DocumentId, ExternalImageData, ExternalImageId};
use api::{ImageFormat, ItemTag, NotificationRequest};
use api::{ImageFormat, ItemTag, NotificationRequest, Shadow, FilterOp, MAX_BLUR_RADIUS};
use api::units::*;
use api;
use crate::device::TextureFilter;
use crate::renderer::PipelineInfo;
use crate::gpu_cache::GpuCacheUpdateList;
@ -30,6 +31,141 @@ pub type FastHashSet<K> = HashSet<K, BuildHasherDefault<FxHasher>>;
/// A concrete plane splitter type used in WebRender.
pub type PlaneSplitter = BspSplitter<f64, WorldPixel>;
/// An arbitrary number which we assume opacity is invisible below.
const OPACITY_EPSILON: f32 = 0.001;
/// Equivalent to api::FilterOp with added internal information
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum Filter {
Identity,
Blur(f32),
Brightness(f32),
Contrast(f32),
Grayscale(f32),
HueRotate(f32),
Invert(f32),
Opacity(api::PropertyBinding<f32>, f32),
Saturate(f32),
Sepia(f32),
DropShadow(Shadow),
#[allow(dead_code)]
DropShadowStack(Vec<Shadow>),
ColorMatrix([f32; 20]),
SrgbToLinear,
LinearToSrgb,
ComponentTransfer,
}
impl Filter {
/// Ensure that the parameters for a filter operation
/// are sensible.
pub fn sanitize(&self) -> Self {
match self {
Filter::Blur(radius) => Filter::Blur(radius.min(MAX_BLUR_RADIUS)),
Filter::DropShadow(shadow) => Filter::DropShadow(Shadow {
offset: shadow.offset,
blur_radius: shadow.blur_radius.min(MAX_BLUR_RADIUS),
color: shadow.color
}),
Filter::DropShadowStack(ref stack) => {
let mut shadows = Vec::with_capacity(stack.len());
for shadow in stack {
shadows.push(Shadow {
blur_radius: shadow.blur_radius.min(MAX_BLUR_RADIUS),
..*shadow
});
}
Filter::DropShadowStack(shadows)
}
filter => filter.clone(),
}
}
pub fn is_visible(&self) -> bool {
match *self {
Filter::Identity |
Filter::Blur(..) |
Filter::Brightness(..) |
Filter::Contrast(..) |
Filter::Grayscale(..) |
Filter::HueRotate(..) |
Filter::Invert(..) |
Filter::Saturate(..) |
Filter::Sepia(..) |
Filter::DropShadow(..) |
Filter::DropShadowStack(..) |
Filter::ColorMatrix(..) |
Filter::SrgbToLinear |
Filter::LinearToSrgb |
Filter::ComponentTransfer => true,
Filter::Opacity(_, amount) => {
amount > OPACITY_EPSILON
}
}
}
pub fn is_noop(&self) -> bool {
match *self {
Filter::Identity => false, // this is intentional
Filter::Blur(length) => length == 0.0,
Filter::Brightness(amount) => amount == 1.0,
Filter::Contrast(amount) => amount == 1.0,
Filter::Grayscale(amount) => amount == 0.0,
Filter::HueRotate(amount) => amount == 0.0,
Filter::Invert(amount) => amount == 0.0,
Filter::Opacity(_, amount) => amount >= 1.0,
Filter::Saturate(amount) => amount == 1.0,
Filter::Sepia(amount) => amount == 0.0,
Filter::DropShadowStack(ref shadows) => {
for shadow in shadows {
if shadow.offset.x != 0.0 || shadow.offset.y != 0.0 || shadow.blur_radius != 0.0 {
return false;
}
}
true
}
Filter::DropShadow(shadow) => {
shadow.offset.x == 0.0 && shadow.offset.y == 0.0 && shadow.blur_radius == 0.0
},
Filter::ColorMatrix(matrix) => {
matrix == [1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
0.0, 0.0, 0.0, 0.0]
}
Filter::SrgbToLinear |
Filter::LinearToSrgb |
Filter::ComponentTransfer => false,
}
}
}
impl From<FilterOp> for Filter {
fn from(op: FilterOp) -> Self {
match op {
FilterOp::Identity => Filter::Identity,
FilterOp::Blur(r) => Filter::Blur(r),
FilterOp::Brightness(b) => Filter::Brightness(b),
FilterOp::Contrast(c) => Filter::Contrast(c),
FilterOp::Grayscale(g) => Filter::Grayscale(g),
FilterOp::HueRotate(h) => Filter::HueRotate(h),
FilterOp::Invert(i) => Filter::Invert(i),
FilterOp::Opacity(binding, opacity) => Filter::Opacity(binding, opacity),
FilterOp::Saturate(s) => Filter::Saturate(s),
FilterOp::Sepia(s) => Filter::Sepia(s),
FilterOp::DropShadow(shadow) => Filter::DropShadow(shadow),
FilterOp::ColorMatrix(mat) => Filter::ColorMatrix(mat),
FilterOp::SrgbToLinear => Filter::SrgbToLinear,
FilterOp::LinearToSrgb => Filter::LinearToSrgb,
FilterOp::ComponentTransfer => Filter::ComponentTransfer,
}
}
}
/// An ID for a texture that is owned by the `texture_cache` module.
///
/// This can include atlases or standalone textures allocated via the texture

View File

@ -2,7 +2,7 @@
* 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::{FilterOp, MixBlendMode, PipelineId, PremultipliedColorF};
use api::{MixBlendMode, PipelineId, PremultipliedColorF};
use api::{PropertyBinding, PropertyBindingId};
use api::{DebugFlags, RasterSpace, ColorF, ImageKey, ClipMode};
use api::units::*;
@ -16,7 +16,7 @@ use euclid::{size2, vec3, TypedPoint2D, TypedScale, TypedSize2D, Vector2D};
use euclid::approxeq::ApproxEq;
use crate::frame_builder::{FrameVisibilityContext, FrameVisibilityState};
use crate::intern::ItemUid;
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter};
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter};
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use crate::gpu_types::UvRectKind;
@ -28,9 +28,9 @@ use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBind
use crate::print_tree::PrintTreePrinter;
use crate::render_backend::DataStores;
use crate::render_task::{ClearMode, RenderTask, TileBlit};
use crate::render_task::{RenderTaskId, RenderTaskLocation};
use crate::render_task::{RenderTaskId, RenderTaskLocation, BlurTaskCache};
use crate::resource_cache::ResourceCache;
use crate::scene::{FilterOpHelpers, SceneProperties};
use crate::scene::SceneProperties;
use crate::scene_builder::Interners;
use smallvec::SmallVec;
use std::{mem, u16};
@ -1133,7 +1133,7 @@ impl TileCache {
PrimitiveInstanceKind::Picture { pic_index,.. } => {
// Pictures can depend on animated opacity bindings.
let pic = &pictures[pic_index.0];
if let Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) = pic.requested_composite_mode {
if let Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) = pic.requested_composite_mode {
opacity_bindings.push(binding.into());
}
@ -1894,7 +1894,7 @@ pub enum PictureCompositeMode {
/// Apply CSS mix-blend-mode effect.
MixBlend(MixBlendMode),
/// Apply a CSS filter (except component transfer).
Filter(FilterOp),
Filter(Filter),
/// Apply a component transfer filter.
ComponentTransferFilter(FilterDataHandle),
/// Draw to intermediate surface, copy straight across. This
@ -2188,10 +2188,10 @@ pub struct PicturePrimitive {
// pages to a texture), this is the pipeline this
// picture is the root of.
pub frame_output_pipeline_id: Option<PipelineId>,
// An optional cache handle for storing extra data
// Optional cache handles for storing extra data
// in the GPU cache, depending on the type of
// picture.
pub extra_gpu_data_handle: GpuCacheHandle,
pub extra_gpu_data_handles: SmallVec<[GpuCacheHandle; 1]>,
/// The spatial node index of this picture when it is
/// composited into the parent picture.
@ -2268,7 +2268,7 @@ impl PicturePrimitive {
match self.requested_composite_mode {
Some(PictureCompositeMode::Filter(ref mut filter)) => {
match *filter {
FilterOp::Opacity(ref binding, ref mut value) => {
Filter::Opacity(ref binding, ref mut value) => {
*value = properties.resolve_float(binding);
}
_ => {}
@ -2336,7 +2336,7 @@ impl PicturePrimitive {
raster_config: None,
context_3d,
frame_output_pipeline_id,
extra_gpu_data_handle: GpuCacheHandle::new(),
extra_gpu_data_handles: SmallVec::new(),
apply_local_clip_rect,
is_backface_visible,
pipeline_id,
@ -2467,7 +2467,7 @@ impl PicturePrimitive {
PictureCompositeMode::TileCache { .. } => {
unreachable!();
}
PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
let blur_std_deviation = blur_radius * device_pixel_scale.0;
let scale_factors = scale_factors(&transform);
let blur_std_deviation = DeviceSize::new(
@ -2518,20 +2518,19 @@ impl PicturePrimitive {
let picture_task_id = frame_state.render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
let blur_render_task_id = RenderTask::new_blur(
blur_std_deviation,
picture_task_id,
frame_state.render_tasks,
RenderTargetKind::Color,
ClearMode::Transparent,
None,
);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
(render_task_id, picture_task_id)
(blur_render_task_id, picture_task_id)
}
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) => {
let blur_std_deviation = blur_radius * device_pixel_scale.0;
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
let blur_std_deviation = shadow.blur_radius * device_pixel_scale.0;
let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
let rounded_std_dev = blur_std_deviation.round();
let rounded_std_dev = DeviceSize::new(rounded_std_dev, rounded_std_dev);
@ -2573,19 +2572,80 @@ impl PicturePrimitive {
let picture_task_id = frame_state.render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
let blur_render_task_id = RenderTask::new_blur(
rounded_std_dev,
picture_task_id,
frame_state.render_tasks,
RenderTargetKind::Color,
ClearMode::Transparent,
None,
);
self.secondary_render_task_id = Some(picture_task_id);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
(render_task_id, picture_task_id)
(blur_render_task_id, picture_task_id)
}
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
let mut max_std_deviation = 0.0;
for shadow in shadows {
// TODO(nical) presumably we should compute the clipped rect for each shadow
// and compute the union of them to determine what we need to rasterize and blur?
max_std_deviation = f32::max(max_std_deviation, shadow.blur_radius * device_pixel_scale.0);
}
max_std_deviation = max_std_deviation.round();
let max_blur_range = (max_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
let mut device_rect = clipped.inflate(max_blur_range, max_blur_range)
.intersection(&unclipped.to_i32())
.unwrap();
device_rect.size = RenderTask::adjusted_blur_source_size(
device_rect.size,
DeviceSize::new(max_std_deviation, max_std_deviation),
);
let uv_rect_kind = calculate_uv_rect_kind(
&pic_rect,
&transform,
&device_rect,
device_pixel_scale,
true,
);
let mut picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, device_rect.size),
unclipped.size,
pic_index,
device_rect.origin,
Vec::new(),
uv_rect_kind,
raster_spatial_node_index,
device_pixel_scale,
);
picture_task.mark_for_saving();
let picture_task_id = frame_state.render_tasks.add(picture_task);
self.secondary_render_task_id = Some(picture_task_id);
let mut blur_tasks = BlurTaskCache::default();
self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
let mut blur_render_task_id = picture_task_id;
for shadow in shadows {
let std_dev = f32::round(shadow.blur_radius * device_pixel_scale.0);
blur_render_task_id = RenderTask::new_blur(
DeviceSize::new(std_dev, std_dev),
picture_task_id,
frame_state.render_tasks,
RenderTargetKind::Color,
ClearMode::Transparent,
Some(&mut blur_tasks),
);
}
// TODO(nical) the second one should to be the blur's task id but we have several blurs now
(picture_task_id, blur_render_task_id)
}
PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {
let uv_rect_kind = calculate_uv_rect_kind(
@ -3002,7 +3062,7 @@ impl PicturePrimitive {
// This inflation factor is to be applied to all primitives within the surface.
let inflation_factor = match composite_mode {
PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
// Only inflate if the caller hasn't already inflated
// the bounding rects for this filter.
if self.options.inflate_if_required {
@ -3130,10 +3190,20 @@ impl PicturePrimitive {
let surface = state.current_surface_mut();
// Inflate the local bounding rect if required by the filter effect.
// This inflaction factor is to be applied to the surface itsefl.
// TODO: in prepare_for_render we round before multiplying with the
// blur sample scale. Should we do this here as well?
let inflation_size = match raster_config.composite_mode {
PictureCompositeMode::Filter(FilterOp::Blur(_)) => surface.inflation_factor,
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) =>
(blur_radius * BLUR_SAMPLE_SCALE).ceil(),
PictureCompositeMode::Filter(Filter::Blur(_)) => surface.inflation_factor,
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
(shadow.blur_radius * BLUR_SAMPLE_SCALE).ceil()
}
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
let mut max = 0.0;
for shadow in shadows {
max = f32::max(max, shadow.blur_radius * BLUR_SAMPLE_SCALE);
}
max.ceil()
}
_ => 0.0,
};
surface.rect = surface.rect.inflate(inflation_size, inflation_size);
@ -3161,10 +3231,20 @@ impl PicturePrimitive {
// Drop shadows draw both a content and shadow rect, so need to expand the local
// rect of any surfaces to be composited in parent surfaces correctly.
if let PictureCompositeMode::Filter(FilterOp::DropShadow(offset, ..)) = raster_config.composite_mode {
let content_rect = surface_rect;
let shadow_rect = surface_rect.translate(&offset);
surface_rect = content_rect.union(&shadow_rect);
match raster_config.composite_mode {
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
let content_rect = surface_rect;
let shadow_rect = surface_rect.translate(&shadow.offset);
surface_rect = content_rect.union(&shadow_rect);
}
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
for shadow in shadows {
let content_rect = surface_rect;
let shadow_rect = surface_rect.translate(&shadow.offset);
surface_rect = content_rect.union(&shadow_rect);
}
}
_ => {}
}
// Propagate up to parent surface, now that we know this surface's static rect
@ -3214,9 +3294,13 @@ impl PicturePrimitive {
match raster_config.composite_mode {
PictureCompositeMode::TileCache { .. } => {}
PictureCompositeMode::Filter(FilterOp::Blur(..)) => {}
PictureCompositeMode::Filter(FilterOp::DropShadow(offset, _, color)) => {
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
PictureCompositeMode::Filter(Filter::Blur(..)) => {}
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
if self.extra_gpu_data_handles.is_empty() {
self.extra_gpu_data_handles.push(GpuCacheHandle::new());
}
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handles[0]) {
// TODO(gw): This is very hacky code below! It stores an extra
// brush primitive below for the special case of a
// drop-shadow where we need a different local
@ -3227,10 +3311,10 @@ impl PicturePrimitive {
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
// [brush specific data]
// [segment_rect, segment data]
let shadow_rect = self.snapped_local_rect.translate(&offset);
let shadow_rect = self.snapped_local_rect.translate(&shadow.offset);
// ImageBrush colors
request.push(color.premultiplied());
request.push(shadow.color.premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([
self.snapped_local_rect.size.width,
@ -3244,10 +3328,38 @@ impl PicturePrimitive {
request.push([0.0, 0.0, 0.0, 0.0]);
}
}
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
for (shadow, extra_handle) in shadows.iter().zip(self.extra_gpu_data_handles.iter_mut()) {
if let Some(mut request) = frame_state.gpu_cache.request(extra_handle) {
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
// [brush specific data]
// [segment_rect, segment data]
let shadow_rect = self.snapped_local_rect.translate(&shadow.offset);
// ImageBrush colors
request.push(shadow.color.premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([
self.snapped_local_rect.size.width,
self.snapped_local_rect.size.height,
0.0,
0.0,
]);
// segment rect / extra data
request.push(shadow_rect);
request.push([0.0, 0.0, 0.0, 0.0]);
}
}
}
PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {}
PictureCompositeMode::Filter(ref filter) => {
if let FilterOp::ColorMatrix(m) = *filter {
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
if let Filter::ColorMatrix(m) = *filter {
if self.extra_gpu_data_handles.is_empty() {
self.extra_gpu_data_handles.push(GpuCacheHandle::new());
}
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handles[0]) {
for i in 0..5 {
request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, ClipMode, ColorF};
use api::{FilterOp, ImageRendering, RepeatMode};
use api::{ImageRendering, RepeatMode};
use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop};
use api::{BoxShadowClipMode, LineStyle, LineOrientation};
use api::{PrimitiveKeyKind, RasterSpace};
@ -52,7 +52,7 @@ use crate::storage;
use crate::texture_cache::TEXTURE_REGION_DIMENSIONS;
use crate::util::{MatrixHelpers, MaxRect, Recycler};
use crate::util::{clamp_to_scale_factor, pack_as_float, project_rect, raster_rect_to_device_pixels};
use crate::internal_types::LayoutPrimitiveInfo;
use crate::internal_types::{LayoutPrimitiveInfo, Filter};
use smallvec::SmallVec;
pub mod borders;
@ -1852,7 +1852,15 @@ impl PrimitiveStore {
Some(ref rc) => match rc.composite_mode {
// If we have a drop shadow filter, we also need to include the shadow in
// our local rect for the purpose of calculating the size of the picture.
PictureCompositeMode::Filter(FilterOp::DropShadow(offset, ..)) => pic.snapped_local_rect.translate(&offset),
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => pic.snapped_local_rect.translate(&shadow.offset),
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
let mut rect = LayoutRect::zero();
for shadow in shadows {
rect = rect.union(&pic.snapped_local_rect.translate(&shadow.offset));
}
rect
}
_ => LayoutRect::zero(),
}
None => {
@ -2132,9 +2140,17 @@ impl PrimitiveStore {
// Inflate the local bounding rect if required by the filter effect.
// This inflaction factor is to be applied to the surface itself.
let inflation_size = match raster_config.composite_mode {
PictureCompositeMode::Filter(FilterOp::Blur(_)) => surface.inflation_factor,
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) =>
(blur_radius * BLUR_SAMPLE_SCALE).ceil(),
PictureCompositeMode::Filter(Filter::Blur(_)) => surface.inflation_factor,
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
(shadow.blur_radius * BLUR_SAMPLE_SCALE).ceil()
}
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
let mut max = 0.0;
for shadow in shadows {
max = f32::max(max, shadow.blur_radius * BLUR_SAMPLE_SCALE);
}
max.ceil()
}
_ => 0.0,
};
surface_rect = surface_rect.inflate(inflation_size, inflation_size);
@ -2143,8 +2159,15 @@ impl PrimitiveStore {
// perspective of its child primitives.
let pic_local_rect = surface_rect * TypedScale::new(1.0);
if pic.snapped_local_rect != pic_local_rect {
if let PictureCompositeMode::Filter(FilterOp::DropShadow(..)) = raster_config.composite_mode {
frame_state.gpu_cache.invalidate(&pic.extra_gpu_data_handle);
match raster_config.composite_mode {
PictureCompositeMode::Filter(Filter::DropShadow(..))
| PictureCompositeMode::Filter(Filter::DropShadowStack(..))
=> {
for handle in &pic.extra_gpu_data_handles {
frame_state.gpu_cache.invalidate(handle);
}
}
_ => {}
}
// Invalidate any segments built for this picture, since the local
// rect has changed.
@ -2397,7 +2420,7 @@ impl PrimitiveStore {
) {
// Only handle opacity filters for now.
let binding = match self.pictures[pic_index.0].requested_composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) => {
Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) => {
binding
}
_ => {

View File

@ -3,14 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{
ColorU, FilterOp, MixBlendMode,
ColorU, MixBlendMode,
PropertyBinding, PropertyBindingId,
};
use api::units::{Au, LayoutSize, LayoutVector2D};
use crate::intern::ItemUid;
use crate::display_list_flattener::IsVisible;
use crate::intern::{Internable, InternDebug, Handle as InternHandle};
use crate::internal_types::LayoutPrimitiveInfo;
use crate::internal_types::{LayoutPrimitiveInfo, Filter};
use crate::picture::PictureCompositeMode;
use crate::prim_store::{
PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
@ -39,6 +39,7 @@ pub enum PictureCompositeKey {
Saturate(Au),
Sepia(Au),
DropShadow(VectorKey, Au, ColorU),
DropShadowStack(Vec<(VectorKey, Au, ColorU)>),
ColorMatrix([Au; 20]),
SrgbToLinear,
LinearToSrgb,
@ -87,21 +88,28 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
}
Some(PictureCompositeMode::Filter(op)) => {
match op {
FilterOp::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
FilterOp::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
FilterOp::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
FilterOp::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
FilterOp::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
FilterOp::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
FilterOp::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
FilterOp::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
FilterOp::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
FilterOp::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
FilterOp::Identity => PictureCompositeKey::Identity,
FilterOp::DropShadow(offset, radius, color) => {
PictureCompositeKey::DropShadow(offset.into(), Au::from_f32_px(radius), color.into())
Filter::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
Filter::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
Filter::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
Filter::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
Filter::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
Filter::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
Filter::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
Filter::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
Filter::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
Filter::Identity => PictureCompositeKey::Identity,
Filter::DropShadowStack(ref shadows) => {
PictureCompositeKey::DropShadowStack(
shadows.iter().map(|shadow| {
(shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
}).collect()
)
}
FilterOp::Opacity(binding, _) => {
Filter::DropShadow(shadow) => {
PictureCompositeKey::DropShadow(shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
}
Filter::Opacity(binding, _) => {
match binding {
PropertyBinding::Value(value) => {
PictureCompositeKey::Opacity(Au::from_f32_px(value))
@ -111,14 +119,14 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
}
}
}
FilterOp::ColorMatrix(values) => {
Filter::ColorMatrix(values) => {
let mut quantized_values: [Au; 20] = [Au(0); 20];
for (value, result) in values.iter().zip(quantized_values.iter_mut()) {
*result = Au::from_f32_px(*value);
}
PictureCompositeKey::ColorMatrix(quantized_values)
}
FilterOp::ComponentTransfer => unreachable!(),
Filter::ComponentTransfer => unreachable!(),
}
}
Some(PictureCompositeMode::ComponentTransferFilter(handle)) => {

View File

@ -676,6 +676,32 @@ pub enum ClearMode {
Transparent,
}
/// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs,
/// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive
/// in a single frame.
pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>;
/// Since we only use it within a single primitive, the key only needs to contain the down-scaling level
/// and the blur std deviation.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BlurTaskKey {
DownScale(u32),
Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 },
}
impl BlurTaskKey {
fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self {
// Quantise the std deviations and store it as integers to work around
// Eq and Hash's f32 allergy.
// The blur radius is rounded before RenderTask::new_blur so we don't need
// a lot of precision.
const QUANTIZATION_FACTOR: f32 = 1024.0;
let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32;
let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32;
BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y }
}
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -905,15 +931,14 @@ impl RenderTask {
let mask_task_id = render_tasks.add(mask_task);
// Blur it
let blur_render_task = RenderTask::new_blur(
RenderTask::new_blur(
DeviceSize::new(blur_radius_dp, blur_radius_dp),
mask_task_id,
render_tasks,
RenderTargetKind::Alpha,
ClearMode::Zero,
);
render_tasks.add(blur_render_task)
None,
)
}
));
}
@ -1023,7 +1048,8 @@ impl RenderTask {
render_tasks: &mut RenderTaskTree,
target_kind: RenderTargetKind,
clear_mode: ClearMode,
) -> Self {
mut blur_cache: Option<&mut BlurTaskCache>,
) -> RenderTaskId {
// Adjust large std deviation value.
let mut adjusted_blur_std_deviation = blur_std_deviation;
let (blur_target_size, uv_rect_kind) = {
@ -1033,6 +1059,7 @@ impl RenderTask {
let mut adjusted_blur_target_size = blur_target_size;
let mut downscaling_src_task_id = src_task_id;
let mut scale_factor = 1.0;
let mut n_downscales = 1;
while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION &&
adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION {
if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
@ -1042,40 +1069,72 @@ impl RenderTask {
adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5;
scale_factor *= 2.0;
adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
let downscaling_task = RenderTask::new_scaling(
downscaling_src_task_id,
render_tasks,
target_kind,
adjusted_blur_target_size,
);
downscaling_src_task_id = render_tasks.add(downscaling_task);
let cached_task = match blur_cache {
Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(),
None => None,
};
downscaling_src_task_id = cached_task.unwrap_or_else(|| {
let downscaling_task = RenderTask::new_scaling(
downscaling_src_task_id,
render_tasks,
target_kind,
adjusted_blur_target_size,
);
render_tasks.add(downscaling_task)
});
if let Some(ref mut cache) = blur_cache {
cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id);
}
n_downscales += 1;
}
let blur_task_v = RenderTask::with_dynamic_location(
adjusted_blur_target_size,
vec![downscaling_src_task_id],
RenderTaskKind::VerticalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation.height,
target_kind,
uv_rect_handle: GpuCacheHandle::new(),
uv_rect_kind,
}),
clear_mode,
);
let blur_task_v_id = render_tasks.add(blur_task_v);
let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
RenderTask::with_dynamic_location(
adjusted_blur_target_size,
vec![blur_task_v_id],
RenderTaskKind::HorizontalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation.width,
target_kind,
uv_rect_handle: GpuCacheHandle::new(),
uv_rect_kind,
}),
clear_mode,
)
let cached_task = match blur_cache {
Some(ref mut cache) => cache.get(&blur_key).cloned(),
None => None,
};
let blur_task_id = cached_task.unwrap_or_else(|| {
let blur_task_v = RenderTask::with_dynamic_location(
adjusted_blur_target_size,
vec![downscaling_src_task_id],
RenderTaskKind::VerticalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation.height,
target_kind,
uv_rect_handle: GpuCacheHandle::new(),
uv_rect_kind,
}),
clear_mode,
);
let blur_task_v_id = render_tasks.add(blur_task_v);
let blur_task_h = RenderTask::with_dynamic_location(
adjusted_blur_target_size,
vec![blur_task_v_id],
RenderTaskKind::HorizontalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation.width,
target_kind,
uv_rect_handle: GpuCacheHandle::new(),
uv_rect_kind,
}),
clear_mode,
);
render_tasks.add(blur_task_h)
});
if let Some(ref mut cache) = blur_cache {
cache.insert(blur_key, blur_task_id);
}
blur_task_id
}
pub fn new_border_segment(
@ -1393,7 +1452,6 @@ impl RenderTask {
if let Some(mut request) = gpu_cache.request(cache_handle) {
let p0 = target_rect.origin.to_f32();
let p1 = target_rect.bottom_right().to_f32();
let image_source = ImageSource {
p0,
p1,

View File

@ -6,7 +6,7 @@ use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch};
use api::{FilterOp, TempFilterData, FilterData, ComponentTransferFuncType};
use api::{PipelineId, PropertyBinding, PropertyBindingId, ItemRange, MixBlendMode, StackingContext};
use api::units::{LayoutSize, LayoutTransform};
use crate::internal_types::FastHashMap;
use crate::internal_types::{FastHashMap, Filter};
use std::sync::Arc;
/// Stores a map of the animated property bindings for the current display list. These
@ -203,73 +203,13 @@ impl Scene {
}
}
/// An arbitrary number which we assume opacity is invisible below.
pub const OPACITY_EPSILON: f32 = 0.001;
pub trait FilterOpHelpers {
fn is_visible(&self) -> bool;
fn is_noop(&self) -> bool;
}
impl FilterOpHelpers for FilterOp {
fn is_visible(&self) -> bool {
match *self {
FilterOp::Identity |
FilterOp::Blur(..) |
FilterOp::Brightness(..) |
FilterOp::Contrast(..) |
FilterOp::Grayscale(..) |
FilterOp::HueRotate(..) |
FilterOp::Invert(..) |
FilterOp::Saturate(..) |
FilterOp::Sepia(..) |
FilterOp::DropShadow(..) |
FilterOp::ColorMatrix(..) |
FilterOp::SrgbToLinear |
FilterOp::LinearToSrgb |
FilterOp::ComponentTransfer => true,
FilterOp::Opacity(_, amount) => {
amount > OPACITY_EPSILON
}
}
}
fn is_noop(&self) -> bool {
match *self {
FilterOp::Identity => false, // this is intentional
FilterOp::Blur(length) => length == 0.0,
FilterOp::Brightness(amount) => amount == 1.0,
FilterOp::Contrast(amount) => amount == 1.0,
FilterOp::Grayscale(amount) => amount == 0.0,
FilterOp::HueRotate(amount) => amount == 0.0,
FilterOp::Invert(amount) => amount == 0.0,
FilterOp::Opacity(_, amount) => amount >= 1.0,
FilterOp::Saturate(amount) => amount == 1.0,
FilterOp::Sepia(amount) => amount == 0.0,
FilterOp::DropShadow(offset, blur, _) => {
offset.x == 0.0 && offset.y == 0.0 && blur == 0.0
},
FilterOp::ColorMatrix(matrix) => {
matrix == [1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
0.0, 0.0, 0.0, 0.0]
}
FilterOp::SrgbToLinear |
FilterOp::LinearToSrgb |
FilterOp::ComponentTransfer => false,
}
}
}
pub trait StackingContextHelpers {
fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
fn filter_ops_for_compositing(
&self,
display_list: &BuiltDisplayList,
input_filters: ItemRange<FilterOp>,
) -> Vec<FilterOp>;
) -> Vec<Filter>;
fn filter_datas_for_compositing(
&self,
display_list: &BuiltDisplayList,
@ -289,13 +229,13 @@ impl StackingContextHelpers for StackingContext {
&self,
display_list: &BuiltDisplayList,
input_filters: ItemRange<FilterOp>,
) -> Vec<FilterOp> {
) -> Vec<Filter> {
// TODO(gw): Now that we resolve these later on,
// we could probably make it a bit
// more efficient than cloning these here.
let mut filters = vec![];
for filter in display_list.get(input_filters) {
filters.push(filter);
filters.push(filter.into());
}
filters
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, BorderStyle, MixBlendMode, PipelineId, PremultipliedColorF};
use api::{DocumentLayer, FilterData, FilterOp, ImageFormat, LineOrientation};
use api::{DocumentLayer, FilterData, ImageFormat, LineOrientation};
use api::units::*;
#[cfg(feature = "pathfinder")]
use api::FontRenderMode;
@ -18,7 +18,7 @@ use crate::frame_builder::FrameGlobalResources;
use crate::gpu_cache::{GpuCache};
use crate::gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
use crate::gpu_types::{TransformData, TransformPalette, ZBufferIdGenerator};
use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource};
use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource, Filter};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
use crate::picture::{RecordedDirtyRegion, SurfaceInfo};
@ -1160,7 +1160,7 @@ impl RenderPass {
#[derive(Debug, Clone, Default)]
pub struct CompositeOps {
// Requires only a single texture as input (e.g. most filters)
pub filters: Vec<FilterOp>,
pub filters: Vec<Filter>,
pub filter_datas: Vec<FilterData>,
// Requires two source textures (e.g. mix-blend-mode)
@ -1168,7 +1168,7 @@ pub struct CompositeOps {
}
impl CompositeOps {
pub fn new(filters: Vec<FilterOp>,
pub fn new(filters: Vec<Filter>,
filter_datas: Vec<FilterData>,
mix_blend_mode: Option<MixBlendMode>) -> Self {
CompositeOps {

View File

@ -521,6 +521,7 @@ pub struct BoxShadowDisplayItem {
pub struct PushShadowDisplayItem {
pub space_and_clip: SpaceAndClipInfo,
pub shadow: Shadow,
pub should_inflate: bool,
}
#[repr(C)]
@ -529,7 +530,6 @@ pub struct Shadow {
pub offset: LayoutVector2D,
pub color: ColorF,
pub blur_radius: f32,
pub should_inflate: bool,
}
#[repr(u8)]
@ -712,31 +712,13 @@ pub enum FilterOp {
Opacity(PropertyBinding<f32>, f32),
Saturate(f32),
Sepia(f32),
DropShadow(LayoutVector2D, f32, ColorF),
DropShadow(Shadow),
ColorMatrix([f32; 20]),
SrgbToLinear,
LinearToSrgb,
ComponentTransfer,
}
impl FilterOp {
/// Ensure that the parameters for a filter operation
/// are sensible.
pub fn sanitize(&self) -> FilterOp {
match self {
FilterOp::Blur(radius) => {
let radius = radius.min(MAX_BLUR_RADIUS);
FilterOp::Blur(radius)
}
FilterOp::DropShadow(offset, radius, color) => {
let radius = radius.min(MAX_BLUR_RADIUS);
FilterOp::DropShadow(*offset, radius, *color)
}
filter => filter.clone(),
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
pub enum ComponentTransferFuncType {

View File

@ -1640,10 +1640,12 @@ impl DisplayListBuilder {
&mut self,
space_and_clip: &di::SpaceAndClipInfo,
shadow: di::Shadow,
should_inflate: bool,
) {
let item = di::DisplayItem::PushShadow(di::PushShadowDisplayItem {
space_and_clip: *space_and_clip,
shadow,
should_inflate,
});
self.push_item(&item);
}

View File

@ -952,8 +952,8 @@ impl<'a> RawtestHarness<'a> {
offset: LayoutVector2D::new(1.0, 1.0),
blur_radius: 1.0,
color: ColorF::new(0.0, 0.0, 0.0, 1.0),
should_inflate: true,
},
true,
);
let info = CommonItemProperties {
clip_rect: rect(110., 110., 50., 2.),
@ -1030,8 +1030,8 @@ impl<'a> RawtestHarness<'a> {
offset: LayoutVector2D::new(1.0, 1.0),
blur_radius: 1.0,
color: shadow_color,
should_inflate: true,
},
true,
);
let info = self.make_common_properties(rect(110., 110., 50., 2.));
builder.push_line(

View File

@ -1696,8 +1696,8 @@ impl YamlFrameReader {
blur_radius,
offset,
color,
should_inflate: true,
},
true,
);
}

View File

@ -238,6 +238,15 @@ fn write_reference_frame(
usize_node(parent, "id", clip_id_mapper.add_spatial_id(reference_frame.id));
}
fn shadow_parameters(shadow: &Shadow) -> String {
format!(
"[{},{}],{},[{}]",
shadow.offset.x, shadow.offset.y,
shadow.blur_radius,
color_to_string(shadow.color)
)
}
fn write_stacking_context(
parent: &mut Table,
sc: &StackingContext,
@ -279,11 +288,11 @@ fn write_stacking_context(
}
FilterOp::Saturate(x) => { filters.push(Yaml::String(format!("saturate({})", x))) }
FilterOp::Sepia(x) => { filters.push(Yaml::String(format!("sepia({})", x))) }
FilterOp::DropShadow(offset, blur, color) => {
filters.push(Yaml::String(format!("drop-shadow([{},{}],{},[{}])",
offset.x, offset.y,
blur,
color_to_string(color))))
FilterOp::DropShadow(shadow) => {
filters.push(Yaml::String(format!(
"drop-shadow({})",
shadow_parameters(&shadow)
)))
}
FilterOp::ColorMatrix(matrix) => {
filters.push(Yaml::String(format!("color-matrix({:?})", matrix)))

View File

@ -601,9 +601,11 @@ impl YamlHelper for Yaml {
let str = format!("---\noffset: {}\nblur-radius: {}\ncolor: {}\n", args[0], args[1], args[2]);
let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse drop-shadow");
let yaml = yaml_doc.pop().unwrap();
Some(FilterOp::DropShadow(yaml["offset"].as_vector().unwrap(),
yaml["blur-radius"].as_f32().unwrap(),
yaml["color"].as_colorf().unwrap()))
Some(FilterOp::DropShadow(Shadow {
offset: yaml["offset"].as_vector().unwrap(),
blur_radius: yaml["blur-radius"].as_f32().unwrap(),
color: yaml["color"].as_colorf().unwrap()
}))
}
("color-matrix", ref args, _) if args.len() == 20 => {
let m: Vec<f32> = args.iter().map(|f| f.parse().unwrap()).collect();