普通文本  |  356行  |  11.01 KB

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