Skip to content

Commit

Permalink
Add border segments to the opaque render pass where possible.
Browse files Browse the repository at this point in the history
Use the new per-segment instance data to allow specifying a
per-segment opacity value. This is used for border segments
to specify which border segments are opaque.

Any time we move segments or primitives to the opaque pass
is typically a significant GPU time win due to the reduced
overdraw. It's also a CPU optimization during batching.
  • Loading branch information
gw3583 committed Oct 17, 2018
1 parent 1d4f27e commit 75ecc81
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 6 deletions.
21 changes: 16 additions & 5 deletions webrender/src/batch.rs
Expand Up @@ -426,6 +426,7 @@ impl AlphaBatchContainer {
struct SegmentInstanceData {
textures: BatchTextures,
user_data: i32,
is_opaque_override: Option<bool>,
}

/// Encapsulates the logic of building batches for items that are blended.
Expand Down Expand Up @@ -1112,10 +1113,10 @@ impl AlphaBatchBuilder {
}
_ => {
if let Some(params) = brush.get_batch_params(
ctx.resource_cache,
gpu_cache,
deferred_resolves,
ctx.prim_store.chase_id == Some(prim_instance.prim_index),
ctx.resource_cache,
gpu_cache,
deferred_resolves,
ctx.prim_store.chase_id == Some(prim_instance.prim_index),
) {
let prim_header_index = prim_headers.push(&prim_header, params.prim_user_data);
if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_instance.prim_index) {
Expand Down Expand Up @@ -1291,8 +1292,16 @@ impl AlphaBatchBuilder {
BrushSegmentTaskId::Empty => return,
};

// If the segment instance data specifies opacity for that
// segment, use it. Otherwise, assume opacity for the segment
// from the overall primitive opacity.
let is_segment_opaque = match segment_data.is_opaque_override {
Some(is_opaque) => is_opaque,
None => prim_instance.opacity.is_opaque,
};

let is_inner = segment.edge_flags.is_empty();
let needs_blending = !prim_instance.opacity.is_opaque ||
let needs_blending = !is_segment_opaque ||
segment.clip_task_id.needs_blending() ||
(!is_inner && transform_kind == TransformedRectKind::Complex);

Expand Down Expand Up @@ -1530,6 +1539,7 @@ impl BrushBatchParameters {
SegmentInstanceData {
textures,
user_data: segment_user_data,
is_opaque_override: None,
}
),
}
Expand Down Expand Up @@ -1657,6 +1667,7 @@ impl BrushPrimitive {
SegmentInstanceData {
textures: BatchTextures::color(cache_item.texture_id),
user_data: cache_item.uv_rect_handle.as_int(gpu_cache),
is_opaque_override: Some(segment.is_opaque),
}
);
}
Expand Down
16 changes: 16 additions & 0 deletions webrender/src/border.rs
Expand Up @@ -192,6 +192,7 @@ impl<'a> DisplayListFlattener<'a> {

pub trait BorderSideHelpers {
fn border_color(&self, is_inner_border: bool) -> ColorF;
fn is_opaque(&self) -> bool;
}

impl BorderSideHelpers for BorderSide {
Expand Down Expand Up @@ -220,6 +221,11 @@ impl BorderSideHelpers for BorderSide {
let black = if lighter { 0.7 } else { 0.3 };
ColorF::new(black, black, black, self.color.a)
}

/// Returns true if all pixels in this border style are opaque.
fn is_opaque(&self) -> bool {
self.color.a >= 1.0 && self.style.is_opaque()
}
}

/// The kind of border corner clip.
Expand Down Expand Up @@ -956,6 +962,12 @@ fn add_corner_segment(
return;
}

let is_opaque =
side0.is_opaque() &&
side1.is_opaque() &&
radius.width <= 0.0 &&
radius.height <= 0.0;

brush_segments.push(
BrushSegment::new(
image_rect,
Expand All @@ -969,6 +981,7 @@ fn add_corner_segment(
border_segments.push(BorderSegmentInfo {
handle: None,
local_task_size: image_rect.size,
is_opaque,
cache_key: RenderTaskCacheKey {
size: DeviceIntSize::zero(),
kind: RenderTaskCacheKeyKind::BorderCorner(
Expand Down Expand Up @@ -1022,6 +1035,8 @@ fn add_edge_segment(
return;
}

let is_opaque = side.is_opaque();

brush_segments.push(
BrushSegment::new(
image_rect,
Expand All @@ -1035,6 +1050,7 @@ fn add_edge_segment(
border_segments.push(BorderSegmentInfo {
handle: None,
local_task_size: size,
is_opaque,
cache_key: RenderTaskCacheKey {
size: DeviceIntSize::zero(),
kind: RenderTaskCacheKeyKind::BorderEdge(
Expand Down
3 changes: 2 additions & 1 deletion webrender/src/prim_store.rs
Expand Up @@ -352,6 +352,7 @@ pub struct BorderSegmentInfo {
pub handle: Option<RenderTaskCacheEntryHandle>,
pub local_task_size: LayoutSize,
pub cache_key: RenderTaskCacheKey,
pub is_opaque: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -2868,7 +2869,7 @@ impl Primitive {
frame_state.gpu_cache,
frame_state.render_tasks,
None,
false, // TODO(gw): We don't calculate opacity for borders yet!
segment.is_opaque,
|render_tasks| {
let task = RenderTask::new_border_segment(
segment.cache_key.size,
Expand Down
23 changes: 23 additions & 0 deletions webrender_api/src/display_item.rs
Expand Up @@ -435,6 +435,29 @@ impl BorderStyle {
pub fn is_hidden(&self) -> bool {
*self == BorderStyle::Hidden || *self == BorderStyle::None
}

/// Returns true if the border style itself is opaque. Other
/// factors (such as color, or border radii) may mean that
/// the border segment isn't opaque regardless of this.
pub fn is_opaque(&self) -> bool {
match *self {
BorderStyle::None |
BorderStyle::Double |
BorderStyle::Dotted |
BorderStyle::Dashed |
BorderStyle::Hidden => {
false
}

BorderStyle::Solid |
BorderStyle::Groove |
BorderStyle::Ridge |
BorderStyle::Inset |
BorderStyle::Outset => {
true
}
}
}
}

#[repr(u32)]
Expand Down

0 comments on commit 75ecc81

Please sign in to comment.