| // 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/reflector_impl.h" |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "content/browser/compositor/browser_compositor_output_surface.h" |
| #include "content/browser/compositor/owned_mailbox.h" |
| #include "ui/compositor/layer.h" |
| |
| namespace content { |
| |
| struct ReflectorImpl::LayerData { |
| LayerData(ui::Layer* layer) : layer(layer) {} |
| |
| ui::Layer* layer; |
| bool needs_set_mailbox = false; |
| }; |
| |
| ReflectorImpl::ReflectorImpl(ui::Compositor* mirrored_compositor, |
| ui::Layer* mirroring_layer) |
| : mirrored_compositor_(mirrored_compositor), |
| flip_texture_(false), |
| composition_count_(0), |
| output_surface_(nullptr) { |
| if (mirroring_layer) |
| AddMirroringLayer(mirroring_layer); |
| } |
| |
| ReflectorImpl::~ReflectorImpl() { |
| } |
| |
| void ReflectorImpl::Shutdown() { |
| if (output_surface_) |
| DetachFromOutputSurface(); |
| // Prevent the ReflectorImpl from picking up a new output surface. |
| mirroring_layers_.clear(); |
| } |
| |
| void ReflectorImpl::DetachFromOutputSurface() { |
| DCHECK(output_surface_); |
| output_surface_->SetReflector(nullptr); |
| DCHECK(mailbox_.get()); |
| mailbox_ = nullptr; |
| output_surface_ = nullptr; |
| for (LayerData* layer_data : mirroring_layers_) |
| layer_data->layer->SetShowSolidColorContent(); |
| } |
| |
| void ReflectorImpl::OnSourceSurfaceReady( |
| BrowserCompositorOutputSurface* output_surface) { |
| if (mirroring_layers_.empty()) |
| return; // Was already Shutdown(). |
| if (output_surface == output_surface_) |
| return; // Is already attached. |
| if (output_surface_) |
| DetachFromOutputSurface(); |
| |
| output_surface_ = output_surface; |
| |
| composition_started_callback_ = |
| output_surface_->CreateCompositionStartedCallback(); |
| |
| flip_texture_ = !output_surface->capabilities().flipped_output_surface; |
| |
| output_surface_->SetReflector(this); |
| } |
| |
| void ReflectorImpl::OnMirroringCompositorResized() { |
| for (LayerData* layer_data : mirroring_layers_) |
| layer_data->layer->SchedulePaint(layer_data->layer->bounds()); |
| } |
| |
| void ReflectorImpl::AddMirroringLayer(ui::Layer* layer) { |
| DCHECK(layer->GetCompositor()); |
| DCHECK(mirroring_layers_.end() == FindLayerData(layer)); |
| |
| LayerData* layer_data = new LayerData(layer); |
| if (mailbox_) |
| layer_data->needs_set_mailbox = true; |
| mirroring_layers_.push_back(layer_data); |
| mirrored_compositor_->ScheduleFullRedraw(); |
| |
| layer->GetCompositor()->AddObserver(this); |
| } |
| |
| void ReflectorImpl::RemoveMirroringLayer(ui::Layer* layer) { |
| DCHECK(layer->GetCompositor()); |
| |
| ScopedVector<LayerData>::iterator iter = FindLayerData(layer); |
| DCHECK(iter != mirroring_layers_.end()); |
| (*iter)->layer->SetShowSolidColorContent(); |
| mirroring_layers_.erase(iter); |
| |
| layer->GetCompositor()->RemoveObserver(this); |
| composition_count_--; |
| if (composition_count_ == 0 && !composition_started_callback_.is_null()) |
| composition_started_callback_.Run(); |
| |
| if (mirroring_layers_.empty() && output_surface_) |
| DetachFromOutputSurface(); |
| } |
| |
| void ReflectorImpl::OnCompositingStarted(ui::Compositor* compositor, |
| base::TimeTicks start_time) { |
| if (composition_count_ > 0 && --composition_count_ == 0 && |
| !composition_started_callback_.is_null()) { |
| composition_started_callback_.Run(); |
| } |
| } |
| |
| void ReflectorImpl::OnSourceTextureMailboxUpdated( |
| scoped_refptr<OwnedMailbox> mailbox) { |
| mailbox_ = mailbox; |
| if (mailbox_.get()) { |
| for (LayerData* layer_data : mirroring_layers_) |
| layer_data->needs_set_mailbox = true; |
| |
| // The texture doesn't have the data. Request full redraw on mirrored |
| // compositor so that the full content will be copied to mirroring |
| // compositor. This full redraw should land us in OnSourceSwapBuffers() to |
| // resize the texture appropriately. |
| mirrored_compositor_->ScheduleFullRedraw(); |
| } |
| } |
| |
| void ReflectorImpl::OnSourceSwapBuffers() { |
| if (mirroring_layers_.empty()) { |
| if (!composition_started_callback_.is_null()) |
| composition_started_callback_.Run(); |
| return; |
| } |
| |
| // Should be attached to the source output surface already. |
| DCHECK(mailbox_.get()); |
| |
| gfx::Size size = output_surface_->SurfaceSize(); |
| |
| // Request full redraw on mirroring compositor. |
| for (LayerData* layer_data : mirroring_layers_) |
| UpdateTexture(layer_data, size, layer_data->layer->bounds()); |
| composition_count_ = mirroring_layers_.size(); |
| } |
| |
| void ReflectorImpl::OnSourcePostSubBuffer(const gfx::Rect& rect) { |
| if (mirroring_layers_.empty()) { |
| if (!composition_started_callback_.is_null()) |
| composition_started_callback_.Run(); |
| return; |
| } |
| |
| // Should be attached to the source output surface already. |
| DCHECK(mailbox_.get()); |
| |
| gfx::Size size = output_surface_->SurfaceSize(); |
| |
| int y = rect.y(); |
| // Flip the coordinates to compositor's one. |
| if (flip_texture_) |
| y = size.height() - rect.y() - rect.height(); |
| gfx::Rect mirroring_rect(rect.x(), y, rect.width(), rect.height()); |
| |
| // Request redraw of the dirty portion in mirroring compositor. |
| for (LayerData* layer_data : mirroring_layers_) |
| UpdateTexture(layer_data, size, mirroring_rect); |
| composition_count_ = mirroring_layers_.size(); |
| } |
| |
| static void ReleaseMailbox(scoped_refptr<OwnedMailbox> mailbox, |
| const gpu::SyncToken& sync_token, |
| bool is_lost) { |
| mailbox->UpdateSyncToken(sync_token); |
| } |
| |
| ScopedVector<ReflectorImpl::LayerData>::iterator ReflectorImpl::FindLayerData( |
| ui::Layer* layer) { |
| return std::find_if(mirroring_layers_.begin(), mirroring_layers_.end(), |
| [layer](const LayerData* layer_data) { |
| return layer_data->layer == layer; |
| }); |
| } |
| |
| void ReflectorImpl::UpdateTexture(ReflectorImpl::LayerData* layer_data, |
| const gfx::Size& source_size, |
| const gfx::Rect& redraw_rect) { |
| if (layer_data->needs_set_mailbox) { |
| layer_data->layer->SetTextureMailbox( |
| cc::TextureMailbox(mailbox_->holder()), |
| cc::SingleReleaseCallback::Create(base::Bind(ReleaseMailbox, mailbox_)), |
| source_size); |
| layer_data->needs_set_mailbox = false; |
| } else { |
| layer_data->layer->SetTextureSize(source_size); |
| } |
| layer_data->layer->SetBounds(gfx::Rect(source_size)); |
| layer_data->layer->SetTextureFlipped(flip_texture_); |
| layer_data->layer->SchedulePaint(redraw_rect); |
| } |
| |
| } // namespace content |