OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/gpu/image_transport_surface_overlay_mac.h" | 5 #include "content/common/gpu/image_transport_surface_overlay_mac.h" |
6 | 6 |
7 #include <CoreGraphics/CoreGraphics.h> | 7 #include <CoreGraphics/CoreGraphics.h> |
8 #include <IOSurface/IOSurface.h> | 8 #include <IOSurface/IOSurface.h> |
9 #include <OpenGL/CGLRenderers.h> | 9 #include <OpenGL/CGLRenderers.h> |
10 #include <OpenGL/CGLTypes.h> | 10 #include <OpenGL/CGLTypes.h> |
11 #include <OpenGL/gl.h> | 11 #include <OpenGL/gl.h> |
12 #include <stddef.h> | 12 #include <stddef.h> |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 | 15 |
16 // This type consistently causes problem on Mac, and needs to be dealt with | 16 // This type consistently causes problem on Mac, and needs to be dealt with |
17 // in a systemic way. | 17 // in a systemic way. |
18 // http://crbug.com/517208 | 18 // http://crbug.com/517208 |
19 #ifndef GL_OES_EGL_image | 19 #ifndef GL_OES_EGL_image |
20 typedef void* GLeglImageOES; | 20 typedef void* GLeglImageOES; |
21 #endif | 21 #endif |
22 | 22 |
23 #include "base/command_line.h" | |
24 #include "base/mac/scoped_cftyperef.h" | 23 #include "base/mac/scoped_cftyperef.h" |
25 #include "base/mac/sdk_forward_declarations.h" | 24 #include "content/common/gpu/ca_layer_partial_damage_tree_mac.h" |
26 #include "content/common/gpu/ca_layer_tree_mac.h" | 25 #include "content/common/gpu/ca_layer_tree_mac.h" |
27 #include "content/common/gpu/gpu_messages.h" | 26 #include "content/common/gpu/gpu_messages.h" |
28 #include "ui/accelerated_widget_mac/io_surface_context.h" | 27 #include "ui/accelerated_widget_mac/io_surface_context.h" |
29 #include "ui/base/cocoa/animation_utils.h" | 28 #include "ui/base/cocoa/animation_utils.h" |
30 #include "ui/base/cocoa/remote_layer_api.h" | 29 #include "ui/base/cocoa/remote_layer_api.h" |
31 #include "ui/base/ui_base_switches.h" | |
32 #include "ui/gfx/geometry/rect_conversions.h" | 30 #include "ui/gfx/geometry/rect_conversions.h" |
33 #include "ui/gfx/transform.h" | 31 #include "ui/gfx/transform.h" |
34 #include "ui/gl/gl_context.h" | 32 #include "ui/gl/gl_context.h" |
35 #include "ui/gl/gl_fence.h" | 33 #include "ui/gl/gl_fence.h" |
36 #include "ui/gl/gl_image_io_surface.h" | 34 #include "ui/gl/gl_image_io_surface.h" |
37 #include "ui/gl/gpu_switching_manager.h" | 35 #include "ui/gl/gpu_switching_manager.h" |
38 #include "ui/gl/scoped_api.h" | 36 #include "ui/gl/scoped_api.h" |
39 #include "ui/gl/scoped_cgl.h" | 37 #include "ui/gl/scoped_cgl.h" |
40 | 38 |
41 namespace { | 39 namespace { |
(...skipping 10 matching lines...) Expand all Loading... |
52 // the next frame. | 50 // the next frame. |
53 const double kVSyncIntervalFractionForDisplayCallback = 0.5; | 51 const double kVSyncIntervalFractionForDisplayCallback = 0.5; |
54 | 52 |
55 // If swaps arrive regularly and nearly at the vsync rate, then attempt to | 53 // If swaps arrive regularly and nearly at the vsync rate, then attempt to |
56 // make animation smooth (each frame is shown for one vsync interval) by sending | 54 // make animation smooth (each frame is shown for one vsync interval) by sending |
57 // them to the window server only when their GL work completes. If frames are | 55 // them to the window server only when their GL work completes. If frames are |
58 // not coming in with each vsync, then just throw them at the window server as | 56 // not coming in with each vsync, then just throw them at the window server as |
59 // they come. | 57 // they come. |
60 const double kMaximumVSyncsBetweenSwapsForSmoothAnimation = 1.5; | 58 const double kMaximumVSyncsBetweenSwapsForSmoothAnimation = 1.5; |
61 | 59 |
62 // When selecting a CALayer to re-use for partial damage, this is the maximum | |
63 // fraction of the merged layer's pixels that may be not-updated by the swap | |
64 // before we consider the CALayer to not be a good enough match, and create a | |
65 // new one. | |
66 const float kMaximumPartialDamageWasteFraction = 1.2f; | |
67 | |
68 // The maximum number of partial damage layers that may be created before we | |
69 // give up and remove them all (doing full damage in the process). | |
70 const size_t kMaximumPartialDamageLayers = 8; | |
71 | |
72 void CheckGLErrors(const char* msg) { | 60 void CheckGLErrors(const char* msg) { |
73 GLenum gl_error; | 61 GLenum gl_error; |
74 while ((gl_error = glGetError()) != GL_NO_ERROR) { | 62 while ((gl_error = glGetError()) != GL_NO_ERROR) { |
75 LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error; | 63 LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error; |
76 } | 64 } |
77 } | 65 } |
78 | 66 |
79 void IOSurfaceContextNoOp(scoped_refptr<ui::IOSurfaceContext>) { | 67 void IOSurfaceContextNoOp(scoped_refptr<ui::IOSurfaceContext>) { |
80 } | 68 } |
81 | 69 |
82 } // namespace | 70 } // namespace |
83 | 71 |
84 @interface CALayer(Private) | 72 @interface CALayer(Private) |
85 -(void)setContentsChanged; | 73 -(void)setContentsChanged; |
86 @end | 74 @end |
87 | 75 |
88 namespace content { | 76 namespace content { |
89 | 77 |
90 scoped_refptr<gfx::GLSurface> ImageTransportSurfaceCreateNativeSurface( | 78 scoped_refptr<gfx::GLSurface> ImageTransportSurfaceCreateNativeSurface( |
91 GpuChannelManager* manager, | 79 GpuChannelManager* manager, |
92 GpuCommandBufferStub* stub, | 80 GpuCommandBufferStub* stub, |
93 gfx::PluginWindowHandle handle) { | 81 gfx::PluginWindowHandle handle) { |
94 return new ImageTransportSurfaceOverlayMac(manager, stub, handle); | 82 return new ImageTransportSurfaceOverlayMac(manager, stub, handle); |
95 } | 83 } |
96 | 84 |
97 class CALayerPartialDamageTree { | |
98 public: | |
99 CALayerPartialDamageTree(bool allow_partial_swap, | |
100 base::ScopedCFTypeRef<IOSurfaceRef> io_surface, | |
101 const gfx::Rect& pixel_frame_rect); | |
102 ~CALayerPartialDamageTree(); | |
103 | |
104 base::ScopedCFTypeRef<IOSurfaceRef> RootLayerIOSurface(); | |
105 void CommitCALayers(CALayer* superlayer, | |
106 scoped_ptr<CALayerPartialDamageTree> old_tree, | |
107 float scale_factor, | |
108 const gfx::Rect& pixel_damage_rect); | |
109 | |
110 private: | |
111 class OverlayPlane; | |
112 | |
113 void UpdateRootAndPartialDamagePlanes(CALayerPartialDamageTree* old_tree, | |
114 const gfx::RectF& pixel_damage_rect); | |
115 void UpdateRootAndPartialDamageCALayers(CALayer* superlayer, | |
116 float scale_factor); | |
117 | |
118 const bool allow_partial_swap_; | |
119 linked_ptr<OverlayPlane> root_plane_; | |
120 std::list<linked_ptr<OverlayPlane>> partial_damage_planes_; | |
121 }; | |
122 | |
123 class CALayerPartialDamageTree::OverlayPlane { | |
124 public: | |
125 static linked_ptr<OverlayPlane> CreateWithFrameRect( | |
126 int z_order, | |
127 base::ScopedCFTypeRef<IOSurfaceRef> io_surface, | |
128 const gfx::RectF& pixel_frame_rect, | |
129 const gfx::RectF& contents_rect) { | |
130 gfx::Transform transform; | |
131 transform.Translate(pixel_frame_rect.x(), pixel_frame_rect.y()); | |
132 return linked_ptr<OverlayPlane>( | |
133 new OverlayPlane(z_order, io_surface, contents_rect, pixel_frame_rect)); | |
134 } | |
135 | |
136 ~OverlayPlane() { | |
137 [ca_layer setContents:nil]; | |
138 [ca_layer removeFromSuperlayer]; | |
139 ca_layer.reset(); | |
140 } | |
141 | |
142 const int z_order; | |
143 const base::ScopedCFTypeRef<IOSurfaceRef> io_surface; | |
144 const gfx::RectF contents_rect; | |
145 const gfx::RectF pixel_frame_rect; | |
146 bool layer_needs_update; | |
147 base::scoped_nsobject<CALayer> ca_layer; | |
148 | |
149 void TakeCALayerFrom(OverlayPlane* other_plane) { | |
150 ca_layer.swap(other_plane->ca_layer); | |
151 } | |
152 | |
153 void UpdateProperties(float scale_factor) { | |
154 if (layer_needs_update) { | |
155 [ca_layer setOpaque:YES]; | |
156 | |
157 id new_contents = static_cast<id>(io_surface.get()); | |
158 if ([ca_layer contents] == new_contents && z_order == 0) | |
159 [ca_layer setContentsChanged]; | |
160 else | |
161 [ca_layer setContents:new_contents]; | |
162 [ca_layer setContentsRect:contents_rect.ToCGRect()]; | |
163 | |
164 [ca_layer setAnchorPoint:CGPointZero]; | |
165 | |
166 if ([ca_layer respondsToSelector:(@selector(setContentsScale:))]) | |
167 [ca_layer setContentsScale:scale_factor]; | |
168 gfx::RectF dip_frame_rect = gfx::RectF(pixel_frame_rect); | |
169 dip_frame_rect.Scale(1 / scale_factor); | |
170 [ca_layer setBounds:CGRectMake(0, 0, dip_frame_rect.width(), | |
171 dip_frame_rect.height())]; | |
172 [ca_layer | |
173 setPosition:CGPointMake(dip_frame_rect.x(), dip_frame_rect.y())]; | |
174 } | |
175 static bool show_borders = | |
176 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
177 switches::kShowMacOverlayBorders); | |
178 if (show_borders) { | |
179 base::ScopedCFTypeRef<CGColorRef> color; | |
180 if (!layer_needs_update) { | |
181 // Green represents contents that are unchanged across frames. | |
182 color.reset(CGColorCreateGenericRGB(0, 1, 0, 1)); | |
183 } else { | |
184 // Red represents damaged contents. | |
185 color.reset(CGColorCreateGenericRGB(1, 0, 0, 1)); | |
186 } | |
187 [ca_layer setBorderWidth:1]; | |
188 [ca_layer setBorderColor:color]; | |
189 } | |
190 layer_needs_update = false; | |
191 } | |
192 | |
193 private: | |
194 OverlayPlane(int z_order, | |
195 base::ScopedCFTypeRef<IOSurfaceRef> io_surface, | |
196 const gfx::RectF& contents_rect, | |
197 const gfx::RectF& pixel_frame_rect) | |
198 : z_order(z_order), | |
199 io_surface(io_surface), | |
200 contents_rect(contents_rect), | |
201 pixel_frame_rect(pixel_frame_rect), | |
202 layer_needs_update(true) {} | |
203 }; | |
204 | |
205 class ImageTransportSurfaceOverlayMac::PendingSwap { | 85 class ImageTransportSurfaceOverlayMac::PendingSwap { |
206 public: | 86 public: |
207 PendingSwap() {} | 87 PendingSwap() {} |
208 ~PendingSwap() { DCHECK(!gl_fence); } | 88 ~PendingSwap() { DCHECK(!gl_fence); } |
209 | 89 |
210 gfx::Size pixel_size; | 90 gfx::Size pixel_size; |
211 float scale_factor; | 91 float scale_factor; |
212 gfx::Rect pixel_damage_rect; | 92 gfx::Rect pixel_damage_rect; |
213 | 93 |
214 scoped_ptr<CALayerPartialDamageTree> partial_damage_tree; | 94 scoped_ptr<CALayerPartialDamageTree> partial_damage_tree; |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 } | 314 } |
435 params.size = swap->pixel_size; | 315 params.size = swap->pixel_size; |
436 params.scale_factor = swap->scale_factor; | 316 params.scale_factor = swap->scale_factor; |
437 params.latency_info.swap(swap->latency_info); | 317 params.latency_info.swap(swap->latency_info); |
438 helper_->SendAcceleratedSurfaceBuffersSwapped(params); | 318 helper_->SendAcceleratedSurfaceBuffersSwapped(params); |
439 | 319 |
440 // Remove this from the queue, and reset any callback timers. | 320 // Remove this from the queue, and reset any callback timers. |
441 pending_swaps_.pop_front(); | 321 pending_swaps_.pop_front(); |
442 } | 322 } |
443 | 323 |
444 void CALayerPartialDamageTree::UpdateRootAndPartialDamagePlanes( | |
445 CALayerPartialDamageTree* old_tree, | |
446 const gfx::RectF& pixel_damage_rect) { | |
447 // This is the plane that will be updated this frame. It may be the root plane | |
448 // or a child plane. | |
449 linked_ptr<OverlayPlane> plane_for_swap; | |
450 | |
451 // If the frame's size changed, if we haven't updated the root layer, if | |
452 // we have full damage, or if we don't support remote layers, then use the | |
453 // root layer directly. | |
454 if (!allow_partial_swap_ || !old_tree || | |
455 old_tree->root_plane_->pixel_frame_rect != | |
456 root_plane_->pixel_frame_rect || | |
457 pixel_damage_rect == root_plane_->pixel_frame_rect) { | |
458 plane_for_swap = root_plane_; | |
459 } | |
460 | |
461 // Walk though the old tree's partial damage layers and see if there is one | |
462 // that is appropriate to re-use. | |
463 if (!plane_for_swap.get() && !pixel_damage_rect.IsEmpty()) { | |
464 gfx::RectF plane_to_reuse_dip_enlarged_rect; | |
465 | |
466 // Find the last partial damage plane to re-use the CALayer from. Grow the | |
467 // new rect for this layer to include this damage, and all nearby partial | |
468 // damage layers. | |
469 linked_ptr<OverlayPlane> plane_to_reuse; | |
470 for (auto& old_plane : old_tree->partial_damage_planes_) { | |
471 gfx::RectF dip_enlarged_rect = old_plane->pixel_frame_rect; | |
472 dip_enlarged_rect.Union(pixel_damage_rect); | |
473 | |
474 // Compute the fraction of the pixels that would not be updated by this | |
475 // swap. If it is too big, try another layer. | |
476 float waste_fraction = dip_enlarged_rect.size().GetArea() * 1.f / | |
477 pixel_damage_rect.size().GetArea(); | |
478 if (waste_fraction > kMaximumPartialDamageWasteFraction) | |
479 continue; | |
480 | |
481 plane_to_reuse = old_plane; | |
482 plane_to_reuse_dip_enlarged_rect.Union(dip_enlarged_rect); | |
483 } | |
484 | |
485 if (plane_to_reuse.get()) { | |
486 gfx::RectF enlarged_contents_rect = plane_to_reuse_dip_enlarged_rect; | |
487 enlarged_contents_rect.Scale(1. / root_plane_->pixel_frame_rect.width(), | |
488 1. / root_plane_->pixel_frame_rect.height()); | |
489 | |
490 plane_for_swap = OverlayPlane::CreateWithFrameRect( | |
491 0, root_plane_->io_surface, plane_to_reuse_dip_enlarged_rect, | |
492 enlarged_contents_rect); | |
493 | |
494 plane_for_swap->TakeCALayerFrom(plane_to_reuse.get()); | |
495 if (plane_to_reuse != old_tree->partial_damage_planes_.back()) | |
496 [plane_for_swap->ca_layer removeFromSuperlayer]; | |
497 } | |
498 } | |
499 | |
500 // If we haven't found an appropriate layer to re-use, create a new one, if | |
501 // we haven't already created too many. | |
502 if (!plane_for_swap.get() && !pixel_damage_rect.IsEmpty() && | |
503 old_tree->partial_damage_planes_.size() < kMaximumPartialDamageLayers) { | |
504 gfx::RectF contents_rect = gfx::RectF(pixel_damage_rect); | |
505 contents_rect.Scale(1. / root_plane_->pixel_frame_rect.width(), | |
506 1. / root_plane_->pixel_frame_rect.height()); | |
507 plane_for_swap = OverlayPlane::CreateWithFrameRect( | |
508 0, root_plane_->io_surface, pixel_damage_rect, contents_rect); | |
509 } | |
510 | |
511 // And if we still don't have a layer, use the root layer. | |
512 if (!plane_for_swap.get() && !pixel_damage_rect.IsEmpty()) | |
513 plane_for_swap = root_plane_; | |
514 | |
515 // Walk all old partial damage planes. Remove anything that is now completely | |
516 // covered, and move everything else into the new |partial_damage_planes_|. | |
517 if (old_tree) { | |
518 for (auto& old_plane : old_tree->partial_damage_planes_) { | |
519 // Intersect the planes' frames with the new root plane to ensure that | |
520 // they don't get kept alive inappropriately. | |
521 gfx::RectF old_plane_frame_rect = old_plane->pixel_frame_rect; | |
522 old_plane_frame_rect.Intersect(root_plane_->pixel_frame_rect); | |
523 | |
524 bool old_plane_covered_by_swap = false; | |
525 if (plane_for_swap.get() && | |
526 plane_for_swap->pixel_frame_rect.Contains(old_plane_frame_rect)) { | |
527 old_plane_covered_by_swap = true; | |
528 } | |
529 if (!old_plane_covered_by_swap) { | |
530 DCHECK(old_plane->ca_layer); | |
531 partial_damage_planes_.push_back(old_plane); | |
532 } | |
533 } | |
534 if (plane_for_swap != root_plane_) | |
535 root_plane_ = old_tree->root_plane_; | |
536 } | |
537 | |
538 // Finally, add the new swap's plane at the back of the list, if it exists. | |
539 if (plane_for_swap.get() && plane_for_swap != root_plane_) { | |
540 partial_damage_planes_.push_back(plane_for_swap); | |
541 } | |
542 } | |
543 | |
544 void CALayerPartialDamageTree::UpdateRootAndPartialDamageCALayers( | |
545 CALayer* superlayer, | |
546 float scale_factor) { | |
547 if (!allow_partial_swap_) { | |
548 DCHECK(partial_damage_planes_.empty()); | |
549 return; | |
550 } | |
551 | |
552 // Allocate and update CALayers for the backbuffer and partial damage layers. | |
553 if (!root_plane_->ca_layer) { | |
554 root_plane_->ca_layer.reset([[CALayer alloc] init]); | |
555 [superlayer setSublayers:nil]; | |
556 [superlayer addSublayer:root_plane_->ca_layer]; | |
557 } | |
558 for (auto& plane : partial_damage_planes_) { | |
559 if (!plane->ca_layer) { | |
560 DCHECK(plane == partial_damage_planes_.back()); | |
561 plane->ca_layer.reset([[CALayer alloc] init]); | |
562 } | |
563 if (![plane->ca_layer superlayer]) { | |
564 DCHECK(plane == partial_damage_planes_.back()); | |
565 [superlayer addSublayer:plane->ca_layer]; | |
566 } | |
567 } | |
568 root_plane_->UpdateProperties(scale_factor); | |
569 for (auto& plane : partial_damage_planes_) | |
570 plane->UpdateProperties(scale_factor); | |
571 } | |
572 | |
573 void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() { | 324 void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() { |
574 TRACE_EVENT0("gpu", | 325 TRACE_EVENT0("gpu", |
575 "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps"); | 326 "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps"); |
576 while (!pending_swaps_.empty()) | 327 while (!pending_swaps_.empty()) |
577 DisplayFirstPendingSwapImmediately(); | 328 DisplayFirstPendingSwapImmediately(); |
578 } | 329 } |
579 | 330 |
580 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() { | 331 void ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback() { |
581 TRACE_EVENT0("gpu", | 332 TRACE_EVENT0("gpu", |
582 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback"); | 333 "ImageTransportSurfaceOverlayMac::CheckPendingSwapsCallback"); |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
758 | 509 |
759 // Compute the previous vsync time. | 510 // Compute the previous vsync time. |
760 base::TimeTicks previous_vsync = | 511 base::TimeTicks previous_vsync = |
761 vsync_interval_ * ((from - vsync_timebase_) / vsync_interval_) + | 512 vsync_interval_ * ((from - vsync_timebase_) / vsync_interval_) + |
762 vsync_timebase_; | 513 vsync_timebase_; |
763 | 514 |
764 // Return |interval_fraction| through the next vsync. | 515 // Return |interval_fraction| through the next vsync. |
765 return previous_vsync + (1 + interval_fraction) * vsync_interval_; | 516 return previous_vsync + (1 + interval_fraction) * vsync_interval_; |
766 } | 517 } |
767 | 518 |
768 CALayerPartialDamageTree::CALayerPartialDamageTree( | |
769 bool allow_partial_swap, | |
770 base::ScopedCFTypeRef<IOSurfaceRef> io_surface, | |
771 const gfx::Rect& pixel_frame_rect) | |
772 : allow_partial_swap_(allow_partial_swap) { | |
773 root_plane_ = OverlayPlane::CreateWithFrameRect( | |
774 0, io_surface, gfx::RectF(pixel_frame_rect), gfx::RectF(0, 0, 1, 1)); | |
775 } | |
776 | |
777 CALayerPartialDamageTree::~CALayerPartialDamageTree() {} | |
778 | |
779 base::ScopedCFTypeRef<IOSurfaceRef> | |
780 CALayerPartialDamageTree::RootLayerIOSurface() { | |
781 return root_plane_->io_surface; | |
782 } | |
783 | |
784 void CALayerPartialDamageTree::CommitCALayers( | |
785 CALayer* superlayer, | |
786 scoped_ptr<CALayerPartialDamageTree> old_tree, | |
787 float scale_factor, | |
788 const gfx::Rect& pixel_damage_rect) { | |
789 UpdateRootAndPartialDamagePlanes(old_tree.get(), | |
790 gfx::RectF(pixel_damage_rect)); | |
791 UpdateRootAndPartialDamageCALayers(superlayer, scale_factor); | |
792 } | |
793 | |
794 } // namespace content | 519 } // namespace content |
OLD | NEW |