Commit 491d1097 authored by Xianzhu Wang's avatar Xianzhu Wang Committed by Commit Bot

[BGPT] Generate effect node for rotated clip and force render surface

Cc requires that a rectangular clip is 2d-axis-aligned with the render
surface to correctly apply the clip. When we find that a rectangular
clip is not 2d-axis-aligned with the render surface, we should create
an effect node and let it create a render surface.

Bug: 890919
Cq-Include-Trybots: luci.chromium.try:linux-blink-gen-property-trees;luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I6f7b462d5506949d1571c60231e066d27b9a238d
Reviewed-on: https://chromium-review.googlesource.com/1244104Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596304}
parent c99cbb2f
...@@ -154,6 +154,7 @@ Bug(none) css3/blending/background-blend-mode-overlapping-accelerated-elements.h ...@@ -154,6 +154,7 @@ Bug(none) css3/blending/background-blend-mode-overlapping-accelerated-elements.h
# Benign subpixel differences. # Benign subpixel differences.
Bug(none) transforms/3d/point-mapping/3d-point-mapping-deep.html [ Failure ] Bug(none) transforms/3d/point-mapping/3d-point-mapping-deep.html [ Failure ]
Bug(none) transforms/3d/point-mapping/3d-point-mapping-preserve-3d.html [ Failure ] Bug(none) transforms/3d/point-mapping/3d-point-mapping-preserve-3d.html [ Failure ]
Bug(none) compositing/direct-image-compositing.html [ Failure ]
Bug(none) compositing/masks/direct-image-mask.html [ Failure ] Bug(none) compositing/masks/direct-image-mask.html [ Failure ]
Bug(none) compositing/geometry/layer-due-to-layer-children.html [ Failure ] Bug(none) compositing/geometry/layer-due-to-layer-children.html [ Failure ]
Bug(none) compositing/perpendicular-layer-sorting.html [ Failure ] Bug(none) compositing/perpendicular-layer-sorting.html [ Failure ]
......
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"transform": 2
},
{
"name": "Child Containment Layer",
"position": [20, 20],
"bounds": [200, 200],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
<!DOCTYPE html>
<div style="margin: 100px; transform: rotate(45deg); overflow: hidden;
width: 200px; height: 200px; border: 20px solid green">
<div style="will-change: transform; width: 400px; height: 400px; background: blue"></div>
</div>
<script>
if (window.testRunner)
testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
</script>
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"opacity": 0.899999976158142,
"transform": 2
},
{
"name": "Ancestor Clipping Layer",
"position": [20, 20],
"bounds": [200, 200],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
},
{
"name": "LayoutBlockFlow (positioned) DIV",
"position": [20, 0],
"bounds": [400, 100],
"contentsOpaque": true,
"backgroundColor": "#00FFFF",
"transform": 2
},
{
"name": "Ancestor Clipping Layer",
"position": [20, 20],
"bounds": [200, 200],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"position": [20, 20],
"bounds": [100, 400],
"contentsOpaque": true,
"backgroundColor": "#FF00FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
<!DOCTYPE html>
<div style="margin: 100px; width: 240px; height: 240px; transform: rotate(45deg)">
<div style="opacity: 0.9">
<div style="overflow: hidden; width: 200px; height: 200px; border: 20px solid green">
<div style="will-change: transform; width: 400px; height: 400px; background: blue"></div>
<div style="position: fixed; top: 0; width: 400px; height: 100px; background: cyan"></div>
<div style="will-change: transform; z-index: 1; position: relative; top: -400px; width: 100px; height: 400px; background: magenta"></div>
</div>
</div>
</div>
<script>
if (window.testRunner)
testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
</script>
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"transform": 2
},
{
"name": "Ancestor Clipping Layer",
"position": [20, 20],
"bounds": [200, 200],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [300, 100],
"transform": 2
},
{
"name": "Ancestor Clipping Layer",
"bounds": [100, 100],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"bounds": [200, 100],
"contentsOpaque": true,
"backgroundColor": "#008000",
"transform": 2
},
{
"name": "LayoutBlockFlow (positioned) DIV",
"bounds": [200, 22],
"contentsOpaque": true,
"backgroundColor": "#FFFF00",
"transform": 2
},
{
"name": "Ancestor Clipping Layer",
"bounds": [100, 100],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"bounds": [50, 200],
"contentsOpaque": true,
"backgroundColor": "#FF0000",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [150, 50]
}
]
}
<!DOCTYPE html>
<div style="margin: 100px; width: 300px; transform: rotate(45deg)">
<div style="position: absolute; width: 200px; height: 22px; background: yellow; z-index: 1; will-change: transform"></div>
<div style="width: 100px; height: 100px; overflow: hidden">
<div style="position: relative; width: 200px; height: 100px; background: green; will-change: transform"></div>
<div style="position: relative; top: -100px; z-index: 2; width: 50px; height: 200px; background: red; will-change: transform"></div>
</div>
</div>
<script>
if (window.testRunner)
testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
</script>
<!DOCTYPE html>
<div style="margin: 100px; width: 240px; height: 240px; transform: rotate(45deg)">
<div style="overflow: hidden; width: 200px; height: 200px; border: 20px solid green">
<div style="will-change: transform; width: 400px; height: 400px; background: blue"></div>
</div>
</div>
<script>
if (window.testRunner)
testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
</script>
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"drawsContent": false,
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"opacity": 0.899999976158142,
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
},
{
"name": "LayoutBlockFlow (positioned) DIV",
"position": [20, 0],
"bounds": [400, 100],
"contentsOpaque": true,
"backgroundColor": "#00FFFF",
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"position": [20, 20],
"bounds": [100, 400],
"contentsOpaque": true,
"backgroundColor": "#FF00FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"drawsContent": false,
"backgroundColor": "#FFFFFF"
},
{
"name": "Scrolling Layer",
"bounds": [800, 600],
"drawsContent": false
},
{
"name": "Scrolling Contents Layer",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [300, 100],
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"bounds": [200, 100],
"contentsOpaque": true,
"backgroundColor": "#008000",
"transform": 2
},
{
"name": "LayoutBlockFlow (positioned) DIV",
"bounds": [200, 22],
"contentsOpaque": true,
"backgroundColor": "#FFFF00",
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"bounds": [50, 200],
"contentsOpaque": true,
"backgroundColor": "#FF0000",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [150, 50]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"bounds": [240, 240],
"transform": 2
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
},
{
"name": "LayoutBlockFlow (positioned) DIV",
"position": [20, 0],
"bounds": [400, 100],
"contentsOpaque": true,
"backgroundColor": "#00FFFF",
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"position": [20, 20],
"bounds": [100, 400],
"contentsOpaque": true,
"backgroundColor": "#FF00FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow DIV",
"position": [20, 20],
"bounds": [400, 400],
"contentsOpaque": true,
"backgroundColor": "#0000FF",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [120, 120]
}
]
}
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF"
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"bounds": [200, 100],
"contentsOpaque": true,
"backgroundColor": "#008000",
"transform": 2
},
{
"name": "LayoutBlockFlow (positioned) DIV",
"bounds": [200, 22],
"contentsOpaque": true,
"backgroundColor": "#FFFF00",
"transform": 2
},
{
"name": "LayoutBlockFlow (relative positioned) DIV",
"bounds": [50, 200],
"contentsOpaque": true,
"backgroundColor": "#FF0000",
"transform": 2
}
],
"transforms": [
{
"id": 1,
"transform": [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[108, 100, 0, 1]
]
},
{
"id": 2,
"parent": 1,
"transform": [
[0.707106781186548, 0.707106781186548, 0, 0],
[-0.707106781186548, 0.707106781186548, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
],
"origin": [150, 50]
}
]
}
...@@ -136,10 +136,9 @@ void PropertyTreeManager::SetupRootEffectNode() { ...@@ -136,10 +136,9 @@ void PropertyTreeManager::SetupRootEffectNode() {
effect_node.has_render_surface = true; effect_node.has_render_surface = true;
root_layer_->SetEffectTreeIndex(effect_node.id); root_layer_->SetEffectTreeIndex(effect_node.id);
current_effect_id_ = effect_node.id; SetCurrentEffectState(effect_node, CcEffectType::kEffect,
current_effect_type_ = CcEffectType::kEffect; &EffectPaintPropertyNode::Root(),
current_effect_ = &EffectPaintPropertyNode::Root(); &ClipPaintPropertyNode::Root());
current_clip_ = current_effect_->OutputClip();
} }
void PropertyTreeManager::SetupRootScrollNode() { void PropertyTreeManager::SetupRootScrollNode() {
...@@ -155,6 +154,26 @@ void PropertyTreeManager::SetupRootScrollNode() { ...@@ -155,6 +154,26 @@ void PropertyTreeManager::SetupRootScrollNode() {
root_layer_->SetScrollTreeIndex(scroll_node.id); root_layer_->SetScrollTreeIndex(scroll_node.id);
} }
void PropertyTreeManager::SetCurrentEffectState(
const cc::EffectNode& cc_effect_node,
CcEffectType effect_type,
const EffectPaintPropertyNode* effect,
const ClipPaintPropertyNode* clip) {
current_.effect_id = cc_effect_node.id;
current_.effect_type = effect_type;
current_.effect = effect;
current_.clip = clip;
if (cc_effect_node.has_render_surface)
current_.render_surface_transform = effect->LocalTransformSpace();
}
// TODO(crbug.com/504464): Remove this when move render surface decision logic
// into cc compositor thread.
void PropertyTreeManager::SetCurrentEffectHasRenderSurface() {
GetEffectTree().Node(current_.effect_id)->has_render_surface = true;
current_.render_surface_transform = current_.effect->LocalTransformSpace();
}
int PropertyTreeManager::EnsureCompositorTransformNode( int PropertyTreeManager::EnsureCompositorTransformNode(
const TransformPaintPropertyNode* transform_node) { const TransformPaintPropertyNode* transform_node) {
DCHECK(transform_node); DCHECK(transform_node);
...@@ -255,14 +274,15 @@ int PropertyTreeManager::EnsureCompositorTransformNode( ...@@ -255,14 +274,15 @@ int PropertyTreeManager::EnsureCompositorTransformNode(
// If the parent transform node flattens transform (as |transform_node| // If the parent transform node flattens transform (as |transform_node|
// flattens inherited transform) while it participates in the 3d sorting // flattens inherited transform) while it participates in the 3d sorting
// context of an ancestor, cc needs a render surface for correct flattening. // context of an ancestor, cc needs a render surface for correct flattening.
if (transform_node->FlattensInheritedTransform() && // TODO(crbug.com/504464): Move the logic into cc compositor thread.
auto* current_cc_effect = GetEffectTree().Node(current_.effect_id);
if (current_cc_effect && !current_cc_effect->has_render_surface &&
current_cc_effect->transform_id == parent_id &&
transform_node->FlattensInheritedTransform() &&
transform_node->Parent() && transform_node->Parent() &&
transform_node->Parent()->RenderingContextId() && transform_node->Parent()->RenderingContextId() &&
!transform_node->Parent()->FlattensInheritedTransform()) { !transform_node->Parent()->FlattensInheritedTransform())
auto* current_cc_effect = GetEffectTree().Node(current_effect_id_); SetCurrentEffectHasRenderSurface();
if (current_cc_effect && current_cc_effect->transform_id == parent_id)
current_cc_effect->has_render_surface = true;
}
auto result = transform_node_map_.Set(transform_node, id); auto result = transform_node_map_.Set(transform_node, id);
DCHECK(result.is_new_entry); DCHECK(result.is_new_entry);
...@@ -379,12 +399,12 @@ int PropertyTreeManager::EnsureCompositorScrollNode( ...@@ -379,12 +399,12 @@ int PropertyTreeManager::EnsureCompositorScrollNode(
} }
void PropertyTreeManager::EmitClipMaskLayer() { void PropertyTreeManager::EmitClipMaskLayer() {
int clip_id = EnsureCompositorClipNode(current_clip_); int clip_id = EnsureCompositorClipNode(current_.clip);
CompositorElementId mask_isolation_id, mask_effect_id; CompositorElementId mask_isolation_id, mask_effect_id;
cc::Layer* mask_layer = client_.CreateOrReuseSynthesizedClipLayer( cc::Layer* mask_layer = client_.CreateOrReuseSynthesizedClipLayer(
current_clip_, mask_isolation_id, mask_effect_id); current_.clip, mask_isolation_id, mask_effect_id);
cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_effect_id_); cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_.effect_id);
// Assignment of mask_isolation.stable_id was delayed until now. // Assignment of mask_isolation.stable_id was delayed until now.
// See PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(). // See PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded().
DCHECK_EQ(static_cast<uint64_t>(cc::EffectNode::INVALID_STABLE_ID), DCHECK_EQ(static_cast<uint64_t>(cc::EffectNode::INVALID_STABLE_ID),
...@@ -392,13 +412,13 @@ void PropertyTreeManager::EmitClipMaskLayer() { ...@@ -392,13 +412,13 @@ void PropertyTreeManager::EmitClipMaskLayer() {
mask_isolation.stable_id = mask_isolation_id.GetInternalValue(); mask_isolation.stable_id = mask_isolation_id.GetInternalValue();
cc::EffectNode& mask_effect = *GetEffectTree().Node( cc::EffectNode& mask_effect = *GetEffectTree().Node(
GetEffectTree().Insert(cc::EffectNode(), current_effect_id_)); GetEffectTree().Insert(cc::EffectNode(), current_.effect_id));
mask_effect.stable_id = mask_effect_id.GetInternalValue(); mask_effect.stable_id = mask_effect_id.GetInternalValue();
mask_effect.clip_id = clip_id; mask_effect.clip_id = clip_id;
mask_effect.has_render_surface = true; mask_effect.has_render_surface = true;
mask_effect.blend_mode = SkBlendMode::kDstIn; mask_effect.blend_mode = SkBlendMode::kDstIn;
const auto* clip_space = current_clip_->LocalTransformSpace(); const auto* clip_space = current_.clip->LocalTransformSpace();
layer_list_builder_->Add(mask_layer); layer_list_builder_->Add(mask_layer);
mask_layer->set_property_tree_sequence_number( mask_layer->set_property_tree_sequence_number(
root_layer_->property_tree_sequence_number()); root_layer_->property_tree_sequence_number());
...@@ -415,7 +435,7 @@ void PropertyTreeManager::EmitClipMaskLayer() { ...@@ -415,7 +435,7 @@ void PropertyTreeManager::EmitClipMaskLayer() {
void PropertyTreeManager::CloseCcEffect() { void PropertyTreeManager::CloseCcEffect() {
DCHECK(effect_stack_.size()); DCHECK(effect_stack_.size());
const EffectStackEntry& previous_state = effect_stack_.back(); const auto& previous_state = effect_stack_.back();
// An effect with exotic blending that is masked by a synthesized clip must // An effect with exotic blending that is masked by a synthesized clip must
// have its blending to the outermost synthesized clip. It is because // have its blending to the outermost synthesized clip. It is because
...@@ -425,18 +445,15 @@ void PropertyTreeManager::CloseCcEffect() { ...@@ -425,18 +445,15 @@ void PropertyTreeManager::CloseCcEffect() {
// thus the clip can't be shared with sibling layers, and must be closed now. // thus the clip can't be shared with sibling layers, and must be closed now.
bool clear_synthetic_effects = bool clear_synthetic_effects =
!IsCurrentCcEffectSynthetic() && !IsCurrentCcEffectSynthetic() &&
current_effect_->BlendMode() != SkBlendMode::kSrcOver; current_.effect->BlendMode() != SkBlendMode::kSrcOver;
// We are about to close an effect that was synthesized for isolating // We are about to close an effect that was synthesized for isolating
// a clip mask. Now emit the actual clip mask that will be composited on // a clip mask. Now emit the actual clip mask that will be composited on
// top of masked contents with SkBlendMode::kDstIn. // top of masked contents with SkBlendMode::kDstIn.
if (IsCurrentCcEffectSynthetic()) if (IsCurrentCcEffectSyntheticForNonTrivialClip())
EmitClipMaskLayer(); EmitClipMaskLayer();
current_effect_id_ = previous_state.effect_id; current_ = previous_state;
current_effect_type_ = previous_state.effect_type;
current_effect_ = previous_state.effect;
current_clip_ = previous_state.clip;
effect_stack_.pop_back(); effect_stack_.pop_back();
if (clear_synthetic_effects) { if (clear_synthetic_effects) {
...@@ -445,6 +462,12 @@ void PropertyTreeManager::CloseCcEffect() { ...@@ -445,6 +462,12 @@ void PropertyTreeManager::CloseCcEffect() {
} }
} }
static bool TransformsAre2dAxisAligned(const TransformPaintPropertyNode* a,
const TransformPaintPropertyNode* b) {
return a == b || GeometryMapper::SourceToDestinationProjection(a, b)
.Preserves2dAxisAlignment();
}
int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip( int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip(
const EffectPaintPropertyNode& next_effect, const EffectPaintPropertyNode& next_effect,
const ClipPaintPropertyNode& next_clip) { const ClipPaintPropertyNode& next_clip) {
...@@ -460,7 +483,7 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip( ...@@ -460,7 +483,7 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip(
// For example with the following clip and effect tree and pending layers: // For example with the following clip and effect tree and pending layers:
// E0 <-- E1 // E0 <-- E1
// C0 <-- C1(rounded) // C0 <-- C1(rounded)
// [P0(E1,C0), P1(E1,C1), P2(E0, C1)] // [P0(E1,C0), P1(E1,C1), P2(E0,C1)]
// In effect stack diagram: // In effect stack diagram:
// P0(C0) P1(C1) // P0(C0) P1(C1)
// [ E1 ] P2(C1) // [ E1 ] P2(C1)
...@@ -470,11 +493,11 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip( ...@@ -470,11 +493,11 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip(
// E0 <+- E1 <-- E_C1_1 <-- E_C1_1M // E0 <+- E1 <-- E_C1_1 <-- E_C1_1M
// +- E_C1_2 <-- E_C1_2M // +- E_C1_2 <-- E_C1_2M
// C0 <-- C1 // C0 <-- C1
// [L0(E1,C0), L1(E_C1_1, C1), L_C1_1(E_C1_1M, C1), L2(E0, C1), // [L0(E1,C0), L1(E_C1_1, C1), L1M(E_C1_1M, C1), L2(E_C1_2, C1),
// L_C1_2(E_C1_2M, C1)] // L2M(E_C1_2M, C1)]
// In effect stack diagram: // In effect stack diagram:
// L_C1_1 // L1M(C1)
// L1(C1) [ E_C1_1M ] L_C2_2 // L1(C1) [ E_C1_1M ] L2M(C1)
// L0(C0) [ E_C1_1 ] L2(C1) [ E_C1_2M ] // L0(C0) [ E_C1_1 ] L2(C1) [ E_C1_2M ]
// [ E1 ][ E_C1_2 ] // [ E1 ][ E_C1_2 ]
// [ E0 ] // [ E0 ]
...@@ -488,23 +511,22 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip( ...@@ -488,23 +511,22 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip(
// emits P1. // emits P1.
// Prior to emitting P2, this method is invoked with (E0, C1). Both previously // Prior to emitting P2, this method is invoked with (E0, C1). Both previously
// entered effects must be closed, because synthetic effect for C1 is enclosed // entered effects must be closed, because synthetic effect for C1 is enclosed
// by E1, thus must be closed before E1 can be closed. A mask layer L_C1_1 // by E1, thus must be closed before E1 can be closed. A mask layer L1M is
// is generated along with an internal effect node for blending. After closing // generated along with an internal effect node for blending. After closing
// both effects, C1 has to be entered again, thus generates another synthetic // both effects, C1 has to be entered again, thus generates another synthetic
// compositor effect. The caller emits P2. // compositor effect. The caller emits P2.
// At last, the caller invokes Finalize() to close the unclosed synthetic // At last, the caller invokes Finalize() to close the unclosed synthetic
// effect. Another mask layer L_C1_2 is generated, along with its internal // effect. Another mask layer L2M is generated, along with its internal
// effect node for blending. // effect node for blending.
const auto& ancestor = const auto& ancestor =
*LowestCommonAncestor(*current_effect_, next_effect).Unalias(); *LowestCommonAncestor(*current_.effect, next_effect).Unalias();
while (current_effect_ != &ancestor) while (current_.effect != &ancestor)
CloseCcEffect(); CloseCcEffect();
bool newly_built = BuildEffectNodesRecursively(&next_effect); bool newly_built = BuildEffectNodesRecursively(&next_effect);
SynthesizeCcEffectsForClipsIfNeeded(&next_clip, SkBlendMode::kSrcOver, SynthesizeCcEffectsForClipsIfNeeded(&next_clip, SkBlendMode::kSrcOver,
newly_built); newly_built);
return current_.effect_id;
return current_effect_id_;
} }
static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find, static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find,
...@@ -523,34 +545,48 @@ static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find, ...@@ -523,34 +545,48 @@ static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find,
return false; return false;
} }
base::Optional<PropertyTreeManager::CcEffectType>
PropertyTreeManager::NeedsSyntheticEffect(
const ClipPaintPropertyNode& clip) const {
if (clip.ClipRect().IsRounded() || clip.ClipPath())
return CcEffectType::kSyntheticForNonTrivialClip;
// Cc requires that a rectangluar clip is 2d-axis-aligned with the render
// surface to correctly apply the clip.
if (!TransformsAre2dAxisAligned(clip.LocalTransformSpace(),
current_.render_surface_transform))
return CcEffectType::kSyntheticFor2dAxisAlignment;
return base::nullopt;
}
SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
const ClipPaintPropertyNode* target_clip, const ClipPaintPropertyNode* target_clip,
SkBlendMode delegated_blend, SkBlendMode delegated_blend,
bool effect_is_newly_built) { bool effect_is_newly_built) {
target_clip = target_clip->Unalias(); target_clip = target_clip->Unalias();
if (delegated_blend != SkBlendMode::kSrcOver) { if (delegated_blend != SkBlendMode::kSrcOver) {
// Exit all synthetic effect node for rounded clip if the next child has // Exit all synthetic effect node if the next child has exotic blending mode
// exotic blending mode because it has to access the backdrop of enclosing // because it has to access the backdrop of enclosing effect.
// effect.
while (IsCurrentCcEffectSynthetic()) while (IsCurrentCcEffectSynthetic())
CloseCcEffect(); CloseCcEffect();
// An effect node can't omit render surface if it has child with exotic // An effect node can't omit render surface if it has child with exotic
// blending mode. See comments below for more detail. // blending mode. See comments below for more detail.
// TODO(crbug.com/504464): Remove premature optimization here. // TODO(crbug.com/504464): Remove premature optimization here.
GetEffectTree().Node(current_effect_id_)->has_render_surface = true; SetCurrentEffectHasRenderSurface();
} else { } else {
// Exit synthetic effects until there are no more synthesized clips below // Exit synthetic effects until there are no more synthesized clips below
// our lowest common ancestor. // our lowest common ancestor.
const auto& lca = const auto& lca =
*LowestCommonAncestor(*current_clip_, *target_clip).Unalias(); *LowestCommonAncestor(*current_.clip, *target_clip).Unalias();
while (current_clip_ != &lca) { while (current_.clip != &lca) {
DCHECK(IsCurrentCcEffectSynthetic()); DCHECK(IsCurrentCcEffectSynthetic());
const auto* pre_exit_clip = current_clip_; const auto* pre_exit_clip = current_.clip;
CloseCcEffect(); CloseCcEffect();
// We may run past the lowest common ancestor because it may not have // We may run past the lowest common ancestor because it may not have
// been synthesized. // been synthesized.
if (IsNodeOnAncestorChain(lca, *pre_exit_clip, *current_clip_)) if (IsNodeOnAncestorChain(lca, *pre_exit_clip, *current_.clip))
break; break;
} }
...@@ -561,48 +597,64 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( ...@@ -561,48 +597,64 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
// See comments in PropertyTreeManager::BuildEffectNodesRecursively(). // See comments in PropertyTreeManager::BuildEffectNodesRecursively().
// TODO(crbug.com/504464): Remove premature optimization here. // TODO(crbug.com/504464): Remove premature optimization here.
if (!effect_is_newly_built && !IsCurrentCcEffectSynthetic() && if (!effect_is_newly_built && !IsCurrentCcEffectSynthetic() &&
current_effect_->Opacity() != 1.f) current_.effect->Opacity() != 1.f)
GetEffectTree().Node(current_effect_id_)->has_render_surface = true; SetCurrentEffectHasRenderSurface();
} }
DCHECK(current_clip_->IsAncestorOf(*target_clip)); DCHECK(current_.clip->IsAncestorOf(*target_clip));
Vector<const ClipPaintPropertyNode*> pending_clips; struct PendingClip {
for (; target_clip != current_clip_; const ClipPaintPropertyNode* clip;
CcEffectType type;
};
Vector<PendingClip> pending_clips;
for (; target_clip != current_.clip;
target_clip = target_clip->Parent()->Unalias()) { target_clip = target_clip->Parent()->Unalias()) {
DCHECK(target_clip); DCHECK(target_clip);
bool should_synthesize = if (auto type = NeedsSyntheticEffect(*target_clip))
target_clip->ClipRect().IsRounded() || target_clip->ClipPath(); pending_clips.emplace_back(PendingClip{target_clip, *type});
if (should_synthesize)
pending_clips.push_back(target_clip);
} }
for (size_t i = pending_clips.size(); i--;) { for (size_t i = pending_clips.size(); i--;) {
const ClipPaintPropertyNode* next_clip = pending_clips[i]; const auto& pending_clip = pending_clips[i];
// For each of clip synthesized, an isolation effect node needs to be // For a non-trivial clip, the synthetic effect is an isolation to enclose
// created to enclose only the layers that should be masked by the clip. // only the layers that should be masked by the synthesized clip.
cc::EffectNode& mask_isolation = *GetEffectTree().Node( // For a non-2d-axis-preserving clip, the synthetic effect creates a render
GetEffectTree().Insert(cc::EffectNode(), current_effect_id_)); // surface which is axis-aligned with the clip.
// mask_isolation.stable_id will be assigned later when the effect is cc::EffectNode& synthetic_effect = *GetEffectTree().Node(
// closed. For now the default value of INVALID_STABLE_ID is used. GetEffectTree().Insert(cc::EffectNode(), current_.effect_id));
// See PropertyTreeManager::EmitClipMaskLayer(). if (pending_clip.type == CcEffectType::kSyntheticForNonTrivialClip) {
mask_isolation.clip_id = EnsureCompositorClipNode(next_clip); synthetic_effect.clip_id = EnsureCompositorClipNode(pending_clip.clip);
mask_isolation.has_render_surface = true; // For non-trivial clip, isolation_effect.stable_id will be assigned later
// when the effect is closed. For now the default value INVALID_STABLE_ID
// is used. See PropertyTreeManager::EmitClipMaskLayer().
} else {
DCHECK_EQ(pending_clip.type, CcEffectType::kSyntheticFor2dAxisAlignment);
synthetic_effect.stable_id =
CompositorElementIdFromUniqueObjectId(NewUniqueObjectId())
.GetInternalValue();
// The clip of the synthetic effect is the parent of the clip, so that
// the clip itself will be applied in the render surface.
synthetic_effect.clip_id =
EnsureCompositorClipNode(pending_clip.clip->Parent());
}
synthetic_effect.transform_id =
EnsureCompositorTransformNode(pending_clip.clip->LocalTransformSpace());
synthetic_effect.has_render_surface = true;
// Clip and kDstIn do not commute. This shall never be reached because // Clip and kDstIn do not commute. This shall never be reached because
// kDstIn is only used internally to implement CSS clip-path and mask, // kDstIn is only used internally to implement CSS clip-path and mask,
// and there is never a difference between the output clip of the effect // and there is never a difference between the output clip of the effect
// and the mask content. // and the mask content.
DCHECK(delegated_blend != SkBlendMode::kDstIn); DCHECK(delegated_blend != SkBlendMode::kDstIn);
mask_isolation.blend_mode = delegated_blend; synthetic_effect.blend_mode = delegated_blend;
delegated_blend = SkBlendMode::kSrcOver; delegated_blend = SkBlendMode::kSrcOver;
effect_stack_.emplace_back( effect_stack_.emplace_back(current_);
EffectStackEntry{current_effect_id_, current_effect_type_, SetCurrentEffectState(synthetic_effect, pending_clip.type, current_.effect,
current_effect_, current_clip_}); pending_clip.clip);
current_effect_id_ = mask_isolation.id; current_.render_surface_transform =
current_effect_type_ = CcEffectType::kSynthesizedClip; pending_clip.clip->LocalTransformSpace();
current_clip_ = next_clip;
} }
return delegated_blend; return delegated_blend;
...@@ -611,12 +663,12 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( ...@@ -611,12 +663,12 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
bool PropertyTreeManager::BuildEffectNodesRecursively( bool PropertyTreeManager::BuildEffectNodesRecursively(
const EffectPaintPropertyNode* next_effect) { const EffectPaintPropertyNode* next_effect) {
next_effect = next_effect ? next_effect->Unalias() : nullptr; next_effect = next_effect ? next_effect->Unalias() : nullptr;
if (next_effect == current_effect_) if (next_effect == current_.effect)
return false; return false;
DCHECK(next_effect); DCHECK(next_effect);
bool newly_built = BuildEffectNodesRecursively(next_effect->Parent()); bool newly_built = BuildEffectNodesRecursively(next_effect->Parent());
DCHECK_EQ(next_effect->Parent()->Unalias(), current_effect_); DCHECK_EQ(next_effect->Parent()->Unalias(), current_.effect);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK(!effect_nodes_converted_.Contains(next_effect)) DCHECK(!effect_nodes_converted_.Contains(next_effect))
...@@ -627,10 +679,11 @@ bool PropertyTreeManager::BuildEffectNodesRecursively( ...@@ -627,10 +679,11 @@ bool PropertyTreeManager::BuildEffectNodesRecursively(
SkBlendMode used_blend_mode; SkBlendMode used_blend_mode;
int output_clip_id; int output_clip_id;
if (next_effect->OutputClip()) { const auto* output_clip = next_effect->OutputClip();
if (output_clip) {
used_blend_mode = SynthesizeCcEffectsForClipsIfNeeded( used_blend_mode = SynthesizeCcEffectsForClipsIfNeeded(
next_effect->OutputClip(), next_effect->BlendMode(), newly_built); output_clip, next_effect->BlendMode(), newly_built);
output_clip_id = EnsureCompositorClipNode(next_effect->OutputClip()); output_clip_id = EnsureCompositorClipNode(output_clip);
} else { } else {
while (IsCurrentCcEffectSynthetic()) while (IsCurrentCcEffectSynthetic())
CloseCcEffect(); CloseCcEffect();
...@@ -638,15 +691,17 @@ bool PropertyTreeManager::BuildEffectNodesRecursively( ...@@ -638,15 +691,17 @@ bool PropertyTreeManager::BuildEffectNodesRecursively(
// blending mode, nor being opacity-only node with more than one child. // blending mode, nor being opacity-only node with more than one child.
// TODO(crbug.com/504464): Remove premature optimization here. // TODO(crbug.com/504464): Remove premature optimization here.
if (next_effect->BlendMode() != SkBlendMode::kSrcOver || if (next_effect->BlendMode() != SkBlendMode::kSrcOver ||
(!newly_built && current_effect_->Opacity() != 1.f)) (!newly_built && current_.effect->Opacity() != 1.f))
GetEffectTree().Node(current_effect_id_)->has_render_surface = true; SetCurrentEffectHasRenderSurface();
used_blend_mode = next_effect->BlendMode(); used_blend_mode = next_effect->BlendMode();
output_clip_id = GetEffectTree().Node(current_effect_id_)->clip_id; output_clip = current_.clip;
output_clip_id = GetEffectTree().Node(current_.effect_id)->clip_id;
DCHECK_EQ(output_clip_id, EnsureCompositorClipNode(output_clip));
} }
cc::EffectNode& effect_node = *GetEffectTree().Node( cc::EffectNode& effect_node = *GetEffectTree().Node(
GetEffectTree().Insert(cc::EffectNode(), current_effect_id_)); GetEffectTree().Insert(cc::EffectNode(), current_.effect_id));
effect_node.stable_id = effect_node.stable_id =
next_effect->GetCompositorElementId().GetInternalValue(); next_effect->GetCompositorElementId().GetInternalValue();
effect_node.clip_id = output_clip_id; effect_node.clip_id = output_clip_id;
...@@ -663,6 +718,7 @@ bool PropertyTreeManager::BuildEffectNodesRecursively( ...@@ -663,6 +718,7 @@ bool PropertyTreeManager::BuildEffectNodesRecursively(
if (!next_effect->Filter().IsEmpty() || if (!next_effect->Filter().IsEmpty() ||
used_blend_mode != SkBlendMode::kSrcOver) used_blend_mode != SkBlendMode::kSrcOver)
effect_node.has_render_surface = true; effect_node.has_render_surface = true;
effect_node.opacity = next_effect->Opacity(); effect_node.opacity = next_effect->Opacity();
if (next_effect->GetColorFilter() != kColorFilterNone) { if (next_effect->GetColorFilter() != kColorFilterNone) {
// Currently color filter is only used by SVG masks. // Currently color filter is only used by SVG masks.
...@@ -691,14 +747,9 @@ bool PropertyTreeManager::BuildEffectNodesRecursively( ...@@ -691,14 +747,9 @@ bool PropertyTreeManager::BuildEffectNodesRecursively(
property_trees_.element_id_to_effect_node_index[compositor_element_id] = property_trees_.element_id_to_effect_node_index[compositor_element_id] =
effect_node.id; effect_node.id;
} }
effect_stack_.emplace_back(EffectStackEntry{current_effect_id_, effect_stack_.emplace_back(current_);
current_effect_type_, SetCurrentEffectState(effect_node, CcEffectType::kEffect, next_effect,
current_effect_, current_clip_}); output_clip);
current_effect_id_ = effect_node.id;
current_effect_type_ = CcEffectType::kEffect;
current_effect_ = next_effect;
if (next_effect->OutputClip())
current_clip_ = next_effect->OutputClip()->Unalias();
return true; return true;
} }
......
...@@ -19,6 +19,7 @@ class Layer; ...@@ -19,6 +19,7 @@ class Layer;
class PropertyTrees; class PropertyTrees;
class ScrollTree; class ScrollTree;
class TransformTree; class TransformTree;
struct EffectNode;
struct TransformNode; struct TransformNode;
} }
...@@ -126,10 +127,39 @@ class PropertyTreeManager { ...@@ -126,10 +127,39 @@ class PropertyTreeManager {
bool effect_is_newly_built); bool effect_is_newly_built);
void EmitClipMaskLayer(); void EmitClipMaskLayer();
void CloseCcEffect(); void CloseCcEffect();
bool IsCurrentCcEffectSynthetic() const { bool IsCurrentCcEffectSynthetic() const {
return current_effect_type_ != CcEffectType::kEffect; return current_.effect_type != CcEffectType::kEffect;
}
bool IsCurrentCcEffectSyntheticForNonTrivialClip() const {
return current_.effect_type == CcEffectType::kSyntheticForNonTrivialClip;
} }
// The type of operation the current cc effect node applies.
enum class CcEffectType {
// The cc effect corresponds to a Blink effect node.
kEffect,
// The cc effect is synthetic for a blink clip node that has to be
// rasterized because the clip is non-trivial.
kSyntheticForNonTrivialClip,
// The cc effect is synthetic to create a render surface that is
// 2d-axis-aligned with a blink clip node that is non-2d-axis-aligned
// in the the original render surface. Cc requires a rectangular clip to be
// 2d-axis-aligned with the render surface to correctly apply the clip.
// TODO(crbug.com/504464): This will be changed when we move render surface
// decision logic into the cc compositor thread.
kSyntheticFor2dAxisAlignment,
};
base::Optional<CcEffectType> NeedsSyntheticEffect(
const ClipPaintPropertyNode&) const;
void SetCurrentEffectState(const cc::EffectNode&,
CcEffectType,
const EffectPaintPropertyNode*,
const ClipPaintPropertyNode*);
void SetCurrentEffectHasRenderSurface();
cc::TransformTree& GetTransformTree(); cc::TransformTree& GetTransformTree();
cc::ClipTree& GetClipTree(); cc::ClipTree& GetClipTree();
cc::EffectTree& GetEffectTree(); cc::EffectTree& GetEffectTree();
...@@ -158,32 +188,36 @@ class PropertyTreeManager { ...@@ -158,32 +188,36 @@ class PropertyTreeManager {
HashMap<const ClipPaintPropertyNode*, int> clip_node_map_; HashMap<const ClipPaintPropertyNode*, int> clip_node_map_;
HashMap<const ScrollPaintPropertyNode*, int> scroll_node_map_; HashMap<const ScrollPaintPropertyNode*, int> scroll_node_map_;
struct EffectState {
// The cc effect node that has the corresponding drawing state to the // The cc effect node that has the corresponding drawing state to the
// effect and clip state from the last SwitchToEffectNodeWithSynthesizedClip. // effect and clip state from the last
int current_effect_id_; // SwitchToEffectNodeWithSynthesizedClip.
// The type of operation the current cc effect node applies. kEffect means
// it corresponds to a Blink effect node. kSynthesizedClip means it implements
// a Blink clip node that has to be rasterized.
enum class CcEffectType { kEffect, kSynthesizedClip } current_effect_type_;
// The effect state of the current cc effect node.
const EffectPaintPropertyNode* current_effect_;
// The clip state of the current cc effect node. This value may be shallower
// than the one passed into SwitchToEffectNodeWithSynthesizedClip because not
// every clip needs to be synthesized as cc effect.
// Is set to output clip of the effect if the type is kEffect, or set to the
// synthesized clip node if the type is kSynthesizedClip.
const ClipPaintPropertyNode* current_clip_;
// This keep track of cc effect stack. Whenever a new cc effect is nested,
// a new entry is pushed, and the entry will be popped when the effect closed.
// Note: This is a "restore stack", i.e. the top element does not represent
// the current state, but the state prior to most recent push.
struct EffectStackEntry {
int effect_id; int effect_id;
CcEffectType effect_type; CcEffectType effect_type;
// The effect state of the cc effect node.
const EffectPaintPropertyNode* effect; const EffectPaintPropertyNode* effect;
// The clip state of the cc effect node. This value may be shallower than
// the one passed into SwitchToEffectNodeWithSynthesizedClip because not
// every clip needs to be synthesized as cc effect.
// Is set to output clip of the effect if the type is kEffect, or set to the
// synthesized clip node if the type is kSyntheticForNonTrivialClip.
const ClipPaintPropertyNode* clip; const ClipPaintPropertyNode* clip;
// The transform space of the containing render surface.
// TODO(crbug.com/504464): Remove this when move render surface decision
// logic into cc compositor thread.
const TransformPaintPropertyNode* render_surface_transform;
}; };
Vector<EffectStackEntry> effect_stack_;
// The current effect state. Virtually it's the top of the effect stack if
// it and effect_stack_ are treated as a whole stack.
EffectState current_;
// This keep track of cc effect stack. Whenever a new cc effect is nested,
// a new entry is pushed, and the entry will be popped when the effect closed.
// Note: This is a "restore stack", i.e. the top element does not represent
// the current state (which is in current_), but the state prior to most
// recent push.
Vector<EffectState> effect_stack_;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
HashSet<const EffectPaintPropertyNode*> effect_nodes_converted_; HashSet<const EffectPaintPropertyNode*> effect_nodes_converted_;
......
...@@ -1827,6 +1827,56 @@ bool TransformationMatrix::IsIntegerTranslation() const { ...@@ -1827,6 +1827,56 @@ bool TransformationMatrix::IsIntegerTranslation() const {
return true; return true;
} }
// This is the same as gfx::Transform::Preserves2dAxisAlignment().
bool TransformationMatrix::Preserves2dAxisAlignment() const {
// Check whether an axis aligned 2-dimensional rect would remain axis-aligned
// after being transformed by this matrix (and implicitly projected by
// dropping any non-zero z-values).
//
// The 4th column can be ignored because translations don't affect axis
// alignment. The 3rd column can be ignored because we are assuming 2d
// inputs, where z-values will be zero. The 3rd row can also be ignored
// because we are assuming 2d outputs, and any resulting z-value is dropped
// anyway. For the inner 2x2 portion, the only effects that keep a rect axis
// aligned are (1) swapping axes and (2) scaling axes. This can be checked by
// verifying only 1 element of every column and row is non-zero. Degenerate
// cases that project the x or y dimension to zero are considered to preserve
// axis alignment.
//
// If the matrix does have perspective component that is affected by x or y
// values: The current implementation conservatively assumes that axis
// alignment is not preserved.
bool has_x_or_y_perspective = M14() != 0 || M24() != 0;
if (has_x_or_y_perspective)
return false;
constexpr double kEpsilon = std::numeric_limits<double>::epsilon();
int num_non_zero_in_row_1 = 0;
int num_non_zero_in_row_2 = 0;
int num_non_zero_in_col_1 = 0;
int num_non_zero_in_col_2 = 0;
if (std::abs(M11()) > kEpsilon) {
num_non_zero_in_col_1++;
num_non_zero_in_row_1++;
}
if (std::abs(M12()) > kEpsilon) {
num_non_zero_in_col_1++;
num_non_zero_in_row_2++;
}
if (std::abs(M21()) > kEpsilon) {
num_non_zero_in_col_2++;
num_non_zero_in_row_1++;
}
if (std::abs(M22()) > kEpsilon) {
num_non_zero_in_col_2++;
num_non_zero_in_row_2++;
}
return num_non_zero_in_row_1 <= 1 && num_non_zero_in_row_2 <= 1 &&
num_non_zero_in_col_1 <= 1 && num_non_zero_in_col_2 <= 1;
}
FloatSize TransformationMatrix::To2DTranslation() const { FloatSize TransformationMatrix::To2DTranslation() const {
DCHECK(IsIdentityOr2DTranslation()); DCHECK(IsIdentityOr2DTranslation());
return FloatSize(matrix_[3][0], matrix_[3][1]); return FloatSize(matrix_[3][0], matrix_[3][1]);
......
...@@ -474,6 +474,10 @@ class PLATFORM_EXPORT TransformationMatrix { ...@@ -474,6 +474,10 @@ class PLATFORM_EXPORT TransformationMatrix {
bool IsIntegerTranslation() const; bool IsIntegerTranslation() const;
// Returns true if axis-aligned 2d rects will remain axis-aligned after being
// transformed by this matrix.
bool Preserves2dAxisAlignment() const;
// If this transformation is identity or 2D translation, returns the // If this transformation is identity or 2D translation, returns the
// translation. // translation.
FloatSize To2DTranslation() const; FloatSize To2DTranslation() const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment