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
28using ppapi::host::HostMessageContext;
29using ppapi::thunk::EnterResourceNoLock;
30using ppapi::thunk::PPB_ImageData_API;
31
32namespace content {
33
34namespace {
35
36int32_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
125PepperCompositorHost::LayerData::LayerData(
126    const scoped_refptr<cc::Layer>& cc,
127    const ppapi::CompositorLayerData& pp) : cc_layer(cc), pp_layer(pp) {}
128
129PepperCompositorHost::LayerData::~LayerData() {}
130
131PepperCompositorHost::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
146PepperCompositorHost::~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
152bool 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
167void PepperCompositorHost::ViewInitiatedPaint() {
168  SendCommitLayersReplyIfNecessary();
169}
170
171void PepperCompositorHost::ViewFlushedPaint() {}
172
173void 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
181void 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
189void 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
197void 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
309int32_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
319bool PepperCompositorHost::IsCompositorHost() {
320  return true;
321}
322
323int32_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