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"
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
;
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
;
63 if (new_layer
->texture
) {
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
;
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
80 if (!CheckPPFloatRect(new_layer
->texture
->source_rect
, 1.0f
, 1.0f
))
81 return PP_ERROR_BADARGUMENT
;
85 if (new_layer
->image
) {
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
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
;
99 EnterResourceNoLock
<PPB_ImageData_API
> enter(new_layer
->image
->resource
,
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
114 if (!CheckPPFloatRect(new_layer
->image
->source_rect
,
115 desc
.size
.width
, desc
.size
.height
)) {
116 return PP_ERROR_BADARGUMENT
;
119 base::SharedMemory
* shm
;
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
)) {
132 return PP_ERROR_NOMEMORY
;
137 return PP_ERROR_BADARGUMENT
;
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.
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();
184 void PepperCompositorHost::ViewInitiatedPaint() {
185 SendCommitLayersReplyIfNecessary();
188 void PepperCompositorHost::ImageReleased(
190 scoped_ptr
<base::SharedMemory
> shared_memory
,
191 scoped_ptr
<cc::SharedBitmap
> bitmap
,
195 shared_memory
.reset();
196 ResourceReleased(id
, sync_point
, is_lost
);
199 void PepperCompositorHost::ResourceReleased(int32_t id
,
202 host()->SendUnsolicitedReply(
204 PpapiPluginMsg_Compositor_ReleaseResource(id
, sync_point
, is_lost
));
207 void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
208 if (!commit_layers_reply_context_
.is_valid())
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,
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));
267 if (new_layer
->texture
) {
268 scoped_refptr
<cc::TextureLayer
> texture_layer(
269 static_cast<cc::TextureLayer
*>(layer
.get()));
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());
290 if (new_layer
->image
) {
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
,
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(
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);
326 // Should not be reached.
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() {
344 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
345 HostMessageContext
* context
,
346 const std::vector
<ppapi::CompositorLayerData
>& layers
,
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()]);
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()
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
]);
369 // ResetLayers() has been called, we need rebuild layer stack.
371 layer_
->RemoveAllChildren();
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()) {
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());
395 *old_layer
= *pp_layer
;
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_
)
410 commit_layers_reply_context_
= context
->MakeReplyMessageContext();
411 return PP_OK_COMPLETIONPENDING
;
414 } // namespace content