Commit 8dccc2d4 authored by Eliot Courtney's avatar Eliot Courtney Committed by Commit Bot

Track occluded region in WindowOcclusionTracker.

We would like to automatically trigger Picture-in-Picture when a certain amount
of the window is occluded. To do this, we need a way to know how much of a
window is occluded by other windows. This occlusion could come from multiple
windows, and an occlusion tracker for aura already exists. This CL extends the
existing occlusion tracking functionality to also track an occluded region for
tracked windows.


Bug: 883157
Bug: 841886
Bug: b/112668686
Bug: b/110011377
Test: unit test
Change-Id: I0d6618ab05109cc46af0cfd72ecc5cbca2f200d4
Reviewed-on: https://chromium-review.googlesource.com/c/1179433
Commit-Queue: Eliot Courtney <edcourtney@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#604532}
parent b5f31e6c
......@@ -1127,7 +1127,8 @@ void WebContentsViewAura::OnWindowTargetVisibilityChanged(bool visible) {
}
void WebContentsViewAura::OnWindowOcclusionChanged(
aura::Window::OcclusionState occlusion_state) {
aura::Window::OcclusionState occlusion_state,
const SkRegion&) {
UpdateWebContentsVisibility();
}
......
......@@ -185,8 +185,8 @@ class CONTENT_EXPORT WebContentsViewAura
void OnWindowDestroying(aura::Window* window) override;
void OnWindowDestroyed(aura::Window* window) override;
void OnWindowTargetVisibilityChanged(bool visible) override;
void OnWindowOcclusionChanged(
aura::Window::OcclusionState occlusion_state) override;
void OnWindowOcclusionChanged(aura::Window::OcclusionState occlusion_state,
const SkRegion&) override;
bool HasHitTestMask() const override;
void GetHitTestMask(gfx::Path* mask) const override;
......
......@@ -88,8 +88,10 @@ void WindowDelegateImpl::OnWindowDestroyed(aura::Window* window) {
void WindowDelegateImpl::OnWindowTargetVisibilityChanged(bool visible) {}
void WindowDelegateImpl::OnWindowOcclusionChanged(
aura::Window::OcclusionState occlusion_state) {
aura::Window::OcclusionState occlusion_state,
const SkRegion&) {
ServerWindow* const server_window = ServerWindow::GetMayBeNull(window_);
// TODO: Send occluded region.
if (server_window)
server_window->owning_window_tree()->SendOcclusionState(window_);
}
......
......@@ -43,8 +43,8 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowDelegateImpl
void OnWindowDestroying(aura::Window* window) override;
void OnWindowDestroyed(aura::Window* window) override;
void OnWindowTargetVisibilityChanged(bool visible) override;
void OnWindowOcclusionChanged(
aura::Window::OcclusionState occlusion_state) override;
void OnWindowOcclusionChanged(aura::Window::OcclusionState occlusion_state,
const SkRegion& occluded_region) override;
bool HasHitTestMask() const override;
void GetHitTestMask(gfx::Path* mask) const override;
......
......@@ -791,7 +791,8 @@ void WindowPortMus::UpdateOcclusionState(Window::OcclusionState new_state) {
occlusion_state_before_hidden_.reset();
}
window_->SetOcclusionState(new_state);
// TODO: Support occlusion region tracking. See crbug.com/900568.
window_->SetOcclusionInfo(new_state, SkRegion());
}
void WindowPortMus::UpdateOcclusionStateAfterVisiblityChange(bool visible) {
......
......@@ -887,11 +887,14 @@ void Window::SetVisible(bool visible) {
NotifyWindowVisibilityChanged(this, visible);
}
void Window::SetOcclusionState(OcclusionState occlusion_state) {
if (occlusion_state != occlusion_state_) {
void Window::SetOcclusionInfo(OcclusionState occlusion_state,
const SkRegion& occluded_region) {
if (occlusion_state != occlusion_state_ ||
occluded_region_ != occluded_region) {
occlusion_state_ = occlusion_state;
occluded_region_ = occluded_region;
if (delegate_)
delegate_->OnWindowOcclusionChanged(occlusion_state);
delegate_->OnWindowOcclusionChanged(occlusion_state, occluded_region);
}
}
......
......@@ -202,6 +202,18 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// WindowOcclusionTracker::ScopedPause.
OcclusionState occlusion_state() const { return occlusion_state_; }
// Returns the currently occluded region. This will be empty unless
// the window is tracked and has a VISIBLE occlusion state. That is,
// this is only maintained when the window is partially occluded. Further,
// this region may extend outside the window bounds. For performance reasons,
// the actual intersection with the window is not computed. The occluded
// region is the set of window rectangles that may occlude this window.
// Note that this means that the occluded region may be updated if one of
// those windows moves, even if the actual intersection of the occluded
// region with this window does not change. Clients may compute the actual
// intersection region if necessary.
const SkRegion& occluded_region() const { return occluded_region_; }
// Returns the window's bounds in root window's coordinates.
gfx::Rect GetBoundsInRootWindow() const;
......@@ -487,8 +499,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// specific changes. Called from Show()/Hide().
void SetVisible(bool visible);
// Updates the occlusion state of the window.
void SetOcclusionState(OcclusionState occlusion_state);
// Updates the occlusion info of the window.
void SetOcclusionInfo(OcclusionState occlusion_state,
const SkRegion& occluded_region);
// Schedules a paint for the Window's entire bounds.
void SchedulePaint();
......@@ -624,6 +637,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// Occlusion state of the window.
OcclusionState occlusion_state_;
// Occluded region of the window.
SkRegion occluded_region_;
int id_;
// The FrameSinkId associated with this window. If this window is embedding
......
......@@ -86,11 +86,12 @@ class AURA_EXPORT WindowDelegate : public ui::EventHandler {
// Window::TargetVisibility() for details.
virtual void OnWindowTargetVisibilityChanged(bool visible) = 0;
// Called when the occlusion state of the Window changes while tracked (see
// WindowOcclusionTracker::Track). |occlusion_state| is the new occlusion
// state of the Window.
virtual void OnWindowOcclusionChanged(
Window::OcclusionState occlusion_state) {}
// Called when the occlusion state or occluded region of the Window changes
// while tracked (see WindowOcclusionTracker::Track). |occlusion_state| is
// the new occlusion state of the Window. |occluded_region| is the new
// occluded region of the Window.
virtual void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
const SkRegion& occluded_region) {}
// Called from Window::HitTest to check if the window has a custom hit test
// mask. It works similar to the views counterparts. That is, if the function
......
......@@ -85,17 +85,6 @@ SkIRect GetWindowBoundsInRootWindow(
return skirect_bounds;
}
// Returns true iff the occlusion states in |tracked_windows| match those
// returned by Window::occlusion_state().
bool OcclusionStatesMatch(
const base::flat_map<Window*, Window::OcclusionState>& tracked_windows) {
for (const auto& tracked_window : tracked_windows) {
if (tracked_window.second != tracked_window.first->occlusion_state())
return false;
}
return true;
}
} // namespace
WindowOcclusionTracker::ScopedPause::ScopedPause(Env* env) : env_(env) {
......@@ -110,8 +99,7 @@ void WindowOcclusionTracker::Track(Window* window) {
DCHECK(window);
DCHECK(window != window->GetRootWindow());
auto insert_result =
tracked_windows_.insert({window, Window::OcclusionState::UNKNOWN});
auto insert_result = tracked_windows_.insert({window, {}});
DCHECK(insert_result.second);
if (!window_observer_.IsObserving(window))
window_observer_.Add(window);
......@@ -123,6 +111,16 @@ WindowOcclusionTracker::WindowOcclusionTracker() = default;
WindowOcclusionTracker::~WindowOcclusionTracker() = default;
bool WindowOcclusionTracker::OcclusionStatesMatch(
const base::flat_map<Window*, OcclusionData>& tracked_windows) {
for (const auto& tracked_window : tracked_windows) {
if (tracked_window.second.occlusion_state !=
tracked_window.first->occlusion_state())
return false;
}
return true;
}
void WindowOcclusionTracker::MaybeComputeOcclusion() {
if (num_pause_occlusion_tracking_ ||
num_times_occlusion_recomputed_in_current_step_ != 0) {
......@@ -133,7 +131,7 @@ void WindowOcclusionTracker::MaybeComputeOcclusion() {
&num_times_occlusion_recomputed_in_current_step_, 0);
// Recompute occlusion states until either:
// - They are stable, i.e. calling Window::SetOcclusionState() on all tracked
// - They are stable, i.e. calling Window::SetOcclusionInfo() on all tracked
// windows does not provoke changes that could affect occlusion.
// - Occlusion states have been recomputed
// |kMaxComputeOcclusionIterationsBeforeStable|
......@@ -150,7 +148,7 @@ void WindowOcclusionTracker::MaybeComputeOcclusion() {
bool found_dirty_root = false;
// Compute occlusion states and store them in |tracked_windows_|. Do not
// call Window::SetOcclusionState() in this phase to prevent changes to the
// call Window::SetOcclusionInfo() in this phase to prevent changes to the
// window tree while it is being traversed.
for (auto& root_window_pair : root_windows_) {
if (root_window_pair.second.dirty) {
......@@ -170,7 +168,7 @@ void WindowOcclusionTracker::MaybeComputeOcclusion() {
++num_times_occlusion_recomputed_;
++num_times_occlusion_recomputed_in_current_step_;
// Call Window::SetOcclusionState() on tracked windows. A WindowDelegate may
// Call Window::SetOcclusionInfo() on tracked windows. A WindowDelegate may
// change the window tree in response to this.
WindowTracker tracked_windows_list;
for (const auto& tracked_window : tracked_windows_)
......@@ -180,15 +178,20 @@ void WindowOcclusionTracker::MaybeComputeOcclusion() {
Window* window = tracked_windows_list.Pop();
auto it = tracked_windows_.find(window);
if (it != tracked_windows_.end() &&
it->second != Window::OcclusionState::UNKNOWN) {
it->second.occlusion_state != Window::OcclusionState::UNKNOWN) {
// Fallback to VISIBLE/HIDDEN if the maximum number of times that
// occlusion can be recomputed was exceeded.
if (exceeded_max_num_times_occlusion_recomputed) {
it->second = window->IsVisible() ? Window::OcclusionState::VISIBLE
: Window::OcclusionState::HIDDEN;
if (window->IsVisible()) {
it->second.occlusion_state = Window::OcclusionState::VISIBLE;
} else {
it->second.occlusion_state = Window::OcclusionState::HIDDEN;
}
it->second.occluded_region = SkRegion();
}
window->SetOcclusionState(it->second);
window->SetOcclusionInfo(it->second.occlusion_state,
it->second.occluded_region);
}
}
}
......@@ -232,19 +235,22 @@ bool WindowOcclusionTracker::RecomputeOcclusionImpl(
const SkIRect* clipped_bounds_for_children =
window->layer()->GetMasksToBounds() ? &window_bounds : clipped_bounds;
bool has_visible_child = false;
SkRegion occluded_region_before_traversing_children = *occluded_region;
for (auto* child : base::Reversed(window->children())) {
has_visible_child |=
RecomputeOcclusionImpl(child, transform_relative_to_root,
clipped_bounds_for_children, occluded_region);
}
// Compute window occlusion state.
if (occluded_region->contains(window_bounds)) {
SetOccluded(window, !has_visible_child);
return has_visible_child;
// Window is fully occluded.
if (occluded_region->contains(window_bounds) && !has_visible_child) {
SetOccluded(window, true, SkRegion());
return false;
}
SetOccluded(window, false);
// Window is partially occluded or unoccluded.
SetOccluded(window, false, occluded_region_before_traversing_children);
if (VisibleWindowIsOpaque(window))
occluded_region->op(window_bounds, SkRegion::kUnion_Op);
return true;
......@@ -303,22 +309,31 @@ bool WindowOcclusionTracker::MaybeObserveAnimatedWindow(Window* window) {
void WindowOcclusionTracker::SetWindowAndDescendantsAreOccluded(
Window* window,
bool is_occluded) {
SetOccluded(window, is_occluded);
SetOccluded(window, is_occluded, SkRegion());
for (Window* child_window : window->children())
SetWindowAndDescendantsAreOccluded(child_window, is_occluded);
}
void WindowOcclusionTracker::SetOccluded(Window* window, bool is_occluded) {
void WindowOcclusionTracker::SetOccluded(Window* window,
bool is_occluded,
const SkRegion& occluded_region) {
auto tracked_window = tracked_windows_.find(window);
if (tracked_window == tracked_windows_.end())
return;
// Set the occluded region of the window.
tracked_window->second.occluded_region = occluded_region;
if (!window->IsVisible())
tracked_window->second = Window::OcclusionState::HIDDEN;
tracked_window->second.occlusion_state = Window::OcclusionState::HIDDEN;
else if (is_occluded)
tracked_window->second = Window::OcclusionState::OCCLUDED;
tracked_window->second.occlusion_state = Window::OcclusionState::OCCLUDED;
else
tracked_window->second = Window::OcclusionState::VISIBLE;
tracked_window->second.occlusion_state = Window::OcclusionState::VISIBLE;
DCHECK(tracked_window->second.occlusion_state ==
Window::OcclusionState::VISIBLE ||
tracked_window->second.occluded_region.isEmpty());
}
bool WindowOcclusionTracker::WindowIsTracked(Window* window) const {
......
......@@ -87,9 +87,22 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
bool dirty = false;
};
// Holds occlusion related information for tracked windows.
struct OcclusionData {
// Occlusion state for a tracked window.
Window::OcclusionState occlusion_state = Window::OcclusionState::UNKNOWN;
// Region in root window coordinates that is occluded.
SkRegion occluded_region;
};
WindowOcclusionTracker();
~WindowOcclusionTracker() override;
// Returns true iff the occlusion states in |tracked_windows| match those
// returned by Window::occlusion_state().
static bool OcclusionStatesMatch(
const base::flat_map<Window*, OcclusionData>& tracked_windows);
// Recomputes the occlusion state of tracked windows under roots marked as
// dirty in |root_windows_| if there are no active ScopedPause instance.
void MaybeComputeOcclusion();
......@@ -126,9 +139,12 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
void SetWindowAndDescendantsAreOccluded(Window* window, bool is_occluded);
// Updates the occlusion state of |window| in |tracked_windows_|, based on
// |is_occluded| and window->IsVisible(). No-op if |window| is not in
// |is_occluded| and window->IsVisible(). Updates the occluded region of
// |window| using |occluded_region|. No-op if |window| is not in
// |tracked_windows_|.
void SetOccluded(Window* window, bool is_occluded);
void SetOccluded(Window* window,
bool is_occluded,
const SkRegion& occluded_region);
// Returns true if |window| is in |tracked_windows_|.
bool WindowIsTracked(Window* window) const;
......@@ -214,8 +230,8 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
Window* new_root) override;
void OnWindowLayerRecreated(Window* window) override;
// Windows whose occlusion state is tracked.
base::flat_map<Window*, Window::OcclusionState> tracked_windows_;
// Windows whose occlusion data is tracked.
base::flat_map<Window*, OcclusionData> tracked_windows_;
// Windows whose bounds or transform are animated.
//
......
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