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 "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"
28 using ppapi::host::HostMessageContext
;
29 using ppapi::thunk::EnterResourceNoLock
;
30 using ppapi::thunk::PPB_ImageData_API
;
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
;
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
;
50 if (new_layer
->texture
) {
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
;
62 if (!new_layer
->texture
->mailbox
.Verify())
63 return PP_ERROR_BADARGUMENT
;
67 if (new_layer
->image
) {
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
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
;
80 EnterResourceNoLock
<PPB_ImageData_API
> enter(new_layer
->image
->resource
,
83 return PP_ERROR_BADRESOURCE
;
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
;
95 if (enter
.object()->GetSharedMemory(&handle
, &byte_count
) != PP_OK
)
96 return PP_ERROR_FAILED
;
99 base::SharedMemoryHandle shm_handle
;
100 if (!::DuplicateHandle(::GetCurrentProcess(),
101 reinterpret_cast<base::SharedMemoryHandle
>(handle
),
102 ::GetCurrentProcess(),
106 DUPLICATE_SAME_ACCESS
)) {
107 return PP_ERROR_FAILED
;
110 base::SharedMemoryHandle
shm_handle(dup(handle
), false);
112 image_shm
->reset(new base::SharedMemory(shm_handle
, true));
113 if (!(*image_shm
)->Map(desc
.stride
* desc
.size
.height
)) {
115 return PP_ERROR_NOMEMORY
;
120 return PP_ERROR_BADARGUMENT
;
125 PepperCompositorHost::LayerData::LayerData(
126 const scoped_refptr
<cc::Layer
>& cc
,
127 const ppapi::CompositorLayerData
& pp
) : cc_layer(cc
), pp_layer(pp
) {}
129 PepperCompositorHost::LayerData::~LayerData() {}
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);
146 PepperCompositorHost::~PepperCompositorHost() {
147 // Unbind from the instance when destroyed if we're still bound.
149 bound_instance_
->BindGraphics(bound_instance_
->pp_instance(), 0);
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();
167 void PepperCompositorHost::ViewInitiatedPaint() {
168 SendCommitLayersReplyIfNecessary();
171 void PepperCompositorHost::ViewFlushedPaint() {}
173 void PepperCompositorHost::ImageReleased(
175 const scoped_ptr
<base::SharedMemory
>& shared_memory
,
178 ResourceReleased(id
, sync_point
, is_lost
);
181 void PepperCompositorHost::ResourceReleased(int32_t id
,
184 host()->SendUnsolicitedReply(
186 PpapiPluginMsg_Compositor_ReleaseResource(id
, sync_point
, is_lost
));
189 void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
190 if (!commit_layers_reply_context_
.is_valid())
192 host()->SendReply(commit_layers_reply_context_
,
193 PpapiPluginMsg_Compositor_CommitLayersReply());
194 commit_layers_reply_context_
= ppapi::host::ReplyMessageContext();
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,
212 gfx::Transform
transform(gfx::Transform::kSkipInitialization
);
213 transform
.matrix().setColMajorf(new_layer
->common
.transform
.matrix
);
214 layer
->SetTransform(transform
);
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
);
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());
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));
249 if (new_layer
->texture
) {
250 scoped_refptr
<cc::TextureLayer
> texture_layer(
251 static_cast<cc::TextureLayer
*>(layer
.get()));
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
)));;
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());
269 if (new_layer
->image
) {
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
,
276 DCHECK(enter
.succeeded());
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
);
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
))));
294 // ImageData is always premultiplied alpha.
295 image_layer
->SetPremultipliedAlpha(true);
299 // Should not be reached.
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
);
313 bool PepperCompositorHost::IsCompositorHost() {
317 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
318 HostMessageContext
* context
,
319 const std::vector
<ppapi::CompositorLayerData
>& layers
,
321 if (commit_layers_reply_context_
.is_valid())
322 return PP_ERROR_INPROGRESS
;
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()]);
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()
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
]);
342 // ResetLayers() has been called, we need rebuild layer stack.
344 layer_
->RemoveAllChildren();
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
.get());
352 scoped_refptr
<cc::Layer
> cc_layer
= data
? data
->cc_layer
: NULL
;
353 ppapi::CompositorLayerData
* old_layer
= data
? &data
->pp_layer
: NULL
;
355 if (!cc_layer
.get()) {
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
);
363 UpdateLayer(cc_layer
, old_layer
, pp_layer
, image_shms
[i
].Pass());
366 *old_layer
= *pp_layer
;
368 layers_
.push_back(LayerData(cc_layer
, *pp_layer
));
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();
377 // If the host is not bound to the instance, return PP_OK immediately.
378 if (!bound_instance_
)
381 commit_layers_reply_context_
= context
->MakeReplyMessageContext();
382 return PP_OK_COMPLETIONPENDING
;
385 } // namespace content