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. 4 5 #include "content/renderer/pepper/pepper_compositor_host.h" 6 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" 27 28 using ppapi::host::HostMessageContext; 29 using ppapi::thunk::EnterResourceNoLock; 30 using ppapi::thunk::PPB_ImageData_API; 31 32 namespace content { 33 34 namespace { 35 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; 42 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; 48 } 49 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; 60 } 61 } 62 if (!new_layer->texture->mailbox.Verify()) 63 return PP_ERROR_BADARGUMENT; 64 return PP_OK; 65 } 66 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; 78 } 79 } 80 EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource, 81 true); 82 if (enter.failed()) 83 return PP_ERROR_BADRESOURCE; 84 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; 91 } 92 93 int handle; 94 uint32_t byte_count; 95 if (enter.object()->GetSharedMemory(&handle, &byte_count) != PP_OK) 96 return PP_ERROR_FAILED; 97 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, 104 0, 105 FALSE, 106 DUPLICATE_SAME_ACCESS)) { 107 return PP_ERROR_FAILED; 108 } 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; 116 } 117 return PP_OK; 118 } 119 120 return PP_ERROR_BADARGUMENT; 121 } 122 123 } // namespace 124 125 PepperCompositorHost::LayerData::LayerData( 126 const scoped_refptr<cc::Layer>& cc, 127 const ppapi::CompositorLayerData& pp) : cc_layer(cc), pp_layer(pp) {} 128 129 PepperCompositorHost::LayerData::~LayerData() {} 130 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); 144 } 145 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); 150 } 151 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(); 163 164 return true; 165 } 166 167 void PepperCompositorHost::ViewInitiatedPaint() { 168 SendCommitLayersReplyIfNecessary(); 169 } 170 171 void PepperCompositorHost::ViewFlushedPaint() {} 172 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); 179 } 180 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)); 187 } 188 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(); 195 } 196 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)); 211 212 gfx::Transform transform(gfx::Transform::kSkipInitialization); 213 transform.matrix().setColMajorf(new_layer->common.transform.matrix); 214 layer->SetTransform(transform); 215 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 == layer_) { 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, clip_parent); 228 clip_parent->AddChild(layer); 229 } 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_) { 235 // Remove the clip parent layer. 236 layer_->ReplaceChild(layer->parent(), layer); 237 layer->SetPosition(gfx::Point()); 238 } 239 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; 247 } 248 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 GL_TEXTURE_2D, 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 } 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()); 266 return; 267 } 268 269 if (new_layer->image) { 270 if (!old_layer || 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, 275 true); 276 DCHECK(enter.succeeded()); 277 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); 284 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)))); 293 294 // ImageData is always premultiplied alpha. 295 image_layer->SetPremultipliedAlpha(true); 296 } 297 return; 298 } 299 // Should not be reached. 300 NOTREACHED(); 301 } 302 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); 311 } 312 313 bool PepperCompositorHost::IsCompositorHost() { 314 return true; 315 } 316 317 int32_t PepperCompositorHost::OnHostMsgCommitLayers( 318 HostMessageContext* context, 319 const std::vector<ppapi::CompositorLayerData>& layers, 320 bool reset) { 321 if (commit_layers_reply_context_.is_valid()) 322 return PP_ERROR_INPROGRESS; 323 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()]); 327 if (!image_shms) 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() 331 // unchanged. 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]); 337 if (rv != PP_OK) 338 return rv; 339 } 340 } 341 342 // ResetLayers() has been called, we need rebuild layer stack. 343 if (reset) { 344 layer_->RemoveAllChildren(); 345 layers_.clear(); 346 } 347 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); 352 scoped_refptr<cc::Layer> cc_layer = data ? data->cc_layer : NULL; 353 ppapi::CompositorLayerData* old_layer = data ? &data->pp_layer : NULL; 354 355 if (!cc_layer) { 356 if (pp_layer->color) 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); 361 } 362 363 UpdateLayer(cc_layer, old_layer, pp_layer, image_shms[i].Pass()); 364 365 if (old_layer) 366 *old_layer = *pp_layer; 367 else 368 layers_.push_back(LayerData(cc_layer, *pp_layer)); 369 } 370 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(); 376 377 // If the host is not bound to the instance, return PP_OK immediately. 378 if (!bound_instance_) 379 return PP_OK; 380 381 commit_layers_reply_context_ = context->MakeReplyMessageContext(); 382 return PP_OK_COMPLETIONPENDING; 383 } 384 385 } // namespace content 386