Mac: Fix performance issues with remote CoreAnimation
[chromium-blink-merge.git] / content / renderer / pepper / pepper_compositor_host.cc
blob284d36c3caed67e4a72d248a8b9fe3fa7ae6c9ce
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;
32 namespace content {
34 namespace {
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;
47 return PP_OK;
50 if (new_layer->texture) {
51 if (old_layer) {
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;
59 return PP_OK;
62 if (!new_layer->texture->mailbox.Verify())
63 return PP_ERROR_BADARGUMENT;
64 return PP_OK;
67 if (new_layer->image) {
68 if (old_layer) {
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
73 // changed.
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;
77 return PP_OK;
80 EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
81 true);
82 if (enter.failed())
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;
93 int handle;
94 uint32_t byte_count;
95 if (enter.object()->GetSharedMemory(&handle, &byte_count) != PP_OK)
96 return PP_ERROR_FAILED;
98 #if defined(OS_WIN)
99 base::SharedMemoryHandle shm_handle;
100 if (!::DuplicateHandle(::GetCurrentProcess(),
101 reinterpret_cast<base::SharedMemoryHandle>(handle),
102 ::GetCurrentProcess(),
103 &shm_handle,
105 FALSE,
106 DUPLICATE_SAME_ACCESS)) {
107 return PP_ERROR_FAILED;
109 #else
110 base::SharedMemoryHandle shm_handle(dup(handle), false);
111 #endif
112 image_shm->reset(new base::SharedMemory(shm_handle, true));
113 if (!(*image_shm)->Map(desc.stride * desc.size.height)) {
114 image_shm->reset();
115 return PP_ERROR_NOMEMORY;
117 return PP_OK;
120 return PP_ERROR_BADARGUMENT;
123 } // namespace
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.
148 if (bound_instance_)
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();
164 return true;
167 void PepperCompositorHost::ViewInitiatedPaint() {
168 SendCommitLayersReplyIfNecessary();
171 void PepperCompositorHost::ViewFlushedPaint() {}
173 void PepperCompositorHost::ImageReleased(
174 int32_t id,
175 const scoped_ptr<base::SharedMemory>& shared_memory,
176 uint32_t sync_point,
177 bool is_lost) {
178 ResourceReleased(id, sync_point, is_lost);
181 void PepperCompositorHost::ResourceReleased(int32_t id,
182 uint32_t sync_point,
183 bool is_lost) {
184 host()->SendUnsolicitedReply(
185 pp_resource(),
186 PpapiPluginMsg_Compositor_ReleaseResource(id, sync_point, is_lost));
189 void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
190 if (!commit_layers_reply_context_.is_valid())
191 return;
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,
210 0.0f));
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));
246 return;
249 if (new_layer->texture) {
250 scoped_refptr<cc::TextureLayer> texture_layer(
251 static_cast<cc::TextureLayer*>(layer.get()));
252 if (!old_layer ||
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)));
262 // TODO(penghuang): get a damage region from the application and
263 // pass it to SetNeedsDisplayRect().
264 texture_layer->SetNeedsDisplay();
266 texture_layer->SetPremultipliedAlpha(new_layer->texture->premult_alpha);
267 gfx::RectF rect = PP_ToGfxRectF(new_layer->texture->source_rect);
268 texture_layer->SetUV(rect.origin(), rect.bottom_right());
269 return;
272 if (new_layer->image) {
273 if (!old_layer ||
274 new_layer->common.resource_id != old_layer->common.resource_id) {
275 scoped_refptr<cc::TextureLayer> image_layer(
276 static_cast<cc::TextureLayer*>(layer.get()));
277 EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
278 true);
279 DCHECK(enter.succeeded());
281 // TODO(penghuang): support all kinds of image.
282 PP_ImageDataDesc desc;
283 PP_Bool rv = enter.object()->Describe(&desc);
284 DCHECK_EQ(rv, PP_TRUE);
285 DCHECK_EQ(desc.stride, desc.size.width * 4);
286 DCHECK_EQ(desc.format, PP_IMAGEDATAFORMAT_RGBA_PREMUL);
288 cc::TextureMailbox mailbox(image_shm.get(),
289 PP_ToGfxSize(desc.size));
290 image_layer->SetTextureMailbox(mailbox,
291 cc::SingleReleaseCallback::Create(
292 base::Bind(&PepperCompositorHost::ImageReleased,
293 weak_factory_.GetWeakPtr(),
294 new_layer->common.resource_id,
295 base::Passed(&image_shm))));
296 // TODO(penghuang): get a damage region from the application and
297 // pass it to SetNeedsDisplayRect().
298 image_layer->SetNeedsDisplay();
300 // ImageData is always premultiplied alpha.
301 image_layer->SetPremultipliedAlpha(true);
303 return;
305 // Should not be reached.
306 NOTREACHED();
309 int32_t PepperCompositorHost::OnResourceMessageReceived(
310 const IPC::Message& msg,
311 HostMessageContext* context) {
312 PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost, msg)
313 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
314 PpapiHostMsg_Compositor_CommitLayers, OnHostMsgCommitLayers)
315 PPAPI_END_MESSAGE_MAP()
316 return ppapi::host::ResourceHost::OnResourceMessageReceived(msg, context);
319 bool PepperCompositorHost::IsCompositorHost() {
320 return true;
323 int32_t PepperCompositorHost::OnHostMsgCommitLayers(
324 HostMessageContext* context,
325 const std::vector<ppapi::CompositorLayerData>& layers,
326 bool reset) {
327 if (commit_layers_reply_context_.is_valid())
328 return PP_ERROR_INPROGRESS;
330 scoped_ptr<scoped_ptr<base::SharedMemory>[]> image_shms;
331 if (layers.size() > 0) {
332 image_shms.reset(new scoped_ptr<base::SharedMemory>[layers.size()]);
333 if (!image_shms)
334 return PP_ERROR_NOMEMORY;
335 // Verfiy the layers first, if an error happens, we will return the error to
336 // plugin and keep current layers set by the previous CommitLayers()
337 // unchanged.
338 for (size_t i = 0; i < layers.size(); ++i) {
339 const ppapi::CompositorLayerData* old_layer = NULL;
340 if (!reset && i < layers_.size())
341 old_layer = &layers_[i].pp_layer;
342 int32_t rv = VerifyCommittedLayer(old_layer, &layers[i], &image_shms[i]);
343 if (rv != PP_OK)
344 return rv;
348 // ResetLayers() has been called, we need rebuild layer stack.
349 if (reset) {
350 layer_->RemoveAllChildren();
351 layers_.clear();
354 for (size_t i = 0; i < layers.size(); ++i) {
355 const ppapi::CompositorLayerData* pp_layer = &layers[i];
356 LayerData* data = i >= layers_.size() ? NULL : &layers_[i];
357 DCHECK(!data || data->cc_layer.get());
358 scoped_refptr<cc::Layer> cc_layer = data ? data->cc_layer : NULL;
359 ppapi::CompositorLayerData* old_layer = data ? &data->pp_layer : NULL;
361 if (!cc_layer.get()) {
362 if (pp_layer->color)
363 cc_layer = cc::SolidColorLayer::Create();
364 else if (pp_layer->texture || pp_layer->image)
365 cc_layer = cc::TextureLayer::CreateForMailbox(NULL);
366 layer_->AddChild(cc_layer);
369 UpdateLayer(cc_layer, old_layer, pp_layer, image_shms[i].Pass());
371 if (old_layer)
372 *old_layer = *pp_layer;
373 else
374 layers_.push_back(LayerData(cc_layer, *pp_layer));
377 // We need to force a commit for each CommitLayers() call, even if no layers
378 // changed since the last call to CommitLayers(). This is so
379 // WiewInitiatedPaint() will always be called.
380 if (layer_->layer_tree_host())
381 layer_->layer_tree_host()->SetNeedsCommit();
383 // If the host is not bound to the instance, return PP_OK immediately.
384 if (!bound_instance_)
385 return PP_OK;
387 commit_layers_reply_context_ = context->MakeReplyMessageContext();
388 return PP_OK_COMPLETIONPENDING;
391 } // namespace content