Home | History | Annotate | Download | only in browser_plugin
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/renderer/browser_plugin/browser_plugin_compositing_helper.h"
      6 
      7 #include "cc/layers/delegated_frame_provider.h"
      8 #include "cc/layers/delegated_frame_resource_collection.h"
      9 #include "cc/layers/delegated_renderer_layer.h"
     10 #include "cc/layers/solid_color_layer.h"
     11 #include "cc/layers/texture_layer.h"
     12 #include "cc/output/context_provider.h"
     13 #include "cc/output/copy_output_request.h"
     14 #include "cc/output/copy_output_result.h"
     15 #include "cc/resources/single_release_callback.h"
     16 #include "content/common/browser_plugin/browser_plugin_messages.h"
     17 #include "content/common/gpu/client/context_provider_command_buffer.h"
     18 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
     19 #include "content/renderer/render_thread_impl.h"
     20 #include "skia/ext/image_operations.h"
     21 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
     22 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     23 #include "third_party/khronos/GLES2/gl2.h"
     24 #include "ui/gfx/size_conversions.h"
     25 #include "ui/gfx/skia_util.h"
     26 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
     27 
     28 namespace content {
     29 
     30 BrowserPluginCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
     31     : route_id(0),
     32       output_surface_id(0),
     33       host_id(0),
     34       software_frame_id(0),
     35       shared_memory(NULL) {
     36 }
     37 
     38 BrowserPluginCompositingHelper::BrowserPluginCompositingHelper(
     39     blink::WebPluginContainer* container,
     40     BrowserPluginManager* manager,
     41     int instance_id,
     42     int host_routing_id)
     43     : instance_id_(instance_id),
     44       host_routing_id_(host_routing_id),
     45       last_route_id_(0),
     46       last_output_surface_id_(0),
     47       last_host_id_(0),
     48       last_mailbox_valid_(false),
     49       ack_pending_(true),
     50       software_ack_pending_(false),
     51       opaque_(true),
     52       container_(container),
     53       browser_plugin_manager_(manager) {
     54 }
     55 
     56 BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() {
     57 }
     58 
     59 void BrowserPluginCompositingHelper::CopyFromCompositingSurface(
     60     int request_id,
     61     gfx::Rect source_rect,
     62     gfx::Size dest_size) {
     63   CHECK(background_layer_);
     64   scoped_ptr<cc::CopyOutputRequest> request =
     65       cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
     66           &BrowserPluginCompositingHelper::CopyFromCompositingSurfaceHasResult,
     67           this,
     68           request_id,
     69           dest_size));
     70   request->set_area(source_rect);
     71   background_layer_->RequestCopyOfOutput(request.Pass());
     72 }
     73 
     74 void BrowserPluginCompositingHelper::DidCommitCompositorFrame() {
     75   if (software_ack_pending_) {
     76     cc::CompositorFrameAck ack;
     77     if (!unacked_software_frames_.empty()) {
     78       ack.last_software_frame_id = unacked_software_frames_.back();
     79       unacked_software_frames_.pop_back();
     80     }
     81 
     82     browser_plugin_manager_->Send(
     83         new BrowserPluginHostMsg_CompositorFrameACK(
     84             host_routing_id_,
     85             instance_id_,
     86             last_route_id_,
     87             last_output_surface_id_,
     88             last_host_id_,
     89             ack));
     90 
     91     software_ack_pending_ = false;
     92   }
     93   if (!resource_collection_.get() || !ack_pending_)
     94     return;
     95 
     96   cc::CompositorFrameAck ack;
     97   resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
     98 
     99   browser_plugin_manager_->Send(
    100       new BrowserPluginHostMsg_CompositorFrameACK(
    101           host_routing_id_,
    102           instance_id_,
    103           last_route_id_,
    104           last_output_surface_id_,
    105           last_host_id_,
    106           ack));
    107 
    108   ack_pending_ = false;
    109 }
    110 
    111 void BrowserPluginCompositingHelper::EnableCompositing(bool enable) {
    112   if (enable && !background_layer_.get()) {
    113     background_layer_ = cc::SolidColorLayer::Create();
    114     background_layer_->SetMasksToBounds(true);
    115     background_layer_->SetBackgroundColor(
    116         SkColorSetARGBInline(255, 255, 255, 255));
    117     web_layer_.reset(new webkit::WebLayerImpl(background_layer_));
    118   }
    119 
    120   container_->setWebLayer(enable ? web_layer_.get() : NULL);
    121 }
    122 
    123 void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerProperties(
    124     const gfx::Size& new_size,
    125     float device_scale_factor,
    126     cc::Layer* layer) {
    127   if (buffer_size_ != new_size) {
    128     buffer_size_ = new_size;
    129     // The container size is in DIP, so is the layer size.
    130     // Buffer size is in physical pixels, so we need to adjust
    131     // it by the device scale factor.
    132     gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
    133         gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
    134     layer->SetBounds(device_scale_adjusted_size);
    135   }
    136 
    137   // Manually manage background layer for transparent webview.
    138   if (!opaque_)
    139     background_layer_->SetIsDrawable(false);
    140 }
    141 
    142 void BrowserPluginCompositingHelper::MailboxReleased(
    143     SwapBuffersInfo mailbox,
    144     unsigned sync_point,
    145     bool lost_resource) {
    146   if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) {
    147     delete mailbox.shared_memory;
    148     mailbox.shared_memory = NULL;
    149   } else if (lost_resource) {
    150     // Reset mailbox's name if the resource was lost.
    151     mailbox.name.SetZero();
    152   }
    153 
    154   // This means the GPU process crashed or guest crashed.
    155   if (last_host_id_ != mailbox.host_id ||
    156       last_output_surface_id_ != mailbox.output_surface_id ||
    157       last_route_id_ != mailbox.route_id)
    158     return;
    159 
    160   if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
    161     unacked_software_frames_.push_back(mailbox.software_frame_id);
    162 
    163   // We need to send an ACK to for every buffer sent to us.
    164   // However, if a buffer is freed up from
    165   // the compositor in cases like switching back to SW mode without a new
    166   // buffer arriving, no ACK is needed.
    167   if (!ack_pending_) {
    168     last_mailbox_valid_ = false;
    169     return;
    170   }
    171   ack_pending_ = false;
    172   switch (mailbox.type) {
    173     case TEXTURE_IMAGE_TRANSPORT: {
    174       std::string mailbox_name(reinterpret_cast<const char*>(mailbox.name.name),
    175                                sizeof(mailbox.name.name));
    176       browser_plugin_manager_->Send(
    177           new BrowserPluginHostMsg_BuffersSwappedACK(
    178               host_routing_id_,
    179               instance_id_,
    180               mailbox.route_id,
    181               mailbox.host_id,
    182               mailbox_name,
    183               sync_point));
    184       break;
    185     }
    186     case GL_COMPOSITOR_FRAME: {
    187       cc::CompositorFrameAck ack;
    188       ack.gl_frame_data.reset(new cc::GLFrameData());
    189       ack.gl_frame_data->mailbox = mailbox.name;
    190       ack.gl_frame_data->size = mailbox.size;
    191       ack.gl_frame_data->sync_point = sync_point;
    192 
    193       browser_plugin_manager_->Send(
    194          new BrowserPluginHostMsg_CompositorFrameACK(
    195              host_routing_id_,
    196              instance_id_,
    197              mailbox.route_id,
    198              mailbox.output_surface_id,
    199              mailbox.host_id,
    200              ack));
    201       break;
    202     }
    203     case SOFTWARE_COMPOSITOR_FRAME:
    204       break;
    205   }
    206 }
    207 
    208 void BrowserPluginCompositingHelper::OnContainerDestroy() {
    209   if (container_)
    210     container_->setWebLayer(NULL);
    211   container_ = NULL;
    212 
    213   if (resource_collection_)
    214     resource_collection_->SetClient(NULL);
    215 
    216   ack_pending_ = false;
    217   software_ack_pending_ = false;
    218   resource_collection_ = NULL;
    219   frame_provider_ = NULL;
    220   texture_layer_ = NULL;
    221   delegated_layer_ = NULL;
    222   background_layer_ = NULL;
    223   web_layer_.reset();
    224 }
    225 
    226 void BrowserPluginCompositingHelper::OnBuffersSwappedPrivate(
    227     const SwapBuffersInfo& mailbox,
    228     unsigned sync_point,
    229     float device_scale_factor) {
    230   DCHECK(!delegated_layer_.get());
    231   // If these mismatch, we are either just starting up, GPU process crashed or
    232   // guest renderer crashed.
    233   // In this case, we are communicating with a new image transport
    234   // surface and must ACK with the new ID's and an empty mailbox.
    235   if (last_route_id_ != mailbox.route_id ||
    236       last_output_surface_id_ != mailbox.output_surface_id ||
    237       last_host_id_ != mailbox.host_id)
    238     last_mailbox_valid_ = false;
    239 
    240   last_route_id_ = mailbox.route_id;
    241   last_output_surface_id_ = mailbox.output_surface_id;
    242   last_host_id_ = mailbox.host_id;
    243 
    244   ack_pending_ = true;
    245   // Browser plugin getting destroyed, do a fast ACK.
    246   if (!background_layer_.get()) {
    247     MailboxReleased(mailbox, sync_point, false);
    248     return;
    249   }
    250 
    251   if (!texture_layer_.get()) {
    252     texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
    253     texture_layer_->SetIsDrawable(true);
    254     SetContentsOpaque(opaque_);
    255 
    256     background_layer_->AddChild(texture_layer_);
    257   }
    258 
    259   // The size of browser plugin container is not always equal to the size
    260   // of the buffer that arrives here. This could be for a number of reasons,
    261   // including autosize and a resize in progress.
    262   // During resize, the container size changes first and then some time
    263   // later, a new buffer with updated size will arrive. During this process,
    264   // we need to make sure that things are still displayed pixel perfect.
    265   // We accomplish this by modifying bounds of the texture layer only
    266   // when a new buffer arrives.
    267   // Visually, this will either display a smaller part of the buffer
    268   // or introduce a gutter around it.
    269   CheckSizeAndAdjustLayerProperties(mailbox.size,
    270                                     device_scale_factor,
    271                                     texture_layer_.get());
    272 
    273   bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME;
    274   bool current_mailbox_valid = is_software_frame ?
    275       mailbox.shared_memory != NULL : !mailbox.name.IsZero();
    276   if (!is_software_frame && !last_mailbox_valid_) {
    277     SwapBuffersInfo empty_info = mailbox;
    278     empty_info.name.SetZero();
    279     MailboxReleased(empty_info, 0, false);
    280     if (!current_mailbox_valid)
    281       return;
    282   }
    283 
    284   cc::TextureMailbox texture_mailbox;
    285   scoped_ptr<cc::SingleReleaseCallback> release_callback;
    286   if (current_mailbox_valid) {
    287     release_callback = cc::SingleReleaseCallback::Create(
    288         base::Bind(&BrowserPluginCompositingHelper::MailboxReleased,
    289                    scoped_refptr<BrowserPluginCompositingHelper>(this),
    290                    mailbox)).Pass();
    291     if (is_software_frame)
    292       texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
    293     else
    294       texture_mailbox = cc::TextureMailbox(mailbox.name, sync_point);
    295   }
    296 
    297   texture_layer_->SetFlipped(!is_software_frame);
    298   texture_layer_->SetTextureMailbox(texture_mailbox, release_callback.Pass());
    299   texture_layer_->SetNeedsDisplay();
    300   last_mailbox_valid_ = current_mailbox_valid;
    301 }
    302 
    303 void BrowserPluginCompositingHelper::OnBuffersSwapped(
    304     const gfx::Size& size,
    305     const std::string& mailbox_name,
    306     int gpu_route_id,
    307     int gpu_host_id,
    308     float device_scale_factor) {
    309   SwapBuffersInfo swap_info;
    310   swap_info.name.SetName(reinterpret_cast<const int8*>(mailbox_name.data()));
    311   swap_info.type = TEXTURE_IMAGE_TRANSPORT;
    312   swap_info.size = size;
    313   swap_info.route_id = gpu_route_id;
    314   swap_info.output_surface_id = 0;
    315   swap_info.host_id = gpu_host_id;
    316   OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor);
    317 }
    318 
    319 void BrowserPluginCompositingHelper::OnCompositorFrameSwapped(
    320     scoped_ptr<cc::CompositorFrame> frame,
    321     int route_id,
    322     uint32 output_surface_id,
    323     int host_id) {
    324   if (frame->gl_frame_data) {
    325     SwapBuffersInfo swap_info;
    326     swap_info.name = frame->gl_frame_data->mailbox;
    327     swap_info.type = GL_COMPOSITOR_FRAME;
    328     swap_info.size = frame->gl_frame_data->size;
    329     swap_info.route_id = route_id;
    330     swap_info.output_surface_id = output_surface_id;
    331     swap_info.host_id = host_id;
    332     OnBuffersSwappedPrivate(swap_info,
    333                             frame->gl_frame_data->sync_point,
    334                             frame->metadata.device_scale_factor);
    335     return;
    336   }
    337 
    338   if (frame->software_frame_data) {
    339     cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
    340 
    341     SwapBuffersInfo swap_info;
    342     swap_info.type = SOFTWARE_COMPOSITOR_FRAME;
    343     swap_info.size = frame_data->size;
    344     swap_info.route_id = route_id;
    345     swap_info.output_surface_id = output_surface_id;
    346     swap_info.host_id = host_id;
    347     swap_info.software_frame_id = frame_data->id;
    348 
    349     scoped_ptr<base::SharedMemory> shared_memory(
    350         new base::SharedMemory(frame_data->handle, true));
    351     const size_t size_in_bytes = 4 * frame_data->size.GetArea();
    352     if (!shared_memory->Map(size_in_bytes)) {
    353       LOG(ERROR) << "Failed to map shared memory of size "
    354                  << size_in_bytes;
    355       // Send ACK right away.
    356       software_ack_pending_ = true;
    357       MailboxReleased(swap_info, 0, false);
    358       DidCommitCompositorFrame();
    359       return;
    360     }
    361 
    362     swap_info.shared_memory = shared_memory.release();
    363     OnBuffersSwappedPrivate(swap_info, 0,
    364                             frame->metadata.device_scale_factor);
    365     software_ack_pending_ = true;
    366     last_route_id_ = route_id;
    367     last_output_surface_id_ = output_surface_id;
    368     last_host_id_ = host_id;
    369     return;
    370   }
    371 
    372   DCHECK(!texture_layer_.get());
    373 
    374   cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
    375   // Do nothing if we are getting destroyed or have no frame data.
    376   if (!frame_data || !background_layer_)
    377     return;
    378 
    379   DCHECK(!frame_data->render_pass_list.empty());
    380   cc::RenderPass* root_pass = frame_data->render_pass_list.back();
    381   gfx::Size frame_size = root_pass->output_rect.size();
    382 
    383   if (last_route_id_ != route_id ||
    384       last_output_surface_id_ != output_surface_id ||
    385       last_host_id_ != host_id) {
    386     // Resource ids are scoped by the output surface.
    387     // If the originating output surface doesn't match the last one, it
    388     // indicates the guest's output surface may have been recreated, in which
    389     // case we should recreate the DelegatedRendererLayer, to avoid matching
    390     // resources from the old one with resources from the new one which would
    391     // have the same id.
    392     frame_provider_ = NULL;
    393 
    394     // Drop the cc::DelegatedFrameResourceCollection so that we will not return
    395     // any resources from the old output surface with the new output surface id.
    396     if (resource_collection_) {
    397       resource_collection_->SetClient(NULL);
    398 
    399       if (resource_collection_->LoseAllResources())
    400         SendReturnedDelegatedResources();
    401       resource_collection_ = NULL;
    402     }
    403     last_output_surface_id_ = output_surface_id;
    404     last_route_id_ = route_id;
    405     last_host_id_ = host_id;
    406   }
    407   if (!resource_collection_) {
    408     resource_collection_ = new cc::DelegatedFrameResourceCollection;
    409     resource_collection_->SetClient(this);
    410   }
    411   if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) {
    412     frame_provider_ = new cc::DelegatedFrameProvider(
    413         resource_collection_.get(), frame->delegated_frame_data.Pass());
    414     if (delegated_layer_.get())
    415       delegated_layer_->RemoveFromParent();
    416     delegated_layer_ =
    417         cc::DelegatedRendererLayer::Create(frame_provider_.get());
    418     delegated_layer_->SetIsDrawable(true);
    419     SetContentsOpaque(opaque_);
    420     background_layer_->AddChild(delegated_layer_);
    421   } else {
    422     frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
    423   }
    424 
    425   CheckSizeAndAdjustLayerProperties(
    426       frame_data->render_pass_list.back()->output_rect.size(),
    427       frame->metadata.device_scale_factor,
    428       delegated_layer_.get());
    429 
    430   ack_pending_ = true;
    431 }
    432 
    433 void BrowserPluginCompositingHelper::UpdateVisibility(bool visible) {
    434   if (texture_layer_.get())
    435     texture_layer_->SetIsDrawable(visible);
    436   if (delegated_layer_.get())
    437     delegated_layer_->SetIsDrawable(visible);
    438 }
    439 
    440 void BrowserPluginCompositingHelper::UnusedResourcesAreAvailable() {
    441   if (ack_pending_)
    442     return;
    443 
    444   SendReturnedDelegatedResources();
    445 }
    446 
    447 void BrowserPluginCompositingHelper::SendReturnedDelegatedResources() {
    448   cc::CompositorFrameAck ack;
    449   if (resource_collection_)
    450     resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
    451   DCHECK(!ack.resources.empty());
    452 
    453   browser_plugin_manager_->Send(
    454       new BrowserPluginHostMsg_ReclaimCompositorResources(
    455           host_routing_id_,
    456           instance_id_,
    457           last_route_id_,
    458           last_output_surface_id_,
    459           last_host_id_,
    460           ack));
    461 }
    462 
    463 void BrowserPluginCompositingHelper::SetContentsOpaque(bool opaque) {
    464   opaque_ = opaque;
    465 
    466   if (texture_layer_.get())
    467     texture_layer_->SetContentsOpaque(opaque_);
    468   if (delegated_layer_.get())
    469     delegated_layer_->SetContentsOpaque(opaque_);
    470 }
    471 
    472 void BrowserPluginCompositingHelper::CopyFromCompositingSurfaceHasResult(
    473     int request_id,
    474     gfx::Size dest_size,
    475     scoped_ptr<cc::CopyOutputResult> result) {
    476   scoped_ptr<SkBitmap> bitmap;
    477   if (result && result->HasBitmap() && !result->size().IsEmpty())
    478     bitmap = result->TakeBitmap();
    479 
    480   SkBitmap resized_bitmap;
    481   if (bitmap) {
    482     resized_bitmap = skia::ImageOperations::Resize(*bitmap,
    483                        skia::ImageOperations::RESIZE_BEST,
    484                        dest_size.width(),
    485                        dest_size.height());
    486   }
    487   browser_plugin_manager_->Send(
    488       new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
    489           host_routing_id_, instance_id_, request_id,
    490           resized_bitmap));
    491 }
    492 
    493 }  // namespace content
    494