Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly support tiling clip mask images. #3220

Merged
merged 11 commits into from Oct 23, 2018

Properly support tiling clip mask images.

  • Loading branch information
emilio committed Oct 23, 2018
commit 87c7012c5f6e700d40a176788b478b6bde5af7d3
@@ -4,22 +4,24 @@

#include shared,clip_shared

varying vec3 vLocalPos;
varying vec3 vClipMaskImageUv;
varying vec2 vLocalPos;
varying vec2 vClipMaskImageUv;

flat varying vec4 vClipMaskUvRect;
flat varying vec4 vClipMaskUvInnerRect;
flat varying float vLayer;

#ifdef WR_VERTEX_SHADER
struct ImageMaskData {
RectWithSize local_rect;
RectWithSize local_mask_rect;
RectWithSize local_tile_rect;
};

ImageMaskData fetch_mask_data(ivec2 address) {
vec4 data = fetch_from_gpu_cache_1_direct(address);
RectWithSize local_rect = RectWithSize(data.xy, data.zw);
ImageMaskData mask_data = ImageMaskData(local_rect);
vec4 data[2] = fetch_from_gpu_cache_2_direct(address);
RectWithSize mask_rect = RectWithSize(data[0].xy, data[0].zw);
RectWithSize tile_rect = RectWithSize(data[1].xy, data[1].zw);
ImageMaskData mask_data = ImageMaskData(mask_rect, tile_rect);
return mask_data;
}

@@ -29,7 +31,7 @@ void main(void) {
Transform clip_transform = fetch_transform(cmi.clip_transform_id);
Transform prim_transform = fetch_transform(cmi.prim_transform_id);
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);

ClipVertexInfo vi = write_clip_tile_vertex(
@@ -38,12 +40,9 @@ void main(void) {
clip_transform,
area
);
vLocalPos = vi.local_pos;
vLocalPos = vi.local_pos.xy / vi.local_pos.z;
vLayer = res.layer;

vec2 local_pos = vLocalPos.xy / vLocalPos.z;

vClipMaskImageUv = vec3((local_pos - local_rect.p0) / local_rect.size, 0.0);
vClipMaskImageUv = (vLocalPos - mask.local_tile_rect.p0) / mask.local_tile_rect.size;
vec2 texture_size = vec2(textureSize(sColor0, 0));
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
@@ -54,17 +53,19 @@ void main(void) {

#ifdef WR_FRAGMENT_SHADER
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,
vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
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);
}
#endif
@@ -1949,32 +1949,51 @@ impl ClipBatcher {
resource_address: GpuCacheAddress::invalid(),
};

let gpu_address = gpu_cache.get_address(&clip_node.gpu_cache_handle);

match clip_node.item {
ClipItem::Image(ref mask) => {
if let Ok(cache_item) = resource_cache.get_cached_image(
ImageRequest {
key: mask.image,
rendering: ImageRendering::Auto,
tile: None,
}
) {
ClipItem::Image { ref mask, ref visible_tiles } => {
let request = ImageRequest {
key: mask.image,
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: gpu_address,
clip_data_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;
};

match *visible_tiles {
Some(ref tiles) => {
for tile in tiles {
add_image(
request.with_tile(tile.tile_offset),
gpu_cache.get_address(&tile.handle),
)
}
}
None => {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
add_image(request, gpu_address)
}
}
}
ClipItem::BoxShadow(ref info) => {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
let rt_handle = info
.cache_handle
.as_ref()
@@ -1997,13 +2016,17 @@ impl ClipBatcher {
ClipItem::Rectangle(_, mode) => {
if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) ||
mode == ClipMode::ClipOut {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
..instance
});
}
}
ClipItem::RoundedRectangle(..) => {
let gpu_address =
gpu_cache.get_address(&clip_node.gpu_cache_handle);
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
..instance
@@ -14,9 +14,10 @@ use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, ROOT_SPATIAL_NODE_IND
use ellipse::Ellipse;
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::{BoxShadowStretchMode};
use image::{for_each_tile, for_each_repetition};
use intern;
use internal_types::FastHashSet;
use prim_store::{ClipData, ImageMaskData, SpaceMapper};
use prim_store::{ClipData, ImageMaskData, SpaceMapper, VisibleImageTile};
use render_task::to_cache_size;
use resource_cache::{ImageRequest, ResourceCache};
use std::{cmp, u32};
@@ -145,11 +146,14 @@ impl From<ClipItemKey> for ClipNode {
)
}
ClipItemKey::ImageMask(rect, image, repeat) => {
ClipItem::Image(ImageMask {
image,
rect: LayoutRect::from_au(rect),
repeat,
})
ClipItem::Image {
mask: ImageMask {
image,
rect: LayoutRect::from_au(rect),
repeat,
},
visible_tiles: None,
}
}
ClipItemKey::BoxShadow(shadow_rect, shadow_radius, prim_shadow_rect, blur_radius, clip_mode) => {
ClipItem::new_box_shadow(
@@ -260,14 +264,80 @@ impl ClipNode {
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
device_pixel_scale: DevicePixelScale,
clipped_rect: &LayoutRect,
) {
if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) {
match self.item {
ClipItem::Image(ref mask) => {
let data = ImageMaskData { local_rect: mask.rect };
data.write_gpu_blocks(request);
match self.item {
ClipItem::Image { ref mask, ref mut visible_tiles } => {
let request = ImageRequest {
key: mask.image,
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 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()
};
for_each_repetition(
&mask.rect,
&visible_rect,
&mask.rect.size,
&mut |origin, _| {
let image_rect = LayoutRect {
origin: *origin,
size: mask.rect.size,
};
for_each_tile(
&image_rect,
&visible_rect,
&device_image_size,
tile_size as u32,
&mut |tile_rect, tile_offset, tile_flags| {
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);
}

tiles.push(VisibleImageTile {
tile_offset,
handle,
edge_flags: tile_flags,
local_rect: *tile_rect,
local_clip_rect: visible_rect,
});
},
)
}
);
*visible_tiles = Some(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([
info.shadow_rect_alloc_size.width,
info.shadow_rect_alloc_size.height,
@@ -282,29 +352,7 @@ impl ClipNode {
]);
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) => {
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
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
@@ -335,8 +383,18 @@ impl ClipNode {
data.write(&mut request);
}
}
ClipItem::Rectangle(..) |
ClipItem::RoundedRectangle(..) => {}
ClipItem::Rectangle(rect, mode) => {
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);
}
}
}
}
}
@@ -555,6 +613,7 @@ impl ClipStore {
gpu_cache,
resource_cache,
device_pixel_scale,
&local_bounding_rect,
);

// Calculate some flags that are required for the segment
@@ -580,7 +639,7 @@ impl ClipStore {
needs_mask |= match node.item {
ClipItem::Rectangle(_, ClipMode::ClipOut) |
ClipItem::RoundedRectangle(..) |
ClipItem::Image(..) |
ClipItem::Image { .. } |
ClipItem::BoxShadow(..) => {
true
}
@@ -754,13 +813,13 @@ impl ClipItemKey {
}
}

#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ClipItem {
Rectangle(LayoutRect, ClipMode),
RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
Image(ImageMask),
Image { mask: ImageMask, visible_tiles: Option<Vec<VisibleImageTile>> },
BoxShadow(BoxShadowClipSource),
}

@@ -873,8 +932,8 @@ impl ClipItem {
ClipItem::Rectangle(_, ClipMode::ClipOut) => None,
ClipItem::RoundedRectangle(clip_rect, _, ClipMode::Clip) => Some(clip_rect),
ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) => None,
ClipItem::Image(ref mask) if mask.repeat => None,
ClipItem::Image(ref mask) => Some(mask.rect),
ClipItem::Image { ref mask, .. } if mask.repeat => None,
ClipItem::Image { ref mask, .. } => Some(mask.rect),
ClipItem::BoxShadow(..) => None,
}
}
@@ -895,7 +954,7 @@ impl ClipItem {
}
ClipItem::Rectangle(_, ClipMode::ClipOut) |
ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) |
ClipItem::Image(..) |
ClipItem::Image { .. } |
ClipItem::BoxShadow(..) => {
return ClipResult::Partial
}
@@ -1006,7 +1065,7 @@ impl ClipItem {
}
}
}
ClipItem::Image(ref mask) => {
ClipItem::Image { ref mask, .. } => {
if mask.repeat {
ClipResult::Partial
} else {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.