// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/compositor/delegated_frame_host.h" #include #include #include #include #include "base/callback_helpers.h" #include "base/command_line.h" #include "base/time/default_tick_clock.h" #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/copy_output_request.h" #include "cc/resources/single_release_callback.h" #include "cc/resources/texture_mailbox.h" #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_factory.h" #include "cc/surfaces/surface_hittest.h" #include "cc/surfaces/surface_manager.h" #include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/resize_lock.h" #include "content/browser/compositor/surface_utils.h" #include "content/browser/gpu/compositor_util.h" #include "content/public/browser/render_widget_host_view_frame_subscriber.h" #include "content/public/common/content_switches.h" #include "media/base/video_frame.h" #include "media/base/video_util.h" #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/effects/SkLumaColorFilter.h" #include "ui/gfx/geometry/dip_util.h" namespace content { namespace { void SatisfyCallback(cc::SurfaceManager* manager, cc::SurfaceSequence sequence) { std::vector sequences; sequences.push_back(sequence.sequence); manager->DidSatisfySequences(sequence.id_namespace, &sequences); } void RequireCallback(cc::SurfaceManager* manager, cc::SurfaceId id, cc::SurfaceSequence sequence) { cc::Surface* surface = manager->GetSurfaceForId(id); if (!surface) { LOG(ERROR) << "Attempting to require callback on nonexistent surface"; return; } surface->AddDestructionDependency(sequence); } } // namespace //////////////////////////////////////////////////////////////////////////////// // DelegatedFrameHost DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client) : client_(client), compositor_(nullptr), tick_clock_(new base::DefaultTickClock()), last_output_surface_id_(0), pending_delegated_ack_count_(0), skipped_frames_(false), background_color_(SK_ColorRED), current_scale_factor_(1.f), can_lock_compositor_(YES_CAN_LOCK), delegated_frame_evictor_(new DelegatedFrameEvictor(this)), begin_frame_source_(nullptr) { ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); factory->AddObserver(this); id_allocator_ = factory->GetContextFactory()->CreateSurfaceIdAllocator(); factory->GetSurfaceManager()->RegisterSurfaceFactoryClient( id_allocator_->id_namespace(), this); } void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) { delegated_frame_evictor_->SetVisible(true); if (surface_id_.is_null() && !released_front_lock_.get()) { if (compositor_) released_front_lock_ = compositor_->GetCompositorLock(); } if (compositor_) { compositor_->SetLatencyInfo(latency_info); } } bool DelegatedFrameHost::HasSavedFrame() { return delegated_frame_evictor_->HasFrame(); } void DelegatedFrameHost::WasHidden() { delegated_frame_evictor_->SetVisible(false); released_front_lock_ = NULL; } void DelegatedFrameHost::MaybeCreateResizeLock() { if (!ShouldCreateResizeLock()) return; DCHECK(compositor_); bool defer_compositor_lock = can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || can_lock_compositor_ == NO_PENDING_COMMIT; if (can_lock_compositor_ == YES_CAN_LOCK) can_lock_compositor_ = YES_DID_LOCK; resize_lock_ = client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock); } bool DelegatedFrameHost::ShouldCreateResizeLock() { if (!client_->DelegatedFrameCanCreateResizeLock()) return false; if (resize_lock_) return false; gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP(); if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty()) return false; if (!compositor_) return false; return true; } void DelegatedFrameHost::CopyFromCompositingSurface( const gfx::Rect& src_subrect, const gfx::Size& output_size, const ReadbackRequestCallback& callback, const SkColorType preferred_color_type) { // Only ARGB888 and RGB565 supported as of now. bool format_support = ((preferred_color_type == kAlpha_8_SkColorType) || (preferred_color_type == kRGB_565_SkColorType) || (preferred_color_type == kN32_SkColorType)); DCHECK(format_support); if (!CanCopyToBitmap()) { callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE); return; } scoped_ptr request = cc::CopyOutputRequest::CreateRequest( base::Bind(&CopyFromCompositingSurfaceHasResult, output_size, preferred_color_type, callback)); if (!src_subrect.IsEmpty()) request->set_area(src_subrect); RequestCopyOfOutput(std::move(request)); } void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame( const gfx::Rect& src_subrect, const scoped_refptr& target, const base::Callback& callback) { if (!CanCopyToVideoFrame()) { callback.Run(gfx::Rect(), false); return; } scoped_ptr request = cc::CopyOutputRequest::CreateRequest(base::Bind( &DelegatedFrameHost:: CopyFromCompositingSurfaceHasResultForVideo, AsWeakPtr(), // For caching the ReadbackYUVInterface on this class. nullptr, target, callback)); request->set_area(src_subrect); RequestCopyOfOutput(std::move(request)); } bool DelegatedFrameHost::CanCopyToBitmap() const { return compositor_ && client_->DelegatedFrameHostGetLayer()->has_external_content(); } bool DelegatedFrameHost::CanCopyToVideoFrame() const { return compositor_ && client_->DelegatedFrameHostGetLayer()->has_external_content(); } void DelegatedFrameHost::BeginFrameSubscription( scoped_ptr subscriber) { frame_subscriber_ = std::move(subscriber); } void DelegatedFrameHost::EndFrameSubscription() { idle_frame_subscriber_textures_.clear(); frame_subscriber_.reset(); } uint32_t DelegatedFrameHost::GetSurfaceIdNamespace() { return id_allocator_->id_namespace(); } cc::SurfaceId DelegatedFrameHost::SurfaceIdAtPoint( cc::SurfaceHittestDelegate* delegate, const gfx::Point& point, gfx::Point* transformed_point) { if (surface_id_.is_null()) return surface_id_; cc::SurfaceHittest hittest(delegate, GetSurfaceManager()); gfx::Transform target_transform; cc::SurfaceId target_surface_id = hittest.GetTargetSurfaceAtPoint(surface_id_, point, &target_transform); *transformed_point = point; if (!target_surface_id.is_null()) target_transform.TransformPoint(transformed_point); return target_surface_id; } void DelegatedFrameHost::TransformPointToLocalCoordSpace( const gfx::Point& point, cc::SurfaceId original_surface, gfx::Point* transformed_point) { *transformed_point = point; if (surface_id_.is_null() || original_surface == surface_id_) return; gfx::Transform transform; cc::SurfaceHittest hittest(nullptr, GetSurfaceManager()); if (hittest.GetTransformToTargetSurface(surface_id_, original_surface, &transform) && transform.GetInverse(&transform)) { transform.TransformPoint(transformed_point); } } bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const { // Should skip a frame only when another frame from the renderer is guaranteed // to replace it. Otherwise may cause hangs when the renderer is waiting for // the completion of latency infos (such as when taking a Snapshot.) if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || can_lock_compositor_ == NO_PENDING_COMMIT || !resize_lock_.get()) return false; return size_in_dip != resize_lock_->expected_size(); } void DelegatedFrameHost::WasResized() { if (client_->DelegatedFrameHostDesiredSizeInDIP() != current_frame_size_in_dip_ && !client_->DelegatedFrameHostIsVisible()) EvictDelegatedFrame(); MaybeCreateResizeLock(); UpdateGutters(); } void DelegatedFrameHost::UpdateGutters() { if (surface_id_.is_null()) { right_gutter_.reset(); bottom_gutter_.reset(); return; } if (current_frame_size_in_dip_.width() < client_->DelegatedFrameHostDesiredSizeInDIP().width()) { right_gutter_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); right_gutter_->SetColor(background_color_); int width = client_->DelegatedFrameHostDesiredSizeInDIP().width() - current_frame_size_in_dip_.width(); // The right gutter also includes the bottom-right corner, if necessary. int height = client_->DelegatedFrameHostDesiredSizeInDIP().height(); right_gutter_->SetBounds( gfx::Rect(current_frame_size_in_dip_.width(), 0, width, height)); client_->DelegatedFrameHostGetLayer()->Add(right_gutter_.get()); } else { right_gutter_.reset(); } if (current_frame_size_in_dip_.height() < client_->DelegatedFrameHostDesiredSizeInDIP().height()) { bottom_gutter_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); bottom_gutter_->SetColor(background_color_); int width = current_frame_size_in_dip_.width(); int height = client_->DelegatedFrameHostDesiredSizeInDIP().height() - current_frame_size_in_dip_.height(); bottom_gutter_->SetBounds( gfx::Rect(0, current_frame_size_in_dip_.height(), width, height)); client_->DelegatedFrameHostGetLayer()->Add(bottom_gutter_.get()); } else { bottom_gutter_.reset(); } } gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const { if (resize_lock_) return resize_lock_->expected_size(); else return client_->DelegatedFrameHostDesiredSizeInDIP(); } void DelegatedFrameHost::CheckResizeLock() { if (!resize_lock_ || resize_lock_->expected_size() != current_frame_size_in_dip_) return; // Since we got the size we were looking for, unlock the compositor. But delay // the release of the lock until we've kicked a frame with the new texture, to // avoid resizing the UI before we have a chance to draw a "good" frame. resize_lock_->UnlockCompositor(); } void DelegatedFrameHost::AttemptFrameSubscriberCapture( const gfx::Rect& damage_rect) { if (!frame_subscriber() || !CanCopyToVideoFrame()) return; const base::TimeTicks now = tick_clock_->NowTicks(); base::TimeTicks present_time; if (vsync_interval_ <= base::TimeDelta()) { present_time = now; } else { const int64_t intervals_elapsed = (now - vsync_timebase_) / vsync_interval_; present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_; } scoped_refptr frame; RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; if (!frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time, &frame, &callback)) return; // Get a texture to re-use; else, create a new one. scoped_refptr subscriber_texture; if (!idle_frame_subscriber_textures_.empty()) { subscriber_texture = idle_frame_subscriber_textures_.back(); idle_frame_subscriber_textures_.pop_back(); } else if (GLHelper* helper = ImageTransportFactory::GetInstance()->GetGLHelper()) { subscriber_texture = new OwnedMailbox(helper); } scoped_ptr request = cc::CopyOutputRequest::CreateRequest(base::Bind( &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo, AsWeakPtr(), subscriber_texture, frame, base::Bind(callback, present_time))); // Setting the source in this copy request asks that the layer abort any prior // uncommitted copy requests made on behalf of the same frame subscriber. // This will not affect any of the copy requests spawned elsewhere from // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for // screenshots) since those copy requests do not specify |frame_subscriber()| // as a source. request->set_source(frame_subscriber()); if (subscriber_texture.get()) { request->SetTextureMailbox(cc::TextureMailbox( subscriber_texture->mailbox(), subscriber_texture->sync_token(), subscriber_texture->target())); } if (surface_factory_.get()) { // To avoid unnecessary composites, go directly to the Surface rather than // through RequestCopyOfOutput (which goes through the browser // compositor). if (!request_copy_of_output_callback_for_testing_.is_null()) request_copy_of_output_callback_for_testing_.Run(std::move(request)); else surface_factory_->RequestCopyOfSurface(surface_id_, std::move(request)); } else { request->set_area(gfx::Rect(current_frame_size_in_dip_)); RequestCopyOfOutput(std::move(request)); } } void DelegatedFrameHost::SwapDelegatedFrame( uint32_t output_surface_id, scoped_ptr frame) { DCHECK(frame->delegated_frame_data.get()); cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get(); float frame_device_scale_factor = frame->metadata.device_scale_factor; DCHECK(!frame_data->render_pass_list.empty()); cc::RenderPass* root_pass = frame_data->render_pass_list.back().get(); gfx::Size frame_size = root_pass->output_rect.size(); gfx::Size frame_size_in_dip = gfx::ConvertSizeToDIP(frame_device_scale_factor, frame_size); gfx::Rect damage_rect = root_pass->damage_rect; damage_rect.Intersect(gfx::Rect(frame_size)); gfx::Rect damage_rect_in_dip = gfx::ConvertRectToDIP(frame_device_scale_factor, damage_rect); if (ShouldSkipFrame(frame_size_in_dip)) { cc::CompositorFrameAck ack; cc::TransferableResource::ReturnResources(frame_data->resource_list, &ack.resources); skipped_latency_info_list_.insert(skipped_latency_info_list_.end(), frame->metadata.latency_info.begin(), frame->metadata.latency_info.end()); client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack); skipped_frames_ = true; return; } if (skipped_frames_) { skipped_frames_ = false; damage_rect = gfx::Rect(frame_size); damage_rect_in_dip = gfx::Rect(frame_size_in_dip); // Give the same damage rect to the compositor. cc::RenderPass* root_pass = frame_data->render_pass_list.back().get(); root_pass->damage_rect = damage_rect; } if (output_surface_id != last_output_surface_id_) { // Resource ids are scoped by the output surface. // If the originating output surface doesn't match the last one, it // indicates the renderer's output surface may have been recreated, in which // case we should recreate the DelegatedRendererLayer, to avoid matching // resources from the old one with resources from the new one which would // have the same id. Changing the layer to showing painted content destroys // the DelegatedRendererLayer. EvictDelegatedFrame(); surface_factory_.reset(); if (!surface_returned_resources_.empty()) SendReturnedDelegatedResources(last_output_surface_id_); last_output_surface_id_ = output_surface_id; } bool skip_frame = false; pending_delegated_ack_count_++; background_color_ = frame->metadata.root_background_color; if (frame_size.IsEmpty()) { DCHECK(frame_data->resource_list.empty()); EvictDelegatedFrame(); } else { ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); cc::SurfaceManager* manager = factory->GetSurfaceManager(); if (!surface_factory_) { surface_factory_ = make_scoped_ptr(new cc::SurfaceFactory(manager, this)); } if (surface_id_.is_null() || frame_size != current_surface_size_ || frame_size_in_dip != current_frame_size_in_dip_) { if (!surface_id_.is_null()) surface_factory_->Destroy(surface_id_); surface_id_ = id_allocator_->GenerateId(); surface_factory_->Create(surface_id_); // manager must outlive compositors using it. client_->DelegatedFrameHostGetLayer()->SetShowSurface( surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)), base::Bind(&RequireCallback, base::Unretained(manager)), frame_size, frame_device_scale_factor, frame_size_in_dip); current_surface_size_ = frame_size; current_scale_factor_ = frame_device_scale_factor; } frame->metadata.latency_info.insert(frame->metadata.latency_info.end(), skipped_latency_info_list_.begin(), skipped_latency_info_list_.end()); skipped_latency_info_list_.clear(); gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP(); if (desired_size != frame_size_in_dip && !desired_size.IsEmpty()) skip_frame = true; cc::SurfaceFactory::DrawCallback ack_callback; if (compositor_ && !skip_frame) { ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn, AsWeakPtr(), output_surface_id); } surface_factory_->SubmitCompositorFrame(surface_id_, std::move(frame), ack_callback); } released_front_lock_ = NULL; current_frame_size_in_dip_ = frame_size_in_dip; CheckResizeLock(); UpdateGutters(); if (!damage_rect_in_dip.IsEmpty()) client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage( damage_rect_in_dip); // Note that |compositor_| may be reset by SetShowSurface or // SetShowDelegatedContent above. if (!compositor_ || skip_frame) { SendDelegatedFrameAck(output_surface_id); } else { can_lock_compositor_ = NO_PENDING_COMMIT; } if (!surface_id_.is_null()) delegated_frame_evictor_->SwappedFrame( client_->DelegatedFrameHostIsVisible()); // Note: the frame may have been evicted immediately. } void DelegatedFrameHost::ClearDelegatedFrame() { if (!surface_id_.is_null()) EvictDelegatedFrame(); } void DelegatedFrameHost::SendDelegatedFrameAck(uint32_t output_surface_id) { cc::CompositorFrameAck ack; if (!surface_returned_resources_.empty()) ack.resources.swap(surface_returned_resources_); client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack); DCHECK_GT(pending_delegated_ack_count_, 0); pending_delegated_ack_count_--; } void DelegatedFrameHost::SurfaceDrawn(uint32_t output_surface_id, cc::SurfaceDrawStatus drawn) { SendDelegatedFrameAck(output_surface_id); } void DelegatedFrameHost::SendReturnedDelegatedResources( uint32_t output_surface_id) { cc::CompositorFrameAck ack; DCHECK(!surface_returned_resources_.empty()); ack.resources.swap(surface_returned_resources_); client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id, ack); } void DelegatedFrameHost::ReturnResources( const cc::ReturnedResourceArray& resources) { if (resources.empty()) return; std::copy(resources.begin(), resources.end(), std::back_inserter(surface_returned_resources_)); if (!pending_delegated_ack_count_) SendReturnedDelegatedResources(last_output_surface_id_); } void DelegatedFrameHost::WillDrawSurface(cc::SurfaceId id, const gfx::Rect& damage_rect) { if (id != surface_id_) return; AttemptFrameSubscriberCapture(damage_rect); } void DelegatedFrameHost::SetBeginFrameSource( cc::BeginFrameSource* begin_frame_source) { // TODO(enne): forward this to DelegatedFrameHostClient to observe and then to // the renderer as an external begin frame source. } void DelegatedFrameHost::EvictDelegatedFrame() { client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent(); if (!surface_id_.is_null()) { surface_factory_->Destroy(surface_id_); surface_id_ = cc::SurfaceId(); } delegated_frame_evictor_->DiscardedFrame(); UpdateGutters(); } // static void DelegatedFrameHost::ReturnSubscriberTexture( base::WeakPtr dfh, scoped_refptr subscriber_texture, const gpu::SyncToken& sync_token) { if (!subscriber_texture.get()) return; if (!dfh) return; subscriber_texture->UpdateSyncToken(sync_token); if (dfh->frame_subscriber_ && subscriber_texture->texture_id()) dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture); } // static void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo( base::WeakPtr dfh, const base::Callback& callback, scoped_refptr subscriber_texture, scoped_ptr release_callback, bool result) { callback.Run(result); gpu::SyncToken sync_token; if (result) { GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); gl_helper->GenerateSyncToken(&sync_token); } if (release_callback) { // A release callback means the texture came from the compositor, so there // should be no |subscriber_texture|. DCHECK(!subscriber_texture.get()); const bool lost_resource = !sync_token.HasData(); release_callback->Run(sync_token, lost_resource); } ReturnSubscriberTexture(dfh, subscriber_texture, sync_token); } // static void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo( base::WeakPtr dfh, scoped_refptr subscriber_texture, scoped_refptr video_frame, const base::Callback& callback, scoped_ptr result) { base::ScopedClosureRunner scoped_callback_runner( base::Bind(callback, gfx::Rect(), false)); base::ScopedClosureRunner scoped_return_subscriber_texture( base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, gpu::SyncToken())); if (!dfh) return; if (result->IsEmpty()) return; if (result->size().IsEmpty()) return; // Compute the dest size we want after the letterboxing resize. Make the // coordinates and sizes even because we letterbox in YUV space // (see CopyRGBToVideoFrame). They need to be even for the UV samples to // line up correctly. // The video frame's visible_rect() and the result's size() are both physical // pixels. gfx::Rect region_in_frame = media::ComputeLetterboxRegion( video_frame->visible_rect(), result->size()); region_in_frame = gfx::Rect(region_in_frame.x() & ~1, region_in_frame.y() & ~1, region_in_frame.width() & ~1, region_in_frame.height() & ~1); if (region_in_frame.IsEmpty()) return; if (!result->HasTexture()) { DCHECK(result->HasBitmap()); scoped_ptr bitmap = result->TakeBitmap(); // Scale the bitmap to the required size, if necessary. SkBitmap scaled_bitmap; if (result->size() != region_in_frame.size()) { skia::ImageOperations::ResizeMethod method = skia::ImageOperations::RESIZE_GOOD; scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method, region_in_frame.width(), region_in_frame.height()); } else { scaled_bitmap = *bitmap.get(); } { SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); media::CopyRGBToVideoFrame( reinterpret_cast(scaled_bitmap.getPixels()), scaled_bitmap.rowBytes(), region_in_frame, video_frame.get()); } ignore_result(scoped_callback_runner.Release()); callback.Run(region_in_frame, true); return; } ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); GLHelper* gl_helper = factory->GetGLHelper(); if (!gl_helper) return; if (subscriber_texture.get() && !subscriber_texture->texture_id()) return; cc::TextureMailbox texture_mailbox; scoped_ptr release_callback; result->TakeTexture(&texture_mailbox, &release_callback); DCHECK(texture_mailbox.IsTexture()); gfx::Rect result_rect(result->size()); content::ReadbackYUVInterface* yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get(); if (yuv_readback_pipeline == NULL || yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() || yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect || yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) { GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST; std::string quality_switch = switches::kTabCaptureDownscaleQuality; // If we're scaling up, we can use the "best" quality. if (result_rect.size().width() < region_in_frame.size().width() && result_rect.size().height() < region_in_frame.size().height()) quality_switch = switches::kTabCaptureUpscaleQuality; std::string switch_value = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( quality_switch); if (switch_value == "fast") quality = GLHelper::SCALER_QUALITY_FAST; else if (switch_value == "good") quality = GLHelper::SCALER_QUALITY_GOOD; else if (switch_value == "best") quality = GLHelper::SCALER_QUALITY_BEST; dfh->yuv_readback_pipeline_.reset( gl_helper->CreateReadbackPipelineYUV(quality, result_rect.size(), result_rect, region_in_frame.size(), true, true)); yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get(); } ignore_result(scoped_callback_runner.Release()); ignore_result(scoped_return_subscriber_texture.Release()); base::Callback finished_callback = base::Bind( &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo, dfh->AsWeakPtr(), base::Bind(callback, region_in_frame), subscriber_texture, base::Passed(&release_callback)); yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(), texture_mailbox.sync_token(), video_frame.get(), region_in_frame.origin(), finished_callback); } //////////////////////////////////////////////////////////////////////////////// // DelegatedFrameHost, ui::CompositorObserver implementation: void DelegatedFrameHost::OnCompositingDidCommit( ui::Compositor* compositor) { if (can_lock_compositor_ == NO_PENDING_COMMIT) { can_lock_compositor_ = YES_CAN_LOCK; if (resize_lock_.get() && resize_lock_->GrabDeferredLock()) can_lock_compositor_ = YES_DID_LOCK; } if (resize_lock_ && resize_lock_->expected_size() == current_frame_size_in_dip_) { resize_lock_.reset(); client_->DelegatedFrameHostResizeLockWasReleased(); // We may have had a resize while we had the lock (e.g. if the lock expired, // or if the UI still gave us some resizes), so make sure we grab a new lock // if necessary. MaybeCreateResizeLock(); } } void DelegatedFrameHost::OnCompositingStarted( ui::Compositor* compositor, base::TimeTicks start_time) { last_draw_ended_ = start_time; } void DelegatedFrameHost::OnCompositingEnded( ui::Compositor* compositor) { } void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) { } void DelegatedFrameHost::OnCompositingLockStateChanged( ui::Compositor* compositor) { // A compositor lock that is part of a resize lock timed out. We // should display a renderer frame. if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) { can_lock_compositor_ = NO_PENDING_RENDERER_FRAME; } } void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) { DCHECK_EQ(compositor, compositor_); ResetCompositor(); DCHECK(!compositor_); } void DelegatedFrameHost::OnUpdateVSyncParameters( base::TimeTicks timebase, base::TimeDelta interval) { SetVSyncParameters(timebase, interval); if (client_->DelegatedFrameHostIsVisible()) client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval); } //////////////////////////////////////////////////////////////////////////////// // DelegatedFrameHost, ImageTransportFactoryObserver implementation: void DelegatedFrameHost::OnLostResources() { if (!surface_id_.is_null()) EvictDelegatedFrame(); idle_frame_subscriber_textures_.clear(); yuv_readback_pipeline_.reset(); client_->DelegatedFrameHostOnLostCompositorResources(); } //////////////////////////////////////////////////////////////////////////////// // DelegatedFrameHost, private: DelegatedFrameHost::~DelegatedFrameHost() { DCHECK(!compositor_); ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); factory->RemoveObserver(this); if (!surface_id_.is_null()) surface_factory_->Destroy(surface_id_); factory->GetSurfaceManager()->UnregisterSurfaceFactoryClient( id_allocator_->id_namespace()); DCHECK(!vsync_manager_.get()); } void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) { DCHECK(!compositor_); if (!compositor) return; compositor_ = compositor; compositor_->AddObserver(this); DCHECK(!vsync_manager_.get()); vsync_manager_ = compositor_->vsync_manager(); vsync_manager_->AddObserver(this); ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); uint32_t parent = compositor->surface_id_allocator()->id_namespace(); factory->GetSurfaceManager()->RegisterSurfaceNamespaceHierarchy( parent, id_allocator_->id_namespace()); } void DelegatedFrameHost::ResetCompositor() { if (!compositor_) return; if (resize_lock_) { resize_lock_.reset(); client_->DelegatedFrameHostResizeLockWasReleased(); } if (compositor_->HasObserver(this)) compositor_->RemoveObserver(this); if (vsync_manager_.get()) { vsync_manager_->RemoveObserver(this); vsync_manager_ = NULL; } ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); uint32_t parent = compositor_->surface_id_allocator()->id_namespace(); factory->GetSurfaceManager()->UnregisterSurfaceNamespaceHierarchy( parent, id_allocator_->id_namespace()); compositor_ = nullptr; } void DelegatedFrameHost::SetVSyncParameters(const base::TimeTicks& timebase, const base::TimeDelta& interval) { vsync_timebase_ = timebase; vsync_interval_ = interval; } void DelegatedFrameHost::LockResources() { DCHECK(!surface_id_.is_null()); delegated_frame_evictor_->LockFrame(); } void DelegatedFrameHost::RequestCopyOfOutput( scoped_ptr request) { if (!request_copy_of_output_callback_for_testing_.is_null()) { request_copy_of_output_callback_for_testing_.Run(std::move(request)); } else { client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput( std::move(request)); } } void DelegatedFrameHost::UnlockResources() { DCHECK(!surface_id_.is_null()); delegated_frame_evictor_->UnlockFrame(); } //////////////////////////////////////////////////////////////////////////////// // DelegatedFrameHost, ui::LayerOwnerDelegate implementation: void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer, ui::Layer* new_layer) { // The new_layer is the one that will be used by our Window, so that's the one // that should keep our frame. old_layer will be returned to the // RecreateLayer caller, and should have a copy. if (!surface_id_.is_null()) { ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); cc::SurfaceManager* manager = factory->GetSurfaceManager(); new_layer->SetShowSurface( surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)), base::Bind(&RequireCallback, base::Unretained(manager)), current_surface_size_, current_scale_factor_, current_frame_size_in_dip_); } } } // namespace content