Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_compositor_host.cc
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 == layer_) {
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, 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_) {
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     }
263     texture_layer->SetPremultipliedAlpha(new_layer->texture->premult_alpha);
264     gfx::RectF rect = PP_ToGfxRectF(new_layer->texture->source_rect);
265     texture_layer->SetUV(rect.origin(), rect.bottom_right());
266     return;
267   }
268
269   if (new_layer->image) {
270     if (!old_layer ||
271         new_layer->common.resource_id != old_layer->common.resource_id) {
272       scoped_refptr<cc::TextureLayer> image_layer(
273           static_cast<cc::TextureLayer*>(layer.get()));
274       EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
275                                                    true);
276       DCHECK(enter.succeeded());
277
278       // TODO(penghuang): support all kinds of image.
279       PP_ImageDataDesc desc;
280       PP_Bool rv = enter.object()->Describe(&desc);
281       DCHECK_EQ(rv, PP_TRUE);
282       DCHECK_EQ(desc.stride, desc.size.width * 4);
283       DCHECK_EQ(desc.format, PP_IMAGEDATAFORMAT_RGBA_PREMUL);
284
285       cc::TextureMailbox mailbox(image_shm.get(),
286                                  PP_ToGfxSize(desc.size));
287       image_layer->SetTextureMailbox(mailbox,
288           cc::SingleReleaseCallback::Create(
289               base::Bind(&PepperCompositorHost::ImageReleased,
290                          weak_factory_.GetWeakPtr(),
291                          new_layer->common.resource_id,
292                          base::Passed(&image_shm))));
293
294       // ImageData is always premultiplied alpha.
295       image_layer->SetPremultipliedAlpha(true);
296     }
297     return;
298   }
299   // Should not be reached.
300   NOTREACHED();
301 }
302
303 int32_t PepperCompositorHost::OnResourceMessageReceived(
304     const IPC::Message& msg,
305     HostMessageContext* context) {
306   PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost, msg)
307   PPAPI_DISPATCH_HOST_RESOURCE_CALL(
308       PpapiHostMsg_Compositor_CommitLayers, OnHostMsgCommitLayers)
309   PPAPI_END_MESSAGE_MAP()
310   return ppapi::host::ResourceHost::OnResourceMessageReceived(msg, context);
311 }
312
313 bool PepperCompositorHost::IsCompositorHost() {
314   return true;
315 }
316
317 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
318     HostMessageContext* context,
319     const std::vector<ppapi::CompositorLayerData>& layers,
320     bool reset) {
321   if (commit_layers_reply_context_.is_valid())
322     return PP_ERROR_INPROGRESS;
323
324   scoped_ptr<scoped_ptr<base::SharedMemory>[]> image_shms;
325   if (layers.size() > 0) {
326     image_shms.reset(new scoped_ptr<base::SharedMemory>[layers.size()]);
327     if (!image_shms)
328       return PP_ERROR_NOMEMORY;
329     // Verfiy the layers first, if an error happens, we will return the error to
330     // plugin and keep current layers set by the previous CommitLayers()
331     // unchanged.
332     for (size_t i = 0; i < layers.size(); ++i) {
333       const ppapi::CompositorLayerData* old_layer = NULL;
334       if (!reset && i < layers_.size())
335         old_layer = &layers_[i].pp_layer;
336       int32_t rv = VerifyCommittedLayer(old_layer, &layers[i], &image_shms[i]);
337       if (rv != PP_OK)
338         return rv;
339     }
340   }
341
342   // ResetLayers() has been called, we need rebuild layer stack.
343   if (reset) {
344     layer_->RemoveAllChildren();
345     layers_.clear();
346   }
347
348   for (size_t i = 0; i < layers.size(); ++i) {
349     const ppapi::CompositorLayerData* pp_layer = &layers[i];
350     LayerData* data = i >= layers_.size() ? NULL : &layers_[i];
351     DCHECK(!data || data->cc_layer);
352     scoped_refptr<cc::Layer> cc_layer = data ? data->cc_layer : NULL;
353     ppapi::CompositorLayerData* old_layer = data ? &data->pp_layer : NULL;
354
355     if (!cc_layer) {
356       if (pp_layer->color)
357         cc_layer = cc::SolidColorLayer::Create();
358       else if (pp_layer->texture || pp_layer->image)
359         cc_layer = cc::TextureLayer::CreateForMailbox(NULL);
360       layer_->AddChild(cc_layer);
361     }
362
363     UpdateLayer(cc_layer, old_layer, pp_layer, image_shms[i].Pass());
364
365     if (old_layer)
366       *old_layer = *pp_layer;
367     else
368       layers_.push_back(LayerData(cc_layer, *pp_layer));
369   }
370
371   // We need to force a commit for each CommitLayers() call, even if no layers
372   // changed since the last call to CommitLayers(). This is so
373   // WiewInitiatedPaint() will always be called.
374   if (layer_->layer_tree_host())
375     layer_->layer_tree_host()->SetNeedsCommit();
376
377   // If the host is not bound to the instance, return PP_OK immediately.
378   if (!bound_instance_)
379     return PP_OK;
380
381   commit_layers_reply_context_ = context->MakeReplyMessageContext();
382   return PP_OK_COMPLETIONPENDING;
383 }
384
385 }  // namespace content