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 28using ppapi::host::HostMessageContext; 29using ppapi::thunk::EnterResourceNoLock; 30using ppapi::thunk::PPB_ImageData_API; 31 32namespace content { 33 34namespace { 35 36int32_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 125PepperCompositorHost::LayerData::LayerData( 126 const scoped_refptr<cc::Layer>& cc, 127 const ppapi::CompositorLayerData& pp) : cc_layer(cc), pp_layer(pp) {} 128 129PepperCompositorHost::LayerData::~LayerData() {} 130 131PepperCompositorHost::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 146PepperCompositorHost::~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 152bool 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 167void PepperCompositorHost::ViewInitiatedPaint() { 168 SendCommitLayersReplyIfNecessary(); 169} 170 171void PepperCompositorHost::ViewFlushedPaint() {} 172 173void 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 181void 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 189void 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 197void 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.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); 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_.get()) { 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 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(); 265 } 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; 270 } 271 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()); 280 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); 287 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(); 299 300 // ImageData is always premultiplied alpha. 301 image_layer->SetPremultipliedAlpha(true); 302 } 303 return; 304 } 305 // Should not be reached. 306 NOTREACHED(); 307} 308 309int32_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); 317} 318 319bool PepperCompositorHost::IsCompositorHost() { 320 return true; 321} 322 323int32_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; 329 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; 345 } 346 } 347 348 // ResetLayers() has been called, we need rebuild layer stack. 349 if (reset) { 350 layer_->RemoveAllChildren(); 351 layers_.clear(); 352 } 353 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; 360 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); 367 } 368 369 UpdateLayer(cc_layer, old_layer, pp_layer, image_shms[i].Pass()); 370 371 if (old_layer) 372 *old_layer = *pp_layer; 373 else 374 layers_.push_back(LayerData(cc_layer, *pp_layer)); 375 } 376 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(); 382 383 // If the host is not bound to the instance, return PP_OK immediately. 384 if (!bound_instance_) 385 return PP_OK; 386 387 commit_layers_reply_context_ = context->MakeReplyMessageContext(); 388 return PP_OK_COMPLETIONPENDING; 389} 390 391} // namespace content 392