Bug 1501322 - Update webrender to 9b1a2f7e46cfb3496d43421b828543b08bb45810. r=kats

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
WR Updater Bot 2018-10-23 15:13:53 +00:00
Родитель 2ee98a6df4
Коммит b0e8d7dd2d
10 изменённых файлов: 485 добавлений и 268 удалений

Просмотреть файл

@ -4,8 +4,8 @@
#include shared,clip_shared #include shared,clip_shared
varying vec3 vLocalPos; varying vec2 vLocalPos;
varying vec3 vClipMaskImageUv; varying vec2 vClipMaskImageUv;
flat varying vec4 vClipMaskUvRect; flat varying vec4 vClipMaskUvRect;
flat varying vec4 vClipMaskUvInnerRect; flat varying vec4 vClipMaskUvInnerRect;
@ -13,13 +13,15 @@ flat varying float vLayer;
#ifdef WR_VERTEX_SHADER #ifdef WR_VERTEX_SHADER
struct ImageMaskData { struct ImageMaskData {
RectWithSize local_rect; RectWithSize local_mask_rect;
RectWithSize local_tile_rect;
}; };
ImageMaskData fetch_mask_data(ivec2 address) { ImageMaskData fetch_mask_data(ivec2 address) {
vec4 data = fetch_from_gpu_cache_1_direct(address); vec4 data[2] = fetch_from_gpu_cache_2_direct(address);
RectWithSize local_rect = RectWithSize(data.xy, data.zw); RectWithSize mask_rect = RectWithSize(data[0].xy, data[0].zw);
ImageMaskData mask_data = ImageMaskData(local_rect); RectWithSize tile_rect = RectWithSize(data[1].xy, data[1].zw);
ImageMaskData mask_data = ImageMaskData(mask_rect, tile_rect);
return mask_data; return mask_data;
} }
@ -29,7 +31,7 @@ void main(void) {
Transform clip_transform = fetch_transform(cmi.clip_transform_id); Transform clip_transform = fetch_transform(cmi.clip_transform_id);
Transform prim_transform = fetch_transform(cmi.prim_transform_id); Transform prim_transform = fetch_transform(cmi.prim_transform_id);
ImageMaskData mask = fetch_mask_data(cmi.clip_data_address); ImageMaskData mask = fetch_mask_data(cmi.clip_data_address);
RectWithSize local_rect = mask.local_rect; RectWithSize local_rect = mask.local_mask_rect;
ImageResource res = fetch_image_resource_direct(cmi.resource_address); ImageResource res = fetch_image_resource_direct(cmi.resource_address);
ClipVertexInfo vi = write_clip_tile_vertex( ClipVertexInfo vi = write_clip_tile_vertex(
@ -38,12 +40,9 @@ void main(void) {
clip_transform, clip_transform,
area area
); );
vLocalPos = vi.local_pos; vLocalPos = vi.local_pos.xy / vi.local_pos.z;
vLayer = res.layer; vLayer = res.layer;
vClipMaskImageUv = (vLocalPos - mask.local_tile_rect.p0) / mask.local_tile_rect.size;
vec2 local_pos = vLocalPos.xy / vLocalPos.z;
vClipMaskImageUv = vec3((local_pos - local_rect.p0) / local_rect.size, 0.0);
vec2 texture_size = vec2(textureSize(sColor0, 0)); vec2 texture_size = vec2(textureSize(sColor0, 0));
vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy; vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy;
// applying a half-texel offset to the UV boundaries to prevent linear samples from the outside // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
@ -54,17 +53,19 @@ void main(void) {
#ifdef WR_FRAGMENT_SHADER #ifdef WR_FRAGMENT_SHADER
void main(void) { void main(void) {
vec2 local_pos = vLocalPos.xy / vLocalPos.z; float alpha = init_transform_fs(vLocalPos);
float alpha = init_transform_fs(local_pos); // TODO: Handle repeating masks?
vec2 clamped_mask_uv = clamp(vClipMaskImageUv, vec2(0.0, 0.0), vec2(1.0, 1.0));
// Ensure we don't draw outside of our tile.
// FIXME(emilio): Can we do this earlier?
if (clamped_mask_uv != vClipMaskImageUv)
discard;
bool repeat_mask = false; //TODO
vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskImageUv.xy) :
clamp(vClipMaskImageUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0));
vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy, vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy,
vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw); vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8 float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8
oFragColor = vec4(alpha * clip_alpha, 1.0, 1.0, 1.0); oFragColor = vec4(alpha * clip_alpha, 1.0, 1.0, 1.0);
} }
#endif #endif

Просмотреть файл

@ -1949,37 +1949,51 @@ impl ClipBatcher {
resource_address: GpuCacheAddress::invalid(), resource_address: GpuCacheAddress::invalid(),
}; };
let gpu_address = gpu_cache.get_address(&clip_node.gpu_cache_handle);
match clip_node.item { match clip_node.item {
ClipItem::Image(ref mask, is_valid) => { ClipItem::Image { ref mask, ref visible_tiles } => {
if is_valid { let request = ImageRequest {
if let Ok(cache_item) = resource_cache.get_cached_image( key: mask.image,
ImageRequest { rendering: ImageRendering::Auto,
key: mask.image, tile: None,
rendering: ImageRendering::Auto, };
tile: None, let mut add_image = |request: ImageRequest, clip_data_address: GpuCacheAddress| {
let cache_item = match resource_cache.get_cached_image(request) {
Ok(item) => item,
Err(..) => {
warn!("Warnings: skip a image mask");
debug!("Mask: {:?}, request: {:?}", mask, request);
return;
}
};
self.images
.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
clip_data_address,
resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
..instance
});
};
match *visible_tiles {
Some(ref tiles) => {
for tile in tiles {
add_image(
request.with_tile(tile.tile_offset),
gpu_cache.get_address(&tile.handle),
)
} }
) {
self.images
.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
clip_data_address: gpu_address,
resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
..instance
});
} else {
warn!("Warnings: skip a image mask");
debug!("Key:{:?} Rect::{:?}", mask.image, mask.rect);
continue;
} }
} else { None => {
warn!("Warnings: clip masks that are tiled blobs are not yet supported (#2852)"); let gpu_address =
continue; gpu_cache.get_address(&clip_node.gpu_cache_handle);
add_image(request, gpu_address)
}
} }
} }
ClipItem::BoxShadow(ref info) => { ClipItem::BoxShadow(ref info) => {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
let rt_handle = info let rt_handle = info
.cache_handle .cache_handle
.as_ref() .as_ref()
@ -2002,6 +2016,8 @@ impl ClipBatcher {
ClipItem::Rectangle(_, mode) => { ClipItem::Rectangle(_, mode) => {
if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) || if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) ||
mode == ClipMode::ClipOut { mode == ClipMode::ClipOut {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
self.rectangles.push(ClipMaskInstance { self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address, clip_data_address: gpu_address,
..instance ..instance
@ -2009,6 +2025,8 @@ impl ClipBatcher {
} }
} }
ClipItem::RoundedRectangle(..) => { ClipItem::RoundedRectangle(..) => {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
self.rectangles.push(ClipMaskInstance { self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address, clip_data_address: gpu_address,
..instance ..instance

Просмотреть файл

@ -14,9 +14,10 @@ use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, ROOT_SPATIAL_NODE_IND
use ellipse::Ellipse; use ellipse::Ellipse;
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::{BoxShadowStretchMode}; use gpu_types::{BoxShadowStretchMode};
use image::{self, Repetition};
use intern; use intern;
use internal_types::FastHashSet; use internal_types::FastHashSet;
use prim_store::{ClipData, ImageMaskData, SpaceMapper}; use prim_store::{ClipData, ImageMaskData, SpaceMapper, VisibleMaskImageTile};
use render_task::to_cache_size; use render_task::to_cache_size;
use resource_cache::{ImageRequest, ResourceCache}; use resource_cache::{ImageRequest, ResourceCache};
use std::{cmp, u32}; use std::{cmp, u32};
@ -145,14 +146,14 @@ impl From<ClipItemKey> for ClipNode {
) )
} }
ClipItemKey::ImageMask(rect, image, repeat) => { ClipItemKey::ImageMask(rect, image, repeat) => {
ClipItem::Image( ClipItem::Image {
ImageMask { mask: ImageMask {
image, image,
rect: LayoutRect::from_au(rect), rect: LayoutRect::from_au(rect),
repeat, repeat,
}, },
true, visible_tiles: None,
) }
} }
ClipItemKey::BoxShadow(shadow_rect, shadow_radius, prim_shadow_rect, blur_radius, clip_mode) => { ClipItemKey::BoxShadow(shadow_rect, shadow_radius, prim_shadow_rect, blur_radius, clip_mode) => {
ClipItem::new_box_shadow( ClipItem::new_box_shadow(
@ -263,14 +264,79 @@ impl ClipNode {
gpu_cache: &mut GpuCache, gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache, resource_cache: &mut ResourceCache,
device_pixel_scale: DevicePixelScale, device_pixel_scale: DevicePixelScale,
clipped_rect: &LayoutRect,
) { ) {
if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) { match self.item {
match self.item { ClipItem::Image { ref mask, ref mut visible_tiles } => {
ClipItem::Image(ref mask, ..) => { let request = ImageRequest {
let data = ImageMaskData { local_rect: mask.rect }; key: mask.image,
data.write_gpu_blocks(request); rendering: ImageRendering::Auto,
tile: None,
};
*visible_tiles = None;
if let Some(props) = resource_cache.get_image_properties(mask.image) {
if let Some(tile_size) = props.tiling {
let mut mask_tiles = Vec::new();
let device_image_size = props.descriptor.size;
let visible_rect = if mask.repeat {
*clipped_rect
} else {
clipped_rect.intersection(&mask.rect).unwrap()
};
let repetitions = image::repetitions(
&mask.rect,
&visible_rect,
mask.rect.size,
);
for Repetition { origin, .. } in repetitions {
let image_rect = LayoutRect {
origin,
size: mask.rect.size,
};
let tiles = image::tiles(
&image_rect,
&visible_rect,
&device_image_size,
tile_size as u32,
);
for tile in tiles {
resource_cache.request_image(
request.with_tile(tile.offset),
gpu_cache,
);
let mut handle = GpuCacheHandle::new();
if let Some(request) = gpu_cache.request(&mut handle) {
let data = ImageMaskData {
local_mask_rect: mask.rect,
local_tile_rect: tile.rect,
};
data.write_gpu_blocks(request);
}
mask_tiles.push(VisibleMaskImageTile {
tile_offset: tile.offset,
handle,
});
}
}
*visible_tiles = Some(mask_tiles);
} else {
if let Some(request) = gpu_cache.request(&mut self.gpu_cache_handle) {
let data = ImageMaskData {
local_mask_rect: mask.rect,
local_tile_rect: mask.rect,
};
data.write_gpu_blocks(request);
}
resource_cache.request_image(request, gpu_cache);
}
} }
ClipItem::BoxShadow(ref info) => { }
ClipItem::BoxShadow(ref mut info) => {
if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) {
request.push([ request.push([
info.shadow_rect_alloc_size.width, info.shadow_rect_alloc_size.width,
info.shadow_rect_alloc_size.height, info.shadow_rect_alloc_size.height,
@ -285,39 +351,7 @@ impl ClipNode {
]); ]);
request.push(info.prim_shadow_rect); request.push(info.prim_shadow_rect);
} }
ClipItem::Rectangle(rect, mode) => {
let data = ClipData::uniform(rect, 0.0, mode);
data.write(&mut request);
}
ClipItem::RoundedRectangle(ref rect, ref radius, mode) => {
let data = ClipData::rounded_rect(rect, radius, mode);
data.write(&mut request);
}
}
}
match self.item {
ClipItem::Image(ref mask, ref mut is_valid) => {
if let Some(properties) = resource_cache.get_image_properties(mask.image) {
// Clip masks with tiled blob images are not currently supported.
// This results in them being ignored, which is not ideal, but
// is better than crashing in resource cache!
// See https://github.com/servo/webrender/issues/2852.
*is_valid = properties.tiling.is_none();
}
if *is_valid {
resource_cache.request_image(
ImageRequest {
key: mask.image,
rendering: ImageRendering::Auto,
tile: None,
},
gpu_cache,
);
}
}
ClipItem::BoxShadow(ref mut info) => {
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a // "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius." // Gaussian blur with a standard deviation equal to half the blur radius."
@ -348,8 +382,18 @@ impl ClipNode {
data.write(&mut request); data.write(&mut request);
} }
} }
ClipItem::Rectangle(..) | ClipItem::Rectangle(rect, mode) => {
ClipItem::RoundedRectangle(..) => {} if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) {
let data = ClipData::uniform(rect, 0.0, mode);
data.write(&mut request);
}
}
ClipItem::RoundedRectangle(ref rect, ref radius, mode) => {
if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) {
let data = ClipData::rounded_rect(rect, radius, mode);
data.write(&mut request);
}
}
} }
} }
} }
@ -568,6 +612,7 @@ impl ClipStore {
gpu_cache, gpu_cache,
resource_cache, resource_cache,
device_pixel_scale, device_pixel_scale,
&local_bounding_rect,
); );
// Calculate some flags that are required for the segment // Calculate some flags that are required for the segment
@ -593,7 +638,7 @@ impl ClipStore {
needs_mask |= match node.item { needs_mask |= match node.item {
ClipItem::Rectangle(_, ClipMode::ClipOut) | ClipItem::Rectangle(_, ClipMode::ClipOut) |
ClipItem::RoundedRectangle(..) | ClipItem::RoundedRectangle(..) |
ClipItem::Image(..) | ClipItem::Image { .. } |
ClipItem::BoxShadow(..) => { ClipItem::BoxShadow(..) => {
true true
} }
@ -767,15 +812,13 @@ impl ClipItemKey {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))] #[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ClipItem { pub enum ClipItem {
Rectangle(LayoutRect, ClipMode), Rectangle(LayoutRect, ClipMode),
RoundedRectangle(LayoutRect, BorderRadius, ClipMode), RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
/// The boolean below is a crash workaround for #2852, will be true unless Image { mask: ImageMask, visible_tiles: Option<Vec<VisibleMaskImageTile>> },
/// the mask is a tiled blob.
Image(ImageMask, bool),
BoxShadow(BoxShadowClipSource), BoxShadow(BoxShadowClipSource),
} }
@ -888,8 +931,8 @@ impl ClipItem {
ClipItem::Rectangle(_, ClipMode::ClipOut) => None, ClipItem::Rectangle(_, ClipMode::ClipOut) => None,
ClipItem::RoundedRectangle(clip_rect, _, ClipMode::Clip) => Some(clip_rect), ClipItem::RoundedRectangle(clip_rect, _, ClipMode::Clip) => Some(clip_rect),
ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) => None, ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) => None,
ClipItem::Image(ref mask, ..) if mask.repeat => None, ClipItem::Image { ref mask, .. } if mask.repeat => None,
ClipItem::Image(ref mask, ..) => Some(mask.rect), ClipItem::Image { ref mask, .. } => Some(mask.rect),
ClipItem::BoxShadow(..) => None, ClipItem::BoxShadow(..) => None,
} }
} }
@ -910,7 +953,7 @@ impl ClipItem {
} }
ClipItem::Rectangle(_, ClipMode::ClipOut) | ClipItem::Rectangle(_, ClipMode::ClipOut) |
ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) | ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) |
ClipItem::Image(..) | ClipItem::Image { .. } |
ClipItem::BoxShadow(..) => { ClipItem::BoxShadow(..) => {
return ClipResult::Partial return ClipResult::Partial
} }
@ -1021,7 +1064,7 @@ impl ClipItem {
} }
} }
} }
ClipItem::Image(ref mask, ..) => { ClipItem::Image { ref mask, .. } => {
if mask.repeat { if mask.repeat {
ClipResult::Partial ClipResult::Partial
} else { } else {

Просмотреть файл

@ -39,7 +39,8 @@ impl HitTestClipNode {
ClipItem::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode), ClipItem::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
ClipItem::RoundedRectangle(ref rect, ref radii, ref mode) => ClipItem::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode), HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
ClipItem::Image(ref mask, ..) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip), ClipItem::Image { ref mask, .. } =>
HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
ClipItem::BoxShadow(_) => HitTestRegion::Invalid, ClipItem::BoxShadow(_) => HitTestRegion::Invalid,
}; };

Просмотреть файл

@ -29,18 +29,85 @@ pub fn simplify_repeated_primitive(
} }
} }
pub fn for_each_repetition( pub struct Repetition {
pub origin: LayoutPoint,
pub edge_flags: EdgeAaSegmentMask,
}
pub struct RepetitionIterator {
current_x: i32,
x_count: i32,
current_y: i32,
y_count: i32,
row_flags: EdgeAaSegmentMask,
current_origin: LayoutPoint,
initial_origin: LayoutPoint,
stride: LayoutSize,
}
impl Iterator for RepetitionIterator {
type Item = Repetition;
fn next(&mut self) -> Option<Self::Item> {
if self.current_x == self.x_count {
self.current_y += 1;
if self.current_y >= self.y_count {
return None;
}
self.current_x = 0;
self.row_flags = EdgeAaSegmentMask::empty();
if self.current_y == self.y_count - 1 {
self.row_flags |= EdgeAaSegmentMask::BOTTOM;
}
self.current_origin.x = self.initial_origin.x;
self.current_origin.y += self.stride.height;
}
let mut edge_flags = self.row_flags;
if self.current_x == 0 {
edge_flags |= EdgeAaSegmentMask::LEFT;
}
if self.current_x == self.x_count - 1 {
edge_flags |= EdgeAaSegmentMask::RIGHT;
}
let repetition = Repetition {
origin: self.current_origin,
edge_flags: edge_flags,
};
self.current_origin.x += self.stride.width;
self.current_x += 1;
Some(repetition)
}
}
pub fn repetitions(
prim_rect: &LayoutRect, prim_rect: &LayoutRect,
visible_rect: &LayoutRect, visible_rect: &LayoutRect,
stride: &LayoutSize, stride: LayoutSize,
callback: &mut FnMut(&LayoutPoint, EdgeAaSegmentMask), ) -> RepetitionIterator {
) {
assert!(stride.width > 0.0); assert!(stride.width > 0.0);
assert!(stride.height > 0.0); assert!(stride.height > 0.0);
let visible_rect = match prim_rect.intersection(&visible_rect) { let visible_rect = match prim_rect.intersection(&visible_rect) {
Some(rect) => rect, Some(rect) => rect,
None => return, None => {
return RepetitionIterator {
current_origin: LayoutPoint::zero(),
initial_origin: LayoutPoint::zero(),
current_x: 0,
current_y: 0,
x_count: 0,
y_count: 0,
stride,
row_flags: EdgeAaSegmentMask::empty(),
}
}
}; };
let nx = if visible_rect.origin.x > prim_rect.origin.x { let nx = if visible_rect.origin.x > prim_rect.origin.x {
@ -58,49 +125,109 @@ pub fn for_each_repetition(
let x0 = prim_rect.origin.x + nx * stride.width; let x0 = prim_rect.origin.x + nx * stride.width;
let y0 = prim_rect.origin.y + ny * stride.height; let y0 = prim_rect.origin.y + ny * stride.height;
let mut p = LayoutPoint::new(x0, y0);
let x_most = visible_rect.max_x(); let x_most = visible_rect.max_x();
let y_most = visible_rect.max_y(); let y_most = visible_rect.max_y();
let x_count = f32::ceil((x_most - x0) / stride.width) as i32; let x_count = f32::ceil((x_most - x0) / stride.width) as i32;
let y_count = f32::ceil((y_most - y0) / stride.height) as i32; let y_count = f32::ceil((y_most - y0) / stride.height) as i32;
for y in 0..y_count { let mut row_flags = EdgeAaSegmentMask::TOP;
let mut row_flags = EdgeAaSegmentMask::empty(); if y_count == 1 {
if y == 0 { row_flags |= EdgeAaSegmentMask::BOTTOM;
row_flags |= EdgeAaSegmentMask::TOP; }
}
if y == y_count - 1 {
row_flags |= EdgeAaSegmentMask::BOTTOM;
}
for x in 0..x_count { RepetitionIterator {
let mut edge_flags = row_flags; current_origin: LayoutPoint::new(x0, y0),
if x == 0 { initial_origin: LayoutPoint::new(x0, y0),
edge_flags |= EdgeAaSegmentMask::LEFT; current_x: 0,
} current_y: 0,
if x == x_count - 1 { x_count,
edge_flags |= EdgeAaSegmentMask::RIGHT; y_count,
} row_flags,
stride,
callback(&p, edge_flags);
p.x += stride.width;
}
p.x = x0;
p.y += stride.height;
} }
} }
pub fn for_each_tile( #[derive(Debug)]
pub struct Tile {
pub rect: LayoutRect,
pub offset: TileOffset,
pub edge_flags: EdgeAaSegmentMask,
}
pub struct TileIterator {
current_x: u16,
x_count: u16,
current_y: u16,
y_count: u16,
origin: TileOffset,
tile_size: LayoutSize,
leftover_offset: TileOffset,
leftover_size: LayoutSize,
local_origin: LayoutPoint,
row_flags: EdgeAaSegmentMask,
}
impl Iterator for TileIterator {
type Item = Tile;
fn next(&mut self) -> Option<Self::Item> {
if self.current_x == self.x_count {
self.current_y += 1;
if self.current_y >= self.y_count {
return None;
}
self.current_x = 0;
self.row_flags = EdgeAaSegmentMask::empty();
if self.current_y == self.y_count - 1 {
self.row_flags |= EdgeAaSegmentMask::BOTTOM;
}
}
let tile_offset = self.origin + vec2(self.current_x, self.current_y);
let mut segment_rect = LayoutRect {
origin: LayoutPoint::new(
self.local_origin.x + tile_offset.x as f32 * self.tile_size.width,
self.local_origin.y + tile_offset.y as f32 * self.tile_size.height,
),
size: self.tile_size,
};
if tile_offset.x == self.leftover_offset.x {
segment_rect.size.width = self.leftover_size.width;
}
if tile_offset.y == self.leftover_offset.y {
segment_rect.size.height = self.leftover_size.height;
}
let mut edge_flags = self.row_flags;
if self.current_x == 0 {
edge_flags |= EdgeAaSegmentMask::LEFT;
}
if self.current_x == self.x_count - 1 {
edge_flags |= EdgeAaSegmentMask::RIGHT;
}
let tile = Tile {
rect: segment_rect,
offset: tile_offset,
edge_flags,
};
self.current_x += 1;
Some(tile)
}
}
pub fn tiles(
prim_rect: &LayoutRect, prim_rect: &LayoutRect,
visible_rect: &LayoutRect, visible_rect: &LayoutRect,
device_image_size: &DeviceUintSize, device_image_size: &DeviceUintSize,
device_tile_size: u32, device_tile_size: u32,
callback: &mut FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask), ) -> TileIterator {
) {
// The image resource is tiled. We have to generate an image primitive // The image resource is tiled. We have to generate an image primitive
// for each tile. // for each tile.
// We need to do this because the image is broken up into smaller tiles in the texture // We need to do this because the image is broken up into smaller tiles in the texture
@ -130,8 +257,21 @@ pub fn for_each_tile(
// the image in layer space intead of iterating over device tiles. // the image in layer space intead of iterating over device tiles.
let visible_rect = match prim_rect.intersection(&visible_rect) { let visible_rect = match prim_rect.intersection(&visible_rect) {
Some(rect) => rect, Some(rect) => rect,
None => return, None => {
return TileIterator {
current_x: 0,
current_y: 0,
x_count: 0,
y_count: 0,
row_flags: EdgeAaSegmentMask::empty(),
origin: TileOffset::zero(),
tile_size: LayoutSize::zero(),
leftover_offset: TileOffset::zero(),
leftover_size: LayoutSize::zero(),
local_origin: LayoutPoint::zero(),
}
}
}; };
let device_tile_size_f32 = device_tile_size as f32; let device_tile_size_f32 = device_tile_size as f32;
@ -183,46 +323,21 @@ pub fn for_each_tile(
let x_count = f32::ceil((visible_rect.max_x() - prim_rect.origin.x) / layer_tile_size.width) as u16 - t0.x; let x_count = f32::ceil((visible_rect.max_x() - prim_rect.origin.x) / layer_tile_size.width) as u16 - t0.x;
let y_count = f32::ceil((visible_rect.max_y() - prim_rect.origin.y) / layer_tile_size.height) as u16 - t0.y; let y_count = f32::ceil((visible_rect.max_y() - prim_rect.origin.y) / layer_tile_size.height) as u16 - t0.y;
for y in 0..y_count { let mut row_flags = EdgeAaSegmentMask::TOP;
if y_count == 1 {
let mut row_flags = EdgeAaSegmentMask::empty(); row_flags |= EdgeAaSegmentMask::BOTTOM;
if y == 0 { }
row_flags |= EdgeAaSegmentMask::TOP; TileIterator {
} current_x: 0,
if y == y_count - 1 { current_y: 0,
row_flags |= EdgeAaSegmentMask::BOTTOM; x_count,
} y_count,
row_flags,
for x in 0..x_count { origin: t0,
let tile_offset = t0 + vec2(x, y); tile_size: layer_tile_size,
leftover_offset,
leftover_size: leftover_layer_size,
let mut segment_rect = LayoutRect { local_origin: prim_rect.origin,
origin: LayoutPoint::new(
prim_rect.origin.x + tile_offset.x as f32 * layer_tile_size.width,
prim_rect.origin.y + tile_offset.y as f32 * layer_tile_size.height,
),
size: layer_tile_size,
};
if tile_offset.x == leftover_offset.x {
segment_rect.size.width = leftover_layer_size.width;
}
if tile_offset.y == leftover_offset.y {
segment_rect.size.height = leftover_layer_size.height;
}
let mut edge_flags = row_flags;
if x == 0 {
edge_flags |= EdgeAaSegmentMask::LEFT;
}
if x == x_count - 1 {
edge_flags |= EdgeAaSegmentMask::RIGHT;
}
callback(&segment_rect, tile_offset, edge_flags);
}
} }
} }
@ -252,7 +367,7 @@ pub fn compute_tile_range(
pub fn for_each_tile_in_range( pub fn for_each_tile_in_range(
range: &TileRange, range: &TileRange,
callback: &mut FnMut(TileOffset), mut callback: impl FnMut(TileOffset),
) { ) {
for y in 0..range.size.height { for y in 0..range.size.height {
for x in 0..range.size.width { for x in 0..range.size.width {
@ -277,20 +392,20 @@ mod tests {
callback: &mut FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask), callback: &mut FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask),
) { ) {
let mut coverage = LayoutRect::zero(); let mut coverage = LayoutRect::zero();
let mut tiles = HashSet::new(); let mut seen_tiles = HashSet::new();
for_each_tile(prim_rect, for tile in tiles(
visible_rect, prim_rect,
device_image_size, visible_rect,
device_tile_size, device_image_size,
&mut |tile_rect, tile_offset, tile_flags| { device_tile_size,
// make sure we don't get sent duplicate tiles ) {
assert!(!tiles.contains(&tile_offset)); // make sure we don't get sent duplicate tiles
tiles.insert(tile_offset); assert!(!seen_tiles.contains(&tile.offset));
coverage = coverage.union(tile_rect); seen_tiles.insert(tile.offset);
assert!(prim_rect.contains_rect(&tile_rect)); coverage = coverage.union(&tile.rect);
callback(tile_rect, tile_offset, tile_flags); assert!(prim_rect.contains_rect(&tile.rect));
}, callback(&tile.rect, tile.offset, tile.edge_flags);
); }
assert!(prim_rect.contains_rect(&coverage)); assert!(prim_rect.contains_rect(&coverage));
assert!(coverage.contains_rect(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero()))); assert!(coverage.contains_rect(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero())));
} }

Просмотреть файл

@ -20,7 +20,7 @@ use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks}; ToGpuBlocks};
use gpu_types::BrushFlags; use gpu_types::BrushFlags;
use image::{for_each_tile, for_each_repetition}; use image::{self, Repetition};
use intern; use intern;
use picture::{PictureCompositeMode, PicturePrimitive}; use picture::{PictureCompositeMode, PicturePrimitive};
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -359,6 +359,8 @@ impl OpacityBinding {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct VisibleImageTile { pub struct VisibleImageTile {
pub tile_offset: TileOffset, pub tile_offset: TileOffset,
pub handle: GpuCacheHandle, pub handle: GpuCacheHandle,
@ -367,6 +369,14 @@ pub struct VisibleImageTile {
pub local_clip_rect: LayoutRect, pub local_clip_rect: LayoutRect,
} }
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct VisibleMaskImageTile {
pub tile_offset: TileOffset,
pub handle: GpuCacheHandle,
}
#[derive(Debug)] #[derive(Debug)]
pub struct VisibleGradientTile { pub struct VisibleGradientTile {
pub handle: GpuCacheHandle, pub handle: GpuCacheHandle,
@ -539,6 +549,8 @@ bitflags! {
/// ///
/// *Note*: the bit values have to match the shader logic in /// *Note*: the bit values have to match the shader logic in
/// `write_transform_vertex()` function. /// `write_transform_vertex()` function.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct EdgeAaSegmentMask: u8 { pub struct EdgeAaSegmentMask: u8 {
const LEFT = 0x1; const LEFT = 0x1;
const TOP = 0x2; const TOP = 0x2;
@ -1262,12 +1274,16 @@ impl ClipCorner {
#[derive(Debug)] #[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct ImageMaskData { pub struct ImageMaskData {
pub local_rect: LayoutRect, /// The local rect of the whole masked area.
pub local_mask_rect: LayoutRect,
/// The local rect of an individual tile.
pub local_tile_rect: LayoutRect,
} }
impl ToGpuBlocks for ImageMaskData { impl ToGpuBlocks for ImageMaskData {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) { fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.local_rect); request.push(self.local_mask_rect);
request.push(self.local_tile_rect);
} }
} }
@ -2159,28 +2175,24 @@ fn decompose_repeated_primitive(
); );
let stride = *stretch_size + *tile_spacing; let stride = *stretch_size + *tile_spacing;
for_each_repetition( let repetitions = image::repetitions(prim_local_rect, &visible_rect, stride);
prim_local_rect, for Repetition { origin, .. } in repetitions {
&visible_rect, let mut handle = GpuCacheHandle::new();
&stride, let rect = LayoutRect {
&mut |origin, _| { origin: origin,
size: *stretch_size,
};
let mut handle = GpuCacheHandle::new(); if let Some(request) = frame_state.gpu_cache.request(&mut handle) {
let rect = LayoutRect { callback(&rect, request);
origin: *origin,
size: *stretch_size,
};
if let Some(request) = frame_state.gpu_cache.request(&mut handle) {
callback(&rect, request);
}
visible_tiles.push(VisibleGradientTile {
local_rect: rect,
local_clip_rect: tight_clip_rect,
handle
});
} }
);
visible_tiles.push(VisibleGradientTile {
local_rect: rect,
local_clip_rect: tight_clip_rect,
handle
});
}
if visible_tiles.is_empty() { if visible_tiles.is_empty() {
// At this point if we don't have tiles to show it means we could probably // At this point if we don't have tiles to show it means we could probably
@ -2329,7 +2341,7 @@ impl BrushPrimitive {
continue; continue;
} }
ClipItem::Image(..) => { ClipItem::Image { .. } => {
rect_clips_only = false; rect_clips_only = false;
continue; continue;
} }
@ -2686,50 +2698,51 @@ impl Primitive {
visible_tiles.clear(); visible_tiles.clear();
for_each_repetition( let repetitions = image::repetitions(
&self.local_rect, &self.local_rect,
&visible_rect, &visible_rect,
&stride, stride,
&mut |origin, edge_flags| {
let edge_flags = base_edge_flags | edge_flags;
let image_rect = LayoutRect {
origin: *origin,
size: stretch_size,
};
for_each_tile(
&image_rect,
&visible_rect,
&device_image_size,
tile_size as u32,
&mut |tile_rect, tile_offset, tile_flags| {
frame_state.resource_cache.request_image(
request.with_tile(tile_offset),
frame_state.gpu_cache,
);
let mut handle = GpuCacheHandle::new();
if let Some(mut request) = frame_state.gpu_cache.request(&mut handle) {
request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([tile_rect.size.width, tile_rect.size.height, 0.0, 0.0]);
request.write_segment(*tile_rect, [0.0; 4]);
}
visible_tiles.push(VisibleImageTile {
tile_offset,
handle,
edge_flags: tile_flags & edge_flags,
local_rect: *tile_rect,
local_clip_rect: tight_clip_rect,
});
}
);
}
); );
for Repetition { origin, edge_flags } in repetitions {
let edge_flags = base_edge_flags | edge_flags;
let image_rect = LayoutRect {
origin,
size: stretch_size,
};
let tiles = image::tiles(
&image_rect,
&visible_rect,
&device_image_size,
tile_size as u32,
);
for tile in tiles {
frame_state.resource_cache.request_image(
request.with_tile(tile.offset),
frame_state.gpu_cache,
);
let mut handle = GpuCacheHandle::new();
if let Some(mut request) = frame_state.gpu_cache.request(&mut handle) {
request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([tile.rect.size.width, tile.rect.size.height, 0.0, 0.0]);
request.write_segment(tile.rect, [0.0; 4]);
}
visible_tiles.push(VisibleImageTile {
tile_offset: tile.offset,
handle,
edge_flags: tile.edge_flags & edge_flags,
local_rect: tile.rect,
local_clip_rect: tight_clip_rect,
});
}
}
if visible_tiles.is_empty() { if visible_tiles.is_empty() {
// At this point if we don't have tiles to show it means we could probably // At this point if we don't have tiles to show it means we could probably
// have done a better a job at culling during an earlier stage. // have done a better a job at culling during an earlier stage.

Просмотреть файл

@ -554,7 +554,7 @@ impl RenderTask {
} }
ClipItem::Rectangle(..) | ClipItem::Rectangle(..) |
ClipItem::RoundedRectangle(..) | ClipItem::RoundedRectangle(..) |
ClipItem::Image(..) => {} ClipItem::Image { .. } => {}
} }
} }

Просмотреть файл

@ -1082,7 +1082,7 @@ impl ResourceCache {
} }
} }
for_each_tile_in_range(&tiles, &mut|tile| { for_each_tile_in_range(&tiles, |tile| {
let descriptor = BlobImageDescriptor { let descriptor = BlobImageDescriptor {
offset: DevicePoint::new( offset: DevicePoint::new(
tile.x as f32 * tile_size as f32, tile.x as f32 * tile_size as f32,

Просмотреть файл

@ -1 +1 @@
838ce479e6ef8eed44d68e5d283649d0963152b6 9b1a2f7e46cfb3496d43421b828543b08bb45810

Просмотреть файл

@ -72,12 +72,18 @@ fn broadcast<T: Clone>(base_vals: &[T], num_items: usize) -> Vec<T> {
vals vals
} }
enum CheckerboardKind {
BlackGrey,
BlackTransparent,
}
fn generate_checkerboard_image( fn generate_checkerboard_image(
border: u32, border: u32,
tile_x_size: u32, tile_x_size: u32,
tile_y_size: u32, tile_y_size: u32,
tile_x_count: u32, tile_x_count: u32,
tile_y_count: u32, tile_y_count: u32,
kind: CheckerboardKind,
) -> (ImageDescriptor, ImageData) { ) -> (ImageDescriptor, ImageData) {
let width = 2 * border + tile_x_size * tile_x_count; let width = 2 * border + tile_x_size * tile_x_count;
let height = 2 * border + tile_y_size * tile_y_count; let height = 2 * border + tile_y_size * tile_y_count;
@ -94,11 +100,22 @@ fn generate_checkerboard_image(
} else { } else {
let xon = ((x - border) % (2 * tile_x_size)) < tile_x_size; let xon = ((x - border) % (2 * tile_x_size)) < tile_x_size;
let yon = ((y - border) % (2 * tile_y_size)) < tile_y_size; let yon = ((y - border) % (2 * tile_y_size)) < tile_y_size;
let value = if xon ^ yon { 0xff } else { 0x7f }; match kind {
pixels.push(value); CheckerboardKind::BlackGrey => {
pixels.push(value); let value = if xon ^ yon { 0xff } else { 0x7f };
pixels.push(value); pixels.push(value);
pixels.push(0xff); pixels.push(value);
pixels.push(value);
pixels.push(0xff);
}
CheckerboardKind::BlackTransparent => {
let value = if xon ^ yon { 0xff } else { 0x00 };
pixels.push(value);
pixels.push(value);
pixels.push(value);
pixels.push(value);
}
}
} }
} }
} }
@ -405,10 +422,10 @@ impl YamlFrameReader {
} }
pub fn add_or_get_image( pub fn add_or_get_image(
&mut self, &mut self,
file: &Path, file: &Path,
tiling: Option<i64>, tiling: Option<i64>,
wrench: &mut Wrench, wrench: &mut Wrench,
) -> (ImageKey, LayoutSize) { ) -> (ImageKey, LayoutSize) {
let key = (file.to_owned(), tiling); let key = (file.to_owned(), tiling);
if let Some(k) = self.image_map.get(&key) { if let Some(k) = self.image_map.get(&key) {
@ -476,7 +493,8 @@ impl YamlFrameReader {
args.get(4).unwrap_or(&"1000").parse::<u32>().unwrap(), args.get(4).unwrap_or(&"1000").parse::<u32>().unwrap(),
args.get(5).unwrap_or(&"1000").parse::<u32>().unwrap(), args.get(5).unwrap_or(&"1000").parse::<u32>().unwrap(),
), ),
("checkerboard", args, _) => { (name @ "transparent-checkerboard", args, _) |
(name @ "checkerboard", args, _) => {
let border = args.get(0).unwrap_or(&"4").parse::<u32>().unwrap(); let border = args.get(0).unwrap_or(&"4").parse::<u32>().unwrap();
let (x_size, y_size, x_count, y_count) = match args.len() { let (x_size, y_size, x_count, y_count) = match args.len() {
@ -497,12 +515,19 @@ impl YamlFrameReader {
} }
}; };
let kind = if name == "transparent-checkerboard" {
CheckerboardKind::BlackTransparent
} else {
CheckerboardKind::BlackGrey
};
generate_checkerboard_image( generate_checkerboard_image(
border, border,
x_size, x_size,
y_size, y_size,
x_count, x_count,
y_count, y_count,
kind,
) )
} }
_ => { _ => {
@ -600,8 +625,9 @@ impl YamlFrameReader {
} }
}; };
let tiling = item["tile-size"].as_i64();
let (image_key, image_dims) = let (image_key, image_dims) =
self.add_or_get_image(&file, None, wrench); self.add_or_get_image(&file, tiling, wrench);
let image_rect = item["rect"] let image_rect = item["rect"]
.as_rect() .as_rect()
.unwrap_or(LayoutRect::new(LayoutPoint::zero(), image_dims)); .unwrap_or(LayoutRect::new(LayoutPoint::zero(), image_dims));