• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
VerifyCommittedLayer(const ppapi::CompositorLayerData * old_layer,const ppapi::CompositorLayerData * new_layer,scoped_ptr<base::SharedMemory> * image_shm)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 
LayerData(const scoped_refptr<cc::Layer> & cc,const ppapi::CompositorLayerData & pp)125 PepperCompositorHost::LayerData::LayerData(
126     const scoped_refptr<cc::Layer>& cc,
127     const ppapi::CompositorLayerData& pp) : cc_layer(cc), pp_layer(pp) {}
128 
~LayerData()129 PepperCompositorHost::LayerData::~LayerData() {}
130 
PepperCompositorHost(RendererPpapiHost * host,PP_Instance instance,PP_Resource resource)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 
~PepperCompositorHost()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 
BindToInstance(PepperPluginInstanceImpl * new_instance)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 
ViewInitiatedPaint()167 void PepperCompositorHost::ViewInitiatedPaint() {
168   SendCommitLayersReplyIfNecessary();
169 }
170 
ViewFlushedPaint()171 void PepperCompositorHost::ViewFlushedPaint() {}
172 
ImageReleased(int32_t id,const scoped_ptr<base::SharedMemory> & shared_memory,uint32_t sync_point,bool is_lost)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 
ResourceReleased(int32_t id,uint32_t sync_point,bool is_lost)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 
SendCommitLayersReplyIfNecessary()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 
UpdateLayer(const scoped_refptr<cc::Layer> & layer,const ppapi::CompositorLayerData * old_layer,const ppapi::CompositorLayerData * new_layer,scoped_ptr<base::SharedMemory> image_shm)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 
OnResourceMessageReceived(const IPC::Message & msg,HostMessageContext * context)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 
IsCompositorHost()319 bool PepperCompositorHost::IsCompositorHost() {
320   return true;
321 }
322 
OnHostMsgCommitLayers(HostMessageContext * context,const std::vector<ppapi::CompositorLayerData> & layers,bool reset)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