| 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 |