OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/common/gpu/ca_layer_partial_damage_tree_mac.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/mac/scoped_nsobject.h" | |
9 #include "base/mac/sdk_forward_declarations.h" | |
10 #include "base/trace_event/trace_event.h" | |
11 #include "ui/base/ui_base_switches.h" | |
12 #include "ui/gfx/transform.h" | |
13 | |
14 @interface CALayer(Private) | |
15 -(void)setContentsChanged; | |
16 @end | |
17 | |
18 namespace content { | |
19 namespace { | |
20 | |
21 // When selecting a CALayer to re-use for partial damage, this is the maximum | |
22 // fraction of the merged layer's pixels that may be not-updated by the swap | |
23 // before we consider the CALayer to not be a good enough match, and create a | |
24 // new one. | |
25 const float kMaximumPartialDamageWasteFraction = 1.2f; | |
26 | |
27 // The maximum number of partial damage layers that may be created before we | |
28 // give up and remove them all (doing full damage in the process). | |
29 const size_t kMaximumPartialDamageLayers = 8; | |
30 | |
31 } // namespace | |
32 | |
33 class CALayerPartialDamageTree::OverlayPlane { | |
34 public: | |
35 OverlayPlane(base::ScopedCFTypeRef<IOSurfaceRef> io_surface, | |
36 const gfx::Rect& pixel_frame_rect, | |
37 const gfx::RectF& contents_rect) | |
38 : io_surface(io_surface), | |
39 contents_rect(contents_rect), | |
40 pixel_frame_rect(pixel_frame_rect), | |
41 layer_needs_update(true) {} | |
42 | |
43 ~OverlayPlane() { | |
44 [ca_layer setContents:nil]; | |
45 [ca_layer removeFromSuperlayer]; | |
46 ca_layer.reset(); | |
47 } | |
48 | |
49 const base::ScopedCFTypeRef<IOSurfaceRef> io_surface; | |
50 const gfx::RectF contents_rect; | |
51 const gfx::Rect pixel_frame_rect; | |
52 bool layer_needs_update; | |
53 base::scoped_nsobject<CALayer> ca_layer; | |
54 | |
55 void TakeCALayerFrom(OverlayPlane* other_plane) { | |
56 ca_layer.swap(other_plane->ca_layer); | |
57 } | |
58 | |
59 void UpdateProperties(float scale_factor) { | |
60 if (layer_needs_update) { | |
61 [ca_layer setOpaque:YES]; | |
62 | |
63 id new_contents = static_cast<id>(io_surface.get()); | |
64 if ([ca_layer contents] == new_contents) | |
65 [ca_layer setContentsChanged]; | |
66 else | |
67 [ca_layer setContents:new_contents]; | |
68 [ca_layer setContentsRect:contents_rect.ToCGRect()]; | |
69 | |
70 [ca_layer setAnchorPoint:CGPointZero]; | |
71 | |
72 if ([ca_layer respondsToSelector:(@selector(setContentsScale:))]) | |
73 [ca_layer setContentsScale:scale_factor]; | |
74 gfx::RectF dip_frame_rect = gfx::RectF(pixel_frame_rect); | |
75 dip_frame_rect.Scale(1 / scale_factor); | |
76 [ca_layer setBounds:CGRectMake(0, 0, dip_frame_rect.width(), | |
77 dip_frame_rect.height())]; | |
78 [ca_layer | |
79 setPosition:CGPointMake(dip_frame_rect.x(), dip_frame_rect.y())]; | |
80 } | |
81 static bool show_borders = | |
82 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
83 switches::kShowMacOverlayBorders); | |
84 if (show_borders) { | |
85 base::ScopedCFTypeRef<CGColorRef> color; | |
86 if (!layer_needs_update) { | |
87 // Green represents contents that are unchanged across frames. | |
88 color.reset(CGColorCreateGenericRGB(0, 1, 0, 1)); | |
89 } else { | |
90 // Red represents damaged contents. | |
91 color.reset(CGColorCreateGenericRGB(1, 0, 0, 1)); | |
92 } | |
93 [ca_layer setBorderWidth:1]; | |
94 [ca_layer setBorderColor:color]; | |
95 } | |
96 layer_needs_update = false; | |
97 } | |
98 | |
99 private: | |
100 }; | |
101 | |
102 void CALayerPartialDamageTree::UpdatePartialDamagePlanes( | |
103 CALayerPartialDamageTree* old_tree, | |
104 const gfx::Rect& pixel_damage_rect) { | |
105 // Don't create partial damage layers if partial swap is disabled. | |
106 if (!allow_partial_swap_) | |
107 return; | |
108 // Only create partial damage layers when building on top of an existing tree. | |
109 if (!old_tree) | |
110 return; | |
111 // If the frame size has changed, discard all of the old partial damage | |
112 // layers. | |
113 if (old_tree->root_plane_->pixel_frame_rect != root_plane_->pixel_frame_rect) | |
114 return; | |
115 // If there is full damage, discard all of the old partial damage layers. | |
116 if (pixel_damage_rect == root_plane_->pixel_frame_rect) | |
117 return; | |
118 | |
119 // If there is no damage, don't change anything. | |
120 if (pixel_damage_rect.IsEmpty()) { | |
121 std::swap(partial_damage_planes_, old_tree->partial_damage_planes_); | |
122 return; | |
123 } | |
124 | |
125 // Find the last partial damage plane to re-use the CALayer from. Grow the | |
126 // new rect for this layer to include this damage, and all nearby partial | |
127 // damage layers. | |
128 scoped_ptr<OverlayPlane> plane_for_swap; | |
129 { | |
130 auto plane_to_reuse_iter = old_tree->partial_damage_planes_.end(); | |
131 gfx::Rect plane_to_reuse_enlarged_pixel_damage_rect; | |
132 | |
133 for (auto old_plane_iter = old_tree->partial_damage_planes_.begin(); | |
134 old_plane_iter != old_tree->partial_damage_planes_.end(); | |
135 ++old_plane_iter) { | |
136 gfx::Rect enlarged_pixel_damage_rect = | |
137 (*old_plane_iter)->pixel_frame_rect; | |
138 enlarged_pixel_damage_rect.Union(pixel_damage_rect); | |
139 | |
140 // Compute the fraction of the pixels that would not be updated by this | |
141 // swap. If it is too big, try another layer. | |
142 float waste_fraction = enlarged_pixel_damage_rect.size().GetArea() * 1.f / | |
143 pixel_damage_rect.size().GetArea(); | |
144 if (waste_fraction > kMaximumPartialDamageWasteFraction) | |
145 continue; | |
146 | |
147 plane_to_reuse_iter = old_plane_iter; | |
148 plane_to_reuse_enlarged_pixel_damage_rect.Union( | |
149 enlarged_pixel_damage_rect); | |
150 } | |
151 if (plane_to_reuse_iter != old_tree->partial_damage_planes_.end()) { | |
152 gfx::RectF enlarged_contents_rect = | |
153 gfx::RectF(plane_to_reuse_enlarged_pixel_damage_rect); | |
154 enlarged_contents_rect.Scale(1. / root_plane_->pixel_frame_rect.width(), | |
155 1. / root_plane_->pixel_frame_rect.height()); | |
156 | |
157 plane_for_swap.reset(new OverlayPlane( | |
158 root_plane_->io_surface, plane_to_reuse_enlarged_pixel_damage_rect, | |
159 enlarged_contents_rect)); | |
160 | |
161 plane_for_swap->TakeCALayerFrom((*plane_to_reuse_iter).get()); | |
162 if (*plane_to_reuse_iter != old_tree->partial_damage_planes_.back()) { | |
163 CALayer* superlayer = [plane_for_swap->ca_layer superlayer]; | |
164 [plane_for_swap->ca_layer removeFromSuperlayer]; | |
165 [superlayer addSublayer:plane_for_swap->ca_layer]; | |
166 } | |
167 } | |
168 } | |
169 | |
170 // If we haven't found an appropriate layer to re-use, create a new one, if | |
171 // we haven't already created too many. | |
172 if (!plane_for_swap.get() && | |
173 old_tree->partial_damage_planes_.size() < kMaximumPartialDamageLayers) { | |
174 gfx::RectF contents_rect = gfx::RectF(pixel_damage_rect); | |
175 contents_rect.Scale(1. / root_plane_->pixel_frame_rect.width(), | |
176 1. / root_plane_->pixel_frame_rect.height()); | |
177 plane_for_swap.reset(new OverlayPlane(root_plane_->io_surface, | |
178 pixel_damage_rect, contents_rect)); | |
179 } | |
180 | |
181 // And if we still don't have a layer, do full damage. | |
182 if (!plane_for_swap.get()) | |
183 return; | |
184 | |
185 // Walk all old partial damage planes. Remove anything that is now completely | |
186 // covered, and move everything else into the new |partial_damage_planes_|. | |
187 for (auto& old_plane : old_tree->partial_damage_planes_) { | |
188 if (!old_plane.get()) | |
189 continue; | |
190 // Intersect the planes' frames with the new root plane to ensure that | |
191 // they don't get kept alive inappropriately. | |
192 gfx::Rect old_plane_frame_rect = old_plane->pixel_frame_rect; | |
193 old_plane_frame_rect.Intersect(root_plane_->pixel_frame_rect); | |
194 | |
195 bool old_plane_covered_by_swap = false; | |
196 if (plane_for_swap.get() && | |
197 plane_for_swap->pixel_frame_rect.Contains(old_plane_frame_rect)) { | |
198 old_plane_covered_by_swap = true; | |
199 } | |
200 if (!old_plane_covered_by_swap) { | |
201 DCHECK(old_plane->ca_layer); | |
202 partial_damage_planes_.push_back(std::move(old_plane)); | |
203 } | |
204 } | |
205 | |
206 partial_damage_planes_.push_back(std::move(plane_for_swap)); | |
207 } | |
208 | |
209 void CALayerPartialDamageTree::UpdateRootAndPartialDamagePlanes( | |
210 scoped_ptr<CALayerPartialDamageTree> old_tree, | |
211 const gfx::Rect& pixel_damage_rect) { | |
212 // First update the partial damage tree. | |
213 UpdatePartialDamagePlanes(old_tree.get(), pixel_damage_rect); | |
214 if (old_tree) { | |
215 if (partial_damage_planes_.empty()) { | |
216 // If there are no partial damage planes, then we will be updating the | |
217 // root layer. Take the CALayer from the old tree. | |
218 root_plane_->TakeCALayerFrom(old_tree->root_plane_.get()); | |
219 } else { | |
220 // If there is a partial damage tree, then just take the old plane | |
221 // from the previous frame, so that there is no update to it. | |
222 root_plane_.swap(old_tree->root_plane_); | |
223 } | |
224 } | |
225 } | |
226 | |
227 void CALayerPartialDamageTree::UpdateCALayers(CALayer* superlayer, | |
228 float scale_factor) { | |
229 if (!allow_partial_swap_) { | |
230 DCHECK(partial_damage_planes_.empty()); | |
231 return; | |
232 } | |
233 | |
234 // Allocate and update CALayers for the backbuffer and partial damage layers. | |
235 if (!root_plane_->ca_layer) { | |
236 DCHECK(partial_damage_planes_.empty()); | |
237 root_plane_->ca_layer.reset([[CALayer alloc] init]); | |
238 [superlayer setSublayers:nil]; | |
239 [superlayer addSublayer:root_plane_->ca_layer]; | |
240 } | |
241 // Excessive logging to debug white screens (crbug.com/583805). | |
242 // TODO(ccameron): change this back to a DLOG. | |
243 if ([root_plane_->ca_layer superlayer] != superlayer) { | |
244 LOG(ERROR) << "CALayerPartialDamageTree root layer not attached to tree."; | |
245 } | |
246 for (auto& plane : partial_damage_planes_) { | |
247 if (!plane->ca_layer) { | |
248 DCHECK(plane == partial_damage_planes_.back()); | |
249 plane->ca_layer.reset([[CALayer alloc] init]); | |
250 } | |
251 if (![plane->ca_layer superlayer]) { | |
252 DCHECK(plane == partial_damage_planes_.back()); | |
253 [superlayer addSublayer:plane->ca_layer]; | |
254 } | |
255 } | |
256 root_plane_->UpdateProperties(scale_factor); | |
257 for (auto& plane : partial_damage_planes_) | |
258 plane->UpdateProperties(scale_factor); | |
259 } | |
260 | |
261 CALayerPartialDamageTree::CALayerPartialDamageTree( | |
262 bool allow_partial_swap, | |
263 base::ScopedCFTypeRef<IOSurfaceRef> io_surface, | |
264 const gfx::Rect& pixel_frame_rect) | |
265 : allow_partial_swap_(allow_partial_swap) { | |
266 root_plane_.reset( | |
267 new OverlayPlane(io_surface, pixel_frame_rect, gfx::RectF(0, 0, 1, 1))); | |
268 } | |
269 | |
270 CALayerPartialDamageTree::~CALayerPartialDamageTree() {} | |
271 | |
272 base::ScopedCFTypeRef<IOSurfaceRef> | |
273 CALayerPartialDamageTree::RootLayerIOSurface() { | |
274 return root_plane_->io_surface; | |
275 } | |
276 | |
277 void CALayerPartialDamageTree::CommitCALayers( | |
278 CALayer* superlayer, | |
279 scoped_ptr<CALayerPartialDamageTree> old_tree, | |
280 float scale_factor, | |
281 const gfx::Rect& pixel_damage_rect) { | |
282 TRACE_EVENT0("gpu", "CALayerPartialDamageTree::CommitCALayers"); | |
283 UpdateRootAndPartialDamagePlanes(std::move(old_tree), pixel_damage_rect); | |
284 UpdateCALayers(superlayer, scale_factor); | |
285 } | |
286 | |
287 } // namespace content | |
OLD | NEW |