Bug 1443807 - Update webrender to commit 5cb71f0f23719795e7c89417d91a7abad8ac20e9. r=jrmuizel

MozReview-Commit-ID: 9JzKooI2sJ2

--HG--
extra : rebase_source : 2762c00c95aa40dcd9d27ef1c5efa66494b8a90b
This commit is contained in:
Kartikaya Gupta 2018-03-09 08:39:35 -05:00
parent f177bf9d3d
commit faa816e22a
23 changed files with 774 additions and 817 deletions

View File

@ -13,12 +13,18 @@ varying vec2 vLocalPos;
varying vec3 vUv;
flat varying vec4 vUvBounds;
flat varying vec4 vColor;
#ifdef WR_FEATURE_ALPHA_PASS
flat varying vec2 vSelect;
#endif
#ifdef WR_VERTEX_SHADER
#define IMAGE_SOURCE_COLOR 0
#define IMAGE_SOURCE_ALPHA 1
#define IMAGE_SOURCE_MASK_FROM_COLOR 2
#ifdef WR_FEATURE_ALPHA_PASS
#define IMAGE_SOURCE_COLOR 0
#define IMAGE_SOURCE_ALPHA 1
#define IMAGE_SOURCE_MASK_FROM_COLOR 2
#endif
void brush_vs(
VertexInfo vi,
@ -53,6 +59,7 @@ void brush_vs(
max(uv0, uv1) - vec2(0.5)
) / texture_size.xyxy;
#ifdef WR_FEATURE_ALPHA_PASS
switch (user_data.y) {
case IMAGE_SOURCE_COLOR:
vSelect = vec2(0.0, 0.0);
@ -65,7 +72,6 @@ void brush_vs(
break;
}
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
}
@ -76,11 +82,12 @@ vec4 brush_fs() {
vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
vec4 texel = TEX_SAMPLE(sColor0, vec3(uv, vUv.z));
vec4 mask = mix(texel.rrrr, texel.aaaa, vSelect.x);
vec4 color = mix(texel, vColor * mask, vSelect.y);
#ifdef WR_FEATURE_ALPHA_PASS
color *= init_transform_fs(vLocalPos);
vec4 mask = mix(texel.rrrr, texel.aaaa, vSelect.x);
vec4 color = mix(texel, vColor * mask, vSelect.y) * init_transform_fs(vLocalPos);
#else
vec4 color = texel;
#endif
return color;

View File

@ -1,114 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_SPECIFIC_BRUSH 1
#include shared,prim_shared,brush
#ifdef WR_FEATURE_ALPHA_PASS
varying vec2 vLocalPos;
#endif
varying vec3 vUv;
flat varying int vImageKind;
flat varying vec4 vUvBounds;
flat varying vec4 vUvBounds_NoClamp;
flat varying vec4 vParams;
flat varying vec4 vColor;
#define BRUSH_PICTURE_SIMPLE 0
#define BRUSH_PICTURE_NINEPATCH 1
#ifdef WR_VERTEX_SHADER
struct Picture {
vec4 color;
};
Picture fetch_picture(int address) {
vec4 data = fetch_from_resource_cache_1(address);
return Picture(data);
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
PictureTask pic_task
) {
vImageKind = user_data.y;
Picture pic = fetch_picture(prim_address);
ImageResource res = fetch_image_resource(user_data.x);
vec2 texture_size = vec2(textureSize(sColor1, 0).xy);
vColor = pic.color;
vec2 uv0 = res.uv_rect.p0;
vec2 uv1 = res.uv_rect.p1;
vec2 src_size = (uv1 - uv0) * res.user_data.x;
vUv.z = res.layer;
// TODO(gw): In the future we'll probably draw these as segments
// with the brush shader. When that occurs, we can
// modify the UVs for each segment in the VS, and the
// FS can become a simple shader that doesn't need
// to adjust the UVs.
switch (vImageKind) {
case BRUSH_PICTURE_SIMPLE: {
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
vUv.xy = mix(uv0, uv1, f);
vUv.xy /= texture_size;
break;
}
case BRUSH_PICTURE_NINEPATCH: {
vec2 local_src_size = src_size / uDevicePixelRatio;
vUv.xy = (vi.local_pos - local_rect.p0) / local_src_size;
vParams.xy = vec2(0.5);
vParams.zw = (local_rect.size / local_src_size - 0.5);
break;
}
default:
vUv.xy = vec2(0.0);
vParams = vec4(0.0);
}
vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
vUvBounds_NoClamp = vec4(uv0, uv1) / texture_size.xyxy;
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 brush_fs() {
vec2 uv;
switch (vImageKind) {
case BRUSH_PICTURE_SIMPLE: {
uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
break;
}
case BRUSH_PICTURE_NINEPATCH: {
uv = clamp(vUv.xy, vec2(0.0), vParams.xy);
uv += max(vec2(0.0), vUv.xy - vParams.zw);
uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
uv = clamp(uv, vUvBounds.xy, vUvBounds.zw);
break;
}
default:
uv = vec2(0.0);
}
vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r;
#ifdef WR_FEATURE_ALPHA_PASS
color *= init_transform_fs(vLocalPos);
#endif
return color;
}
#endif

View File

@ -53,7 +53,15 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
ClipArea area) {
vec2 actual_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
vec4 node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node);
vec4 node_pos;
// Select the local position, based on whether we are rasterizing this
// clip mask in local- or sccreen-space.
if (area.local_space) {
node_pos = vec4(actual_pos / uDevicePixelRatio, 0.0, 1.0);
} else {
node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node);
}
// compute the point position inside the scroll node, in CSS space
vec2 vertex_pos = actual_pos +

View File

@ -24,7 +24,6 @@ in int aBlurDirection;
struct BlurTask {
RenderTaskCommonData common_data;
float blur_radius;
float scale_factor;
vec4 color;
};
@ -34,7 +33,6 @@ BlurTask fetch_blur_task(int address) {
BlurTask task = BlurTask(
task_data.common_data,
task_data.data1.x,
task_data.data1.y,
task_data.data2
);

View File

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include shared,prim_shared,clip_shared
varying vec3 vPos;
varying vec2 vUv;
flat varying vec4 vUvBounds;
flat varying float vLayer;
flat varying vec4 vEdge;
flat varying vec4 vUvBounds_NoClamp;
flat varying float vClipMode;
#ifdef WR_VERTEX_SHADER
struct BoxShadowData {
vec2 src_rect_size;
float clip_mode;
RectWithSize dest_rect;
};
BoxShadowData fetch_data(ivec2 address) {
vec4 data[2] = fetch_from_resource_cache_2_direct(address);
RectWithSize dest_rect = RectWithSize(data[1].xy, data[1].zw);
BoxShadowData bs_data = BoxShadowData(data[0].xy, data[0].z, dest_rect);
return bs_data;
}
void main(void) {
ClipMaskInstance cmi = fetch_clip_item();
ClipArea area = fetch_clip_area(cmi.render_task_address);
ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
BoxShadowData bs_data = fetch_data(cmi.clip_data_address);
ImageResource res = fetch_image_resource_direct(cmi.resource_address);
ClipVertexInfo vi = write_clip_tile_vertex(bs_data.dest_rect,
scroll_node,
area);
vLayer = res.layer;
vPos = vi.local_pos;
vClipMode = bs_data.clip_mode;
vec2 uv0 = res.uv_rect.p0;
vec2 uv1 = res.uv_rect.p1;
vec2 texture_size = vec2(textureSize(sColor0, 0));
vec2 local_pos = vPos.xy / vPos.z;
vEdge.xy = vec2(0.5);
vEdge.zw = (bs_data.dest_rect.size / bs_data.src_rect_size) - vec2(0.5);
vUv = (local_pos - bs_data.dest_rect.p0) / bs_data.src_rect_size;
vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
vUvBounds_NoClamp = vec4(uv0, uv1) / texture_size.xyxy;
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
vec2 local_pos = vPos.xy / vPos.z;
vec2 uv = clamp(vUv.xy, vec2(0.0), vEdge.xy);
uv += max(vec2(0.0), vUv.xy - vEdge.zw);
uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
uv = clamp(uv, vUvBounds.xy, vUvBounds.zw);
float in_shadow_rect = point_inside_rect(
local_pos,
vLocalBounds.xy,
vLocalBounds.zw
);
float texel = TEX_SAMPLE(sColor0, vec3(uv, vLayer)).r;
float alpha = mix(texel, 1.0 - texel, vClipMode);
oFragColor = vec4(mix(vClipMode, alpha, in_shadow_rect));
}
#endif

View File

@ -86,21 +86,12 @@ uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
in ivec4 aData0;
in ivec4 aData1;
// Work around Angle bug that forgets to update sampler metadata,
// by making the use of those samplers uniform across programs.
// https://github.com/servo/webrender/wiki/Driver-issues#texturesize-in-vertex-shaders
void markCacheTexturesUsed() {
vec2 size = vec2(textureSize(sCacheA8, 0)) + vec2(textureSize(sCacheRGBA8, 0));
if (size.x > 1000000.0) {
gl_Position = vec4(0.0);
}
}
// get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
// TODO: convert back to a function once the driver issues are resolved, if ever.
// https://github.com/servo/webrender/pull/623
// https://github.com/servo/servo/issues/13953
#define get_fetch_uv(i, vpi) ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
// Do the division with unsigned ints because that's more efficient with D3D
#define get_fetch_uv(i, vpi) ivec2(int(uint(vpi) * (uint(i) % uint(WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))), int(uint(i) / uint(WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)))
vec4[8] fetch_from_resource_cache_8(int address) {
@ -259,7 +250,6 @@ RenderTaskCommonData fetch_render_task_common_data(int index) {
#define PIC_TYPE_IMAGE 1
#define PIC_TYPE_TEXT_SHADOW 2
#define PIC_TYPE_BOX_SHADOW 3
/*
The dynamic picture that this brush exists on. Right now, it
@ -289,6 +279,7 @@ PictureTask fetch_picture_task(int address) {
struct ClipArea {
RenderTaskCommonData common_data;
vec2 screen_origin;
bool local_space;
};
ClipArea fetch_clip_area(int index) {
@ -300,11 +291,13 @@ ClipArea fetch_clip_area(int index) {
0.0
);
area.screen_origin = vec2(0.0);
area.local_space = false;
} else {
RenderTaskData task_data = fetch_render_task_data(index);
area.common_data = task_data.common_data;
area.screen_origin = task_data.data1.xy;
area.local_space = task_data.data1.z == 0.0;
}
return area;
@ -375,8 +368,6 @@ PrimitiveInstance fetch_prim_instance() {
pi.user_data1 = aData1.z;
pi.user_data2 = aData1.w;
markCacheTexturesUsed();
return pi;
}
@ -404,8 +395,6 @@ CompositeInstance fetch_composite_instance() {
ci.user_data2 = aData1.z;
ci.user_data3 = aData1.w;
markCacheTexturesUsed();
return ci;
}
@ -822,7 +811,7 @@ float do_clip() {
vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw));
// check for the dummy bounds, which are given to the opaque objects
return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0:
all(inside) ? texelFetch(sSharedCacheA8, ivec3(vClipMaskUv), 0).r : 0.0;
all(inside) ? texelFetch(sCacheA8, ivec3(vClipMaskUv), 0).r : 0.0;
}
#ifdef WR_FEATURE_DITHERING

View File

@ -16,7 +16,7 @@ use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{CachedGradient, ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
@ -55,7 +55,6 @@ pub enum BrushImageSourceKind {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BrushBatchKind {
Picture,
Solid,
Line,
Image(ImageBufferKind),
@ -522,10 +521,7 @@ impl AlphaBatchBuilder {
let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
let batch = self.batch_list.get_suitable_batch(key, &pic_metadata.screen_rect.as_ref().expect("bug").clipped);
let render_task_id = match pic.surface {
Some(PictureSurface::RenderTask(render_task_id)) => render_task_id,
Some(PictureSurface::TextureCache(..)) | None => panic!("BUG: unexpected surface in splitting"),
};
let render_task_id = pic.surface.expect("BUG: unexpected surface in splitting");
let source_task_address = render_tasks.get_task_address(render_task_id);
let gpu_address = gpu_handle.as_int(gpu_cache);
@ -574,7 +570,7 @@ impl AlphaBatchBuilder {
// TODO(gw): Support culling on shadow image types.
let is_image = match pic.kind {
PictureKind::Image { .. } => true,
PictureKind::BoxShadow { .. } | PictureKind::TextShadow { .. } => false,
PictureKind::TextShadow { .. } => false,
};
if !is_image || metadata.screen_rect.is_some() {
@ -801,7 +797,7 @@ impl AlphaBatchBuilder {
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
let is_shadow = match pic.kind {
PictureKind::TextShadow { .. } => true,
PictureKind::BoxShadow { .. } | PictureKind::Image { .. } => false,
PictureKind::Image { .. } => false,
};
// TODO(gw): It probably makes sense to base this decision on the content
@ -894,38 +890,7 @@ impl AlphaBatchBuilder {
&ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
match picture.surface {
Some(PictureSurface::TextureCache(ref cache_item)) => {
match picture.kind {
PictureKind::TextShadow { .. } |
PictureKind::Image { .. } => {
panic!("BUG: only supported as render tasks for now");
}
PictureKind::BoxShadow { image_kind, .. } => {
let textures = BatchTextures::color(cache_item.texture_id);
let kind = BrushBatchKind::Picture;
self.add_brush_to_batch(
&picture.brush,
prim_metadata,
kind,
specified_blend_mode,
non_segmented_blend_mode,
textures,
clip_chain_rect_index,
clip_task_address,
&task_relative_bounding_rect,
prim_cache_address,
scroll_id,
task_address,
transform_kind,
z,
render_tasks,
[cache_item.uv_rect_handle.as_int(gpu_cache), image_kind as i32, 0],
);
}
}
}
Some(PictureSurface::RenderTask(cache_task_id)) => {
Some(cache_task_id) => {
let cache_task_address = render_tasks.get_task_address(cache_task_id);
let textures = BatchTextures::render_target_cache();
@ -959,9 +924,6 @@ impl AlphaBatchBuilder {
};
batch.push(PrimitiveInstance::from(instance));
}
PictureKind::BoxShadow { .. } => {
panic!("BUG: should be handled as a texture cache surface");
}
PictureKind::Image {
composite_mode,
secondary_render_task_id,
@ -1447,9 +1409,6 @@ impl BrushPrimitive {
],
))
}
BrushKind::Mask { .. } => {
unreachable!("bug: mask brushes not expected in normal alpha pass");
}
}
}
}
@ -1487,7 +1446,6 @@ impl AlphaBatchHelpers for PrimitiveStore {
}
}
BrushKind::Solid { .. } |
BrushKind::Mask { .. } |
BrushKind::Line { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
@ -1599,6 +1557,7 @@ pub struct ClipBatcher {
pub images: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
pub border_clears: Vec<ClipMaskInstance>,
pub borders: Vec<ClipMaskInstance>,
pub box_shadows: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
}
impl ClipBatcher {
@ -1608,9 +1567,26 @@ impl ClipBatcher {
images: FastHashMap::default(),
border_clears: Vec::new(),
borders: Vec::new(),
box_shadows: FastHashMap::default(),
}
}
pub fn add_clip_region(
&mut self,
task_address: RenderTaskAddress,
clip_data_address: GpuCacheAddress,
) {
let instance = ClipMaskInstance {
render_task_address: task_address,
scroll_node_data_index: ClipScrollNodeIndex(0),
segment: 0,
clip_data_address,
resource_address: GpuCacheAddress::invalid(),
};
self.rectangles.push(instance);
}
pub fn add(
&mut self,
task_address: RenderTaskAddress,
@ -1659,6 +1635,18 @@ impl ClipBatcher {
continue;
}
}
ClipSource::BoxShadow(ref info) => {
debug_assert_ne!(info.cache_item.texture_id, SourceTexture::Invalid);
self.box_shadows
.entry(info.cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
clip_data_address: gpu_address,
resource_address: gpu_cache.get_address(&info.cache_item.uv_rect_handle),
..instance
});
}
ClipSource::Rectangle(..) => {
if work_item.coordinate_system_id != coordinate_system_id {
self.rectangles.push(ClipMaskInstance {

View File

@ -2,19 +2,42 @@
* 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::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, ComplexClipRegion, LayerPoint};
use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, ComplexClipRegion};
use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, LocalClip};
use api::PipelineId;
use app_units::Au;
use api::{DeviceIntSize};
use clip::ClipSource;
use display_list_flattener::DisplayListFlattener;
use gpu_types::BrushImageKind;
use gpu_cache::GpuCacheHandle;
use prim_store::{BrushKind, BrushPrimitive, PrimitiveContainer};
use prim_store::ScrollNodeAndClipChain;
use picture::PicturePrimitive;
use render_task::MAX_BLUR_STD_DEVIATION;
use resource_cache::CacheItem;
use util::RectHelpers;
#[derive(Debug)]
pub struct BoxShadowClipSource {
// Parameters that define the shadow and are constant.
pub shadow_radius: BorderRadius,
pub blur_radius: f32,
pub clip_mode: BoxShadowClipMode,
// The current cache key (in device-pixels), and handles
// to the cached clip region and blurred texture.
pub cache_key: Option<(DeviceIntSize, BoxShadowCacheKey)>,
pub cache_item: CacheItem,
pub clip_data_handle: GpuCacheHandle,
// Local-space size of the required render task size.
pub shadow_rect_alloc_size: LayerSize,
// The minimal shadow rect for the parameters above,
// used when drawing the shadow rect to be blurred.
pub minimal_shadow_rect: LayerRect,
// Local space rect for the shadow to be drawn or
// stretched in the shadow primitive.
pub prim_shadow_rect: LayerRect,
}
// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
@ -22,31 +45,25 @@ pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
// Taken from https://searchfox.org/mozilla-central/rev/c633ffa4c4611f202ca11270dcddb7b29edddff8/layout/painting/nsCSSRendering.cpp#4412
pub const MAX_BLUR_RADIUS : f32 = 300.;
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
// A cache key that uniquely identifies a minimally sized
// and blurred box-shadow rect that can be stored in the
// texture cache and applied to clip-masks.
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BoxShadowCacheKey {
pub width: Au,
pub height: Au,
pub blur_radius: Au,
pub spread_radius: Au,
pub offset_x: Au,
pub offset_y: Au,
pub br_top_left_w: Au,
pub br_top_left_h: Au,
pub br_top_right_w: Au,
pub br_top_right_h: Au,
pub br_bottom_left_w: Au,
pub br_bottom_left_h: Au,
pub br_bottom_right_w: Au,
pub br_bottom_right_h: Au,
pub blur_radius_dp: i32,
pub clip_mode: BoxShadowClipMode,
pub rect_size: DeviceIntSize,
pub br_top_left: DeviceIntSize,
pub br_top_right: DeviceIntSize,
pub br_bottom_right: DeviceIntSize,
pub br_bottom_left: DeviceIntSize,
}
impl<'a> DisplayListFlattener<'a> {
pub fn add_box_shadow(
&mut self,
pipeline_id: PipelineId,
clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
box_offset: &LayerVector2D,
@ -60,33 +77,50 @@ impl<'a> DisplayListFlattener<'a> {
return;
}
let (spread_amount, brush_clip_mode) = match clip_mode {
// Inset shadows get smaller as spread radius increases.
let (spread_amount, prim_clip_mode) = match clip_mode {
BoxShadowClipMode::Outset => {
(spread_radius, ClipMode::Clip)
(spread_radius, ClipMode::ClipOut)
}
BoxShadowClipMode::Inset => {
(-spread_radius, ClipMode::ClipOut)
(-spread_radius, ClipMode::Clip)
}
};
// Ensure the blur radius is somewhat sensible.
blur_radius = f32::min(blur_radius, MAX_BLUR_RADIUS);
// Adjust the border radius of the box shadow per CSS-spec.
let shadow_radius = adjust_border_radius_for_box_shadow(
border_radius,
spread_amount,
);
// Apply parameters that affect where the shadow rect
// exists in the local space of the primitive.
let shadow_rect = prim_info.rect
.translate(box_offset)
.inflate(spread_amount, spread_amount);
// If blur radius is zero, we can use a fast path with
// no blur applied.
if blur_radius == 0.0 {
if box_offset.x == 0.0 && box_offset.y == 0.0 && spread_amount == 0.0 {
// Trivial reject of box-shadows that are not visible.
if box_offset.x == 0.0 &&
box_offset.y == 0.0 &&
spread_amount == 0.0 {
return;
}
let mut clips = Vec::with_capacity(2);
clips.push(ClipSource::Rectangle(*prim_info.local_clip.clip_rect()));
let fast_info = match clip_mode {
BoxShadowClipMode::Outset => {
if !shadow_rect.is_well_formed_and_nonempty() {
return;
}
// TODO(gw): Add a fast path for ClipOut + zero border radius!
clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
@ -107,11 +141,13 @@ impl<'a> DisplayListFlattener<'a> {
)
}
BoxShadowClipMode::Inset => {
clips.push(ClipSource::new_rounded_rect(
shadow_rect,
shadow_radius,
ClipMode::ClipOut
));
if shadow_rect.is_well_formed_and_nonempty() {
clips.push(ClipSource::new_rounded_rect(
shadow_rect,
shadow_radius,
ClipMode::ClipOut
));
}
LayerPrimitiveInfo::with_clip(
prim_info.rect,
@ -140,213 +176,83 @@ impl<'a> DisplayListFlattener<'a> {
),
);
} else {
// Normal path for box-shadows with a valid blur radius.
let blur_offset = BLUR_SAMPLE_SCALE * blur_radius;
let mut extra_clips = vec![];
let cache_key = BoxShadowCacheKey {
width: Au::from_f32_px(shadow_rect.size.width),
height: Au::from_f32_px(shadow_rect.size.height),
blur_radius: Au::from_f32_px(blur_radius),
spread_radius: Au::from_f32_px(spread_radius),
offset_x: Au::from_f32_px(box_offset.x),
offset_y: Au::from_f32_px(box_offset.y),
br_top_left_w: Au::from_f32_px(border_radius.top_left.width),
br_top_left_h: Au::from_f32_px(border_radius.top_left.height),
br_top_right_w: Au::from_f32_px(border_radius.top_right.width),
br_top_right_h: Au::from_f32_px(border_radius.top_right.height),
br_bottom_left_w: Au::from_f32_px(border_radius.bottom_left.width),
br_bottom_left_h: Au::from_f32_px(border_radius.bottom_left.height),
br_bottom_right_w: Au::from_f32_px(border_radius.bottom_right.width),
br_bottom_right_h: Au::from_f32_px(border_radius.bottom_right.height),
// Add a normal clip mask to clip out the contents
// of the surrounding primitive.
extra_clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
border_radius,
prim_clip_mode,
));
// Get the local rect of where the shadow will be drawn,
// expanded to include room for the blurred region.
let dest_rect = shadow_rect.inflate(blur_offset, blur_offset);
// Draw the box-shadow as a solid rect, using a box-shadow
// clip mask source.
let prim = BrushPrimitive::new(
BrushKind::Solid {
color: *color,
},
None,
);
// Create the box-shadow clip source.
let shadow_clip_source = ClipSource::new_box_shadow(
shadow_rect,
shadow_radius,
dest_rect,
blur_radius,
clip_mode,
};
);
match clip_mode {
let prim_info = match clip_mode {
BoxShadowClipMode::Outset => {
let mut width;
let mut height;
let brush_prim;
let mut image_kind = BrushImageKind::NinePatch;
// Certain spread-radii make the shadow invalid.
if !shadow_rect.is_well_formed_and_nonempty() {
return;
}
// Create a minimal size primitive mask to blur. In this
// case, we ensure the size of each corner is the same,
// to simplify the shader logic that stretches the blurred
// result across the primitive.
let max_width = shadow_radius.top_left.width
.max(shadow_radius.bottom_left.width)
.max(shadow_radius.top_right.width)
.max(shadow_radius.bottom_right.width);
let max_height = shadow_radius.top_left.height
.max(shadow_radius.bottom_left.height)
.max(shadow_radius.top_right.height)
.max(shadow_radius.bottom_right.height);
// Add the box-shadow clip source.
extra_clips.push(shadow_clip_source);
width = 2.0 * max_width + BLUR_SAMPLE_SCALE * blur_radius;
height = 2.0 * max_height + BLUR_SAMPLE_SCALE * blur_radius;
// If the width or height ends up being bigger than the original
// primitive shadow rect, just blur the entire rect and draw that
// as a simple blit.
if width > prim_info.rect.size.width || height > prim_info.rect.size.height {
image_kind = BrushImageKind::Simple;
width = prim_info.rect.size.width + spread_amount * 2.0;
height = prim_info.rect.size.height + spread_amount * 2.0;
}
let clip_rect = LayerRect::new(
LayerPoint::zero(),
LayerSize::new(width, height)
);
brush_prim = BrushPrimitive::new(
BrushKind::Mask {
clip_mode: brush_clip_mode,
rect: clip_rect,
radii: shadow_radius,
},
None,
);
// Construct a mask primitive to add to the picture.
let brush_rect = LayerRect::new(LayerPoint::zero(),
LayerSize::new(width, height));
let brush_info = LayerPrimitiveInfo::new(brush_rect);
let brush_prim_index = self.create_primitive(
&brush_info,
Vec::new(),
PrimitiveContainer::Brush(brush_prim),
);
// Create a box shadow picture and add the mask primitive to it.
let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
let mut pic_prim = PicturePrimitive::new_box_shadow(
blur_radius,
*color,
clip_mode,
image_kind,
cache_key,
pipeline_id,
);
pic_prim.add_primitive(
brush_prim_index,
clip_and_scroll
);
extra_clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
border_radius,
ClipMode::ClipOut,
));
let pic_info = LayerPrimitiveInfo::with_clip_rect(
pic_rect,
// Outset shadows are expanded by the shadow
// region from the original primitive.
LayerPrimitiveInfo::with_clip_rect(
dest_rect,
*prim_info.local_clip.clip_rect()
);
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
)
}
BoxShadowClipMode::Inset => {
// TODO(gw): Inset shadows still need an optimization pass.
// We draw and blur way more pixels than needed.
// Draw a picture that covers the area of the primitive rect.
let brush_rect = LayerRect::new(
LayerPoint::zero(),
prim_info.rect.size
);
// Define where the inset box shadow rect is, local
// to the brush rect above.
let clip_rect = brush_rect.translate(box_offset)
.inflate(spread_amount, spread_amount);
// Ensure there are more than one pixel around the edges, so that there
// is non-zero data to blur, in the case of an inset shadow
// with zero spread and zero offset.
// The size of inflation edge is determined by std deviation because large
// std deviation blur would be downscaled first. Thus, we need more thick
// edge to prevent edge get blurred after downscled.
let mut adjusted_blur_std_deviation = blur_radius * 0.5;
let mut inflate_size = 1.0;
while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
adjusted_blur_std_deviation *= 0.5;
inflate_size *= 2.0;
// If the inner shadow rect contains the prim
// rect, no pixels will be shadowed.
if border_radius.is_zero() &&
shadow_rect.inflate(-blur_radius, -blur_radius).contains_rect(&prim_info.rect) {
return;
}
let brush_rect = brush_rect.inflate(inflate_size + box_offset.x.abs(), inflate_size + box_offset.y.abs());
let brush_prim = BrushPrimitive::new(
BrushKind::Mask {
clip_mode: brush_clip_mode,
rect: clip_rect,
radii: shadow_radius,
},
None,
);
let brush_info = LayerPrimitiveInfo::new(brush_rect);
let brush_prim_index = self.create_primitive(
&brush_info,
Vec::new(),
PrimitiveContainer::Brush(brush_prim),
);
// Create a box shadow picture primitive and add
// the brush primitive to it.
let mut pic_prim = PicturePrimitive::new_box_shadow(
blur_radius,
*color,
BoxShadowClipMode::Inset,
// TODO(gw): Make use of optimization for inset.
BrushImageKind::NinePatch,
cache_key,
pipeline_id,
);
pic_prim.add_primitive(
brush_prim_index,
clip_and_scroll
);
let clip_rect = prim_info.local_clip.clip_rect();
let clip_rect = match prim_info.rect.intersection(clip_rect) {
Some(clip_rect) => clip_rect,
None => return,
};
// Draw the picture one pixel outside the original
// rect to account for the inflate above. This
// extra edge will be clipped by the local clip
// rect set below.
let pic_rect = prim_info.rect.inflate(inflate_size + box_offset.x.abs(), inflate_size + box_offset.y.abs());
let pic_info = LayerPrimitiveInfo::with_clip_rect(
pic_rect,
clip_rect
);
// Add a normal clip to ensure nothing gets drawn
// outside the primitive rect.
if !border_radius.is_zero() {
extra_clips.push(ClipSource::new_rounded_rect(
prim_info.rect,
border_radius,
ClipMode::Clip,
));
// Inset shadows are still visible, even if the
// inset shadow rect becomes invalid (they will
// just look like a solid rectangle).
if shadow_rect.is_well_formed_and_nonempty() {
extra_clips.push(shadow_clip_source);
}
// Add the picture primitive to the frame.
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
// Inset shadows draw inside the original primitive.
prim_info.clone()
}
}
};
self.add_primitive(
clip_and_scroll,
&prim_info,
extra_clips,
PrimitiveContainer::Brush(prim),
);
}
}
}

View File

@ -3,15 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
use api::{ImageRendering, LayerRect, LayoutPoint, LayoutVector2D, LocalClip};
use api::{ImageRendering, LayerRect, LayerSize, LayoutPoint, LayoutVector2D, LocalClip};
use api::{BoxShadowClipMode, LayerPoint, LayerToWorldScale};
use border::{BorderCornerClipSource, ensure_no_corner_overlap};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::ClipScrollNodeIndex;
use prim_store::{ClipData, ImageMaskData};
use resource_cache::{ImageRequest, ResourceCache};
use render_task::to_cache_size;
use resource_cache::{CacheItem, ImageRequest, ResourceCache};
use util::{LayerToWorldFastTransform, MaxRect, calculate_screen_bounding_rect};
use util::extract_inner_rect_safe;
use std::sync::Arc;
@ -78,6 +81,7 @@ pub enum ClipSource {
/// adjacent border edges. Expand to handle dotted style
/// and different styles per edge.
BorderCorner(BorderCornerClipSource),
BoxShadow(BoxShadowClipSource),
}
impl From<ClipRegion> for ClipSources {
@ -115,6 +119,89 @@ impl ClipSource {
clip_mode,
)
}
pub fn new_box_shadow(
shadow_rect: LayerRect,
shadow_radius: BorderRadius,
prim_shadow_rect: LayerRect,
blur_radius: f32,
clip_mode: BoxShadowClipMode,
) -> ClipSource {
// Get the fractional offsets required to match the
// source rect with a minimal rect.
let fract_offset = LayerPoint::new(
shadow_rect.origin.x.fract().abs(),
shadow_rect.origin.y.fract().abs(),
);
let fract_size = LayerSize::new(
shadow_rect.size.width.fract().abs(),
shadow_rect.size.height.fract().abs(),
);
// Create a minimal size primitive mask to blur. In this
// case, we ensure the size of each corner is the same,
// to simplify the shader logic that stretches the blurred
// result across the primitive.
let max_corner_width = shadow_radius.top_left.width
.max(shadow_radius.bottom_left.width)
.max(shadow_radius.top_right.width)
.max(shadow_radius.bottom_right.width);
let max_corner_height = shadow_radius.top_left.height
.max(shadow_radius.bottom_left.height)
.max(shadow_radius.top_right.height)
.max(shadow_radius.bottom_right.height);
// Get maximum distance that can be affected by given blur radius.
let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
// If the largest corner is smaller than the blur radius, we need to ensure
// that it's big enough that the corners don't affect the middle segments.
let used_corner_width = max_corner_width.max(blur_region);
let used_corner_height = max_corner_height.max(blur_region);
// Minimal nine-patch size, corner + internal + corner.
let min_shadow_rect_size = LayerSize::new(
2.0 * used_corner_width + blur_region,
2.0 * used_corner_height + blur_region,
);
// The minimal rect to blur.
let mut minimal_shadow_rect = LayerRect::new(
LayerPoint::new(
blur_region + fract_offset.x,
blur_region + fract_offset.y,
),
LayerSize::new(
min_shadow_rect_size.width + fract_size.width,
min_shadow_rect_size.height + fract_size.height,
),
);
// If the width or height ends up being bigger than the original
// primitive shadow rect, just blur the entire rect and draw that
// as a simple blit. This is necessary for correctness, since the
// blur of one corner may affect the blur in another corner.
minimal_shadow_rect.size.width = minimal_shadow_rect.size.width.min(shadow_rect.size.width);
minimal_shadow_rect.size.height = minimal_shadow_rect.size.height.min(shadow_rect.size.height);
// Expand the shadow rect by enough room for the blur to take effect.
let shadow_rect_alloc_size = LayerSize::new(
2.0 * blur_region + minimal_shadow_rect.size.width.ceil(),
2.0 * blur_region + minimal_shadow_rect.size.height.ceil(),
);
ClipSource::BoxShadow(BoxShadowClipSource {
shadow_rect_alloc_size,
shadow_radius,
prim_shadow_rect,
blur_radius,
clip_mode,
cache_item: CacheItem::invalid(),
cache_key: None,
clip_data_handle: GpuCacheHandle::new(),
minimal_shadow_rect,
})
}
}
#[derive(Debug)]
@ -186,6 +273,7 @@ impl ClipSources {
local_inner = local_inner
.and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner)));
}
ClipSource::BoxShadow(..) |
ClipSource::BorderCorner { .. } => {
can_calculate_inner_rect = false;
break;
@ -210,6 +298,7 @@ impl ClipSources {
&mut self,
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
device_pixel_scale: DevicePixelScale,
) {
for &mut (ref mut source, ref mut handle) in &mut self.clips {
if let Some(mut request) = gpu_cache.request(handle) {
@ -218,6 +307,15 @@ impl ClipSources {
let data = ImageMaskData { local_rect: mask.rect };
data.write_gpu_blocks(request);
}
ClipSource::BoxShadow(ref info) => {
request.push([
info.shadow_rect_alloc_size.width,
info.shadow_rect_alloc_size.height,
info.clip_mode as i32 as f32,
0.0,
]);
request.push(info.prim_shadow_rect);
}
ClipSource::Rectangle(rect) => {
let data = ClipData::uniform(rect, 0.0, ClipMode::Clip);
data.write(&mut request);
@ -232,15 +330,49 @@ impl ClipSources {
}
}
if let ClipSource::Image(ref mask) = *source {
resource_cache.request_image(
ImageRequest {
key: mask.image,
rendering: ImageRendering::Auto,
tile: None,
},
gpu_cache,
);
match *source {
ClipSource::Image(ref mask) => {
resource_cache.request_image(
ImageRequest {
key: mask.image,
rendering: ImageRendering::Auto,
tile: None,
},
gpu_cache,
);
}
ClipSource::BoxShadow(ref mut info) => {
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
let blur_radius_dp = (info.blur_radius * 0.5 * device_pixel_scale.0).round();
// Create the cache key for this box-shadow render task.
let content_scale = LayerToWorldScale::new(1.0) * device_pixel_scale;
let cache_size = to_cache_size(info.shadow_rect_alloc_size * content_scale);
let bs_cache_key = BoxShadowCacheKey {
blur_radius_dp: blur_radius_dp as i32,
clip_mode: info.clip_mode,
rect_size: (info.shadow_rect_alloc_size * content_scale).round().to_i32(),
br_top_left: (info.shadow_radius.top_left * content_scale).round().to_i32(),
br_top_right: (info.shadow_radius.top_right * content_scale).round().to_i32(),
br_bottom_right: (info.shadow_radius.bottom_right * content_scale).round().to_i32(),
br_bottom_left: (info.shadow_radius.bottom_left * content_scale).round().to_i32(),
};
info.cache_key = Some((cache_size, bs_cache_key));
if let Some(mut request) = gpu_cache.request(&mut info.clip_data_handle) {
let data = ClipData::rounded_rect(
&info.minimal_shadow_rect,
&info.shadow_radius,
ClipMode::Clip,
);
data.write(&mut request);
}
}
_ => {}
}
}
}

View File

@ -370,7 +370,11 @@ impl ClipScrollNode {
};
let clip_sources = clip_store.get_mut(clip_sources_handle);
clip_sources.update(gpu_cache, resource_cache);
clip_sources.update(
gpu_cache,
resource_cache,
device_pixel_scale,
);
let (screen_inner_rect, screen_outer_rect) =
clip_sources.get_screen_bounds(&self.world_viewport_transform, device_pixel_scale);

View File

@ -741,7 +741,6 @@ impl<'a> DisplayListFlattener<'a> {
let mut prim_info = prim_info.clone();
prim_info.rect = bounds;
self.add_box_shadow(
pipeline_id,
clip_and_scroll,
&prim_info,
&box_shadow_info.offset,
@ -1312,9 +1311,8 @@ impl<'a> DisplayListFlattener<'a> {
*composite_mode = Some(PictureCompositeMode::Blit);
}
}
PictureKind::TextShadow { .. } |
PictureKind::BoxShadow { .. } => {
panic!("bug: text/box pictures invalid here");
PictureKind::TextShadow { .. } => {
panic!("bug: text pictures invalid here");
}
}
}

View File

@ -13,7 +13,7 @@ use gpu_cache::GpuCache;
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, PictureType};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
use picture::{ContentOrigin, PictureSurface};
use picture::{ContentOrigin};
use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveRun, PrimitiveStore};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
@ -235,7 +235,7 @@ impl FrameBuilder {
);
let render_task_id = frame_state.render_tasks.add(root_render_task);
pic.surface = Some(PictureSurface::RenderTask(render_task_id));
pic.surface = Some(render_task_id);
Some(render_task_id)
}

View File

@ -9,6 +9,15 @@ use render_task::RenderTaskAddress;
// Contains type that must exactly match the same structures declared in GLSL.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub enum RasterizationSpace {
Local = 0,
Screen = 1,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
@ -195,16 +204,6 @@ impl From<BrushInstance> for PrimitiveInstance {
}
}
// Defines how a brush image is stretched onto the primitive.
// In the future, we may draw with segments for each portion
// of the primitive, in which case this will be redundant.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub enum BrushImageKind {
Simple = 0, // A normal rect
NinePatch = 1, // A nine-patch image (stretch inside segments)
}
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -244,7 +243,6 @@ pub struct ClipChainRectIndex(pub usize);
pub enum PictureType {
Image = 1,
TextShadow = 2,
BoxShadow = 3,
}
#[derive(Debug, Copy, Clone)]

View File

@ -300,8 +300,10 @@ fn get_regions_for_clip_scroll_node(
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect),
ClipSource::BorderCorner(_) =>
unreachable!("Didn't expect to hit test against BorderCorner"),
ClipSource::BorderCorner(_) |
ClipSource::BoxShadow(_) => {
unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow");
}
}
}).collect()
}

View File

@ -2,38 +2,22 @@
* 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::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize};
use api::{DeviceIntPoint, DeviceIntRect};
use api::{LayerPoint, LayerRect, LayerToWorldScale, LayerVector2D};
use api::{BoxShadowClipMode, ColorF, FilterOp, MixBlendMode, PipelineId};
use api::{ColorF, FilterOp, MixBlendMode, PipelineId};
use api::{PremultipliedColorF, Shadow};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip_scroll_tree::ClipScrollNodeIndex;
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState};
use gpu_cache::{GpuCacheHandle, GpuDataRequest};
use gpu_types::{BrushImageKind, PictureType};
use gpu_types::{PictureType};
use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use prim_store::ScrollNodeAndClipChain;
use render_task::{ClearMode, RenderTask, RenderTaskCacheKey};
use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use render_task::{ClearMode, RenderTask};
use render_task::{RenderTaskId, RenderTaskLocation, to_cache_size};
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
// Note: zero-square tasks are prohibited in WR task tree, so
// we ensure each dimension to be at least the length of 1 after rounding.
fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
DeviceIntSize::new(
1.max(size.width.round() as i32),
1.max(size.height.round() as i32),
)
}
/*
A picture represents a dynamically rendered image. It consists of:
@ -75,14 +59,6 @@ pub enum PictureKind {
blur_radius: f32,
content_rect: LayerRect,
},
BoxShadow {
blur_radius: f32,
color: ColorF,
clip_mode: BoxShadowClipMode,
image_kind: BrushImageKind,
content_rect: LayerRect,
cache_key: BoxShadowCacheKey,
},
Image {
// If a mix-blend-mode, contains the render task for
// the readback of the framebuffer that we use to sample
@ -112,21 +88,11 @@ pub enum PictureKind {
},
}
// The type of surface that a picture can be drawn to.
// RenderTask surfaces are not retained across frames.
// TextureCache surfaces are stored across frames, and
// also shared between display lists.
#[derive(Debug)]
pub enum PictureSurface {
RenderTask(RenderTaskId),
TextureCache(CacheItem),
}
#[derive(Debug)]
pub struct PicturePrimitive {
// If this picture is drawn to an intermediate surface,
// the associated target information.
pub surface: Option<PictureSurface>,
pub surface: Option<RenderTaskId>,
// Details specific to this type of picture.
pub kind: PictureKind,
@ -193,34 +159,6 @@ impl PicturePrimitive {
}
}
pub fn new_box_shadow(
blur_radius: f32,
color: ColorF,
clip_mode: BoxShadowClipMode,
image_kind: BrushImageKind,
cache_key: BoxShadowCacheKey,
pipeline_id: PipelineId,
) -> Self {
PicturePrimitive {
runs: Vec::new(),
surface: None,
kind: PictureKind::BoxShadow {
blur_radius,
color,
clip_mode,
image_kind,
content_rect: LayerRect::zero(),
cache_key,
},
pipeline_id,
cull_children: false,
brush: BrushPrimitive::new(
BrushKind::Picture,
None,
),
}
}
pub fn new_image(
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
@ -269,8 +207,8 @@ impl PicturePrimitive {
});
}
pub fn update_local_rect(&mut self,
prim_local_rect: LayerRect,
pub fn update_local_rect(
&mut self,
prim_run_rect: PrimitiveRunLocalRect,
) -> LayerRect {
let local_content_rect = prim_run_rect.local_rect_in_actual_parent_space;
@ -304,25 +242,6 @@ impl PicturePrimitive {
content_rect.translate(&offset)
}
PictureKind::BoxShadow { blur_radius, clip_mode, ref mut content_rect, .. } => {
// We need to inflate the content rect if outset.
*content_rect = match clip_mode {
BoxShadowClipMode::Outset => {
let full_offset = blur_radius * BLUR_SAMPLE_SCALE;
// For a non-uniform radii, we need to expand
// the content rect on all sides for the blur.
local_content_rect.inflate(
full_offset,
full_offset,
)
}
BoxShadowClipMode::Inset => {
local_content_rect
}
};
prim_local_rect
}
}
}
@ -362,7 +281,7 @@ impl PicturePrimitive {
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let picture_task_id = frame_state.render_tasks.add(picture_task);
let (blur_render_task, _) = RenderTask::new_blur(
let blur_render_task = RenderTask::new_blur(
blur_std_deviation,
picture_task_id,
frame_state.render_tasks,
@ -373,7 +292,7 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
self.surface = Some(render_task_id);
}
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => {
let rect = (prim_local_rect.translate(&-offset) * content_scale).round().to_i32();
@ -392,7 +311,7 @@ impl PicturePrimitive {
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let picture_task_id = frame_state.render_tasks.add(picture_task);
let (blur_render_task, _) = RenderTask::new_blur(
let blur_render_task = RenderTask::new_blur(
blur_std_deviation.round(),
picture_task_id,
frame_state.render_tasks,
@ -405,7 +324,7 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
self.surface = Some(render_task_id);
}
Some(PictureCompositeMode::MixBlend(..)) => {
let picture_task = RenderTask::new_picture(
@ -426,7 +345,7 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
self.surface = Some(render_task_id);
}
Some(PictureCompositeMode::Filter(filter)) => {
// If this filter is not currently going to affect
@ -460,7 +379,7 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
self.surface = Some(render_task_id);
}
}
Some(PictureCompositeMode::Blit) => {
@ -477,7 +396,7 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
self.surface = Some(render_task_id);
}
None => {
pic_state.tasks.extend(pic_state_for_children.tasks);
@ -511,7 +430,7 @@ impl PicturePrimitive {
let picture_task_id = frame_state.render_tasks.add(picture_task);
let (blur_render_task, _) = RenderTask::new_blur(
let blur_render_task = RenderTask::new_blur(
blur_std_deviation,
picture_task_id,
frame_state.render_tasks,
@ -522,78 +441,7 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
}
PictureKind::BoxShadow { blur_radius, clip_mode, color, content_rect, cache_key, .. } => {
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
let cache_size = to_cache_size(content_rect.size * content_scale);
// Request the texture cache item for this box-shadow key. If it
// doesn't exist in the cache, the closure is invoked to build
// a render task chain to draw the cacheable result.
let cache_item = frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size: cache_size,
kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
},
frame_state.gpu_cache,
frame_state.render_tasks,
|render_tasks| {
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
let device_radius = (blur_radius * frame_context.device_pixel_scale.0).round();
let blur_std_deviation = device_radius * 0.5;
let blur_clear_mode = match clip_mode {
BoxShadowClipMode::Outset => {
ClearMode::One
}
BoxShadowClipMode::Inset => {
ClearMode::Zero
}
};
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, cache_size),
prim_index,
RenderTargetKind::Alpha,
ContentOrigin::Local(content_rect.origin),
color.premultiplied(),
ClearMode::Zero,
Vec::new(),
PictureType::BoxShadow,
);
let picture_task_id = render_tasks.add(picture_task);
let (blur_render_task, scale_factor) = RenderTask::new_blur(
blur_std_deviation,
picture_task_id,
render_tasks,
RenderTargetKind::Alpha,
blur_clear_mode,
color.premultiplied(),
);
let root_task_id = render_tasks.add(blur_render_task);
pic_state.tasks.push(root_task_id);
// TODO(gw): Remove the nastiness with having to pass
// the scale factor through the texture cache
// item user data. This will disappear once
// the brush_picture shader is updated to draw
// segments, since the scale factor will not
// be used at all then during drawing.
(root_task_id, [scale_factor, 0.0, 0.0], false)
}
);
self.surface = Some(PictureSurface::TextureCache(cache_item));
self.surface = Some(render_task_id);
}
}
}
@ -634,9 +482,6 @@ impl PicturePrimitive {
}
}
}
PictureKind::BoxShadow { color, .. } => {
request.push(color.premultiplied());
}
}
}
}

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, BorderRadius, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
@ -190,11 +190,6 @@ pub struct PrimitiveMetadata {
#[derive(Debug)]
pub enum BrushKind {
Mask {
clip_mode: ClipMode,
rect: LayerRect,
radii: BorderRadius,
},
Solid {
color: ColorF,
},
@ -248,7 +243,6 @@ impl BrushKind {
BrushKind::RadialGradient { .. } |
BrushKind::LinearGradient { .. } => true,
BrushKind::Mask { .. } |
BrushKind::Clear |
BrushKind::Line { .. } => false,
}
@ -340,27 +334,6 @@ impl BrushPrimitive {
// Opaque black with operator dest out
request.push(PremultipliedColorF::BLACK);
}
BrushKind::Mask { clip_mode, rect, radii } => {
request.push([
clip_mode as u32 as f32,
0.0,
0.0,
0.0
]);
request.push(rect);
request.push([
radii.top_left.width,
radii.top_left.height,
radii.top_right.width,
radii.top_right.height,
]);
request.push([
radii.bottom_right.width,
radii.bottom_right.height,
radii.bottom_left.width,
radii.bottom_left.height,
]);
}
BrushKind::Line { color, wavy_line_thickness, style, orientation } => {
request.push(color);
request.push([
@ -990,7 +963,6 @@ impl PrimitiveStore {
let opacity = match brush.kind {
BrushKind::Clear => PrimitiveOpacity::translucent(),
BrushKind::Solid { ref color } => PrimitiveOpacity::from_alpha(color.a),
BrushKind::Mask { .. } => PrimitiveOpacity::translucent(),
BrushKind::Line { .. } => PrimitiveOpacity::translucent(),
BrushKind::Image { .. } => PrimitiveOpacity::translucent(),
BrushKind::YuvImage { .. } => PrimitiveOpacity::opaque(),
@ -1206,7 +1178,7 @@ impl PrimitiveStore {
// Pass the image opacity, so that the cached render task
// item inherits the same opacity properties.
(target_to_cache_task_id, [0.0; 3], image_properties.descriptor.is_opaque)
(target_to_cache_task_id, image_properties.descriptor.is_opaque)
}
);
}
@ -1288,7 +1260,6 @@ impl PrimitiveStore {
);
}
}
BrushKind::Mask { .. } |
BrushKind::Solid { .. } |
BrushKind::Clear |
BrushKind::Line { .. } |
@ -1410,6 +1381,31 @@ impl PrimitiveStore {
ClipSource::Rectangle(rect) => {
(rect, None, ClipMode::Clip)
}
ClipSource::BoxShadow(ref info) => {
// For inset box shadows, we can clip out any
// pixels that are inside the shadow region
// and are beyond the inner rect, as they can't
// be affected by the blur radius.
let inner_clip_mode = match info.clip_mode {
BoxShadowClipMode::Outset => None,
BoxShadowClipMode::Inset => Some(ClipMode::ClipOut),
};
// Push a region into the segment builder where the
// box-shadow can have an effect on the result. This
// ensures clip-mask tasks get allocated for these
// pixel regions, even if no other clips affect them.
segment_builder.push_mask_region(
info.prim_shadow_rect,
info.prim_shadow_rect.inflate(
-0.5 * info.shadow_rect_alloc_size.width,
-0.5 * info.shadow_rect_alloc_size.height,
),
inner_clip_mode,
);
continue;
}
ClipSource::BorderCorner(..) |
ClipSource::Image(..) => {
// TODO(gw): We can easily extend the segment builder
@ -1439,7 +1435,7 @@ impl PrimitiveStore {
relative_transform.transform_rect(&local_clip_rect)
};
segment_builder.push_rect(local_clip_rect, radius, mode);
segment_builder.push_clip_rect(local_clip_rect, radius, mode);
}
}
@ -1531,6 +1527,10 @@ impl PrimitiveStore {
bounds,
clips.clone(),
prim_run_context.scroll_node.coordinate_system_id,
frame_state.clip_store,
frame_state.gpu_cache,
frame_state.resource_cache,
frame_state.render_tasks,
);
let clip_task_id = frame_state.render_tasks.add(clip_task);
@ -1575,6 +1575,7 @@ impl PrimitiveStore {
prim_clips.update(
frame_state.gpu_cache,
frame_state.resource_cache,
frame_context.device_pixel_scale,
);
let (screen_inner_rect, screen_outer_rect) =
prim_clips.get_screen_bounds(transform, frame_context.device_pixel_scale);
@ -1663,6 +1664,10 @@ impl PrimitiveStore {
combined_outer_rect,
clips,
prim_coordinate_system_id,
frame_state.clip_store,
frame_state.gpu_cache,
frame_state.resource_cache,
frame_state.render_tasks,
);
let clip_task_id = frame_state.render_tasks.add(clip_task);
@ -1716,7 +1721,6 @@ impl PrimitiveStore {
may_need_clip_mask = composite_mode.is_some();
(true, Some(reference_frame_index))
}
PictureKind::BoxShadow { .. } |
PictureKind::TextShadow { .. } => {
(false, None)
}
@ -1756,10 +1760,7 @@ impl PrimitiveStore {
pic.runs = pic_context_for_children.prim_runs;
let metadata = &mut self.cpu_metadata[prim_index.0];
metadata.local_rect = pic.update_local_rect(
metadata.local_rect,
result,
);
metadata.local_rect = pic.update_local_rect(result);
}
let (local_rect, unclipped_device_rect) = {

View File

@ -3,19 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, ImageDescriptor, ImageFormat};
use api::PremultipliedColorF;
use box_shadow::BoxShadowCacheKey;
use clip::ClipWorkItem;
use api::{DeviceSize, PremultipliedColorF};
use box_shadow::{BoxShadowCacheKey};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::CoordinateSystemId;
use device::TextureFilter;
use gpu_cache::{GpuCache, GpuCacheHandle};
use gpu_types::{ImageSource, PictureType};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::{ImageSource, PictureType, RasterizationSpace};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::ContentOrigin;
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use resource_cache::CacheItem;
use resource_cache::{CacheItem, ResourceCache};
use std::{cmp, ops, usize, f32, i32};
use texture_cache::{TextureCache, TextureCacheHandle};
use tiling::{RenderPass, RenderTargetIndex};
@ -154,6 +154,13 @@ pub struct CacheMaskTask {
pub coordinate_system_id: CoordinateSystemId,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipRegionTask {
pub clip_data_address: GpuCacheAddress,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -173,7 +180,6 @@ pub struct BlurTask {
pub blur_std_deviation: f32,
pub target_kind: RenderTargetKind,
pub color: PremultipliedColorF,
pub scale_factor: f32,
pub uv_rect_handle: GpuCacheHandle,
}
@ -182,7 +188,6 @@ impl BlurTask {
fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
pt.add_item(format!("std deviation: {}", self.blur_std_deviation));
pt.add_item(format!("target: {:?}", self.target_kind));
pt.add_item(format!("scale: {}", self.scale_factor));
}
}
@ -219,6 +224,7 @@ pub struct RenderTaskData {
pub enum RenderTaskKind {
Picture(PictureTask),
CacheMask(CacheMaskTask),
ClipRegion(ClipRegionTask),
VerticalBlur(BlurTask),
HorizontalBlur(BlurTask),
Readback(DeviceIntRect),
@ -316,9 +322,79 @@ impl RenderTask {
outer_rect: DeviceIntRect,
clips: Vec<ClipWorkItem>,
prim_coordinate_system_id: CoordinateSystemId,
clip_store: &mut ClipStore,
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
render_tasks: &mut RenderTaskTree,
) -> Self {
let mut children = Vec::new();
// Step through the clip sources that make up this mask. If we find
// any box-shadow clip sources, request that image from the render
// task cache. This allows the blurred box-shadow rect to be cached
// in the texture cache across frames.
// TODO(gw): Consider moving this logic outside this function, especially
// as we add more clip sources that depend on render tasks.
// TODO(gw): If this ever shows up in a profile, we could pre-calculate
// whether a ClipSources contains any box-shadows and skip
// this iteration for the majority of cases.
for clip_item in &clips {
let clip_sources = clip_store.get_opt_mut(&clip_item.clip_sources).expect("bug");
for &mut (ref mut clip, _) in &mut clip_sources.clips {
match *clip {
ClipSource::BoxShadow(ref mut info) => {
let (cache_size, cache_key) = info.cache_key
.as_ref()
.expect("bug: no cache key set")
.clone();
let blur_radius_dp = cache_key.blur_radius_dp as f32;
let clip_data_address = gpu_cache.get_address(&info.clip_data_handle);
// Request a cacheable render task with a blurred, minimal
// sized box-shadow rect.
info.cache_item = resource_cache.request_render_task(
RenderTaskCacheKey {
size: cache_size,
kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
},
gpu_cache,
render_tasks,
|render_tasks| {
// Draw the rounded rect.
let mask_task = RenderTask::new_rounded_rect_mask(
cache_size,
clip_data_address,
);
let mask_task_id = render_tasks.add(mask_task);
// Blur it
let blur_render_task = RenderTask::new_blur(
blur_radius_dp,
mask_task_id,
render_tasks,
RenderTargetKind::Alpha,
ClearMode::Zero,
PremultipliedColorF::TRANSPARENT,
);
let root_task_id = render_tasks.add(blur_render_task);
children.push(root_task_id);
(root_task_id, false)
}
);
}
ClipSource::Rectangle(..) |
ClipSource::RoundedRectangle(..) |
ClipSource::Image(..) |
ClipSource::BorderCorner(..) => {}
}
}
}
RenderTask {
children: Vec::new(),
children,
location: RenderTaskLocation::Dynamic(None, outer_rect.size),
kind: RenderTaskKind::CacheMask(CacheMaskTask {
actual_rect: outer_rect,
@ -330,6 +406,21 @@ impl RenderTask {
}
}
pub fn new_rounded_rect_mask(
size: DeviceIntSize,
clip_data_address: GpuCacheAddress,
) -> Self {
RenderTask {
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, size),
kind: RenderTaskKind::ClipRegion(ClipRegionTask {
clip_data_address,
}),
clear_mode: ClearMode::One,
saved_index: None,
}
}
// Construct a render task to apply a blur to a primitive.
// The render task chain that is constructed looks like:
//
@ -355,7 +446,7 @@ impl RenderTask {
target_kind: RenderTargetKind,
clear_mode: ClearMode,
color: PremultipliedColorF,
) -> (Self, f32) {
) -> Self {
// Adjust large std deviation value.
let mut adjusted_blur_std_deviation = blur_std_deviation;
let blur_target_size = render_tasks[src_task_id].get_dynamic_size();
@ -377,7 +468,6 @@ impl RenderTask {
);
downscaling_src_task_id = render_tasks.add(downscaling_task);
}
scale_factor = blur_target_size.width as f32 / adjusted_blur_target_size.width as f32;
let blur_task_v = RenderTask {
children: vec![downscaling_src_task_id],
@ -386,7 +476,6 @@ impl RenderTask {
blur_std_deviation: adjusted_blur_std_deviation,
target_kind,
color,
scale_factor,
uv_rect_handle: GpuCacheHandle::new(),
}),
clear_mode,
@ -402,14 +491,13 @@ impl RenderTask {
blur_std_deviation: adjusted_blur_std_deviation,
target_kind,
color,
scale_factor,
uv_rect_handle: GpuCacheHandle::new(),
}),
clear_mode,
saved_index: None,
};
(blur_task_h, scale_factor)
blur_task_h
}
pub fn new_scaling(
@ -466,7 +554,17 @@ impl RenderTask {
[
task.actual_rect.origin.x as f32,
task.actual_rect.origin.y as f32,
RasterizationSpace::Screen as i32 as f32,
],
[0.0; 4],
)
}
RenderTaskKind::ClipRegion(..) => {
(
[
0.0,
0.0,
RasterizationSpace::Local as i32 as f32,
],
[0.0; 4],
)
@ -476,7 +574,7 @@ impl RenderTask {
(
[
task.blur_std_deviation,
task.scale_factor,
0.0,
0.0,
],
task.color.to_array()
@ -521,6 +619,7 @@ impl RenderTask {
RenderTaskKind::HorizontalBlur(ref info) => {
&info.uv_rect_handle
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
@ -573,6 +672,7 @@ impl RenderTask {
match self.kind {
RenderTaskKind::Readback(..) => RenderTargetKind::Color,
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) => {
RenderTargetKind::Alpha
}
@ -609,8 +709,15 @@ impl RenderTask {
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Blit(..) => false,
RenderTaskKind::CacheMask(..) => true,
// TODO(gw): For now, we've disabled the shared clip mask
// optimization. It's of dubious value in the
// future once we start to cache clip tasks anyway.
// I have left shared texture support here though,
// just in case we want it in the future.
RenderTaskKind::CacheMask(..) => false,
}
}
@ -631,6 +738,7 @@ impl RenderTask {
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) => {
return;
}
@ -659,6 +767,9 @@ impl RenderTask {
pt.new_level(format!("CacheMask with {} clips", task.clips.len()));
pt.add_item(format!("rect: {:?}", task.actual_rect));
}
RenderTaskKind::ClipRegion(..) => {
pt.new_level("ClipRegion".to_owned());
}
RenderTaskKind::VerticalBlur(ref task) => {
pt.new_level("VerticalBlur".to_owned());
task.print_with(pt);
@ -777,7 +888,7 @@ impl RenderTaskCache {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
mut f: F,
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3], bool) {
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool) {
// Get the texture cache handle for this cache key,
// or create one.
let cache_entry = self.entries
@ -790,7 +901,7 @@ impl RenderTaskCache {
if texture_cache.request(&mut cache_entry.handle, gpu_cache) {
// Invoke user closure to get render task chain
// to draw this into the texture cache.
let (render_task_id, user_data, is_opaque) = f(render_tasks);
let (render_task_id, is_opaque) = f(render_tasks);
let render_task = &mut render_tasks[render_task_id];
// Select the right texture page to allocate from.
@ -825,7 +936,7 @@ impl RenderTaskCache {
descriptor,
TextureFilter::Linear,
None,
user_data,
[0.0; 3],
None,
gpu_cache,
);
@ -849,3 +960,17 @@ impl RenderTaskCache {
texture_cache.get(&cache_entry.handle)
}
}
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
// Note: zero-square tasks are prohibited in WR task tree, so
// we ensure each dimension to be at least the length of 1 after rounding.
pub fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
DeviceIntSize::new(
1.max(size.width.round() as i32),
1.max(size.height.round() as i32),
)
}

View File

@ -113,14 +113,6 @@ const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag {
label: "B_Solid",
color: debug_colors::RED,
};
const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag {
label: "B_Mask",
color: debug_colors::BLACK,
};
const GPU_TAG_BRUSH_PICTURE: GpuProfileTag = GpuProfileTag {
label: "B_Picture",
color: debug_colors::SILVER,
};
const GPU_TAG_BRUSH_LINE: GpuProfileTag = GpuProfileTag {
label: "Line",
color: debug_colors::DARKRED,
@ -221,7 +213,6 @@ impl BatchKind {
BatchKind::SplitComposite => "SplitComposite",
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Picture => "Brush (Picture)",
BrushBatchKind::Solid => "Brush (Solid)",
BrushBatchKind::Line => "Brush (Line)",
BrushBatchKind::Image(..) => "Brush (Image)",
@ -242,7 +233,6 @@ impl BatchKind {
BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE,
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Picture => GPU_TAG_BRUSH_PICTURE,
BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID,
BrushBatchKind::Line => GPU_TAG_BRUSH_LINE,
BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
@ -1602,8 +1592,6 @@ pub struct Renderer {
cs_blur_rgba8: LazilyCompiledShader,
// Brush shaders
brush_mask_rounded_rect: LazilyCompiledShader,
brush_picture: BrushShader,
brush_solid: BrushShader,
brush_line: BrushShader,
brush_image: Vec<Option<BrushShader>>,
@ -1617,6 +1605,7 @@ pub struct Renderer {
/// draw clip instances into the cached clip mask. The results
/// of these shaders are also used by the primitive shaders.
cs_clip_rectangle: LazilyCompiledShader,
cs_clip_box_shadow: LazilyCompiledShader,
cs_clip_image: LazilyCompiledShader,
cs_clip_border: LazilyCompiledShader,
@ -1801,14 +1790,6 @@ impl Renderer {
options.precache_shaders)
};
let brush_mask_rounded_rect = try!{
LazilyCompiledShader::new(ShaderKind::Brush,
"brush_mask_rounded_rect",
&[],
&mut device,
options.precache_shaders)
};
let brush_solid = try!{
BrushShader::new("brush_solid",
&mut device,
@ -1837,13 +1818,6 @@ impl Renderer {
options.precache_shaders)
};
let brush_picture = try!{
BrushShader::new("brush_picture",
&mut device,
&[],
options.precache_shaders)
};
let brush_radial_gradient = try!{
BrushShader::new("brush_radial_gradient",
&mut device,
@ -1890,6 +1864,14 @@ impl Renderer {
options.precache_shaders)
};
let cs_clip_box_shadow = try!{
LazilyCompiledShader::new(ShaderKind::ClipCache,
"cs_clip_box_shadow",
&[],
&mut device,
options.precache_shaders)
};
let cs_clip_image = try!{
LazilyCompiledShader::new(ShaderKind::ClipCache,
"cs_clip_image",
@ -2272,8 +2254,6 @@ impl Renderer {
cs_text_run,
cs_blur_a8,
cs_blur_rgba8,
brush_mask_rounded_rect,
brush_picture,
brush_solid,
brush_line,
brush_image,
@ -2283,6 +2263,7 @@ impl Renderer {
brush_radial_gradient,
brush_linear_gradient,
cs_clip_rectangle,
cs_clip_box_shadow,
cs_clip_border,
cs_clip_image,
ps_text_run,
@ -2514,6 +2495,11 @@ impl Renderer {
"Borders",
target.clip_batcher.borders.len(),
);
debug_target.add(
debug_server::BatchKind::Clip,
"BoxShadows",
target.clip_batcher.box_shadows.len(),
);
debug_target.add(
debug_server::BatchKind::Cache,
"Vertical Blur",
@ -2529,11 +2515,6 @@ impl Renderer {
"Rectangles",
target.clip_batcher.rectangles.len(),
);
debug_target.add(
debug_server::BatchKind::Cache,
"Rectangle Brush (Rounded Rect)",
target.brush_mask_rounded_rects.len(),
);
for (_, items) in target.clip_batcher.images.iter() {
debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len());
}
@ -3202,15 +3183,6 @@ impl Renderer {
&mut self.renderer_errors,
);
}
BrushBatchKind::Picture => {
self.brush_picture.bind(
&mut self.device,
key.blend_mode,
projection,
0,
&mut self.renderer_errors,
);
}
BrushBatchKind::Line => {
self.brush_line.bind(
&mut self.device,
@ -3977,20 +3949,6 @@ impl Renderer {
self.handle_scaling(render_tasks, &target.scalings, SourceTexture::CacheA8);
if !target.brush_mask_rounded_rects.is_empty() {
self.device.set_blend(false);
let _timer = self.gpu_profile.start_timer(GPU_TAG_BRUSH_MASK);
self.brush_mask_rounded_rect
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
self.draw_instanced_batch(
&target.brush_mask_rounded_rects,
VertexArrayKind::Primitive,
&BatchTextures::no_texture(),
stats,
);
}
// Draw the clip items into the tiled alpha mask.
{
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP);
@ -4050,6 +4008,26 @@ impl Renderer {
stats,
);
}
// draw box-shadow clips
for (mask_texture_id, items) in target.clip_batcher.box_shadows.iter() {
let _gm2 = self.gpu_profile.start_marker("box-shadows");
let textures = BatchTextures {
colors: [
mask_texture_id.clone(),
SourceTexture::Invalid,
SourceTexture::Invalid,
],
};
self.cs_clip_box_shadow
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
self.draw_instanced_batch(
items,
VertexArrayKind::Clip,
&textures,
stats,
);
}
// draw image masks
for (mask_texture_id, items) in target.clip_batcher.images.iter() {
let _gm2 = self.gpu_profile.start_marker("clip images");
@ -4683,8 +4661,6 @@ impl Renderer {
self.cs_text_run.deinit(&mut self.device);
self.cs_blur_a8.deinit(&mut self.device);
self.cs_blur_rgba8.deinit(&mut self.device);
self.brush_mask_rounded_rect.deinit(&mut self.device);
self.brush_picture.deinit(&mut self.device);
self.brush_solid.deinit(&mut self.device);
self.brush_line.deinit(&mut self.device);
self.brush_blend.deinit(&mut self.device);
@ -4692,6 +4668,7 @@ impl Renderer {
self.brush_radial_gradient.deinit(&mut self.device);
self.brush_linear_gradient.deinit(&mut self.device);
self.cs_clip_rectangle.deinit(&mut self.device);
self.cs_clip_box_shadow.deinit(&mut self.device);
self.cs_clip_image.deinit(&mut self.device);
self.cs_clip_border.deinit(&mut self.device);
self.ps_text_run.deinit(&mut self.device);

View File

@ -320,7 +320,7 @@ impl ResourceCache {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
f: F,
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3], bool) {
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool) {
self.cached_render_tasks.request_render_task(
key,
&mut self.texture_cache,

View File

@ -144,14 +144,14 @@ impl Event {
#[derive(Debug)]
struct Item {
rect: LayerRect,
mode: ClipMode,
mode: Option<ClipMode>,
flags: ItemFlags,
}
impl Item {
fn new(
rect: LayerRect,
mode: ClipMode,
mode: Option<ClipMode>,
has_mask: bool,
) -> Item {
let flags = if has_mask {
@ -192,15 +192,86 @@ impl SegmentBuilder {
inner_rect,
};
builder.push_rect(local_rect, None, ClipMode::Clip);
builder.push_rect(local_clip_rect, None, ClipMode::Clip);
builder.push_clip_rect(local_rect, None, ClipMode::Clip);
builder.push_clip_rect(local_clip_rect, None, ClipMode::Clip);
builder
}
// Push a region defined by an inner and outer rect where there
// is a mask required. This ensures that segments which intersect
// with these areas will get a clip mask task allocated. This
// is currently used to mark where a box-shadow region can affect
// the pixels of a clip-mask. It might be useful for other types
// such as dashed and dotted borders in the future.
pub fn push_mask_region(
&mut self,
outer_rect: LayerRect,
inner_rect: LayerRect,
inner_clip_mode: Option<ClipMode>,
) {
debug_assert!(outer_rect.contains_rect(&inner_rect));
let p0 = outer_rect.origin;
let p1 = inner_rect.origin;
let p2 = inner_rect.bottom_right();
let p3 = outer_rect.bottom_right();
let segments = &[
LayerRect::new(
LayerPoint::new(p0.x, p0.y),
LayerSize::new(p1.x - p0.x, p1.y - p0.y),
),
LayerRect::new(
LayerPoint::new(p2.x, p0.y),
LayerSize::new(p3.x - p2.x, p1.y - p0.y),
),
LayerRect::new(
LayerPoint::new(p2.x, p2.y),
LayerSize::new(p3.x - p2.x, p3.y - p2.y),
),
LayerRect::new(
LayerPoint::new(p0.x, p2.y),
LayerSize::new(p1.x - p0.x, p3.y - p2.y),
),
LayerRect::new(
LayerPoint::new(p1.x, p0.y),
LayerSize::new(p2.x - p1.x, p1.y - p0.y),
),
LayerRect::new(
LayerPoint::new(p2.x, p1.y),
LayerSize::new(p3.x - p2.x, p2.y - p1.y),
),
LayerRect::new(
LayerPoint::new(p1.x, p2.y),
LayerSize::new(p2.x - p1.x, p3.y - p2.y),
),
LayerRect::new(
LayerPoint::new(p0.x, p1.y),
LayerSize::new(p1.x - p0.x, p2.y - p1.y),
),
];
for segment in segments {
self.items.push(Item::new(
*segment,
None,
true
));
}
if inner_clip_mode.is_some() {
self.items.push(Item::new(
inner_rect,
inner_clip_mode,
false,
));
}
}
// Push some kind of clipping region into the segment builder.
// If radius is None, it's a simple rect.
pub fn push_rect(
pub fn push_clip_rect(
&mut self,
rect: LayerRect,
radius: Option<BorderRadius>,
@ -213,6 +284,7 @@ impl SegmentBuilder {
bounding_rect.intersection(&rect)
});
}
let mode = Some(mode);
match radius {
Some(radius) => {
@ -480,7 +552,7 @@ fn emit_segment_if_needed(
if item.flags.contains(ItemFlags::X_ACTIVE | ItemFlags::Y_ACTIVE) {
has_clip_mask |= item.flags.contains(ItemFlags::HAS_MASK);
if item.mode == ClipMode::ClipOut && !item.flags.contains(ItemFlags::HAS_MASK) {
if item.mode == Some(ClipMode::ClipOut) && !item.flags.contains(ItemFlags::HAS_MASK) {
return None;
}
}
@ -578,7 +650,7 @@ mod test {
);
let mut segments = Vec::new();
for &(rect, radius, mode) in clips {
sb.push_rect(rect, radius, mode);
sb.push_clip_rect(rect, radius, mode);
}
sb.build(|segment| {
segments.push(Segment {

View File

@ -10,15 +10,14 @@ use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree, ClipScrollNodeIndex};
use device::{FrameId, Texture};
use gpu_cache::{GpuCache};
use gpu_types::{BlurDirection, BlurInstance, BrushFlags, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipScrollNodeData, ClipScrollNodeIndex as GPUClipScrollNodeIndex};
use gpu_types::{PrimitiveInstance};
use gpu_types::{BlurDirection, BlurInstance};
use gpu_types::{ClipScrollNodeData};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{PictureKind};
use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushKind, DeferredResolve, EdgeAaSegmentMask};
use prim_store::{DeferredResolve};
use profiler::FrameProfileCounters;
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlitSource, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
use resource_cache::ResourceCache;
use std::{cmp, usize, f32, i32};
@ -404,6 +403,7 @@ impl RenderTarget for ColorRenderTarget {
}
}
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) => {
panic!("Should not be added to color target!");
}
@ -480,7 +480,6 @@ impl RenderTarget for ColorRenderTarget {
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AlphaRenderTarget {
pub clip_batcher: ClipBatcher,
pub brush_mask_rounded_rects: Vec<PrimitiveInstance>,
// List of blur operations to apply for this render target.
pub vertical_blurs: Vec<BlurInstance>,
pub horizontal_blurs: Vec<BlurInstance>,
@ -500,7 +499,6 @@ impl RenderTarget for AlphaRenderTarget {
) -> Self {
AlphaRenderTarget {
clip_batcher: ClipBatcher::new(),
brush_mask_rounded_rects: Vec::new(),
vertical_blurs: Vec::new(),
horizontal_blurs: Vec::new(),
scalings: Vec::new(),
@ -532,6 +530,7 @@ impl RenderTarget for AlphaRenderTarget {
match task.kind {
RenderTaskKind::Readback(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::Blit(..) => {
panic!("BUG: should not be added to alpha target!");
}
@ -553,73 +552,6 @@ impl RenderTarget for AlphaRenderTarget {
render_tasks,
);
}
RenderTaskKind::Picture(ref task_info) => {
let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index);
match prim_metadata.prim_kind {
PrimitiveKind::Picture => {
let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
let task_index = render_tasks.get_task_address(task_id);
for run in &prim.runs {
for i in 0 .. run.count {
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
let sub_metadata = ctx.prim_store.get_metadata(sub_prim_index);
let sub_prim_address =
gpu_cache.get_address(&sub_metadata.gpu_location);
match sub_metadata.prim_kind {
PrimitiveKind::Brush => {
let instance = BrushInstance {
picture_address: task_index,
prim_address: sub_prim_address,
// TODO(gw): In the future, when brush
// primitives on picture backed
// tasks support clip masks and
// transform primitives, these
// will need to be filled out!
clip_chain_rect_index: ClipChainRectIndex(0),
scroll_id: GPUClipScrollNodeIndex(0),
clip_task_address: RenderTaskAddress(0),
z: 0,
segment_index: 0,
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
edge_flags: EdgeAaSegmentMask::empty(),
user_data: [0; 3],
};
let brush = &ctx.prim_store.cpu_brushes[sub_metadata.cpu_prim_index.0];
let batch = match brush.kind {
BrushKind::Solid { .. } |
BrushKind::Clear |
BrushKind::Picture |
BrushKind::Line { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::LinearGradient { .. } |
BrushKind::Image { .. } => {
unreachable!("bug: unexpected brush here");
}
BrushKind::Mask { .. } => {
&mut self.brush_mask_rounded_rects
}
};
batch.push(PrimitiveInstance::from(instance));
}
_ => {
unreachable!("Unexpected sub primitive type");
}
}
}
}
}
_ => {
// No other primitives make use of primitive caching yet!
unreachable!()
}
}
}
RenderTaskKind::CacheMask(ref task_info) => {
let task_address = render_tasks.get_task_address(task_id);
self.clip_batcher.add(
@ -631,6 +563,13 @@ impl RenderTarget for AlphaRenderTarget {
clip_store,
);
}
RenderTaskKind::ClipRegion(ref task) => {
let task_address = render_tasks.get_task_address(task_id);
self.clip_batcher.add_clip_region(
task_address,
task.clip_data_address,
);
}
RenderTaskKind::Scaling(..) => {
self.scalings.push(ScalingInfo {
src_task_id: task.children[0],
@ -704,6 +643,7 @@ impl TextureCacheRenderTarget {
}
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) => {
@ -801,10 +741,9 @@ impl RenderPass {
}
RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => {
let is_shared_alpha = self.tasks.iter().any(|&task_id| {
match render_tasks[task_id].kind {
RenderTaskKind::CacheMask(..) => true,
_ => false,
}
let task = &render_tasks[task_id];
task.is_shared() &&
task.target_kind() == RenderTargetKind::Alpha
});
let saved_color = if self.tasks.iter().any(|&task_id| {
let t = &render_tasks[task_id];

View File

@ -32,6 +32,10 @@ const SHADERS: &[Shader] = &[
name: "cs_clip_image",
features: CLIP_FEATURES,
},
Shader {
name: "cs_clip_box_shadow",
features: CLIP_FEATURES,
},
Shader {
name: "cs_clip_border",
features: CLIP_FEATURES,
@ -83,10 +87,6 @@ const SHADERS: &[Shader] = &[
name: "brush_solid",
features: &[],
},
Shader {
name: "brush_picture",
features: &[],
},
Shader {
name: "brush_blend",
features: &[],

View File

@ -1 +1 @@
0da6c839b3a0e165f1115fb9fe286be7540c24ed
5cb71f0f23719795e7c89417d91a7abad8ac20e9