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/layers/layer.h"
12 #include "cc/layers/solid_color_layer.h"
13 #include "cc/layers/texture_layer.h"
14 #include "cc/resources/texture_mailbox.h"
15 #include "cc/trees/layer_tree_host.h"
16 #include "content/child/child_shared_bitmap_manager.h"
17 #include "content/child/child_thread_impl.h"
18 #include "content/public/renderer/renderer_ppapi_host.h"
19 #include "content/renderer/pepper/gfx_conversion.h"
20 #include "content/renderer/pepper/host_globals.h"
21 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
22 #include "content/renderer/pepper/ppb_image_data_impl.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "ppapi/host/dispatch_host_message.h"
25 #include "ppapi/host/ppapi_host.h"
26 #include "ppapi/proxy/ppapi_messages.h"
27 #include "ppapi/thunk/enter.h"
28 #include "ppapi/thunk/ppb_image_data_api.h"
29 #include "third_party/khronos/GLES2/gl2.h"
30 #include "ui/gfx/transform.h"
32 using ppapi::host::HostMessageContext
;
33 using ppapi::thunk::EnterResourceNoLock
;
34 using ppapi::thunk::PPB_ImageData_API
;
40 bool CheckPPFloatRect(const PP_FloatRect
& rect
, float width
, float height
) {
41 const float kEpsilon
= std::numeric_limits
<float>::epsilon();
42 return (rect
.point
.x
>= -kEpsilon
&&
43 rect
.point
.y
>= -kEpsilon
&&
44 rect
.point
.x
+ rect
.size
.width
<= width
+ kEpsilon
&&
45 rect
.point
.y
+ rect
.size
.height
<= height
+ kEpsilon
);
48 int32_t VerifyCommittedLayer(
49 const ppapi::CompositorLayerData
* old_layer
,
50 const ppapi::CompositorLayerData
* new_layer
,
51 scoped_ptr
<base::SharedMemory
>* image_shm
) {
52 if (!new_layer
->is_valid())
53 return PP_ERROR_BADARGUMENT
;
55 if (new_layer
->color
) {
56 // Make sure the old layer is a color layer too.
57 if (old_layer
&& !old_layer
->color
)
58 return PP_ERROR_BADARGUMENT
;
62 if (new_layer
->texture
) {
64 // Make sure the old layer is a texture layer too.
65 if (!new_layer
->texture
)
66 return PP_ERROR_BADARGUMENT
;
67 // The mailbox should be same, if the resource_id is not changed.
68 if (new_layer
->common
.resource_id
== old_layer
->common
.resource_id
) {
69 if (new_layer
->texture
->mailbox
!= old_layer
->texture
->mailbox
)
70 return PP_ERROR_BADARGUMENT
;
74 if (!new_layer
->texture
->mailbox
.Verify())
75 return PP_ERROR_BADARGUMENT
;
77 // Make sure the source rect is not beyond the dimensions of the
79 if (!CheckPPFloatRect(new_layer
->texture
->source_rect
, 1.0f
, 1.0f
))
80 return PP_ERROR_BADARGUMENT
;
84 if (new_layer
->image
) {
86 // Make sure the old layer is an image layer too.
87 if (!new_layer
->image
)
88 return PP_ERROR_BADARGUMENT
;
89 // The image data resource should be same, if the resource_id is not
91 if (new_layer
->common
.resource_id
== old_layer
->common
.resource_id
) {
92 if (new_layer
->image
->resource
!= old_layer
->image
->resource
)
93 return PP_ERROR_BADARGUMENT
;
98 EnterResourceNoLock
<PPB_ImageData_API
> enter(new_layer
->image
->resource
,
101 return PP_ERROR_BADRESOURCE
;
103 // TODO(penghuang): support all kinds of image.
104 PP_ImageDataDesc desc
;
105 if (enter
.object()->Describe(&desc
) != PP_TRUE
||
106 desc
.stride
!= desc
.size
.width
* 4 ||
107 desc
.format
!= PP_IMAGEDATAFORMAT_RGBA_PREMUL
) {
108 return PP_ERROR_BADARGUMENT
;
111 // Make sure the source rect is not beyond the dimensions of the
113 if (!CheckPPFloatRect(new_layer
->image
->source_rect
,
114 desc
.size
.width
, desc
.size
.height
)) {
115 return PP_ERROR_BADARGUMENT
;
120 if (enter
.object()->GetSharedMemory(&handle
, &byte_count
) != PP_OK
)
121 return PP_ERROR_FAILED
;
124 base::SharedMemoryHandle shm_handle
;
125 if (!::DuplicateHandle(::GetCurrentProcess(),
126 reinterpret_cast<base::SharedMemoryHandle
>(handle
),
127 ::GetCurrentProcess(),
131 DUPLICATE_SAME_ACCESS
)) {
132 return PP_ERROR_FAILED
;
135 base::SharedMemoryHandle
shm_handle(dup(handle
), false);
137 image_shm
->reset(new base::SharedMemory(shm_handle
, true));
138 if (!(*image_shm
)->Map(desc
.stride
* desc
.size
.height
)) {
140 return PP_ERROR_NOMEMORY
;
145 return PP_ERROR_BADARGUMENT
;
150 PepperCompositorHost::LayerData::LayerData(
151 const scoped_refptr
<cc::Layer
>& cc
,
152 const ppapi::CompositorLayerData
& pp
) : cc_layer(cc
), pp_layer(pp
) {}
154 PepperCompositorHost::LayerData::~LayerData() {}
156 PepperCompositorHost::PepperCompositorHost(
157 RendererPpapiHost
* host
,
158 PP_Instance instance
,
159 PP_Resource resource
)
160 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
161 bound_instance_(NULL
),
162 weak_factory_(this) {
163 layer_
= cc::Layer::Create();
164 // TODO(penghuang): SetMasksToBounds() can be expensive if the layer is
165 // transformed. Possibly better could be to explicitly clip the child layers
166 // (by modifying their bounds).
167 layer_
->SetMasksToBounds(true);
168 layer_
->SetIsDrawable(true);
171 PepperCompositorHost::~PepperCompositorHost() {
172 // Unbind from the instance when destroyed if we're still bound.
174 bound_instance_
->BindGraphics(bound_instance_
->pp_instance(), 0);
177 bool PepperCompositorHost::BindToInstance(
178 PepperPluginInstanceImpl
* new_instance
) {
179 if (new_instance
&& new_instance
->pp_instance() != pp_instance())
180 return false; // Can't bind other instance's contexts.
181 if (bound_instance_
== new_instance
)
182 return true; // Rebinding the same device, nothing to do.
183 if (bound_instance_
&& new_instance
)
184 return false; // Can't change a bound device.
185 bound_instance_
= new_instance
;
186 if (!bound_instance_
)
187 SendCommitLayersReplyIfNecessary();
192 void PepperCompositorHost::ViewInitiatedPaint() {
193 SendCommitLayersReplyIfNecessary();
196 void PepperCompositorHost::ImageReleased(
198 scoped_ptr
<base::SharedMemory
> shared_memory
,
199 scoped_ptr
<cc::SharedBitmap
> bitmap
,
203 shared_memory
.reset();
204 ResourceReleased(id
, sync_point
, is_lost
);
207 void PepperCompositorHost::ResourceReleased(int32_t id
,
210 host()->SendUnsolicitedReply(
212 PpapiPluginMsg_Compositor_ReleaseResource(id
, sync_point
, is_lost
));
215 void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
216 if (!commit_layers_reply_context_
.is_valid())
218 host()->SendReply(commit_layers_reply_context_
,
219 PpapiPluginMsg_Compositor_CommitLayersReply());
220 commit_layers_reply_context_
= ppapi::host::ReplyMessageContext();
223 void PepperCompositorHost::UpdateLayer(
224 const scoped_refptr
<cc::Layer
>& layer
,
225 const ppapi::CompositorLayerData
* old_layer
,
226 const ppapi::CompositorLayerData
* new_layer
,
227 scoped_ptr
<base::SharedMemory
> image_shm
) {
228 // Always update properties on cc::Layer, because cc::Layer
229 // will ignore any setting with unchanged value.
230 layer
->SetIsDrawable(true);
231 layer
->SetBlendMode(SkXfermode::kSrcOver_Mode
);
232 layer
->SetOpacity(new_layer
->common
.opacity
);
233 layer
->SetBounds(PP_ToGfxSize(new_layer
->common
.size
));
234 layer
->SetTransformOrigin(gfx::Point3F(new_layer
->common
.size
.width
/ 2,
235 new_layer
->common
.size
.height
/ 2,
238 gfx::Transform
transform(gfx::Transform::kSkipInitialization
);
239 transform
.matrix().setColMajorf(new_layer
->common
.transform
.matrix
);
240 layer
->SetTransform(transform
);
242 // Consider a (0,0,0,0) rect as no clip rect.
243 if (new_layer
->common
.clip_rect
.point
.x
!= 0 ||
244 new_layer
->common
.clip_rect
.point
.y
!= 0 ||
245 new_layer
->common
.clip_rect
.size
.width
!= 0 ||
246 new_layer
->common
.clip_rect
.size
.height
!= 0) {
247 scoped_refptr
<cc::Layer
> clip_parent
= layer
->parent();
248 if (clip_parent
.get() == layer_
.get()) {
249 // Create a clip parent layer, if it does not exist.
250 clip_parent
= cc::Layer::Create();
251 clip_parent
->SetMasksToBounds(true);
252 clip_parent
->SetIsDrawable(true);
253 layer_
->ReplaceChild(layer
.get(), clip_parent
);
254 clip_parent
->AddChild(layer
);
256 gfx::Point position
= PP_ToGfxPoint(new_layer
->common
.clip_rect
.point
);
257 clip_parent
->SetPosition(position
);
258 clip_parent
->SetBounds(PP_ToGfxSize(new_layer
->common
.clip_rect
.size
));
259 layer
->SetPosition(gfx::Point(-position
.x(), -position
.y()));
260 } else if (layer
->parent() != layer_
.get()) {
261 // Remove the clip parent layer.
262 layer_
->ReplaceChild(layer
->parent(), layer
);
263 layer
->SetPosition(gfx::Point());
266 if (new_layer
->color
) {
267 layer
->SetBackgroundColor(SkColorSetARGBMacro(
268 new_layer
->color
->alpha
* 255,
269 new_layer
->color
->red
* 255,
270 new_layer
->color
->green
* 255,
271 new_layer
->color
->blue
* 255));
275 if (new_layer
->texture
) {
276 scoped_refptr
<cc::TextureLayer
> texture_layer(
277 static_cast<cc::TextureLayer
*>(layer
.get()));
279 new_layer
->common
.resource_id
!= old_layer
->common
.resource_id
) {
280 cc::TextureMailbox
mailbox(new_layer
->texture
->mailbox
,
281 new_layer
->texture
->target
,
282 new_layer
->texture
->sync_point
);
283 texture_layer
->SetTextureMailbox(mailbox
,
284 cc::SingleReleaseCallback::Create(
285 base::Bind(&PepperCompositorHost::ResourceReleased
,
286 weak_factory_
.GetWeakPtr(),
287 new_layer
->common
.resource_id
)));
288 // TODO(penghuang): get a damage region from the application and
289 // pass it to SetNeedsDisplayRect().
290 texture_layer
->SetNeedsDisplay();
292 texture_layer
->SetPremultipliedAlpha(new_layer
->texture
->premult_alpha
);
293 gfx::RectF rect
= PP_ToGfxRectF(new_layer
->texture
->source_rect
);
294 texture_layer
->SetUV(rect
.origin(), rect
.bottom_right());
298 if (new_layer
->image
) {
300 new_layer
->common
.resource_id
!= old_layer
->common
.resource_id
) {
301 scoped_refptr
<cc::TextureLayer
> image_layer(
302 static_cast<cc::TextureLayer
*>(layer
.get()));
303 EnterResourceNoLock
<PPB_ImageData_API
> enter(new_layer
->image
->resource
,
305 DCHECK(enter
.succeeded());
307 // TODO(penghuang): support all kinds of image.
308 PP_ImageDataDesc desc
;
309 PP_Bool rv
= enter
.object()->Describe(&desc
);
310 DCHECK_EQ(rv
, PP_TRUE
);
311 DCHECK_EQ(desc
.stride
, desc
.size
.width
* 4);
312 DCHECK_EQ(desc
.format
, PP_IMAGEDATAFORMAT_RGBA_PREMUL
);
313 scoped_ptr
<cc::SharedBitmap
> bitmap
=
314 ChildThreadImpl::current()
315 ->shared_bitmap_manager()
316 ->GetBitmapForSharedMemory(image_shm
.get());
318 cc::TextureMailbox
mailbox(bitmap
.get(), PP_ToGfxSize(desc
.size
));
319 image_layer
->SetTextureMailbox(
321 cc::SingleReleaseCallback::Create(base::Bind(
322 &PepperCompositorHost::ImageReleased
, weak_factory_
.GetWeakPtr(),
323 new_layer
->common
.resource_id
, base::Passed(&image_shm
),
324 base::Passed(&bitmap
))));
325 // TODO(penghuang): get a damage region from the application and
326 // pass it to SetNeedsDisplayRect().
327 image_layer
->SetNeedsDisplay();
329 // ImageData is always premultiplied alpha.
330 image_layer
->SetPremultipliedAlpha(true);
334 // Should not be reached.
338 int32_t PepperCompositorHost::OnResourceMessageReceived(
339 const IPC::Message
& msg
,
340 HostMessageContext
* context
) {
341 PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost
, msg
)
342 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
343 PpapiHostMsg_Compositor_CommitLayers
, OnHostMsgCommitLayers
)
344 PPAPI_END_MESSAGE_MAP()
345 return ppapi::host::ResourceHost::OnResourceMessageReceived(msg
, context
);
348 bool PepperCompositorHost::IsCompositorHost() {
352 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
353 HostMessageContext
* context
,
354 const std::vector
<ppapi::CompositorLayerData
>& layers
,
356 if (commit_layers_reply_context_
.is_valid())
357 return PP_ERROR_INPROGRESS
;
359 scoped_ptr
<scoped_ptr
<base::SharedMemory
>[]> image_shms
;
360 if (layers
.size() > 0) {
361 image_shms
.reset(new scoped_ptr
<base::SharedMemory
>[layers
.size()]);
363 return PP_ERROR_NOMEMORY
;
364 // Verfiy the layers first, if an error happens, we will return the error to
365 // plugin and keep current layers set by the previous CommitLayers()
367 for (size_t i
= 0; i
< layers
.size(); ++i
) {
368 const ppapi::CompositorLayerData
* old_layer
= NULL
;
369 if (!reset
&& i
< layers_
.size())
370 old_layer
= &layers_
[i
].pp_layer
;
371 int32_t rv
= VerifyCommittedLayer(old_layer
, &layers
[i
], &image_shms
[i
]);
377 // ResetLayers() has been called, we need rebuild layer stack.
379 layer_
->RemoveAllChildren();
383 for (size_t i
= 0; i
< layers
.size(); ++i
) {
384 const ppapi::CompositorLayerData
* pp_layer
= &layers
[i
];
385 LayerData
* data
= i
>= layers_
.size() ? NULL
: &layers_
[i
];
386 DCHECK(!data
|| data
->cc_layer
.get());
387 scoped_refptr
<cc::Layer
> cc_layer
= data
? data
->cc_layer
: NULL
;
388 ppapi::CompositorLayerData
* old_layer
= data
? &data
->pp_layer
: NULL
;
390 if (!cc_layer
.get()) {
392 cc_layer
= cc::SolidColorLayer::Create();
393 else if (pp_layer
->texture
|| pp_layer
->image
)
394 cc_layer
= cc::TextureLayer::CreateForMailbox(NULL
);
395 layer_
->AddChild(cc_layer
);
398 UpdateLayer(cc_layer
, old_layer
, pp_layer
, image_shms
[i
].Pass());
401 *old_layer
= *pp_layer
;
403 layers_
.push_back(LayerData(cc_layer
, *pp_layer
));
406 // We need to force a commit for each CommitLayers() call, even if no layers
407 // changed since the last call to CommitLayers(). This is so
408 // WiewInitiatedPaint() will always be called.
409 if (layer_
->layer_tree_host())
410 layer_
->layer_tree_host()->SetNeedsCommit();
412 // If the host is not bound to the instance, return PP_OK immediately.
413 if (!bound_instance_
)
416 commit_layers_reply_context_
= context
->MakeReplyMessageContext();
417 return PP_OK_COMPLETIONPENDING
;
420 } // namespace content