// 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/synchronization/lock.h"
#include "cc/layers/texture_layer_client.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/resources/single_release_callback.h"
#include "cc/trees/blocking_task_runner.h"
#include "cc/trees/layer_tree_host.h"
namespace cc {
scoped_refptr<TextureLayer> TextureLayer::Create(TextureLayerClient* client) {
return scoped_refptr<TextureLayer>(new TextureLayer(client, false));
}
scoped_refptr<TextureLayer> TextureLayer::CreateForMailbox(
TextureLayerClient* client) {
return scoped_refptr<TextureLayer>(new TextureLayer(client, true));
}
TextureLayer::TextureLayer(TextureLayerClient* client, bool uses_mailbox)
: Layer(),
client_(client),
uses_mailbox_(uses_mailbox),
flipped_(true),
uv_top_left_(0.f, 0.f),
uv_bottom_right_(1.f, 1.f),
premultiplied_alpha_(true),
blend_background_color_(false),
rate_limit_context_(false),
content_committed_(false),
texture_id_(0),
needs_set_mailbox_(false) {
vertex_opacity_[0] = 1.0f;
vertex_opacity_[1] = 1.0f;
vertex_opacity_[2] = 1.0f;
vertex_opacity_[3] = 1.0f;
}
TextureLayer::~TextureLayer() {
}
void TextureLayer::ClearClient() {
if (rate_limit_context_ && client_ && layer_tree_host())
layer_tree_host()->StopRateLimiter();
client_ = NULL;
if (uses_mailbox_)
SetTextureMailbox(TextureMailbox(), scoped_ptr<SingleReleaseCallback>());
else
SetTextureId(0);
}
scoped_ptr<LayerImpl> TextureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
return TextureLayerImpl::Create(tree_impl, id(), uses_mailbox_).
PassAs<LayerImpl>();
}
void TextureLayer::SetFlipped(bool flipped) {
if (flipped_ == flipped)
return;
flipped_ = flipped;
SetNeedsCommit();
}
void TextureLayer::SetUV(gfx::PointF top_left, 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::SetRateLimitContext(bool rate_limit) {
if (!rate_limit && rate_limit_context_ && client_ && layer_tree_host())
layer_tree_host()->StopRateLimiter();
rate_limit_context_ = rate_limit;
}
void TextureLayer::SetTextureId(unsigned id) {
DCHECK(!uses_mailbox_);
if (texture_id_ == id)
return;
if (texture_id_ && layer_tree_host())
layer_tree_host()->AcquireLayerTextures();
texture_id_ = id;
SetNeedsCommit();
// The texture id needs to be removed from the active tree before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
void TextureLayer::SetTextureMailboxInternal(
const TextureMailbox& mailbox,
scoped_ptr<SingleReleaseCallback> release_callback,
bool requires_commit) {
DCHECK(uses_mailbox_);
DCHECK(!mailbox.IsValid() || !holder_ref_ ||
!mailbox.Equals(holder_ref_->holder()->mailbox()));
DCHECK_EQ(mailbox.IsValid(), !!release_callback);
// If we never commited the mailbox, we need to release it here.
if (mailbox.IsValid())
holder_ref_ = MailboxHolder::Create(mailbox, release_callback.Pass());
else
holder_ref_.reset();
needs_set_mailbox_ = true;
// If we are within a commit, no need to do it again immediately after.
if (requires_commit)
SetNeedsCommit();
else
SetNeedsPushProperties();
// The active frame needs to be replaced and the mailbox returned before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
void TextureLayer::SetTextureMailbox(
const TextureMailbox& mailbox,
scoped_ptr<SingleReleaseCallback> release_callback) {
SetTextureMailboxInternal(
mailbox,
release_callback.Pass(),
true /* requires_commit */);
}
void TextureLayer::WillModifyTexture() {
if (!uses_mailbox_ && layer_tree_host() && (DrawsContent() ||
content_committed_)) {
layer_tree_host()->AcquireLayerTextures();
content_committed_ = false;
}
}
void TextureLayer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) {
Layer::SetNeedsDisplayRect(dirty_rect);
if (rate_limit_context_ && client_ && layer_tree_host() && DrawsContent())
layer_tree_host()->StartRateLimiter();
}
void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) {
if (layer_tree_host() == host) {
Layer::SetLayerTreeHost(host);
return;
}
if (layer_tree_host()) {
if (texture_id_) {
layer_tree_host()->AcquireLayerTextures();
// The texture id needs to be removed from the active tree before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
if (rate_limit_context_ && client_)
layer_tree_host()->StopRateLimiter();
}
// 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 && uses_mailbox_ && holder_ref_) {
needs_set_mailbox_ = true;
// The active frame needs to be replaced and the mailbox returned before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
Layer::SetLayerTreeHost(host);
}
bool TextureLayer::DrawsContent() const {
return (client_ || texture_id_ || holder_ref_) && Layer::DrawsContent();
}
bool TextureLayer::Update(ResourceUpdateQueue* queue,
const OcclusionTracker* occlusion) {
bool updated = Layer::Update(queue, occlusion);
if (client_) {
if (uses_mailbox_) {
TextureMailbox mailbox;
scoped_ptr<SingleReleaseCallback> release_callback;
if (client_->PrepareTextureMailbox(
&mailbox,
&release_callback,
layer_tree_host()->UsingSharedMemoryResources())) {
// Already within a commit, no need to do another one immediately.
SetTextureMailboxInternal(
mailbox,
release_callback.Pass(),
false /* requires_commit */);
updated = true;
}
} else {
texture_id_ = client_->PrepareTexture();
updated = true;
SetNeedsPushProperties();
// The texture id needs to be removed from the active tree before the
// commit is called complete.
SetNextCommitWaitsForActivation();
}
}
// SetTextureMailbox 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();
}
void TextureLayer::PushPropertiesTo(LayerImpl* layer) {
Layer::PushPropertiesTo(layer);
TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
texture_layer->set_flipped(flipped_);
texture_layer->set_uv_top_left(uv_top_left_);
texture_layer->set_uv_bottom_right(uv_bottom_right_);
texture_layer->set_vertex_opacity(vertex_opacity_);
texture_layer->set_premultiplied_alpha(premultiplied_alpha_);
texture_layer->set_blend_background_color(blend_background_color_);
if (uses_mailbox_ && needs_set_mailbox_) {
TextureMailbox texture_mailbox;
scoped_ptr<SingleReleaseCallback> release_callback;
if (holder_ref_) {
MailboxHolder* holder = holder_ref_->holder();
texture_mailbox = holder->mailbox();
release_callback = holder->GetCallbackForImplThread();
}
texture_layer->SetTextureMailbox(texture_mailbox, release_callback.Pass());
needs_set_mailbox_ = false;
} else {
texture_layer->set_texture_id(texture_id_);
content_committed_ = DrawsContent();
}
}
Region TextureLayer::VisibleContentOpaqueRegion() const {
if (contents_opaque())
return visible_content_rect();
if (blend_background_color_ && (SkColorGetA(background_color()) == 0xFF))
return visible_content_rect();
return Region();
}
TextureLayer::MailboxHolder::MainThreadReference::MainThreadReference(
MailboxHolder* holder)
: holder_(holder) {
holder_->InternalAddRef();
}
TextureLayer::MailboxHolder::MainThreadReference::~MainThreadReference() {
holder_->InternalRelease();
}
TextureLayer::MailboxHolder::MailboxHolder(
const TextureMailbox& mailbox,
scoped_ptr<SingleReleaseCallback> release_callback)
: message_loop_(BlockingTaskRunner::current()),
internal_references_(0),
mailbox_(mailbox),
release_callback_(release_callback.Pass()),
sync_point_(mailbox.sync_point()),
is_lost_(false) {
}
TextureLayer::MailboxHolder::~MailboxHolder() {
DCHECK_EQ(0u, internal_references_);
}
scoped_ptr<TextureLayer::MailboxHolder::MainThreadReference>
TextureLayer::MailboxHolder::Create(
const TextureMailbox& mailbox,
scoped_ptr<SingleReleaseCallback> release_callback) {
return scoped_ptr<MainThreadReference>(new MainThreadReference(
new MailboxHolder(mailbox, release_callback.Pass())));
}
void TextureLayer::MailboxHolder::Return(unsigned sync_point, bool is_lost) {
base::AutoLock lock(arguments_lock_);
sync_point_ = sync_point;
is_lost_ = is_lost;
}
scoped_ptr<SingleReleaseCallback>
TextureLayer::MailboxHolder::GetCallbackForImplThread() {
// We can't call GetCallbackForImplThread if we released the main thread
// reference.
DCHECK_GT(internal_references_, 0u);
InternalAddRef();
return SingleReleaseCallback::Create(
base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this));
}
void TextureLayer::MailboxHolder::InternalAddRef() {
++internal_references_;
}
void TextureLayer::MailboxHolder::InternalRelease() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (!--internal_references_) {
release_callback_->Run(sync_point_, is_lost_);
mailbox_ = TextureMailbox();
release_callback_.reset();
}
}
void TextureLayer::MailboxHolder::ReturnAndReleaseOnImplThread(
unsigned sync_point, bool is_lost) {
Return(sync_point, is_lost);
message_loop_->PostTask(FROM_HERE,
base::Bind(&MailboxHolder::InternalRelease, this));
}
} // namespace cc