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