OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/browser/compositor/io_surface_layer_mac.h" | |
6 | |
7 #include <CoreFoundation/CoreFoundation.h> | |
8 #include <OpenGL/CGLIOSurface.h> | |
9 #include <OpenGL/CGLRenderers.h> | |
10 #include <OpenGL/gl.h> | |
11 #include <OpenGL/OpenGL.h> | |
12 | |
13 #include "base/debug/trace_event.h" | |
14 #include "base/mac/mac_util.h" | |
15 #include "base/mac/sdk_forward_declarations.h" | |
16 #include "content/browser/compositor/io_surface_context_mac.h" | |
17 #include "content/browser/compositor/io_surface_texture_mac.h" | |
18 #include "ui/base/cocoa/animation_utils.h" | |
19 #include "ui/gfx/size_conversions.h" | |
20 #include "ui/gl/gpu_switching_manager.h" | |
21 | |
22 //////////////////////////////////////////////////////////////////////////////// | |
23 // IOSurfaceLayerHelper | |
24 | |
25 namespace content { | |
26 | |
27 IOSurfaceLayerHelper::IOSurfaceLayerHelper( | |
28 IOSurfaceLayerClient* client, | |
29 IOSurfaceLayer* layer) | |
30 : client_(client), | |
31 layer_(layer), | |
32 needs_display_(false), | |
33 has_pending_frame_(false), | |
34 did_not_draw_counter_(0), | |
35 is_pumping_frames_(false), | |
36 timer_( | |
37 FROM_HERE, | |
38 base::TimeDelta::FromSeconds(1) / 6, | |
39 this, | |
40 &IOSurfaceLayerHelper::TimerFired) {} | |
41 | |
42 IOSurfaceLayerHelper::~IOSurfaceLayerHelper() { | |
43 // Any acks that were waiting on this layer to draw will not occur, so ack | |
44 // them now to prevent blocking the renderer. | |
45 AckPendingFrame(true); | |
46 } | |
47 | |
48 void IOSurfaceLayerHelper::GotNewFrame() { | |
49 // A trace value of 2 indicates that there is a pending swap ack. See | |
50 // canDrawInCGLContext for other value meanings. | |
51 TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 2); | |
52 | |
53 has_pending_frame_ = true; | |
54 needs_display_ = true; | |
55 timer_.Reset(); | |
56 | |
57 // If reqested, draw immediately and don't bother trying to use the | |
58 // isAsynchronous property to ensure smooth animation. If this is while | |
59 // frames are being pumped then ack and display immediately to get a | |
60 // correct-sized frame displayed as soon as possible. | |
61 if (is_pumping_frames_ || client_->IOSurfaceLayerShouldAckImmediately()) { | |
62 SetNeedsDisplayAndDisplayAndAck(); | |
63 } else { | |
64 if (![layer_ isAsynchronous]) | |
65 [layer_ setAsynchronous:YES]; | |
66 } | |
67 } | |
68 | |
69 void IOSurfaceLayerHelper::SetNeedsDisplay() { | |
70 needs_display_ = true; | |
71 } | |
72 | |
73 bool IOSurfaceLayerHelper::CanDraw() { | |
74 // If we return NO 30 times in a row, switch to being synchronous to avoid | |
75 // burning CPU cycles on this callback. | |
76 if (needs_display_) { | |
77 did_not_draw_counter_ = 0; | |
78 } else { | |
79 did_not_draw_counter_ += 1; | |
80 if (did_not_draw_counter_ == 30) | |
81 [layer_ setAsynchronous:NO]; | |
82 } | |
83 | |
84 // Add an instantaneous blip to the PendingSwapAck state to indicate | |
85 // that CoreAnimation asked if a frame is ready. A blip up to to 3 (usually | |
86 // from 2, indicating that a swap ack is pending) indicates that we | |
87 // requested a draw. A blip up to 1 (usually from 0, indicating there is no | |
88 // pending swap ack) indicates that we did not request a draw. This would | |
89 // be more natural to do with a tracing pseudo-thread | |
90 // http://crbug.com/366300 | |
91 TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, needs_display_ ? 3 : 1); | |
92 TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, | |
93 has_pending_frame_ ? 2 : 0); | |
94 | |
95 return needs_display_; | |
96 } | |
97 | |
98 void IOSurfaceLayerHelper::DidDraw(bool success) { | |
99 needs_display_ = false; | |
100 AckPendingFrame(success); | |
101 } | |
102 | |
103 void IOSurfaceLayerHelper::AckPendingFrame(bool success) { | |
104 if (!has_pending_frame_) | |
105 return; | |
106 has_pending_frame_ = false; | |
107 if (success) | |
108 client_->IOSurfaceLayerDidDrawFrame(); | |
109 else | |
110 client_->IOSurfaceLayerHitError(); | |
111 // A trace value of 0 indicates that there is no longer a pending swap ack. | |
112 TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0); | |
113 } | |
114 | |
115 void IOSurfaceLayerHelper::SetNeedsDisplayAndDisplayAndAck() { | |
116 // Drawing using setNeedsDisplay and displayIfNeeded will result in | |
117 // subsequent canDrawInCGLContext callbacks getting dropped, and jerky | |
118 // animation. Disable asynchronous drawing before issuing these calls as a | |
119 // workaround. | |
120 // http://crbug.com/395827 | |
121 if ([layer_ isAsynchronous]) | |
122 [layer_ setAsynchronous:NO]; | |
123 | |
124 [layer_ setNeedsDisplay]; | |
125 DisplayIfNeededAndAck(); | |
126 } | |
127 | |
128 void IOSurfaceLayerHelper::DisplayIfNeededAndAck() { | |
129 if (!needs_display_) | |
130 return; | |
131 | |
132 // As in SetNeedsDisplayAndDisplayAndAck, disable asynchronous drawing before | |
133 // issuing displayIfNeeded. | |
134 // http://crbug.com/395827 | |
135 if ([layer_ isAsynchronous]) | |
136 [layer_ setAsynchronous:NO]; | |
137 | |
138 // Do not bother drawing while pumping new frames -- wait until the waiting | |
139 // block ends to draw any of the new frames. | |
140 if (!is_pumping_frames_) | |
141 [layer_ displayIfNeeded]; | |
142 | |
143 // Calls to setNeedsDisplay can sometimes be ignored, especially if issued | |
144 // rapidly (e.g, with vsync off). This is unacceptable because the failure | |
145 // to ack a single frame will hang the renderer. Ensure that the renderer | |
146 // not be blocked by lying and claiming that we drew the frame. | |
147 AckPendingFrame(true); | |
148 } | |
149 | |
150 void IOSurfaceLayerHelper::TimerFired() { | |
151 DisplayIfNeededAndAck(); | |
152 } | |
153 | |
154 void IOSurfaceLayerHelper::BeginPumpingFrames() { | |
155 is_pumping_frames_ = true; | |
156 } | |
157 | |
158 void IOSurfaceLayerHelper::EndPumpingFrames() { | |
159 is_pumping_frames_ = false; | |
160 DisplayIfNeededAndAck(); | |
161 } | |
162 | |
163 } // namespace content | |
164 | |
165 //////////////////////////////////////////////////////////////////////////////// | |
166 // IOSurfaceLayer | |
167 | |
168 @implementation IOSurfaceLayer | |
169 | |
170 - (id)initWithClient:(content::IOSurfaceLayerClient*)client | |
171 withScaleFactor:(float)scale_factor { | |
172 if (self = [super init]) { | |
173 helper_.reset(new content::IOSurfaceLayerHelper(client, self)); | |
174 | |
175 iosurface_ = content::IOSurfaceTexture::Create(); | |
176 context_ = content::IOSurfaceContext::Get( | |
177 content::IOSurfaceContext::kCALayerContext); | |
178 if (!iosurface_.get() || !context_.get()) { | |
179 LOG(ERROR) << "Failed create CompositingIOSurface or context"; | |
180 [self resetClient]; | |
181 [self release]; | |
182 return nil; | |
183 } | |
184 | |
185 [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; | |
186 [self setAnchorPoint:CGPointMake(0, 0)]; | |
187 // Setting contents gravity is necessary to prevent the layer from being | |
188 // scaled during dyanmic resizes (especially with devtools open). | |
189 [self setContentsGravity:kCAGravityTopLeft]; | |
190 if ([self respondsToSelector:(@selector(setContentsScale:))]) { | |
191 [self setContentsScale:scale_factor]; | |
192 } | |
193 } | |
194 return self; | |
195 } | |
196 | |
197 - (void)dealloc { | |
198 DCHECK(!helper_); | |
199 [super dealloc]; | |
200 } | |
201 | |
202 - (bool)gotFrameWithIOSurface:(IOSurfaceID)io_surface_id | |
203 withPixelSize:(gfx::Size)pixel_size | |
204 withScaleFactor:(float)scale_factor { | |
205 return iosurface_->SetIOSurface(io_surface_id, pixel_size); | |
206 } | |
207 | |
208 - (void)poisonContextAndSharegroup { | |
209 context_->PoisonContextAndSharegroup(); | |
210 } | |
211 | |
212 - (bool)hasBeenPoisoned { | |
213 return context_->HasBeenPoisoned(); | |
214 } | |
215 | |
216 - (float)scaleFactor { | |
217 if ([self respondsToSelector:(@selector(contentsScale))]) | |
218 return [self contentsScale]; | |
219 return 1; | |
220 } | |
221 | |
222 - (int)rendererID { | |
223 GLint current_renderer_id = -1; | |
224 if (CGLGetParameter(context_->cgl_context(), | |
225 kCGLCPCurrentRendererID, | |
226 ¤t_renderer_id) == kCGLNoError) { | |
227 return current_renderer_id & kCGLRendererIDMatchingMask; | |
228 } | |
229 return -1; | |
230 } | |
231 | |
232 - (void)resetClient { | |
233 helper_.reset(); | |
234 } | |
235 | |
236 - (void)gotNewFrame { | |
237 helper_->GotNewFrame(); | |
238 } | |
239 | |
240 - (void)setNeedsDisplayAndDisplayAndAck { | |
241 helper_->SetNeedsDisplayAndDisplayAndAck(); | |
242 } | |
243 | |
244 - (void)displayIfNeededAndAck { | |
245 helper_->DisplayIfNeededAndAck(); | |
246 } | |
247 | |
248 - (void)beginPumpingFrames { | |
249 helper_->BeginPumpingFrames(); | |
250 } | |
251 | |
252 - (void)endPumpingFrames { | |
253 helper_->EndPumpingFrames(); | |
254 } | |
255 | |
256 // The remaining methods implement the CAOpenGLLayer interface. | |
257 | |
258 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { | |
259 if (!context_.get()) | |
260 return [super copyCGLPixelFormatForDisplayMask:mask]; | |
261 return CGLRetainPixelFormat(CGLGetPixelFormat(context_->cgl_context())); | |
262 } | |
263 | |
264 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { | |
265 if (!context_.get()) | |
266 return [super copyCGLContextForPixelFormat:pixelFormat]; | |
267 return CGLRetainContext(context_->cgl_context()); | |
268 } | |
269 | |
270 - (void)setNeedsDisplay { | |
271 if (helper_) | |
272 helper_->SetNeedsDisplay(); | |
273 [super setNeedsDisplay]; | |
274 } | |
275 | |
276 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext | |
277 pixelFormat:(CGLPixelFormatObj)pixelFormat | |
278 forLayerTime:(CFTimeInterval)timeInterval | |
279 displayTime:(const CVTimeStamp*)timeStamp { | |
280 if (helper_) | |
281 return helper_->CanDraw(); | |
282 return NO; | |
283 } | |
284 | |
285 - (void)drawInCGLContext:(CGLContextObj)glContext | |
286 pixelFormat:(CGLPixelFormatObj)pixelFormat | |
287 forLayerTime:(CFTimeInterval)timeInterval | |
288 displayTime:(const CVTimeStamp*)timeStamp { | |
289 TRACE_EVENT0("browser", "IOSurfaceLayer::drawInCGLContext"); | |
290 | |
291 bool draw_succeeded = iosurface_->DrawIOSurface(); | |
292 if (helper_) | |
293 helper_->DidDraw(draw_succeeded); | |
294 | |
295 [super drawInCGLContext:glContext | |
296 pixelFormat:pixelFormat | |
297 forLayerTime:timeInterval | |
298 displayTime:timeStamp]; | |
299 } | |
300 | |
301 @end | |
OLD | NEW |