root/content/renderer/child_frame_compositing_helper.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. shared_memory
  2. CreateCompositingHelperForBrowserPlugin
  3. CreateCompositingHelperForRenderFrame
  4. frame_
  5. GetBrowserPluginManager
  6. GetContainer
  7. GetInstanceID
  8. SendCompositorFrameSwappedACKToBrowser
  9. SendBuffersSwappedACKToBrowser
  10. SendReclaimCompositorResourcesToBrowser
  11. CopyFromCompositingSurface
  12. DidCommitCompositorFrame
  13. EnableCompositing
  14. CheckSizeAndAdjustLayerProperties
  15. MailboxReleased
  16. OnContainerDestroy
  17. ChildFrameGone
  18. OnBuffersSwappedPrivate
  19. OnBuffersSwapped
  20. OnCompositorFrameSwapped
  21. UpdateVisibility
  22. UnusedResourcesAreAvailable
  23. SendReturnedDelegatedResources
  24. SetContentsOpaque
  25. CopyFromCompositingSurfaceHasResult

// 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/renderer/child_frame_compositing_helper.h"

#include "cc/layers/delegated_frame_provider.h"
#include "cc/layers/delegated_frame_resource_collection.h"
#include "cc/layers/delegated_renderer_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/layers/texture_layer.h"
#include "cc/output/context_provider.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/resources/single_release_callback.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
#include "content/common/frame_messages.h"
#include "content/common/gpu/client/context_provider_command_buffer.h"
#include "content/renderer/browser_plugin/browser_plugin.h"
#include "content/renderer/browser_plugin/browser_plugin_manager.h"
#include "content/renderer/render_frame_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "skia/ext/image_operations.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebPluginContainer.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/skia_util.h"
#include "webkit/renderer/compositor_bindings/web_layer_impl.h"

namespace content {

ChildFrameCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
    : route_id(0),
      output_surface_id(0),
      host_id(0),
      software_frame_id(0),
      shared_memory(NULL) {}

ChildFrameCompositingHelper*
ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
    const base::WeakPtr<BrowserPlugin>& browser_plugin) {
  return new ChildFrameCompositingHelper(
      browser_plugin, NULL, NULL, browser_plugin->render_view_routing_id());
}

ChildFrameCompositingHelper*
ChildFrameCompositingHelper::CreateCompositingHelperForRenderFrame(
    blink::WebFrame* frame,
    RenderFrameImpl* render_frame,
    int host_routing_id) {
  return new ChildFrameCompositingHelper(
      base::WeakPtr<BrowserPlugin>(), frame, render_frame, host_routing_id);
}

ChildFrameCompositingHelper::ChildFrameCompositingHelper(
    const base::WeakPtr<BrowserPlugin>& browser_plugin,
    blink::WebFrame* frame,
    RenderFrameImpl* render_frame,
    int host_routing_id)
    : host_routing_id_(host_routing_id),
      last_route_id_(0),
      last_output_surface_id_(0),
      last_host_id_(0),
      last_mailbox_valid_(false),
      ack_pending_(true),
      software_ack_pending_(false),
      opaque_(true),
      browser_plugin_(browser_plugin),
      render_frame_(render_frame),
      frame_(frame) {}

ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}

BrowserPluginManager* ChildFrameCompositingHelper::GetBrowserPluginManager() {
  if (!browser_plugin_)
    return NULL;

  return browser_plugin_->browser_plugin_manager();
}

blink::WebPluginContainer* ChildFrameCompositingHelper::GetContainer() {
  if (!browser_plugin_)
    return NULL;

  return browser_plugin_->container();
}

int ChildFrameCompositingHelper::GetInstanceID() {
  if (!browser_plugin_)
    return 0;

  return browser_plugin_->guest_instance_id();
}

void ChildFrameCompositingHelper::SendCompositorFrameSwappedACKToBrowser(
    FrameHostMsg_CompositorFrameSwappedACK_Params& params) {
  // This function will be removed when BrowserPluginManager is removed and
  // BrowserPlugin is modified to use a RenderFrame.
  if (GetBrowserPluginManager()) {
    GetBrowserPluginManager()->Send(
        new BrowserPluginHostMsg_CompositorFrameSwappedACK(
            host_routing_id_, GetInstanceID(), params));
  } else if (render_frame_) {
    render_frame_->Send(
        new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_, params));
  }
}

void ChildFrameCompositingHelper::SendBuffersSwappedACKToBrowser(
    FrameHostMsg_BuffersSwappedACK_Params& params) {
  // This function will be removed when BrowserPluginManager is removed and
  // BrowserPlugin is modified to use a RenderFrame.
  if (GetBrowserPluginManager()) {
    GetBrowserPluginManager()->Send(new BrowserPluginHostMsg_BuffersSwappedACK(
        host_routing_id_, GetInstanceID(), params));
  } else if (render_frame_) {
    render_frame_->Send(
        new FrameHostMsg_BuffersSwappedACK(host_routing_id_, params));
  }
}

void ChildFrameCompositingHelper::SendReclaimCompositorResourcesToBrowser(
    FrameHostMsg_ReclaimCompositorResources_Params& params) {
  // This function will be removed when BrowserPluginManager is removed and
  // BrowserPlugin is modified to use a RenderFrame.
  if (GetBrowserPluginManager()) {
    GetBrowserPluginManager()->Send(
        new BrowserPluginHostMsg_ReclaimCompositorResources(
            host_routing_id_, GetInstanceID(), params));
  } else if (render_frame_) {
    render_frame_->Send(
        new FrameHostMsg_ReclaimCompositorResources(host_routing_id_, params));
  }
}

void ChildFrameCompositingHelper::CopyFromCompositingSurface(
    int request_id,
    gfx::Rect source_rect,
    gfx::Size dest_size) {
  CHECK(background_layer_);
  scoped_ptr<cc::CopyOutputRequest> request =
      cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
          &ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult,
          this,
          request_id,
          dest_size));
  request->set_area(source_rect);
  background_layer_->RequestCopyOfOutput(request.Pass());
}

void ChildFrameCompositingHelper::DidCommitCompositorFrame() {
  if (software_ack_pending_) {
    FrameHostMsg_CompositorFrameSwappedACK_Params params;
    params.producing_host_id = last_host_id_;
    params.producing_route_id = last_route_id_;
    params.output_surface_id = last_output_surface_id_;
    if (!unacked_software_frames_.empty()) {
      params.ack.last_software_frame_id = unacked_software_frames_.back();
      unacked_software_frames_.pop_back();
    }

    SendCompositorFrameSwappedACKToBrowser(params);

    software_ack_pending_ = false;
  }
  if (!resource_collection_.get() || !ack_pending_)
    return;

  FrameHostMsg_CompositorFrameSwappedACK_Params params;
  params.producing_host_id = last_host_id_;
  params.producing_route_id = last_route_id_;
  params.output_surface_id = last_output_surface_id_;
  resource_collection_->TakeUnusedResourcesForChildCompositor(
      &params.ack.resources);

  SendCompositorFrameSwappedACKToBrowser(params);

  ack_pending_ = false;
}

void ChildFrameCompositingHelper::EnableCompositing(bool enable) {
  if (enable && !background_layer_.get()) {
    background_layer_ = cc::SolidColorLayer::Create();
    background_layer_->SetMasksToBounds(true);
    background_layer_->SetBackgroundColor(
        SkColorSetARGBInline(255, 255, 255, 255));
    web_layer_.reset(new webkit::WebLayerImpl(background_layer_));
  }

  if (GetContainer()) {
    GetContainer()->setWebLayer(enable ? web_layer_.get() : NULL);
  } else if (frame_) {
    frame_->setRemoteWebLayer(enable ? web_layer_.get() : NULL);
  }
}

void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
    const gfx::Size& new_size,
    float device_scale_factor,
    cc::Layer* layer) {
  if (buffer_size_ != new_size) {
    buffer_size_ = new_size;
    // The container size is in DIP, so is the layer size.
    // Buffer size is in physical pixels, so we need to adjust
    // it by the device scale factor.
    gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
        gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
    layer->SetBounds(device_scale_adjusted_size);
  }

  // Manually manage background layer for transparent webview.
  if (!opaque_)
    background_layer_->SetIsDrawable(false);
}

void ChildFrameCompositingHelper::MailboxReleased(SwapBuffersInfo mailbox,
                                                  uint32 sync_point,
                                                  bool lost_resource) {
  if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) {
    delete mailbox.shared_memory;
    mailbox.shared_memory = NULL;
  } else if (lost_resource) {
    // Reset mailbox's name if the resource was lost.
    mailbox.name.SetZero();
  }

  // This means the GPU process crashed or guest crashed.
  if (last_host_id_ != mailbox.host_id ||
      last_output_surface_id_ != mailbox.output_surface_id ||
      last_route_id_ != mailbox.route_id)
    return;

  if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
    unacked_software_frames_.push_back(mailbox.software_frame_id);

  // We need to send an ACK to for every buffer sent to us.
  // However, if a buffer is freed up from
  // the compositor in cases like switching back to SW mode without a new
  // buffer arriving, no ACK is needed.
  if (!ack_pending_) {
    last_mailbox_valid_ = false;
    return;
  }
  ack_pending_ = false;
  switch (mailbox.type) {
    case TEXTURE_IMAGE_TRANSPORT: {
      FrameHostMsg_BuffersSwappedACK_Params params;
      params.gpu_host_id = mailbox.host_id;
      params.gpu_route_id = mailbox.route_id;
      params.mailbox = mailbox.name;
      params.sync_point = sync_point;
      SendBuffersSwappedACKToBrowser(params);
      break;
    }
    case GL_COMPOSITOR_FRAME: {
      FrameHostMsg_CompositorFrameSwappedACK_Params params;
      params.producing_host_id = mailbox.host_id;
      params.producing_route_id = mailbox.route_id;
      params.output_surface_id = mailbox.output_surface_id;
      params.ack.gl_frame_data.reset(new cc::GLFrameData());
      params.ack.gl_frame_data->mailbox = mailbox.name;
      params.ack.gl_frame_data->size = mailbox.size;
      params.ack.gl_frame_data->sync_point = sync_point;
      SendCompositorFrameSwappedACKToBrowser(params);
      break;
    }
    case SOFTWARE_COMPOSITOR_FRAME:
      break;
  }
}

void ChildFrameCompositingHelper::OnContainerDestroy() {
  if (GetContainer())
    GetContainer()->setWebLayer(NULL);

  if (resource_collection_)
    resource_collection_->SetClient(NULL);

  ack_pending_ = false;
  software_ack_pending_ = false;
  resource_collection_ = NULL;
  frame_provider_ = NULL;
  texture_layer_ = NULL;
  delegated_layer_ = NULL;
  background_layer_ = NULL;
  web_layer_.reset();
}

void ChildFrameCompositingHelper::ChildFrameGone() {
  background_layer_->SetBackgroundColor(SkColorSetARGBInline(255, 0, 128, 0));
  background_layer_->RemoveAllChildren();
  background_layer_->SetIsDrawable(true);
  background_layer_->SetContentsOpaque(true);
}

void ChildFrameCompositingHelper::OnBuffersSwappedPrivate(
    const SwapBuffersInfo& mailbox,
    uint32 sync_point,
    float device_scale_factor) {
  DCHECK(!delegated_layer_.get());
  // If these mismatch, we are either just starting up, GPU process crashed or
  // guest renderer crashed.
  // In this case, we are communicating with a new image transport
  // surface and must ACK with the new ID's and an empty mailbox.
  if (last_route_id_ != mailbox.route_id ||
      last_output_surface_id_ != mailbox.output_surface_id ||
      last_host_id_ != mailbox.host_id)
    last_mailbox_valid_ = false;

  last_route_id_ = mailbox.route_id;
  last_output_surface_id_ = mailbox.output_surface_id;
  last_host_id_ = mailbox.host_id;

  ack_pending_ = true;
  // Browser plugin getting destroyed, do a fast ACK.
  if (!background_layer_.get()) {
    MailboxReleased(mailbox, sync_point, false);
    return;
  }

  if (!texture_layer_.get()) {
    texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
    texture_layer_->SetIsDrawable(true);
    SetContentsOpaque(opaque_);

    background_layer_->AddChild(texture_layer_);
  }

  // The size of browser plugin container is not always equal to the size
  // of the buffer that arrives here. This could be for a number of reasons,
  // including autosize and a resize in progress.
  // During resize, the container size changes first and then some time
  // later, a new buffer with updated size will arrive. During this process,
  // we need to make sure that things are still displayed pixel perfect.
  // We accomplish this by modifying bounds of the texture layer only
  // when a new buffer arrives.
  // Visually, this will either display a smaller part of the buffer
  // or introduce a gutter around it.
  CheckSizeAndAdjustLayerProperties(
      mailbox.size, device_scale_factor, texture_layer_.get());

  bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME;
  bool current_mailbox_valid = is_software_frame ? mailbox.shared_memory != NULL
                                                 : !mailbox.name.IsZero();
  if (!is_software_frame && !last_mailbox_valid_) {
    SwapBuffersInfo empty_info = mailbox;
    empty_info.name.SetZero();
    MailboxReleased(empty_info, 0, false);
    if (!current_mailbox_valid)
      return;
  }

  cc::TextureMailbox texture_mailbox;
  scoped_ptr<cc::SingleReleaseCallback> release_callback;
  if (current_mailbox_valid) {
    release_callback =
        cc::SingleReleaseCallback::Create(
            base::Bind(&ChildFrameCompositingHelper::MailboxReleased,
                       scoped_refptr<ChildFrameCompositingHelper>(this),
                       mailbox)).Pass();
    if (is_software_frame) {
      texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
    } else {
      texture_mailbox =
          cc::TextureMailbox(mailbox.name, GL_TEXTURE_2D, sync_point);
    }
  }

  texture_layer_->SetFlipped(!is_software_frame);
  texture_layer_->SetTextureMailbox(texture_mailbox, release_callback.Pass());
  texture_layer_->SetNeedsDisplay();
  last_mailbox_valid_ = current_mailbox_valid;
}

void ChildFrameCompositingHelper::OnBuffersSwapped(
    const gfx::Size& size,
    const gpu::Mailbox& mailbox,
    int gpu_route_id,
    int gpu_host_id,
    float device_scale_factor) {
  SwapBuffersInfo swap_info;
  swap_info.name = mailbox;
  swap_info.type = TEXTURE_IMAGE_TRANSPORT;
  swap_info.size = size;
  swap_info.route_id = gpu_route_id;
  swap_info.output_surface_id = 0;
  swap_info.host_id = gpu_host_id;
  OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor);
}

void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
    scoped_ptr<cc::CompositorFrame> frame,
    int route_id,
    uint32 output_surface_id,
    int host_id) {

  if (frame->gl_frame_data) {
    SwapBuffersInfo swap_info;
    swap_info.name = frame->gl_frame_data->mailbox;
    swap_info.type = GL_COMPOSITOR_FRAME;
    swap_info.size = frame->gl_frame_data->size;
    swap_info.route_id = route_id;
    swap_info.output_surface_id = output_surface_id;
    swap_info.host_id = host_id;
    OnBuffersSwappedPrivate(swap_info,
                            frame->gl_frame_data->sync_point,
                            frame->metadata.device_scale_factor);
    return;
  }

  if (frame->software_frame_data) {
    cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();

    SwapBuffersInfo swap_info;
    swap_info.type = SOFTWARE_COMPOSITOR_FRAME;
    swap_info.size = frame_data->size;
    swap_info.route_id = route_id;
    swap_info.output_surface_id = output_surface_id;
    swap_info.host_id = host_id;
    swap_info.software_frame_id = frame_data->id;

    scoped_ptr<base::SharedMemory> shared_memory(
        new base::SharedMemory(frame_data->handle, true));
    const size_t size_in_bytes = 4 * frame_data->size.GetArea();
    if (!shared_memory->Map(size_in_bytes)) {
      LOG(ERROR) << "Failed to map shared memory of size " << size_in_bytes;
      // Send ACK right away.
      software_ack_pending_ = true;
      MailboxReleased(swap_info, 0, false);
      DidCommitCompositorFrame();
      return;
    }

    swap_info.shared_memory = shared_memory.release();
    OnBuffersSwappedPrivate(swap_info, 0, frame->metadata.device_scale_factor);
    software_ack_pending_ = true;
    last_route_id_ = route_id;
    last_output_surface_id_ = output_surface_id;
    last_host_id_ = host_id;
    return;
  }

  DCHECK(!texture_layer_.get());

  cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
  // Do nothing if we are getting destroyed or have no frame data.
  if (!frame_data || !background_layer_)
    return;

  DCHECK(!frame_data->render_pass_list.empty());
  cc::RenderPass* root_pass = frame_data->render_pass_list.back();
  gfx::Size frame_size = root_pass->output_rect.size();

  if (last_route_id_ != route_id ||
      last_output_surface_id_ != output_surface_id ||
      last_host_id_ != host_id) {
    // Resource ids are scoped by the output surface.
    // If the originating output surface doesn't match the last one, it
    // indicates the guest'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.
    frame_provider_ = NULL;

    // Drop the cc::DelegatedFrameResourceCollection so that we will not return
    // any resources from the old output surface with the new output surface id.
    if (resource_collection_) {
      resource_collection_->SetClient(NULL);

      if (resource_collection_->LoseAllResources())
        SendReturnedDelegatedResources();
      resource_collection_ = NULL;
    }
    last_output_surface_id_ = output_surface_id;
    last_route_id_ = route_id;
    last_host_id_ = host_id;
  }
  if (!resource_collection_) {
    resource_collection_ = new cc::DelegatedFrameResourceCollection;
    resource_collection_->SetClient(this);
  }
  if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) {
    frame_provider_ = new cc::DelegatedFrameProvider(
        resource_collection_.get(), frame->delegated_frame_data.Pass());
    if (delegated_layer_.get())
      delegated_layer_->RemoveFromParent();
    delegated_layer_ =
        cc::DelegatedRendererLayer::Create(frame_provider_.get());
    delegated_layer_->SetIsDrawable(true);
    SetContentsOpaque(opaque_);
    background_layer_->AddChild(delegated_layer_);
  } else {
    frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
  }

  CheckSizeAndAdjustLayerProperties(
      frame_data->render_pass_list.back()->output_rect.size(),
      frame->metadata.device_scale_factor,
      delegated_layer_.get());

  ack_pending_ = true;
}

void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
  if (texture_layer_.get())
    texture_layer_->SetIsDrawable(visible);
  if (delegated_layer_.get())
    delegated_layer_->SetIsDrawable(visible);
}

void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
  if (ack_pending_)
    return;

  SendReturnedDelegatedResources();
}

void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
  FrameHostMsg_ReclaimCompositorResources_Params params;
  if (resource_collection_)
    resource_collection_->TakeUnusedResourcesForChildCompositor(
        &params.ack.resources);
  DCHECK(!params.ack.resources.empty());

  params.route_id = last_route_id_;
  params.output_surface_id = last_output_surface_id_;
  params.renderer_host_id = last_host_id_;
  SendReclaimCompositorResourcesToBrowser(params);
}

void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque) {
  opaque_ = opaque;

  if (texture_layer_.get())
    texture_layer_->SetContentsOpaque(opaque_);
  if (delegated_layer_.get())
    delegated_layer_->SetContentsOpaque(opaque_);
}

void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
    int request_id,
    gfx::Size dest_size,
    scoped_ptr<cc::CopyOutputResult> result) {
  scoped_ptr<SkBitmap> bitmap;
  if (result && result->HasBitmap() && !result->size().IsEmpty())
    bitmap = result->TakeBitmap();

  SkBitmap resized_bitmap;
  if (bitmap) {
    resized_bitmap =
        skia::ImageOperations::Resize(*bitmap,
                                      skia::ImageOperations::RESIZE_BEST,
                                      dest_size.width(),
                                      dest_size.height());
  }
  if (GetBrowserPluginManager()) {
    GetBrowserPluginManager()->Send(
        new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
            host_routing_id_, GetInstanceID(), request_id, resized_bitmap));
  }
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */