OLD | NEW |
| (Empty) |
1 // Copyright 2014 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/delegated_frame_host.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 #include <utility> | |
10 #include <vector> | |
11 | |
12 #include "base/callback_helpers.h" | |
13 #include "base/command_line.h" | |
14 #include "base/time/default_tick_clock.h" | |
15 #include "cc/output/compositor_frame.h" | |
16 #include "cc/output/compositor_frame_ack.h" | |
17 #include "cc/output/copy_output_request.h" | |
18 #include "cc/resources/single_release_callback.h" | |
19 #include "cc/resources/texture_mailbox.h" | |
20 #include "cc/surfaces/surface.h" | |
21 #include "cc/surfaces/surface_factory.h" | |
22 #include "cc/surfaces/surface_hittest.h" | |
23 #include "cc/surfaces/surface_manager.h" | |
24 #include "content/browser/compositor/gl_helper.h" | |
25 #include "content/browser/compositor/resize_lock.h" | |
26 #include "content/browser/compositor/surface_utils.h" | |
27 #include "content/browser/gpu/compositor_util.h" | |
28 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | |
29 #include "content/public/common/content_switches.h" | |
30 #include "media/base/video_frame.h" | |
31 #include "media/base/video_util.h" | |
32 #include "skia/ext/image_operations.h" | |
33 #include "third_party/skia/include/core/SkCanvas.h" | |
34 #include "third_party/skia/include/core/SkPaint.h" | |
35 #include "third_party/skia/include/effects/SkLumaColorFilter.h" | |
36 #include "ui/gfx/geometry/dip_util.h" | |
37 | |
38 namespace content { | |
39 | |
40 namespace { | |
41 | |
42 void SatisfyCallback(cc::SurfaceManager* manager, | |
43 cc::SurfaceSequence sequence) { | |
44 std::vector<uint32_t> sequences; | |
45 sequences.push_back(sequence.sequence); | |
46 manager->DidSatisfySequences(sequence.id_namespace, &sequences); | |
47 } | |
48 | |
49 void RequireCallback(cc::SurfaceManager* manager, | |
50 cc::SurfaceId id, | |
51 cc::SurfaceSequence sequence) { | |
52 cc::Surface* surface = manager->GetSurfaceForId(id); | |
53 if (!surface) { | |
54 LOG(ERROR) << "Attempting to require callback on nonexistent surface"; | |
55 return; | |
56 } | |
57 surface->AddDestructionDependency(sequence); | |
58 } | |
59 | |
60 } // namespace | |
61 | |
62 //////////////////////////////////////////////////////////////////////////////// | |
63 // DelegatedFrameHost | |
64 | |
65 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client) | |
66 : client_(client), | |
67 compositor_(nullptr), | |
68 tick_clock_(new base::DefaultTickClock()), | |
69 last_output_surface_id_(0), | |
70 pending_delegated_ack_count_(0), | |
71 skipped_frames_(false), | |
72 background_color_(SK_ColorRED), | |
73 current_scale_factor_(1.f), | |
74 can_lock_compositor_(YES_CAN_LOCK), | |
75 delegated_frame_evictor_(new DelegatedFrameEvictor(this)), | |
76 begin_frame_source_(nullptr) { | |
77 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
78 factory->AddObserver(this); | |
79 id_allocator_ = factory->GetContextFactory()->CreateSurfaceIdAllocator(); | |
80 factory->GetSurfaceManager()->RegisterSurfaceFactoryClient( | |
81 id_allocator_->id_namespace(), this); | |
82 } | |
83 | |
84 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) { | |
85 delegated_frame_evictor_->SetVisible(true); | |
86 | |
87 if (surface_id_.is_null() && | |
88 !released_front_lock_.get()) { | |
89 if (compositor_) | |
90 released_front_lock_ = compositor_->GetCompositorLock(); | |
91 } | |
92 | |
93 if (compositor_) { | |
94 compositor_->SetLatencyInfo(latency_info); | |
95 } | |
96 } | |
97 | |
98 bool DelegatedFrameHost::HasSavedFrame() { | |
99 return delegated_frame_evictor_->HasFrame(); | |
100 } | |
101 | |
102 void DelegatedFrameHost::WasHidden() { | |
103 delegated_frame_evictor_->SetVisible(false); | |
104 released_front_lock_ = NULL; | |
105 } | |
106 | |
107 void DelegatedFrameHost::MaybeCreateResizeLock() { | |
108 if (!ShouldCreateResizeLock()) | |
109 return; | |
110 DCHECK(compositor_); | |
111 | |
112 bool defer_compositor_lock = | |
113 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || | |
114 can_lock_compositor_ == NO_PENDING_COMMIT; | |
115 | |
116 if (can_lock_compositor_ == YES_CAN_LOCK) | |
117 can_lock_compositor_ = YES_DID_LOCK; | |
118 | |
119 resize_lock_ = | |
120 client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock); | |
121 } | |
122 | |
123 bool DelegatedFrameHost::ShouldCreateResizeLock() { | |
124 if (!client_->DelegatedFrameCanCreateResizeLock()) | |
125 return false; | |
126 | |
127 if (resize_lock_) | |
128 return false; | |
129 | |
130 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP(); | |
131 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty()) | |
132 return false; | |
133 | |
134 if (!compositor_) | |
135 return false; | |
136 | |
137 return true; | |
138 } | |
139 | |
140 void DelegatedFrameHost::CopyFromCompositingSurface( | |
141 const gfx::Rect& src_subrect, | |
142 const gfx::Size& output_size, | |
143 const ReadbackRequestCallback& callback, | |
144 const SkColorType preferred_color_type) { | |
145 // Only ARGB888 and RGB565 supported as of now. | |
146 bool format_support = ((preferred_color_type == kAlpha_8_SkColorType) || | |
147 (preferred_color_type == kRGB_565_SkColorType) || | |
148 (preferred_color_type == kN32_SkColorType)); | |
149 DCHECK(format_support); | |
150 if (!CanCopyToBitmap()) { | |
151 callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE); | |
152 return; | |
153 } | |
154 | |
155 scoped_ptr<cc::CopyOutputRequest> request = | |
156 cc::CopyOutputRequest::CreateRequest( | |
157 base::Bind(&CopyFromCompositingSurfaceHasResult, output_size, | |
158 preferred_color_type, callback)); | |
159 if (!src_subrect.IsEmpty()) | |
160 request->set_area(src_subrect); | |
161 RequestCopyOfOutput(std::move(request)); | |
162 } | |
163 | |
164 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame( | |
165 const gfx::Rect& src_subrect, | |
166 const scoped_refptr<media::VideoFrame>& target, | |
167 const base::Callback<void(const gfx::Rect&, bool)>& callback) { | |
168 if (!CanCopyToVideoFrame()) { | |
169 callback.Run(gfx::Rect(), false); | |
170 return; | |
171 } | |
172 | |
173 scoped_ptr<cc::CopyOutputRequest> request = | |
174 cc::CopyOutputRequest::CreateRequest(base::Bind( | |
175 &DelegatedFrameHost:: | |
176 CopyFromCompositingSurfaceHasResultForVideo, | |
177 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class. | |
178 nullptr, | |
179 target, | |
180 callback)); | |
181 request->set_area(src_subrect); | |
182 RequestCopyOfOutput(std::move(request)); | |
183 } | |
184 | |
185 bool DelegatedFrameHost::CanCopyToBitmap() const { | |
186 return compositor_ && | |
187 client_->DelegatedFrameHostGetLayer()->has_external_content(); | |
188 } | |
189 | |
190 bool DelegatedFrameHost::CanCopyToVideoFrame() const { | |
191 return compositor_ && | |
192 client_->DelegatedFrameHostGetLayer()->has_external_content(); | |
193 } | |
194 | |
195 void DelegatedFrameHost::BeginFrameSubscription( | |
196 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) { | |
197 frame_subscriber_ = std::move(subscriber); | |
198 } | |
199 | |
200 void DelegatedFrameHost::EndFrameSubscription() { | |
201 idle_frame_subscriber_textures_.clear(); | |
202 frame_subscriber_.reset(); | |
203 } | |
204 | |
205 uint32_t DelegatedFrameHost::GetSurfaceIdNamespace() { | |
206 return id_allocator_->id_namespace(); | |
207 } | |
208 | |
209 cc::SurfaceId DelegatedFrameHost::SurfaceIdAtPoint( | |
210 cc::SurfaceHittestDelegate* delegate, | |
211 const gfx::Point& point, | |
212 gfx::Point* transformed_point) { | |
213 if (surface_id_.is_null()) | |
214 return surface_id_; | |
215 cc::SurfaceHittest hittest(delegate, GetSurfaceManager()); | |
216 gfx::Transform target_transform; | |
217 cc::SurfaceId target_surface_id = | |
218 hittest.GetTargetSurfaceAtPoint(surface_id_, point, &target_transform); | |
219 *transformed_point = point; | |
220 if (!target_surface_id.is_null()) | |
221 target_transform.TransformPoint(transformed_point); | |
222 return target_surface_id; | |
223 } | |
224 | |
225 void DelegatedFrameHost::TransformPointToLocalCoordSpace( | |
226 const gfx::Point& point, | |
227 cc::SurfaceId original_surface, | |
228 gfx::Point* transformed_point) { | |
229 *transformed_point = point; | |
230 if (surface_id_.is_null() || original_surface == surface_id_) | |
231 return; | |
232 | |
233 gfx::Transform transform; | |
234 cc::SurfaceHittest hittest(nullptr, GetSurfaceManager()); | |
235 if (hittest.GetTransformToTargetSurface(surface_id_, original_surface, | |
236 &transform) && | |
237 transform.GetInverse(&transform)) { | |
238 transform.TransformPoint(transformed_point); | |
239 } | |
240 } | |
241 | |
242 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const { | |
243 // Should skip a frame only when another frame from the renderer is guaranteed | |
244 // to replace it. Otherwise may cause hangs when the renderer is waiting for | |
245 // the completion of latency infos (such as when taking a Snapshot.) | |
246 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || | |
247 can_lock_compositor_ == NO_PENDING_COMMIT || | |
248 !resize_lock_.get()) | |
249 return false; | |
250 | |
251 return size_in_dip != resize_lock_->expected_size(); | |
252 } | |
253 | |
254 void DelegatedFrameHost::WasResized() { | |
255 if (client_->DelegatedFrameHostDesiredSizeInDIP() != | |
256 current_frame_size_in_dip_ && | |
257 !client_->DelegatedFrameHostIsVisible()) | |
258 EvictDelegatedFrame(); | |
259 MaybeCreateResizeLock(); | |
260 UpdateGutters(); | |
261 } | |
262 | |
263 void DelegatedFrameHost::UpdateGutters() { | |
264 if (surface_id_.is_null()) { | |
265 right_gutter_.reset(); | |
266 bottom_gutter_.reset(); | |
267 return; | |
268 } | |
269 if (current_frame_size_in_dip_.width() < | |
270 client_->DelegatedFrameHostDesiredSizeInDIP().width()) { | |
271 right_gutter_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); | |
272 right_gutter_->SetColor(background_color_); | |
273 int width = client_->DelegatedFrameHostDesiredSizeInDIP().width() - | |
274 current_frame_size_in_dip_.width(); | |
275 // The right gutter also includes the bottom-right corner, if necessary. | |
276 int height = client_->DelegatedFrameHostDesiredSizeInDIP().height(); | |
277 right_gutter_->SetBounds( | |
278 gfx::Rect(current_frame_size_in_dip_.width(), 0, width, height)); | |
279 | |
280 client_->DelegatedFrameHostGetLayer()->Add(right_gutter_.get()); | |
281 } else { | |
282 right_gutter_.reset(); | |
283 } | |
284 | |
285 if (current_frame_size_in_dip_.height() < | |
286 client_->DelegatedFrameHostDesiredSizeInDIP().height()) { | |
287 bottom_gutter_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); | |
288 bottom_gutter_->SetColor(background_color_); | |
289 int width = current_frame_size_in_dip_.width(); | |
290 int height = client_->DelegatedFrameHostDesiredSizeInDIP().height() - | |
291 current_frame_size_in_dip_.height(); | |
292 bottom_gutter_->SetBounds( | |
293 gfx::Rect(0, current_frame_size_in_dip_.height(), width, height)); | |
294 client_->DelegatedFrameHostGetLayer()->Add(bottom_gutter_.get()); | |
295 | |
296 } else { | |
297 bottom_gutter_.reset(); | |
298 } | |
299 } | |
300 | |
301 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const { | |
302 if (resize_lock_) | |
303 return resize_lock_->expected_size(); | |
304 else | |
305 return client_->DelegatedFrameHostDesiredSizeInDIP(); | |
306 } | |
307 | |
308 void DelegatedFrameHost::CheckResizeLock() { | |
309 if (!resize_lock_ || | |
310 resize_lock_->expected_size() != current_frame_size_in_dip_) | |
311 return; | |
312 | |
313 // Since we got the size we were looking for, unlock the compositor. But delay | |
314 // the release of the lock until we've kicked a frame with the new texture, to | |
315 // avoid resizing the UI before we have a chance to draw a "good" frame. | |
316 resize_lock_->UnlockCompositor(); | |
317 } | |
318 | |
319 void DelegatedFrameHost::AttemptFrameSubscriberCapture( | |
320 const gfx::Rect& damage_rect) { | |
321 if (!frame_subscriber() || !CanCopyToVideoFrame()) | |
322 return; | |
323 | |
324 const base::TimeTicks now = tick_clock_->NowTicks(); | |
325 base::TimeTicks present_time; | |
326 if (vsync_interval_ <= base::TimeDelta()) { | |
327 present_time = now; | |
328 } else { | |
329 const int64_t intervals_elapsed = (now - vsync_timebase_) / vsync_interval_; | |
330 present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_; | |
331 } | |
332 | |
333 scoped_refptr<media::VideoFrame> frame; | |
334 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; | |
335 if (!frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time, | |
336 &frame, &callback)) | |
337 return; | |
338 | |
339 // Get a texture to re-use; else, create a new one. | |
340 scoped_refptr<OwnedMailbox> subscriber_texture; | |
341 if (!idle_frame_subscriber_textures_.empty()) { | |
342 subscriber_texture = idle_frame_subscriber_textures_.back(); | |
343 idle_frame_subscriber_textures_.pop_back(); | |
344 } else if (GLHelper* helper = | |
345 ImageTransportFactory::GetInstance()->GetGLHelper()) { | |
346 subscriber_texture = new OwnedMailbox(helper); | |
347 } | |
348 | |
349 scoped_ptr<cc::CopyOutputRequest> request = | |
350 cc::CopyOutputRequest::CreateRequest(base::Bind( | |
351 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo, | |
352 AsWeakPtr(), | |
353 subscriber_texture, | |
354 frame, | |
355 base::Bind(callback, present_time))); | |
356 // Setting the source in this copy request asks that the layer abort any prior | |
357 // uncommitted copy requests made on behalf of the same frame subscriber. | |
358 // This will not affect any of the copy requests spawned elsewhere from | |
359 // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for | |
360 // screenshots) since those copy requests do not specify |frame_subscriber()| | |
361 // as a source. | |
362 request->set_source(frame_subscriber()); | |
363 if (subscriber_texture.get()) { | |
364 request->SetTextureMailbox(cc::TextureMailbox( | |
365 subscriber_texture->mailbox(), subscriber_texture->sync_token(), | |
366 subscriber_texture->target())); | |
367 } | |
368 | |
369 if (surface_factory_.get()) { | |
370 // To avoid unnecessary composites, go directly to the Surface rather than | |
371 // through RequestCopyOfOutput (which goes through the browser | |
372 // compositor). | |
373 if (!request_copy_of_output_callback_for_testing_.is_null()) | |
374 request_copy_of_output_callback_for_testing_.Run(std::move(request)); | |
375 else | |
376 surface_factory_->RequestCopyOfSurface(surface_id_, std::move(request)); | |
377 } else { | |
378 request->set_area(gfx::Rect(current_frame_size_in_dip_)); | |
379 RequestCopyOfOutput(std::move(request)); | |
380 } | |
381 } | |
382 | |
383 void DelegatedFrameHost::SwapDelegatedFrame( | |
384 uint32_t output_surface_id, | |
385 scoped_ptr<cc::CompositorFrame> frame) { | |
386 DCHECK(frame->delegated_frame_data.get()); | |
387 cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get(); | |
388 float frame_device_scale_factor = frame->metadata.device_scale_factor; | |
389 | |
390 DCHECK(!frame_data->render_pass_list.empty()); | |
391 | |
392 cc::RenderPass* root_pass = frame_data->render_pass_list.back().get(); | |
393 | |
394 gfx::Size frame_size = root_pass->output_rect.size(); | |
395 gfx::Size frame_size_in_dip = | |
396 gfx::ConvertSizeToDIP(frame_device_scale_factor, frame_size); | |
397 | |
398 gfx::Rect damage_rect = root_pass->damage_rect; | |
399 damage_rect.Intersect(gfx::Rect(frame_size)); | |
400 gfx::Rect damage_rect_in_dip = | |
401 gfx::ConvertRectToDIP(frame_device_scale_factor, damage_rect); | |
402 | |
403 if (ShouldSkipFrame(frame_size_in_dip)) { | |
404 cc::CompositorFrameAck ack; | |
405 cc::TransferableResource::ReturnResources(frame_data->resource_list, | |
406 &ack.resources); | |
407 | |
408 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(), | |
409 frame->metadata.latency_info.begin(), | |
410 frame->metadata.latency_info.end()); | |
411 | |
412 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack); | |
413 skipped_frames_ = true; | |
414 return; | |
415 } | |
416 | |
417 if (skipped_frames_) { | |
418 skipped_frames_ = false; | |
419 damage_rect = gfx::Rect(frame_size); | |
420 damage_rect_in_dip = gfx::Rect(frame_size_in_dip); | |
421 | |
422 // Give the same damage rect to the compositor. | |
423 cc::RenderPass* root_pass = frame_data->render_pass_list.back().get(); | |
424 root_pass->damage_rect = damage_rect; | |
425 } | |
426 | |
427 if (output_surface_id != last_output_surface_id_) { | |
428 // Resource ids are scoped by the output surface. | |
429 // If the originating output surface doesn't match the last one, it | |
430 // indicates the renderer's output surface may have been recreated, in which | |
431 // case we should recreate the DelegatedRendererLayer, to avoid matching | |
432 // resources from the old one with resources from the new one which would | |
433 // have the same id. Changing the layer to showing painted content destroys | |
434 // the DelegatedRendererLayer. | |
435 EvictDelegatedFrame(); | |
436 | |
437 surface_factory_.reset(); | |
438 if (!surface_returned_resources_.empty()) | |
439 SendReturnedDelegatedResources(last_output_surface_id_); | |
440 | |
441 last_output_surface_id_ = output_surface_id; | |
442 } | |
443 bool skip_frame = false; | |
444 pending_delegated_ack_count_++; | |
445 | |
446 background_color_ = frame->metadata.root_background_color; | |
447 | |
448 if (frame_size.IsEmpty()) { | |
449 DCHECK(frame_data->resource_list.empty()); | |
450 EvictDelegatedFrame(); | |
451 } else { | |
452 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
453 cc::SurfaceManager* manager = factory->GetSurfaceManager(); | |
454 if (!surface_factory_) { | |
455 surface_factory_ = | |
456 make_scoped_ptr(new cc::SurfaceFactory(manager, this)); | |
457 } | |
458 if (surface_id_.is_null() || frame_size != current_surface_size_ || | |
459 frame_size_in_dip != current_frame_size_in_dip_) { | |
460 if (!surface_id_.is_null()) | |
461 surface_factory_->Destroy(surface_id_); | |
462 surface_id_ = id_allocator_->GenerateId(); | |
463 surface_factory_->Create(surface_id_); | |
464 // manager must outlive compositors using it. | |
465 client_->DelegatedFrameHostGetLayer()->SetShowSurface( | |
466 surface_id_, | |
467 base::Bind(&SatisfyCallback, base::Unretained(manager)), | |
468 base::Bind(&RequireCallback, base::Unretained(manager)), frame_size, | |
469 frame_device_scale_factor, frame_size_in_dip); | |
470 current_surface_size_ = frame_size; | |
471 current_scale_factor_ = frame_device_scale_factor; | |
472 } | |
473 | |
474 frame->metadata.latency_info.insert(frame->metadata.latency_info.end(), | |
475 skipped_latency_info_list_.begin(), | |
476 skipped_latency_info_list_.end()); | |
477 skipped_latency_info_list_.clear(); | |
478 | |
479 gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP(); | |
480 if (desired_size != frame_size_in_dip && !desired_size.IsEmpty()) | |
481 skip_frame = true; | |
482 | |
483 cc::SurfaceFactory::DrawCallback ack_callback; | |
484 if (compositor_ && !skip_frame) { | |
485 ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn, | |
486 AsWeakPtr(), output_surface_id); | |
487 } | |
488 surface_factory_->SubmitCompositorFrame(surface_id_, std::move(frame), | |
489 ack_callback); | |
490 } | |
491 released_front_lock_ = NULL; | |
492 current_frame_size_in_dip_ = frame_size_in_dip; | |
493 CheckResizeLock(); | |
494 | |
495 UpdateGutters(); | |
496 | |
497 if (!damage_rect_in_dip.IsEmpty()) | |
498 client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage( | |
499 damage_rect_in_dip); | |
500 | |
501 // Note that |compositor_| may be reset by SetShowSurface or | |
502 // SetShowDelegatedContent above. | |
503 if (!compositor_ || skip_frame) { | |
504 SendDelegatedFrameAck(output_surface_id); | |
505 } else { | |
506 can_lock_compositor_ = NO_PENDING_COMMIT; | |
507 } | |
508 if (!surface_id_.is_null()) | |
509 delegated_frame_evictor_->SwappedFrame( | |
510 client_->DelegatedFrameHostIsVisible()); | |
511 // Note: the frame may have been evicted immediately. | |
512 } | |
513 | |
514 void DelegatedFrameHost::ClearDelegatedFrame() { | |
515 if (!surface_id_.is_null()) | |
516 EvictDelegatedFrame(); | |
517 } | |
518 | |
519 void DelegatedFrameHost::SendDelegatedFrameAck(uint32_t output_surface_id) { | |
520 cc::CompositorFrameAck ack; | |
521 if (!surface_returned_resources_.empty()) | |
522 ack.resources.swap(surface_returned_resources_); | |
523 client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack); | |
524 DCHECK_GT(pending_delegated_ack_count_, 0); | |
525 pending_delegated_ack_count_--; | |
526 } | |
527 | |
528 void DelegatedFrameHost::SurfaceDrawn(uint32_t output_surface_id, | |
529 cc::SurfaceDrawStatus drawn) { | |
530 SendDelegatedFrameAck(output_surface_id); | |
531 } | |
532 | |
533 void DelegatedFrameHost::SendReturnedDelegatedResources( | |
534 uint32_t output_surface_id) { | |
535 cc::CompositorFrameAck ack; | |
536 DCHECK(!surface_returned_resources_.empty()); | |
537 ack.resources.swap(surface_returned_resources_); | |
538 | |
539 client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id, | |
540 ack); | |
541 } | |
542 | |
543 void DelegatedFrameHost::ReturnResources( | |
544 const cc::ReturnedResourceArray& resources) { | |
545 if (resources.empty()) | |
546 return; | |
547 std::copy(resources.begin(), | |
548 resources.end(), | |
549 std::back_inserter(surface_returned_resources_)); | |
550 if (!pending_delegated_ack_count_) | |
551 SendReturnedDelegatedResources(last_output_surface_id_); | |
552 } | |
553 | |
554 void DelegatedFrameHost::WillDrawSurface(cc::SurfaceId id, | |
555 const gfx::Rect& damage_rect) { | |
556 if (id != surface_id_) | |
557 return; | |
558 AttemptFrameSubscriberCapture(damage_rect); | |
559 } | |
560 | |
561 void DelegatedFrameHost::SetBeginFrameSource( | |
562 cc::BeginFrameSource* begin_frame_source) { | |
563 // TODO(enne): forward this to DelegatedFrameHostClient to observe and then to | |
564 // the renderer as an external begin frame source. | |
565 } | |
566 | |
567 void DelegatedFrameHost::EvictDelegatedFrame() { | |
568 client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent(); | |
569 if (!surface_id_.is_null()) { | |
570 surface_factory_->Destroy(surface_id_); | |
571 surface_id_ = cc::SurfaceId(); | |
572 } | |
573 delegated_frame_evictor_->DiscardedFrame(); | |
574 UpdateGutters(); | |
575 } | |
576 | |
577 // static | |
578 void DelegatedFrameHost::ReturnSubscriberTexture( | |
579 base::WeakPtr<DelegatedFrameHost> dfh, | |
580 scoped_refptr<OwnedMailbox> subscriber_texture, | |
581 const gpu::SyncToken& sync_token) { | |
582 if (!subscriber_texture.get()) | |
583 return; | |
584 if (!dfh) | |
585 return; | |
586 | |
587 subscriber_texture->UpdateSyncToken(sync_token); | |
588 | |
589 if (dfh->frame_subscriber_ && subscriber_texture->texture_id()) | |
590 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture); | |
591 } | |
592 | |
593 // static | |
594 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo( | |
595 base::WeakPtr<DelegatedFrameHost> dfh, | |
596 const base::Callback<void(bool)>& callback, | |
597 scoped_refptr<OwnedMailbox> subscriber_texture, | |
598 scoped_ptr<cc::SingleReleaseCallback> release_callback, | |
599 bool result) { | |
600 callback.Run(result); | |
601 | |
602 gpu::SyncToken sync_token; | |
603 if (result) { | |
604 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); | |
605 gl_helper->GenerateSyncToken(&sync_token); | |
606 } | |
607 if (release_callback) { | |
608 // A release callback means the texture came from the compositor, so there | |
609 // should be no |subscriber_texture|. | |
610 DCHECK(!subscriber_texture.get()); | |
611 const bool lost_resource = !sync_token.HasData(); | |
612 release_callback->Run(sync_token, lost_resource); | |
613 } | |
614 ReturnSubscriberTexture(dfh, subscriber_texture, sync_token); | |
615 } | |
616 | |
617 // static | |
618 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo( | |
619 base::WeakPtr<DelegatedFrameHost> dfh, | |
620 scoped_refptr<OwnedMailbox> subscriber_texture, | |
621 scoped_refptr<media::VideoFrame> video_frame, | |
622 const base::Callback<void(const gfx::Rect&, bool)>& callback, | |
623 scoped_ptr<cc::CopyOutputResult> result) { | |
624 base::ScopedClosureRunner scoped_callback_runner( | |
625 base::Bind(callback, gfx::Rect(), false)); | |
626 base::ScopedClosureRunner scoped_return_subscriber_texture( | |
627 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, | |
628 gpu::SyncToken())); | |
629 | |
630 if (!dfh) | |
631 return; | |
632 if (result->IsEmpty()) | |
633 return; | |
634 if (result->size().IsEmpty()) | |
635 return; | |
636 | |
637 // Compute the dest size we want after the letterboxing resize. Make the | |
638 // coordinates and sizes even because we letterbox in YUV space | |
639 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to | |
640 // line up correctly. | |
641 // The video frame's visible_rect() and the result's size() are both physical | |
642 // pixels. | |
643 gfx::Rect region_in_frame = media::ComputeLetterboxRegion( | |
644 video_frame->visible_rect(), result->size()); | |
645 region_in_frame = gfx::Rect(region_in_frame.x() & ~1, | |
646 region_in_frame.y() & ~1, | |
647 region_in_frame.width() & ~1, | |
648 region_in_frame.height() & ~1); | |
649 if (region_in_frame.IsEmpty()) | |
650 return; | |
651 | |
652 if (!result->HasTexture()) { | |
653 DCHECK(result->HasBitmap()); | |
654 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap(); | |
655 // Scale the bitmap to the required size, if necessary. | |
656 SkBitmap scaled_bitmap; | |
657 if (result->size() != region_in_frame.size()) { | |
658 skia::ImageOperations::ResizeMethod method = | |
659 skia::ImageOperations::RESIZE_GOOD; | |
660 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method, | |
661 region_in_frame.width(), | |
662 region_in_frame.height()); | |
663 } else { | |
664 scaled_bitmap = *bitmap.get(); | |
665 } | |
666 | |
667 { | |
668 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); | |
669 | |
670 media::CopyRGBToVideoFrame( | |
671 reinterpret_cast<uint8_t*>(scaled_bitmap.getPixels()), | |
672 scaled_bitmap.rowBytes(), region_in_frame, video_frame.get()); | |
673 } | |
674 ignore_result(scoped_callback_runner.Release()); | |
675 callback.Run(region_in_frame, true); | |
676 return; | |
677 } | |
678 | |
679 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
680 GLHelper* gl_helper = factory->GetGLHelper(); | |
681 if (!gl_helper) | |
682 return; | |
683 if (subscriber_texture.get() && !subscriber_texture->texture_id()) | |
684 return; | |
685 | |
686 cc::TextureMailbox texture_mailbox; | |
687 scoped_ptr<cc::SingleReleaseCallback> release_callback; | |
688 result->TakeTexture(&texture_mailbox, &release_callback); | |
689 DCHECK(texture_mailbox.IsTexture()); | |
690 | |
691 gfx::Rect result_rect(result->size()); | |
692 | |
693 content::ReadbackYUVInterface* yuv_readback_pipeline = | |
694 dfh->yuv_readback_pipeline_.get(); | |
695 if (yuv_readback_pipeline == NULL || | |
696 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() || | |
697 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect || | |
698 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) { | |
699 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST; | |
700 std::string quality_switch = switches::kTabCaptureDownscaleQuality; | |
701 // If we're scaling up, we can use the "best" quality. | |
702 if (result_rect.size().width() < region_in_frame.size().width() && | |
703 result_rect.size().height() < region_in_frame.size().height()) | |
704 quality_switch = switches::kTabCaptureUpscaleQuality; | |
705 | |
706 std::string switch_value = | |
707 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
708 quality_switch); | |
709 if (switch_value == "fast") | |
710 quality = GLHelper::SCALER_QUALITY_FAST; | |
711 else if (switch_value == "good") | |
712 quality = GLHelper::SCALER_QUALITY_GOOD; | |
713 else if (switch_value == "best") | |
714 quality = GLHelper::SCALER_QUALITY_BEST; | |
715 | |
716 dfh->yuv_readback_pipeline_.reset( | |
717 gl_helper->CreateReadbackPipelineYUV(quality, | |
718 result_rect.size(), | |
719 result_rect, | |
720 region_in_frame.size(), | |
721 true, | |
722 true)); | |
723 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get(); | |
724 } | |
725 | |
726 ignore_result(scoped_callback_runner.Release()); | |
727 ignore_result(scoped_return_subscriber_texture.Release()); | |
728 | |
729 base::Callback<void(bool result)> finished_callback = base::Bind( | |
730 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo, | |
731 dfh->AsWeakPtr(), base::Bind(callback, region_in_frame), | |
732 subscriber_texture, base::Passed(&release_callback)); | |
733 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(), | |
734 texture_mailbox.sync_token(), | |
735 video_frame.get(), | |
736 region_in_frame.origin(), | |
737 finished_callback); | |
738 } | |
739 | |
740 //////////////////////////////////////////////////////////////////////////////// | |
741 // DelegatedFrameHost, ui::CompositorObserver implementation: | |
742 | |
743 void DelegatedFrameHost::OnCompositingDidCommit( | |
744 ui::Compositor* compositor) { | |
745 if (can_lock_compositor_ == NO_PENDING_COMMIT) { | |
746 can_lock_compositor_ = YES_CAN_LOCK; | |
747 if (resize_lock_.get() && resize_lock_->GrabDeferredLock()) | |
748 can_lock_compositor_ = YES_DID_LOCK; | |
749 } | |
750 if (resize_lock_ && | |
751 resize_lock_->expected_size() == current_frame_size_in_dip_) { | |
752 resize_lock_.reset(); | |
753 client_->DelegatedFrameHostResizeLockWasReleased(); | |
754 // We may have had a resize while we had the lock (e.g. if the lock expired, | |
755 // or if the UI still gave us some resizes), so make sure we grab a new lock | |
756 // if necessary. | |
757 MaybeCreateResizeLock(); | |
758 } | |
759 } | |
760 | |
761 void DelegatedFrameHost::OnCompositingStarted( | |
762 ui::Compositor* compositor, base::TimeTicks start_time) { | |
763 last_draw_ended_ = start_time; | |
764 } | |
765 | |
766 void DelegatedFrameHost::OnCompositingEnded( | |
767 ui::Compositor* compositor) { | |
768 } | |
769 | |
770 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) { | |
771 } | |
772 | |
773 void DelegatedFrameHost::OnCompositingLockStateChanged( | |
774 ui::Compositor* compositor) { | |
775 // A compositor lock that is part of a resize lock timed out. We | |
776 // should display a renderer frame. | |
777 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) { | |
778 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME; | |
779 } | |
780 } | |
781 | |
782 void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) { | |
783 DCHECK_EQ(compositor, compositor_); | |
784 ResetCompositor(); | |
785 DCHECK(!compositor_); | |
786 } | |
787 | |
788 void DelegatedFrameHost::OnUpdateVSyncParameters( | |
789 base::TimeTicks timebase, | |
790 base::TimeDelta interval) { | |
791 SetVSyncParameters(timebase, interval); | |
792 if (client_->DelegatedFrameHostIsVisible()) | |
793 client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval); | |
794 } | |
795 | |
796 //////////////////////////////////////////////////////////////////////////////// | |
797 // DelegatedFrameHost, ImageTransportFactoryObserver implementation: | |
798 | |
799 void DelegatedFrameHost::OnLostResources() { | |
800 if (!surface_id_.is_null()) | |
801 EvictDelegatedFrame(); | |
802 idle_frame_subscriber_textures_.clear(); | |
803 yuv_readback_pipeline_.reset(); | |
804 | |
805 client_->DelegatedFrameHostOnLostCompositorResources(); | |
806 } | |
807 | |
808 //////////////////////////////////////////////////////////////////////////////// | |
809 // DelegatedFrameHost, private: | |
810 | |
811 DelegatedFrameHost::~DelegatedFrameHost() { | |
812 DCHECK(!compositor_); | |
813 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
814 factory->RemoveObserver(this); | |
815 | |
816 if (!surface_id_.is_null()) | |
817 surface_factory_->Destroy(surface_id_); | |
818 factory->GetSurfaceManager()->UnregisterSurfaceFactoryClient( | |
819 id_allocator_->id_namespace()); | |
820 | |
821 DCHECK(!vsync_manager_.get()); | |
822 } | |
823 | |
824 void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) { | |
825 DCHECK(!compositor_); | |
826 if (!compositor) | |
827 return; | |
828 compositor_ = compositor; | |
829 compositor_->AddObserver(this); | |
830 DCHECK(!vsync_manager_.get()); | |
831 vsync_manager_ = compositor_->vsync_manager(); | |
832 vsync_manager_->AddObserver(this); | |
833 | |
834 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
835 uint32_t parent = compositor->surface_id_allocator()->id_namespace(); | |
836 factory->GetSurfaceManager()->RegisterSurfaceNamespaceHierarchy( | |
837 parent, id_allocator_->id_namespace()); | |
838 } | |
839 | |
840 void DelegatedFrameHost::ResetCompositor() { | |
841 if (!compositor_) | |
842 return; | |
843 if (resize_lock_) { | |
844 resize_lock_.reset(); | |
845 client_->DelegatedFrameHostResizeLockWasReleased(); | |
846 } | |
847 if (compositor_->HasObserver(this)) | |
848 compositor_->RemoveObserver(this); | |
849 if (vsync_manager_.get()) { | |
850 vsync_manager_->RemoveObserver(this); | |
851 vsync_manager_ = NULL; | |
852 } | |
853 | |
854 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
855 uint32_t parent = compositor_->surface_id_allocator()->id_namespace(); | |
856 factory->GetSurfaceManager()->UnregisterSurfaceNamespaceHierarchy( | |
857 parent, id_allocator_->id_namespace()); | |
858 | |
859 compositor_ = nullptr; | |
860 } | |
861 | |
862 void DelegatedFrameHost::SetVSyncParameters(const base::TimeTicks& timebase, | |
863 const base::TimeDelta& interval) { | |
864 vsync_timebase_ = timebase; | |
865 vsync_interval_ = interval; | |
866 } | |
867 | |
868 void DelegatedFrameHost::LockResources() { | |
869 DCHECK(!surface_id_.is_null()); | |
870 delegated_frame_evictor_->LockFrame(); | |
871 } | |
872 | |
873 void DelegatedFrameHost::RequestCopyOfOutput( | |
874 scoped_ptr<cc::CopyOutputRequest> request) { | |
875 if (!request_copy_of_output_callback_for_testing_.is_null()) { | |
876 request_copy_of_output_callback_for_testing_.Run(std::move(request)); | |
877 } else { | |
878 client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput( | |
879 std::move(request)); | |
880 } | |
881 } | |
882 | |
883 void DelegatedFrameHost::UnlockResources() { | |
884 DCHECK(!surface_id_.is_null()); | |
885 delegated_frame_evictor_->UnlockFrame(); | |
886 } | |
887 | |
888 //////////////////////////////////////////////////////////////////////////////// | |
889 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation: | |
890 | |
891 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer, | |
892 ui::Layer* new_layer) { | |
893 // The new_layer is the one that will be used by our Window, so that's the one | |
894 // that should keep our frame. old_layer will be returned to the | |
895 // RecreateLayer caller, and should have a copy. | |
896 if (!surface_id_.is_null()) { | |
897 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); | |
898 cc::SurfaceManager* manager = factory->GetSurfaceManager(); | |
899 new_layer->SetShowSurface( | |
900 surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)), | |
901 base::Bind(&RequireCallback, base::Unretained(manager)), | |
902 current_surface_size_, current_scale_factor_, | |
903 current_frame_size_in_dip_); | |
904 } | |
905 } | |
906 | |
907 } // namespace content | |
OLD | NEW |