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/child/child_shared_bitmap_manager.h"
15 #include "content/child/child_thread_impl.h"
16 #include "content/public/renderer/renderer_ppapi_host.h"
17 #include "content/renderer/pepper/gfx_conversion.h"
18 #include "content/renderer/pepper/host_globals.h"
19 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
20 #include "content/renderer/pepper/ppb_image_data_impl.h"
21 #include "ppapi/c/pp_errors.h"
22 #include "ppapi/host/dispatch_host_message.h"
23 #include "ppapi/host/ppapi_host.h"
24 #include "ppapi/proxy/ppapi_messages.h"
25 #include "ppapi/thunk/enter.h"
26 #include "ppapi/thunk/ppb_image_data_api.h"
27 #include "third_party/khronos/GLES2/gl2.h"
28 #include "ui/gfx/transform.h"
30 using ppapi::host::HostMessageContext
;
31 using ppapi::thunk::EnterResourceNoLock
;
32 using ppapi::thunk::PPB_ImageData_API
;
38 int32_t VerifyCommittedLayer(
39 const ppapi::CompositorLayerData
* old_layer
,
40 const ppapi::CompositorLayerData
* new_layer
,
41 scoped_ptr
<base::SharedMemory
>* image_shm
) {
42 if (!new_layer
->is_valid())
43 return PP_ERROR_BADARGUMENT
;
45 if (new_layer
->color
) {
46 // Make sure the old layer is a color layer too.
47 if (old_layer
&& !old_layer
->color
)
48 return PP_ERROR_BADARGUMENT
;
52 if (new_layer
->texture
) {
54 // Make sure the old layer is a texture layer too.
55 if (!new_layer
->texture
)
56 return PP_ERROR_BADARGUMENT
;
57 // The mailbox should be same, if the resource_id is not changed.
58 if (new_layer
->common
.resource_id
== old_layer
->common
.resource_id
) {
59 if (new_layer
->texture
->mailbox
!= old_layer
->texture
->mailbox
)
60 return PP_ERROR_BADARGUMENT
;
64 if (!new_layer
->texture
->mailbox
.Verify())
65 return PP_ERROR_BADARGUMENT
;
69 if (new_layer
->image
) {
71 // Make sure the old layer is an image layer too.
72 if (!new_layer
->image
)
73 return PP_ERROR_BADARGUMENT
;
74 // The image data resource should be same, if the resource_id is not
76 if (new_layer
->common
.resource_id
== old_layer
->common
.resource_id
) {
77 if (new_layer
->image
->resource
!= old_layer
->image
->resource
)
78 return PP_ERROR_BADARGUMENT
;
82 EnterResourceNoLock
<PPB_ImageData_API
> enter(new_layer
->image
->resource
,
85 return PP_ERROR_BADRESOURCE
;
87 // TODO(penghuang): support all kinds of image.
88 PP_ImageDataDesc desc
;
89 if (enter
.object()->Describe(&desc
) != PP_TRUE
||
90 desc
.stride
!= desc
.size
.width
* 4 ||
91 desc
.format
!= PP_IMAGEDATAFORMAT_RGBA_PREMUL
) {
92 return PP_ERROR_BADARGUMENT
;
97 if (enter
.object()->GetSharedMemory(&handle
, &byte_count
) != PP_OK
)
98 return PP_ERROR_FAILED
;
101 base::SharedMemoryHandle shm_handle
;
102 if (!::DuplicateHandle(::GetCurrentProcess(),
103 reinterpret_cast<base::SharedMemoryHandle
>(handle
),
104 ::GetCurrentProcess(),
108 DUPLICATE_SAME_ACCESS
)) {
109 return PP_ERROR_FAILED
;
112 base::SharedMemoryHandle
shm_handle(dup(handle
), false);
114 image_shm
->reset(new base::SharedMemory(shm_handle
, true));
115 if (!(*image_shm
)->Map(desc
.stride
* desc
.size
.height
)) {
117 return PP_ERROR_NOMEMORY
;
122 return PP_ERROR_BADARGUMENT
;
127 PepperCompositorHost::LayerData::LayerData(
128 const scoped_refptr
<cc::Layer
>& cc
,
129 const ppapi::CompositorLayerData
& pp
) : cc_layer(cc
), pp_layer(pp
) {}
131 PepperCompositorHost::LayerData::~LayerData() {}
133 PepperCompositorHost::PepperCompositorHost(
134 RendererPpapiHost
* host
,
135 PP_Instance instance
,
136 PP_Resource resource
)
137 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
138 bound_instance_(NULL
),
139 weak_factory_(this) {
140 layer_
= cc::Layer::Create();
141 // TODO(penghuang): SetMasksToBounds() can be expensive if the layer is
142 // transformed. Possibly better could be to explicitly clip the child layers
143 // (by modifying their bounds).
144 layer_
->SetMasksToBounds(true);
145 layer_
->SetIsDrawable(true);
148 PepperCompositorHost::~PepperCompositorHost() {
149 // Unbind from the instance when destroyed if we're still bound.
151 bound_instance_
->BindGraphics(bound_instance_
->pp_instance(), 0);
154 bool PepperCompositorHost::BindToInstance(
155 PepperPluginInstanceImpl
* new_instance
) {
156 if (new_instance
&& new_instance
->pp_instance() != pp_instance())
157 return false; // Can't bind other instance's contexts.
158 if (bound_instance_
== new_instance
)
159 return true; // Rebinding the same device, nothing to do.
160 if (bound_instance_
&& new_instance
)
161 return false; // Can't change a bound device.
162 bound_instance_
= new_instance
;
163 if (!bound_instance_
)
164 SendCommitLayersReplyIfNecessary();
169 void PepperCompositorHost::ViewInitiatedPaint() {
170 SendCommitLayersReplyIfNecessary();
173 void PepperCompositorHost::ImageReleased(
175 scoped_ptr
<base::SharedMemory
> shared_memory
,
176 scoped_ptr
<cc::SharedBitmap
> bitmap
,
180 shared_memory
.reset();
181 ResourceReleased(id
, sync_point
, is_lost
);
184 void PepperCompositorHost::ResourceReleased(int32_t id
,
187 host()->SendUnsolicitedReply(
189 PpapiPluginMsg_Compositor_ReleaseResource(id
, sync_point
, is_lost
));
192 void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
193 if (!commit_layers_reply_context_
.is_valid())
195 host()->SendReply(commit_layers_reply_context_
,
196 PpapiPluginMsg_Compositor_CommitLayersReply());
197 commit_layers_reply_context_
= ppapi::host::ReplyMessageContext();
200 void PepperCompositorHost::UpdateLayer(
201 const scoped_refptr
<cc::Layer
>& layer
,
202 const ppapi::CompositorLayerData
* old_layer
,
203 const ppapi::CompositorLayerData
* new_layer
,
204 scoped_ptr
<base::SharedMemory
> image_shm
) {
205 // Always update properties on cc::Layer, because cc::Layer
206 // will ignore any setting with unchanged value.
207 layer
->SetIsDrawable(true);
208 layer
->SetBlendMode(SkXfermode::kSrcOver_Mode
);
209 layer
->SetOpacity(new_layer
->common
.opacity
);
210 layer
->SetBounds(PP_ToGfxSize(new_layer
->common
.size
));
211 layer
->SetTransformOrigin(gfx::Point3F(new_layer
->common
.size
.width
/ 2,
212 new_layer
->common
.size
.height
/ 2,
215 gfx::Transform
transform(gfx::Transform::kSkipInitialization
);
216 transform
.matrix().setColMajorf(new_layer
->common
.transform
.matrix
);
217 layer
->SetTransform(transform
);
219 // Consider a (0,0,0,0) rect as no clip rect.
220 if (new_layer
->common
.clip_rect
.point
.x
!= 0 ||
221 new_layer
->common
.clip_rect
.point
.y
!= 0 ||
222 new_layer
->common
.clip_rect
.size
.width
!= 0 ||
223 new_layer
->common
.clip_rect
.size
.height
!= 0) {
224 scoped_refptr
<cc::Layer
> clip_parent
= layer
->parent();
225 if (clip_parent
.get() == layer_
.get()) {
226 // Create a clip parent layer, if it does not exist.
227 clip_parent
= cc::Layer::Create();
228 clip_parent
->SetMasksToBounds(true);
229 clip_parent
->SetIsDrawable(true);
230 layer_
->ReplaceChild(layer
.get(), clip_parent
);
231 clip_parent
->AddChild(layer
);
233 gfx::Point position
= PP_ToGfxPoint(new_layer
->common
.clip_rect
.point
);
234 clip_parent
->SetPosition(position
);
235 clip_parent
->SetBounds(PP_ToGfxSize(new_layer
->common
.clip_rect
.size
));
236 layer
->SetPosition(gfx::Point(-position
.x(), -position
.y()));
237 } else if (layer
->parent() != layer_
.get()) {
238 // Remove the clip parent layer.
239 layer_
->ReplaceChild(layer
->parent(), layer
);
240 layer
->SetPosition(gfx::Point());
243 if (new_layer
->color
) {
244 layer
->SetBackgroundColor(SkColorSetARGBMacro(
245 new_layer
->color
->alpha
* 255,
246 new_layer
->color
->red
* 255,
247 new_layer
->color
->green
* 255,
248 new_layer
->color
->blue
* 255));
252 if (new_layer
->texture
) {
253 scoped_refptr
<cc::TextureLayer
> texture_layer(
254 static_cast<cc::TextureLayer
*>(layer
.get()));
256 new_layer
->common
.resource_id
!= old_layer
->common
.resource_id
) {
257 cc::TextureMailbox
mailbox(new_layer
->texture
->mailbox
,
258 new_layer
->texture
->target
,
259 new_layer
->texture
->sync_point
);
260 texture_layer
->SetTextureMailbox(mailbox
,
261 cc::SingleReleaseCallback::Create(
262 base::Bind(&PepperCompositorHost::ResourceReleased
,
263 weak_factory_
.GetWeakPtr(),
264 new_layer
->common
.resource_id
)));
265 // TODO(penghuang): get a damage region from the application and
266 // pass it to SetNeedsDisplayRect().
267 texture_layer
->SetNeedsDisplay();
269 texture_layer
->SetPremultipliedAlpha(new_layer
->texture
->premult_alpha
);
270 gfx::RectF rect
= PP_ToGfxRectF(new_layer
->texture
->source_rect
);
271 texture_layer
->SetUV(rect
.origin(), rect
.bottom_right());
275 if (new_layer
->image
) {
277 new_layer
->common
.resource_id
!= old_layer
->common
.resource_id
) {
278 scoped_refptr
<cc::TextureLayer
> image_layer(
279 static_cast<cc::TextureLayer
*>(layer
.get()));
280 EnterResourceNoLock
<PPB_ImageData_API
> enter(new_layer
->image
->resource
,
282 DCHECK(enter
.succeeded());
284 // TODO(penghuang): support all kinds of image.
285 PP_ImageDataDesc desc
;
286 PP_Bool rv
= enter
.object()->Describe(&desc
);
287 DCHECK_EQ(rv
, PP_TRUE
);
288 DCHECK_EQ(desc
.stride
, desc
.size
.width
* 4);
289 DCHECK_EQ(desc
.format
, PP_IMAGEDATAFORMAT_RGBA_PREMUL
);
290 scoped_ptr
<cc::SharedBitmap
> bitmap
=
291 ChildThreadImpl::current()
292 ->shared_bitmap_manager()
293 ->GetBitmapForSharedMemory(image_shm
.get());
295 cc::TextureMailbox
mailbox(bitmap
.get(), PP_ToGfxSize(desc
.size
));
296 image_layer
->SetTextureMailbox(
298 cc::SingleReleaseCallback::Create(base::Bind(
299 &PepperCompositorHost::ImageReleased
, weak_factory_
.GetWeakPtr(),
300 new_layer
->common
.resource_id
, base::Passed(&image_shm
),
301 base::Passed(&bitmap
))));
302 // TODO(penghuang): get a damage region from the application and
303 // pass it to SetNeedsDisplayRect().
304 image_layer
->SetNeedsDisplay();
306 // ImageData is always premultiplied alpha.
307 image_layer
->SetPremultipliedAlpha(true);
311 // Should not be reached.
315 int32_t PepperCompositorHost::OnResourceMessageReceived(
316 const IPC::Message
& msg
,
317 HostMessageContext
* context
) {
318 PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost
, msg
)
319 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
320 PpapiHostMsg_Compositor_CommitLayers
, OnHostMsgCommitLayers
)
321 PPAPI_END_MESSAGE_MAP()
322 return ppapi::host::ResourceHost::OnResourceMessageReceived(msg
, context
);
325 bool PepperCompositorHost::IsCompositorHost() {
329 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
330 HostMessageContext
* context
,
331 const std::vector
<ppapi::CompositorLayerData
>& layers
,
333 if (commit_layers_reply_context_
.is_valid())
334 return PP_ERROR_INPROGRESS
;
336 scoped_ptr
<scoped_ptr
<base::SharedMemory
>[]> image_shms
;
337 if (layers
.size() > 0) {
338 image_shms
.reset(new scoped_ptr
<base::SharedMemory
>[layers
.size()]);
340 return PP_ERROR_NOMEMORY
;
341 // Verfiy the layers first, if an error happens, we will return the error to
342 // plugin and keep current layers set by the previous CommitLayers()
344 for (size_t i
= 0; i
< layers
.size(); ++i
) {
345 const ppapi::CompositorLayerData
* old_layer
= NULL
;
346 if (!reset
&& i
< layers_
.size())
347 old_layer
= &layers_
[i
].pp_layer
;
348 int32_t rv
= VerifyCommittedLayer(old_layer
, &layers
[i
], &image_shms
[i
]);
354 // ResetLayers() has been called, we need rebuild layer stack.
356 layer_
->RemoveAllChildren();
360 for (size_t i
= 0; i
< layers
.size(); ++i
) {
361 const ppapi::CompositorLayerData
* pp_layer
= &layers
[i
];
362 LayerData
* data
= i
>= layers_
.size() ? NULL
: &layers_
[i
];
363 DCHECK(!data
|| data
->cc_layer
.get());
364 scoped_refptr
<cc::Layer
> cc_layer
= data
? data
->cc_layer
: NULL
;
365 ppapi::CompositorLayerData
* old_layer
= data
? &data
->pp_layer
: NULL
;
367 if (!cc_layer
.get()) {
369 cc_layer
= cc::SolidColorLayer::Create();
370 else if (pp_layer
->texture
|| pp_layer
->image
)
371 cc_layer
= cc::TextureLayer::CreateForMailbox(NULL
);
372 layer_
->AddChild(cc_layer
);
375 UpdateLayer(cc_layer
, old_layer
, pp_layer
, image_shms
[i
].Pass());
378 *old_layer
= *pp_layer
;
380 layers_
.push_back(LayerData(cc_layer
, *pp_layer
));
383 // We need to force a commit for each CommitLayers() call, even if no layers
384 // changed since the last call to CommitLayers(). This is so
385 // WiewInitiatedPaint() will always be called.
386 if (layer_
->layer_tree_host())
387 layer_
->layer_tree_host()->SetNeedsCommit();
389 // If the host is not bound to the instance, return PP_OK immediately.
390 if (!bound_instance_
)
393 commit_layers_reply_context_
= context
->MakeReplyMessageContext();
394 return PP_OK_COMPLETIONPENDING
;
397 } // namespace content