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

Sync changes from mozilla-central gfx/wr #3848

Merged
merged 6 commits into from Feb 4, 2020
@@ -9,41 +9,37 @@
#define LINE_STYLE_DASHED 2
#define LINE_STYLE_WAVY 3

// Local space position
// Fragment position in the coordinate system used for positioning decorations.
// To keep the code independent of whether the line is horizontal or vertical,
// vLocalPos.x is always parallel, and .y always perpendicular, to the line
// being decorated.
varying vec2 vLocalPos;

flat varying float vAxisSelect;
flat varying int vStyle;
flat varying vec4 vParams;

#ifdef WR_VERTEX_SHADER

#define LINE_ORIENTATION_VERTICAL 0
#define LINE_ORIENTATION_HORIZONTAL 1

// The size of the mask tile we're rendering, in pixels.
in vec4 aTaskRect;

// The size of the mask tile. aLocalSize.x is always horizontal and .y vertical,
// regardless of the line's orientation. The size is chosen by
// prim_store::get_line_decoration_sizes.
in vec2 aLocalSize;

// A LINE_STYLE_* value, indicating what sort of line to draw.
in int aStyle;
in int aOrientation;
in float aWavyLineThickness;

void main(void) {
vec2 size;
// 0.0 for a horizontal line, 1.0 for a vertical line.
in float aAxisSelect;

switch (aOrientation) {
case LINE_ORIENTATION_HORIZONTAL:
vAxisSelect = 0.0;
size = aLocalSize;
break;
case LINE_ORIENTATION_VERTICAL:
vAxisSelect = 1.0;
size = aLocalSize.yx;
break;
default:
vAxisSelect = 0.0;
size = vec2(0.0);
}
// The thickness of the wavy line itself, not the amplitude of the waves (i.e.,
// the thickness of the final decorated line).
in float aWavyLineThickness;

void main(void) {
vec2 size = mix(aLocalSize, aLocalSize.yx, aAxisSelect);
vStyle = aStyle;

switch (vStyle) {
@@ -86,7 +82,7 @@ void main(void) {
vParams = vec4(0.0);
}

vLocalPos = aPosition.xy * aLocalSize;
vLocalPos = mix(aPosition.xy, aPosition.yx, aAxisSelect) * size;

gl_Position = uTransform * vec4(aTaskRect.xy + aTaskRect.zw * aPosition.xy, 0.0, 1.0);
}
@@ -98,13 +94,10 @@ void main(void) {

void main(void) {
// Find the appropriate distance to apply the step over.
vec2 local_pos = vLocalPos;
float aa_range = compute_aa_range(local_pos);
vec2 pos = vLocalPos;
float aa_range = compute_aa_range(pos);
float alpha = 1.0;

// Select the x/y coord, depending on which axis this edge is.
vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);

switch (vStyle) {
case LINE_STYLE_SOLID: {
break;
@@ -1212,15 +1212,12 @@ impl BatchBuilder {
ctx.screen_world_rect,
ctx.spatial_tree,
);
let local_tile_clip_rect = LayoutRect::from_untyped(&tile_cache.local_rect.to_untyped());
let local_tile_clip_rect = match local_tile_clip_rect.intersection(&prim_info.combined_local_clip_rect) {
Some(rect) => rect,
None => {
return;
}
};
// TODO(gw): As a follow up to the valid_rect work, see why we use
// prim_info.combined_local_clip_rect here instead of the
// local_clip_rect built in the TileCacheInstance. Perhaps
// these can be unified or are different for a good reason?
let world_clip_rect = map_local_to_world
.map(&local_tile_clip_rect)
.map(&prim_info.combined_local_clip_rect)
.expect("bug: unable to map clip rect");
let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round();
let z_id = composite_state.z_generator.next();
@@ -66,6 +66,7 @@ pub struct CompositeTile {
pub rect: DeviceRect,
pub clip_rect: DeviceRect,
pub dirty_rect: DeviceRect,
pub valid_rect: DeviceRect,
pub z_id: ZBufferId,
pub tile_id: TileId,
}
@@ -303,6 +304,14 @@ impl CompositeState {
) {
let mut visible_tile_count = 0;

// TODO(gw): For now, we apply the valid rect as part of the clip rect
// during native compositing. This works for the initial
// implementation, since the valid rect is determined only
// by the bounding rect of the picture cache slice. When
// we implement proper per-tile valid rects, we will need to
// supply the valid rect directly to the compositor interface.
let mut combined_valid_rect = DeviceRect::zero();

for key in &tile_cache.tiles_to_draw {
let tile = &tile_cache.tiles[key];
if !tile.is_visible {
@@ -314,6 +323,15 @@ impl CompositeState {

let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round();
let dirty_rect = (tile.world_dirty_rect * global_device_pixel_scale).round();
// The device rect is guaranteed to be aligned on a device pixel - the round
// above is just to deal with float accuracy. However, the valid rect is not
// always aligned to a device pixel. To handle this, round out to get all
// required pixels, and intersect with the tile device rect.
let valid_rect = (tile.world_valid_rect * global_device_pixel_scale)
.round_out()
.intersection(&device_rect)
.unwrap_or_else(DeviceRect::zero);
combined_valid_rect = combined_valid_rect.union(&valid_rect);
let surface = tile.surface.as_ref().expect("no tile surface set!");

let (surface, is_opaque) = match surface {
@@ -335,6 +353,7 @@ impl CompositeState {
let tile = CompositeTile {
surface,
rect: device_rect,
valid_rect,
dirty_rect,
clip_rect: device_clip_rect,
z_id,
@@ -345,14 +364,16 @@ impl CompositeState {
}

if visible_tile_count > 0 {
self.descriptor.surfaces.push(
CompositeSurfaceDescriptor {
slice: tile_cache.slice,
surface_id: tile_cache.native_surface_id,
offset: tile_cache.device_position,
clip_rect: device_clip_rect,
}
);
if let Some(clip_rect) = device_clip_rect.intersection(&combined_valid_rect) {
self.descriptor.surfaces.push(
CompositeSurfaceDescriptor {
slice: tile_cache.slice,
surface_id: tile_cache.native_surface_id,
offset: tile_cache.device_position,
clip_rect,
}
);
}
}
}

@@ -406,13 +406,13 @@ struct TilePreUpdateContext {

/// The visible part of the screen in world coords.
global_screen_world_rect: WorldRect,
}

// Immutable context passed to picture cache tiles during post_update
struct TilePostUpdateContext<'a> {
/// The local rect of the overall picture cache
local_rect: PictureRect,
}

// Immutable context passed to picture cache tiles during post_update
struct TilePostUpdateContext<'a> {
/// The local clip rect (in picture space) of the entire picture cache
local_clip_rect: PictureRect,

@@ -667,6 +667,10 @@ pub struct Tile {
/// TODO(gw): We have multiple dirty rects available due to the quadtree above. In future,
/// expose these as multiple dirty rects, which will help in some cases.
pub world_dirty_rect: WorldRect,
/// Picture space rect that contains valid pixels region of this tile.
local_valid_rect: PictureRect,
/// World space rect that contains valid pixels region of this tile.
pub world_valid_rect: WorldRect,
/// Uniquely describes the content of this tile, in a way that can be
/// (reasonably) efficiently hashed and compared.
pub current_descriptor: TileDescriptor,
@@ -707,6 +711,8 @@ impl Tile {
Tile {
local_tile_rect: PictureRect::zero(),
world_tile_rect: WorldRect::zero(),
local_valid_rect: PictureRect::zero(),
world_valid_rect: WorldRect::zero(),
local_dirty_rect: PictureRect::zero(),
world_dirty_rect: WorldRect::zero(),
surface: None,
@@ -819,12 +825,17 @@ impl Tile {
ctx: &TilePreUpdateContext,
) {
self.local_tile_rect = local_tile_rect;
self.local_valid_rect = local_tile_rect.intersection(&ctx.local_rect).unwrap();
self.invalidation_reason = None;

self.world_tile_rect = ctx.pic_to_world_mapper
.map(&self.local_tile_rect)
.expect("bug: map local tile rect");

self.world_valid_rect = ctx.pic_to_world_mapper
.map(&self.local_valid_rect)
.expect("bug: map local valid rect");

// Check if this tile is currently on screen.
self.is_visible = self.world_tile_rect.intersects(&ctx.global_screen_world_rect);

@@ -975,9 +986,8 @@ impl Tile {
// Check if this tile can be considered opaque. Opacity state must be updated only
// after all early out checks have been performed. Otherwise, we might miss updating
// the native surface next time this tile becomes visible.
let clipped_rect = self.local_tile_rect
.intersection(&ctx.local_rect)
.and_then(|r| r.intersection(&ctx.local_clip_rect))
let clipped_rect = self.local_valid_rect
.intersection(&ctx.local_clip_rect)
.unwrap_or_else(PictureRect::zero);
self.is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect);

@@ -2196,6 +2206,7 @@ impl TileCacheInstance {
mem::swap(&mut self.tiles, &mut self.old_tiles);

let ctx = TilePreUpdateContext {
local_rect: self.local_rect,
pic_to_world_mapper,
fract_offset: self.fract_offset,
background_color: self.background_color,
@@ -2719,7 +2730,6 @@ impl TileCacheInstance {
}

let ctx = TilePostUpdateContext {
local_rect: self.local_rect,
local_clip_rect: self.local_clip_rect,
backdrop: self.backdrop,
spatial_nodes: &self.spatial_nodes,
@@ -4024,19 +4034,15 @@ impl PicturePrimitive {

// Get the overall world space rect of the picture cache. Used to clip
// the tile rects below for occlusion testing to the relevant area.
let local_clip_rect = tile_cache.local_rect
.intersection(&tile_cache.local_clip_rect)
.unwrap_or_else(PictureRect::zero);

let world_clip_rect = map_pic_to_world
.map(&local_clip_rect)
.map(&tile_cache.local_clip_rect)
.expect("bug: unable to map clip rect");

for key in &tile_cache.tiles_to_draw {
let tile = tile_cache.tiles.get_mut(key).expect("bug: no tile found!");

// Get the world space rect that this tile will actually occupy on screem
let tile_draw_rect = match world_clip_rect.intersection(&tile.world_tile_rect) {
let world_draw_rect = match world_clip_rect.intersection(&tile.world_valid_rect) {
Some(rect) => rect,
None => {
tile.is_visible = false;
@@ -4048,7 +4054,7 @@ impl PicturePrimitive {
// then mark it as not visible and skip drawing. When it's not occluded
// it will fail this test, and get rasterized by the render task setup
// code below.
if frame_state.composite_state.is_tile_occluded(tile_cache.slice, tile_draw_rect) {
if frame_state.composite_state.is_tile_occluded(tile_cache.slice, world_draw_rect) {
// If this tile has an allocated native surface, free it, since it's completely
// occluded. We will need to re-allocate this surface if it becomes visible,
// but that's likely to be rare (e.g. when there is no content display list
@@ -4129,14 +4129,32 @@ pub fn get_raster_rects(
Some((clipped.to_i32(), unclipped))
}

/// Get the inline (horizontal) and block (vertical) sizes
/// for a given line decoration.
pub fn get_line_decoration_sizes(
/// Choose the decoration mask tile size for a given line.
///
/// Given a line with overall size `rect_size` and the given `orientation`,
/// return the dimensions of a single mask tile for the decoration pattern
/// described by `style` and `wavy_line_thickness`.
///
/// If `style` is `Solid`, no mask tile is necessary; return `None`. The other
/// styles each have their own characteristic periods of repetition, so for each
/// one, this function returns a `LayoutSize` with the right aspect ratio and
/// whose specific size is convenient for the `cs_line_decoration.glsl` fragment
/// shader to work with. The shader uses a local coordinate space in which the
/// tile fills a rectangle with one corner at the origin, and with the size this
/// function returns.
///
/// The returned size is not necessarily in pixels; device scaling and other
/// concerns can still affect the actual task size.
///
/// Regardless of whether `orientation` is `Vertical` or `Horizontal`, the
/// `width` and `height` of the returned size are always horizontal and
/// vertical, respectively.
pub fn get_line_decoration_size(
rect_size: &LayoutSize,
orientation: LineOrientation,
style: LineStyle,
wavy_line_thickness: f32,
) -> Option<(f32, f32)> {
) -> Option<LayoutSize> {
let h = match orientation {
LineOrientation::Horizontal => rect_size.height,
LineOrientation::Vertical => rect_size.width,
@@ -4148,30 +4166,35 @@ pub fn get_line_decoration_sizes(
// quality on a wider range of inputs!
// See nsCSSRendering::PaintDecorationLine in Gecko.

match style {
let (parallel, perpendicular) = match style {
LineStyle::Solid => {
None
return None;
}
LineStyle::Dashed => {
let dash_length = (3.0 * h).min(64.0).max(1.0);

Some((2.0 * dash_length, 4.0))
(2.0 * dash_length, 4.0)
}
LineStyle::Dotted => {
let diameter = h.min(64.0).max(1.0);
let period = 2.0 * diameter;

Some((period, diameter))
(period, diameter)
}
LineStyle::Wavy => {
let line_thickness = wavy_line_thickness.max(1.0);
let slope_length = h - line_thickness;
let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0);
let approx_period = 2.0 * (slope_length + flat_length);

Some((approx_period, h))
(approx_period, h)
}
}
};

Some(match orientation {
LineOrientation::Horizontal => LayoutSize::new(parallel, perpendicular),
LineOrientation::Vertical => LayoutSize::new(perpendicular, parallel),
})
}

fn update_opacity_binding(
@@ -773,7 +773,10 @@ impl TextureCacheRenderTarget {
task_rect: target_rect.0.to_f32(),
local_size: info.local_size,
style: info.style as i32,
orientation: info.orientation as i32,
axis_select: match info.orientation {
LineOrientation::Horizontal => 0.0,
LineOrientation::Vertical => 1.0,
},
wavy_line_thickness: info.wavy_line_thickness,
});
}
@@ -1056,7 +1059,7 @@ pub struct LineDecorationJob {
pub local_size: LayoutSize,
pub wavy_line_thickness: f32,
pub style: i32,
pub orientation: i32,
pub axis_select: f32,
}

#[cfg_attr(feature = "capture", derive(Serialize))]
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.