Home | History | Annotate | Download | only in pepper
      1 // Copyright 2014 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/pepper/pepper_compositor_host.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/shared_memory.h"
      9 #include "cc/layers/layer.h"
     10 #include "cc/layers/solid_color_layer.h"
     11 #include "cc/layers/texture_layer.h"
     12 #include "cc/resources/texture_mailbox.h"
     13 #include "cc/trees/layer_tree_host.h"
     14 #include "content/public/renderer/renderer_ppapi_host.h"
     15 #include "content/renderer/pepper/gfx_conversion.h"
     16 #include "content/renderer/pepper/host_globals.h"
     17 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
     18 #include "content/renderer/pepper/ppb_image_data_impl.h"
     19 #include "ppapi/c/pp_errors.h"
     20 #include "ppapi/host/dispatch_host_message.h"
     21 #include "ppapi/host/ppapi_host.h"
     22 #include "ppapi/proxy/ppapi_messages.h"
     23 #include "ppapi/thunk/enter.h"
     24 #include "ppapi/thunk/ppb_image_data_api.h"
     25 #include "third_party/khronos/GLES2/gl2.h"
     26 #include "ui/gfx/transform.h"
     27 
     28 using ppapi::host::HostMessageContext;
     29 using ppapi::thunk::EnterResourceNoLock;
     30 using ppapi::thunk::PPB_ImageData_API;
     31 
     32 namespace content {
     33 
     34 namespace {
     35 
     36 int32_t VerifyCommittedLayer(
     37     const ppapi::CompositorLayerData* old_layer,
     38     const ppapi::CompositorLayerData* new_layer,
     39     scoped_ptr<base::SharedMemory>* image_shm) {
     40   if (!new_layer->is_valid())
     41     return PP_ERROR_BADARGUMENT;
     42 
     43   if (new_layer->color) {
     44     // Make sure the old layer is a color layer too.
     45     if (old_layer && !old_layer->color)
     46       return PP_ERROR_BADARGUMENT;
     47     return PP_OK;
     48   }
     49 
     50   if (new_layer->texture) {
     51     if (old_layer) {
     52       // Make sure the old layer is a texture layer too.
     53       if (!new_layer->texture)
     54         return PP_ERROR_BADARGUMENT;
     55       // The mailbox should be same, if the resource_id is not changed.
     56       if (new_layer->common.resource_id == old_layer->common.resource_id) {
     57         if (new_layer->texture->mailbox != old_layer->texture->mailbox)
     58           return PP_ERROR_BADARGUMENT;
     59         return PP_OK;
     60       }
     61     }
     62     if (!new_layer->texture->mailbox.Verify())
     63       return PP_ERROR_BADARGUMENT;
     64     return PP_OK;
     65   }
     66 
     67   if (new_layer->image) {
     68     if (old_layer) {
     69       // Make sure the old layer is an image layer too.
     70       if (!new_layer->image)
     71         return PP_ERROR_BADARGUMENT;
     72       // The image data resource should be same, if the resource_id is not
     73       // changed.
     74       if (new_layer->common.resource_id == old_layer->common.resource_id) {
     75         if (new_layer->image->resource != old_layer->image->resource)
     76           return PP_ERROR_BADARGUMENT;
     77         return PP_OK;
     78       }
     79     }
     80     EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
     81                                                  true);
     82     if (enter.failed())
     83       return PP_ERROR_BADRESOURCE;
     84 
     85     // TODO(penghuang): support all kinds of image.
     86     PP_ImageDataDesc desc;
     87     if (enter.object()->Describe(&desc) != PP_TRUE ||
     88         desc.stride != desc.size.width * 4 ||
     89         desc.format != PP_IMAGEDATAFORMAT_RGBA_PREMUL) {
     90       return PP_ERROR_BADARGUMENT;
     91     }
     92 
     93     int handle;
     94     uint32_t byte_count;
     95     if (enter.object()->GetSharedMemory(&handle, &byte_count) != PP_OK)
     96       return PP_ERROR_FAILED;
     97 
     98 #if defined(OS_WIN)
     99     base::SharedMemoryHandle shm_handle;
    100     if (!::DuplicateHandle(::GetCurrentProcess(),
    101                            reinterpret_cast<base::SharedMemoryHandle>(handle),
    102                            ::GetCurrentProcess(),
    103                            &shm_handle,
    104                            0,
    105                            FALSE,
    106                            DUPLICATE_SAME_ACCESS)) {
    107       return PP_ERROR_FAILED;
    108     }
    109 #else
    110     base::SharedMemoryHandle shm_handle(dup(handle), false);
    111 #endif
    112     image_shm->reset(new base::SharedMemory(shm_handle, true));
    113     if (!(*image_shm)->Map(desc.stride * desc.size.height)) {
    114       image_shm->reset();
    115       return PP_ERROR_NOMEMORY;
    116     }
    117     return PP_OK;
    118   }
    119 
    120   return PP_ERROR_BADARGUMENT;
    121 }
    122 
    123 }  // namespace
    124 
    125 PepperCompositorHost::LayerData::LayerData(
    126     const scoped_refptr<cc::Layer>& cc,
    127     const ppapi::CompositorLayerData& pp) : cc_layer(cc), pp_layer(pp) {}
    128 
    129 PepperCompositorHost::LayerData::~LayerData() {}
    130 
    131 PepperCompositorHost::PepperCompositorHost(
    132     RendererPpapiHost* host,
    133     PP_Instance instance,
    134     PP_Resource resource)
    135     : ResourceHost(host->GetPpapiHost(), instance, resource),
    136       bound_instance_(NULL),
    137       weak_factory_(this) {
    138   layer_ = cc::Layer::Create();
    139   // TODO(penghuang): SetMasksToBounds() can be expensive if the layer is
    140   // transformed. Possibly better could be to explicitly clip the child layers
    141   // (by modifying their bounds).
    142   layer_->SetMasksToBounds(true);
    143   layer_->SetIsDrawable(true);
    144 }
    145 
    146 PepperCompositorHost::~PepperCompositorHost() {
    147   // Unbind from the instance when destroyed if we're still bound.
    148   if (bound_instance_)
    149     bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0);
    150 }
    151 
    152 bool PepperCompositorHost::BindToInstance(
    153     PepperPluginInstanceImpl* new_instance) {
    154   if (new_instance && new_instance->pp_instance() != pp_instance())
    155     return false;  // Can't bind other instance's contexts.
    156   if (bound_instance_ == new_instance)
    157     return true;  // Rebinding the same device, nothing to do.
    158   if (bound_instance_ && new_instance)
    159     return false;  // Can't change a bound device.
    160   bound_instance_ = new_instance;
    161   if (!bound_instance_)
    162     SendCommitLayersReplyIfNecessary();
    163 
    164   return true;
    165 }
    166 
    167 void PepperCompositorHost::ViewInitiatedPaint() {
    168   SendCommitLayersReplyIfNecessary();
    169 }
    170 
    171 void PepperCompositorHost::ViewFlushedPaint() {}
    172 
    173 void PepperCompositorHost::ImageReleased(
    174     int32_t id,
    175     const scoped_ptr<base::SharedMemory>& shared_memory,
    176     uint32_t sync_point,
    177     bool is_lost) {
    178   ResourceReleased(id, sync_point, is_lost);
    179 }
    180 
    181 void PepperCompositorHost::ResourceReleased(int32_t id,
    182                                             uint32_t sync_point,
    183                                             bool is_lost) {
    184   host()->SendUnsolicitedReply(
    185       pp_resource(),
    186       PpapiPluginMsg_Compositor_ReleaseResource(id, sync_point, is_lost));
    187 }
    188 
    189 void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
    190   if (!commit_layers_reply_context_.is_valid())
    191     return;
    192   host()->SendReply(commit_layers_reply_context_,
    193                     PpapiPluginMsg_Compositor_CommitLayersReply());
    194   commit_layers_reply_context_ = ppapi::host::ReplyMessageContext();
    195 }
    196 
    197 void PepperCompositorHost::UpdateLayer(
    198     const scoped_refptr<cc::Layer>& layer,
    199     const ppapi::CompositorLayerData* old_layer,
    200     const ppapi::CompositorLayerData* new_layer,
    201     scoped_ptr<base::SharedMemory> image_shm) {
    202   // Always update properties on cc::Layer, because cc::Layer
    203   // will ignore any setting with unchanged value.
    204   layer->SetIsDrawable(true);
    205   layer->SetBlendMode(SkXfermode::kSrcOver_Mode);
    206   layer->SetOpacity(new_layer->common.opacity);
    207   layer->SetBounds(PP_ToGfxSize(new_layer->common.size));
    208   layer->SetTransformOrigin(gfx::Point3F(new_layer->common.size.width / 2,
    209                                          new_layer->common.size.height / 2,
    210                                          0.0f));
    211 
    212   gfx::Transform transform(gfx::Transform::kSkipInitialization);
    213   transform.matrix().setColMajorf(new_layer->common.transform.matrix);
    214   layer->SetTransform(transform);
    215 
    216   // Consider a (0,0,0,0) rect as no clip rect.
    217   if (new_layer->common.clip_rect.point.x != 0 ||
    218       new_layer->common.clip_rect.point.y != 0 ||
    219       new_layer->common.clip_rect.size.width != 0 ||
    220       new_layer->common.clip_rect.size.height != 0) {
    221     scoped_refptr<cc::Layer> clip_parent = layer->parent();
    222     if (clip_parent.get() == layer_.get()) {
    223       // Create a clip parent layer, if it does not exist.
    224       clip_parent = cc::Layer::Create();
    225       clip_parent->SetMasksToBounds(true);
    226       clip_parent->SetIsDrawable(true);
    227       layer_->ReplaceChild(layer.get(), clip_parent);
    228       clip_parent->AddChild(layer);
    229     }
    230     gfx::Point position = PP_ToGfxPoint(new_layer->common.clip_rect.point);
    231     clip_parent->SetPosition(position);
    232     clip_parent->SetBounds(PP_ToGfxSize(new_layer->common.clip_rect.size));
    233     layer->SetPosition(gfx::Point(-position.x(), -position.y()));
    234   } else if (layer->parent() != layer_.get()) {
    235     // Remove the clip parent layer.
    236     layer_->ReplaceChild(layer->parent(), layer);
    237     layer->SetPosition(gfx::Point());
    238   }
    239 
    240   if (new_layer->color) {
    241     layer->SetBackgroundColor(SkColorSetARGBMacro(
    242         new_layer->color->alpha * 255,
    243         new_layer->color->red * 255,
    244         new_layer->color->green * 255,
    245         new_layer->color->blue * 255));
    246     return;
    247   }
    248 
    249   if (new_layer->texture) {
    250     scoped_refptr<cc::TextureLayer> texture_layer(
    251         static_cast<cc::TextureLayer*>(layer.get()));
    252     if (!old_layer ||
    253         new_layer->common.resource_id != old_layer->common.resource_id) {
    254       cc::TextureMailbox mailbox(new_layer->texture->mailbox,
    255                                  new_layer->texture->target,
    256                                  new_layer->texture->sync_point);
    257       texture_layer->SetTextureMailbox(mailbox,
    258           cc::SingleReleaseCallback::Create(
    259               base::Bind(&PepperCompositorHost::ResourceReleased,
    260                          weak_factory_.GetWeakPtr(),
    261                          new_layer->common.resource_id)));
    262       // TODO(penghuang): get a damage region from the application and
    263       // pass it to SetNeedsDisplayRect().
    264       texture_layer->SetNeedsDisplay();
    265     }
    266     texture_layer->SetPremultipliedAlpha(new_layer->texture->premult_alpha);
    267     gfx::RectF rect = PP_ToGfxRectF(new_layer->texture->source_rect);
    268     texture_layer->SetUV(rect.origin(), rect.bottom_right());
    269     return;
    270   }
    271 
    272   if (new_layer->image) {
    273     if (!old_layer ||
    274         new_layer->common.resource_id != old_layer->common.resource_id) {
    275       scoped_refptr<cc::TextureLayer> image_layer(
    276           static_cast<cc::TextureLayer*>(layer.get()));
    277       EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
    278                                                    true);
    279       DCHECK(enter.succeeded());
    280 
    281       // TODO(penghuang): support all kinds of image.
    282       PP_ImageDataDesc desc;
    283       PP_Bool rv = enter.object()->Describe(&desc);
    284       DCHECK_EQ(rv, PP_TRUE);
    285       DCHECK_EQ(desc.stride, desc.size.width * 4);
    286       DCHECK_EQ(desc.format, PP_IMAGEDATAFORMAT_RGBA_PREMUL);
    287 
    288       cc::TextureMailbox mailbox(image_shm.get(),
    289                                  PP_ToGfxSize(desc.size));
    290       image_layer->SetTextureMailbox(mailbox,
    291           cc::SingleReleaseCallback::Create(
    292               base::Bind(&PepperCompositorHost::ImageReleased,
    293                          weak_factory_.GetWeakPtr(),
    294                          new_layer->common.resource_id,
    295                          base::Passed(&image_shm))));
    296       // TODO(penghuang): get a damage region from the application and
    297       // pass it to SetNeedsDisplayRect().
    298       image_layer->SetNeedsDisplay();
    299 
    300       // ImageData is always premultiplied alpha.
    301       image_layer->SetPremultipliedAlpha(true);
    302     }
    303     return;
    304   }
    305   // Should not be reached.
    306   NOTREACHED();
    307 }
    308 
    309 int32_t PepperCompositorHost::OnResourceMessageReceived(
    310     const IPC::Message& msg,
    311     HostMessageContext* context) {
    312   PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost, msg)
    313   PPAPI_DISPATCH_HOST_RESOURCE_CALL(
    314       PpapiHostMsg_Compositor_CommitLayers, OnHostMsgCommitLayers)
    315   PPAPI_END_MESSAGE_MAP()
    316   return ppapi::host::ResourceHost::OnResourceMessageReceived(msg, context);
    317 }
    318 
    319 bool PepperCompositorHost::IsCompositorHost() {
    320   return true;
    321 }
    322 
    323 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
    324     HostMessageContext* context,
    325     const std::vector<ppapi::CompositorLayerData>& layers,
    326     bool reset) {
    327   if (commit_layers_reply_context_.is_valid())
    328     return PP_ERROR_INPROGRESS;
    329 
    330   scoped_ptr<scoped_ptr<base::SharedMemory>[]> image_shms;
    331   if (layers.size() > 0) {
    332     image_shms.reset(new scoped_ptr<base::SharedMemory>[layers.size()]);
    333     if (!image_shms)
    334       return PP_ERROR_NOMEMORY;
    335     // Verfiy the layers first, if an error happens, we will return the error to
    336     // plugin and keep current layers set by the previous CommitLayers()
    337     // unchanged.
    338     for (size_t i = 0; i < layers.size(); ++i) {
    339       const ppapi::CompositorLayerData* old_layer = NULL;
    340       if (!reset && i < layers_.size())
    341         old_layer = &layers_[i].pp_layer;
    342       int32_t rv = VerifyCommittedLayer(old_layer, &layers[i], &image_shms[i]);
    343       if (rv != PP_OK)
    344         return rv;
    345     }
    346   }
    347 
    348   // ResetLayers() has been called, we need rebuild layer stack.
    349   if (reset) {
    350     layer_->RemoveAllChildren();
    351     layers_.clear();
    352   }
    353 
    354   for (size_t i = 0; i < layers.size(); ++i) {
    355     const ppapi::CompositorLayerData* pp_layer = &layers[i];
    356     LayerData* data = i >= layers_.size() ? NULL : &layers_[i];
    357     DCHECK(!data || data->cc_layer.get());
    358     scoped_refptr<cc::Layer> cc_layer = data ? data->cc_layer : NULL;
    359     ppapi::CompositorLayerData* old_layer = data ? &data->pp_layer : NULL;
    360 
    361     if (!cc_layer.get()) {
    362       if (pp_layer->color)
    363         cc_layer = cc::SolidColorLayer::Create();
    364       else if (pp_layer->texture || pp_layer->image)
    365         cc_layer = cc::TextureLayer::CreateForMailbox(NULL);
    366       layer_->AddChild(cc_layer);
    367     }
    368 
    369     UpdateLayer(cc_layer, old_layer, pp_layer, image_shms[i].Pass());
    370 
    371     if (old_layer)
    372       *old_layer = *pp_layer;
    373     else
    374       layers_.push_back(LayerData(cc_layer, *pp_layer));
    375   }
    376 
    377   // We need to force a commit for each CommitLayers() call, even if no layers
    378   // changed since the last call to CommitLayers(). This is so
    379   // WiewInitiatedPaint() will always be called.
    380   if (layer_->layer_tree_host())
    381     layer_->layer_tree_host()->SetNeedsCommit();
    382 
    383   // If the host is not bound to the instance, return PP_OK immediately.
    384   if (!bound_instance_)
    385     return PP_OK;
    386 
    387   commit_layers_reply_context_ = context->MakeReplyMessageContext();
    388   return PP_OK_COMPLETIONPENDING;
    389 }
    390 
    391 }  // namespace content
    392