blob: e97162e7b1e0b84a10282fb9073dc71a28ad598f [file] [log] [blame]
// Copyright 2010 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 "cc/layers/texture_layer.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/simple_enclosed_region.h"
#include "cc/layers/texture_layer_client.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/trees/layer_tree_host.h"
#include "components/viz/common/resources/single_release_callback.h"
namespace cc {
scoped_refptr<TextureLayer> TextureLayer::CreateForMailbox(
TextureLayerClient* client) {
return scoped_refptr<TextureLayer>(new TextureLayer(client));
}
TextureLayer::TextureLayer(TextureLayerClient* client)
: client_(client), weak_ptr_factory_(this) {}
TextureLayer::~TextureLayer() = default;
void TextureLayer::ClearClient() {
client_ = nullptr;
ClearTexture();
UpdateDrawsContent(HasDrawableContent());
}
void TextureLayer::ClearTexture() {
SetTransferableResource(viz::TransferableResource(), nullptr);
}
std::unique_ptr<LayerImpl> TextureLayer::CreateLayerImpl(
LayerTreeImpl* tree_impl) {
return TextureLayerImpl::Create(tree_impl, id());
}
void TextureLayer::SetFlipped(bool flipped) {
if (flipped_ == flipped)
return;
flipped_ = flipped;
SetNeedsCommit();
}
void TextureLayer::SetNearestNeighbor(bool nearest_neighbor) {
if (nearest_neighbor_ == nearest_neighbor)
return;
nearest_neighbor_ = nearest_neighbor;
SetNeedsCommit();
}
void TextureLayer::SetUV(const gfx::PointF& top_left,
const gfx::PointF& bottom_right) {
if (uv_top_left_ == top_left && uv_bottom_right_ == bottom_right)
return;
uv_top_left_ = top_left;
uv_bottom_right_ = bottom_right;
SetNeedsCommit();
}
void TextureLayer::SetVertexOpacity(float bottom_left,
float top_left,
float top_right,
float bottom_right) {
// Indexing according to the quad vertex generation:
// 1--2
// | |
// 0--3
if (vertex_opacity_[0] == bottom_left &&
vertex_opacity_[1] == top_left &&
vertex_opacity_[2] == top_right &&
vertex_opacity_[3] == bottom_right)
return;
vertex_opacity_[0] = bottom_left;
vertex_opacity_[1] = top_left;
vertex_opacity_[2] = top_right;
vertex_opacity_[3] = bottom_right;
SetNeedsCommit();
}
void TextureLayer::SetPremultipliedAlpha(bool premultiplied_alpha) {
if (premultiplied_alpha_ == premultiplied_alpha)
return;
premultiplied_alpha_ = premultiplied_alpha;
SetNeedsCommit();
}
void TextureLayer::SetBlendBackgroundColor(bool blend) {
if (blend_background_color_ == blend)
return;
blend_background_color_ = blend;
SetNeedsCommit();
}
void TextureLayer::SetTransferableResourceInternal(
const viz::TransferableResource& resource,
std::unique_ptr<viz::SingleReleaseCallback> release_callback,
bool requires_commit) {
DCHECK(resource.mailbox_holder.mailbox.IsZero() || !holder_ref_ ||
resource != holder_ref_->holder()->resource());
DCHECK_EQ(resource.mailbox_holder.mailbox.IsZero(), !release_callback);
// If we never commited the mailbox, we need to release it here.
if (!resource.mailbox_holder.mailbox.IsZero()) {
holder_ref_ = TransferableResourceHolder::Create(
resource, std::move(release_callback));
} else {
holder_ref_ = nullptr;
}
needs_set_resource_ = true;
// If we are within a commit, no need to do it again immediately after.
if (requires_commit)
SetNeedsCommit();
else
SetNeedsPushProperties();
UpdateDrawsContent(HasDrawableContent());
// The active frame needs to be replaced and the mailbox returned before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
void TextureLayer::SetTransferableResource(
const viz::TransferableResource& resource,
std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
bool requires_commit = true;
SetTransferableResourceInternal(resource, std::move(release_callback),
requires_commit);
}
void TextureLayer::SetNeedsDisplayRect(const gfx::Rect& dirty_rect) {
Layer::SetNeedsDisplayRect(dirty_rect);
}
void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) {
if (layer_tree_host() == host) {
Layer::SetLayerTreeHost(host);
return;
}
// If we're removed from the tree, the TextureLayerImpl will be destroyed, and
// we will need to set the mailbox again on a new TextureLayerImpl the next
// time we push.
if (!host && holder_ref_) {
needs_set_resource_ = true;
// The active frame needs to be replaced and the mailbox returned before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
if (host) {
// When attached to a new LayerTreHost, all previously registered
// SharedBitmapIds will need to be re-sent to the new TextureLayerImpl
// representing this layer on the compositor thread.
to_register_bitmaps_.insert(
std::make_move_iterator(registered_bitmaps_.begin()),
std::make_move_iterator(registered_bitmaps_.end()));
registered_bitmaps_.clear();
}
Layer::SetLayerTreeHost(host);
}
bool TextureLayer::HasDrawableContent() const {
return (client_ || holder_ref_) && Layer::HasDrawableContent();
}
bool TextureLayer::Update() {
bool updated = Layer::Update();
if (client_) {
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
if (client_->PrepareTransferableResource(this, &resource,
&release_callback)) {
// Already within a commit, no need to do another one immediately.
bool requires_commit = false;
SetTransferableResourceInternal(resource, std::move(release_callback),
requires_commit);
updated = true;
}
}
// SetTransferableResource could be called externally and the same mailbox
// used for different textures. Such callers notify this layer that the
// texture has changed by calling SetNeedsDisplay, so check for that here.
return updated || !update_rect().IsEmpty();
}
bool TextureLayer::IsSnappedToPixelGridInTarget() {
// Often layers are positioned with CSS to "50%", which can often leave them
// with a fractional (N + 0.5) pixel position. This would leave them looking
// fuzzy, so we request that TextureLayers are snapped to the pixel grid,
// since their content is generated externally and we can not adjust for it
// inside the content (unlike for PictureLayers).
return true;
}
void TextureLayer::PushPropertiesTo(LayerImpl* layer) {
Layer::PushPropertiesTo(layer);
TRACE_EVENT0("cc", "TextureLayer::PushPropertiesTo");
TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
texture_layer->SetFlipped(flipped_);
texture_layer->SetNearestNeighbor(nearest_neighbor_);
texture_layer->SetUVTopLeft(uv_top_left_);
texture_layer->SetUVBottomRight(uv_bottom_right_);
texture_layer->SetVertexOpacity(vertex_opacity_);
texture_layer->SetPremultipliedAlpha(premultiplied_alpha_);
texture_layer->SetBlendBackgroundColor(blend_background_color_);
if (needs_set_resource_) {
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
if (holder_ref_) {
TransferableResourceHolder* holder = holder_ref_->holder();
resource = holder->resource();
release_callback = holder->GetCallbackForImplThread(
layer_tree_host()->GetTaskRunnerProvider()->MainThreadTaskRunner());
}
texture_layer->SetTransferableResource(resource,
std::move(release_callback));
needs_set_resource_ = false;
}
for (auto& pair : to_register_bitmaps_)
texture_layer->RegisterSharedBitmapId(pair.first, pair.second);
// Store the registered SharedBitmapIds in case we get a new TextureLayerImpl,
// in a new tree, to re-send them to.
registered_bitmaps_.insert(
std::make_move_iterator(to_register_bitmaps_.begin()),
std::make_move_iterator(to_register_bitmaps_.end()));
to_register_bitmaps_.clear();
for (const auto& id : to_unregister_bitmap_ids_)
texture_layer->UnregisterSharedBitmapId(id);
to_unregister_bitmap_ids_.clear();
}
SharedBitmapIdRegistration TextureLayer::RegisterSharedBitmapId(
const viz::SharedBitmapId& id,
scoped_refptr<CrossThreadSharedBitmap> bitmap) {
DCHECK(to_register_bitmaps_.find(id) == to_register_bitmaps_.end());
DCHECK(registered_bitmaps_.find(id) == registered_bitmaps_.end());
to_register_bitmaps_[id] = std::move(bitmap);
base::Erase(to_unregister_bitmap_ids_, id);
// This does not SetNeedsCommit() to be as lazy as possible. Notifying a
// SharedBitmapId is not needed until it is used, and using it will require
// a commit, so we can wait for that commit before forwarding the
// notification instead of forcing it to happen as a side effect of this
// method.
SetNeedsPushProperties();
return SharedBitmapIdRegistration(weak_ptr_factory_.GetWeakPtr(), id);
}
void TextureLayer::UnregisterSharedBitmapId(viz::SharedBitmapId id) {
// If we didn't get to sending the registration to the compositor thread yet,
// just remove it.
to_register_bitmaps_.erase(id);
// Since we also track all previously sent registrations, we must remove that
// to in order to prevent re-registering on another LayerTreeHost.
registered_bitmaps_.erase(id);
to_unregister_bitmap_ids_.push_back(id);
// Unregistering a SharedBitmapId needs to happen eventually to prevent
// leaking the SharedMemory in the display compositor. But this attempts to be
// lazy and not force a commit prematurely, so just requests a
// PushPropertiesTo() without requesting a commit.
SetNeedsPushProperties();
}
TextureLayer::TransferableResourceHolder::MainThreadReference::
MainThreadReference(TransferableResourceHolder* holder)
: holder_(holder) {
holder_->InternalAddRef();
}
TextureLayer::TransferableResourceHolder::MainThreadReference::
~MainThreadReference() {
#if DCHECK_IS_ON()
{
base::AutoLock hold(holder_->posted_internal_derefs_lock_);
++holder_->posted_internal_derefs_;
}
#endif
holder_->InternalRelease();
}
TextureLayer::TransferableResourceHolder::TransferableResourceHolder(
const viz::TransferableResource& resource,
std::unique_ptr<viz::SingleReleaseCallback> release_callback)
: resource_(resource),
release_callback_(std::move(release_callback)),
sync_token_(resource.mailbox_holder.sync_token) {}
TextureLayer::TransferableResourceHolder::~TransferableResourceHolder() {
#if DCHECK_IS_ON()
{
// If the MessageLoop is destroyed while a posted deref is waiting to run,
// this object will be destroyed with an internal_references_ still present.
// So we must also include the outstanding posted derefences.
base::AutoLock hold(posted_internal_derefs_lock_);
DCHECK_EQ(internal_references_, posted_internal_derefs_);
}
#endif
if (release_callback_) {
// We land here if the dereferences are posted but not run and the
// MessageLoop is destroyed, destroying those tasks and this object with it.
// We run the ReleaseCallback in that case assuming the MessageLoop is being
// destroyed on the main thread.
DCHECK(main_thread_checker_.CalledOnValidThread());
release_callback_->Run(sync_token_, is_lost_);
}
}
std::unique_ptr<TextureLayer::TransferableResourceHolder::MainThreadReference>
TextureLayer::TransferableResourceHolder::Create(
const viz::TransferableResource& resource,
std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
return std::make_unique<MainThreadReference>(
new TransferableResourceHolder(resource, std::move(release_callback)));
}
void TextureLayer::TransferableResourceHolder::Return(
const gpu::SyncToken& sync_token,
bool is_lost) {
base::AutoLock lock(arguments_lock_);
sync_token_ = sync_token;
is_lost_ = is_lost;
}
std::unique_ptr<viz::SingleReleaseCallback>
TextureLayer::TransferableResourceHolder::GetCallbackForImplThread(
scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner) {
// We can't call GetCallbackForImplThread if we released the main thread
// reference.
DCHECK_GT(internal_references_, 0);
InternalAddRef();
return viz::SingleReleaseCallback::Create(
base::BindOnce(&TransferableResourceHolder::ReturnAndReleaseOnImplThread,
this, std::move(main_thread_task_runner)));
}
void TextureLayer::TransferableResourceHolder::InternalAddRef() {
++internal_references_;
}
void TextureLayer::TransferableResourceHolder::InternalRelease() {
DCHECK(main_thread_checker_.CalledOnValidThread());
#if DCHECK_IS_ON()
{
base::AutoLock hold(posted_internal_derefs_lock_);
--posted_internal_derefs_;
}
#endif
if (!--internal_references_) {
release_callback_->Run(sync_token_, is_lost_);
resource_ = viz::TransferableResource();
release_callback_ = nullptr;
}
}
void TextureLayer::TransferableResourceHolder::ReturnAndReleaseOnImplThread(
const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner,
const gpu::SyncToken& sync_token,
bool is_lost) {
Return(sync_token, is_lost);
#if DCHECK_IS_ON()
{
base::AutoLock hold(posted_internal_derefs_lock_);
++posted_internal_derefs_;
}
#endif
main_thread_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&TransferableResourceHolder::InternalRelease, this));
}
} // namespace cc