OLD | NEW |
(Empty) | |
| 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 "content/public/renderer/renderer_ppapi_host.h" |
| 14 #include "content/renderer/pepper/gfx_conversion.h" |
| 15 #include "content/renderer/pepper/host_globals.h" |
| 16 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
| 17 #include "content/renderer/pepper/ppb_image_data_impl.h" |
| 18 #include "ppapi/c/pp_errors.h" |
| 19 #include "ppapi/host/dispatch_host_message.h" |
| 20 #include "ppapi/host/ppapi_host.h" |
| 21 #include "ppapi/proxy/ppapi_messages.h" |
| 22 #include "ppapi/thunk/enter.h" |
| 23 #include "ppapi/thunk/ppb_image_data_api.h" |
| 24 #include "third_party/khronos/GLES2/gl2.h" |
| 25 #include "ui/gfx/transform.h" |
| 26 |
| 27 using ppapi::host::HostMessageContext; |
| 28 using ppapi::thunk::EnterResourceNoLock; |
| 29 using ppapi::thunk::PPB_ImageData_API; |
| 30 |
| 31 namespace content { |
| 32 |
| 33 namespace { |
| 34 |
| 35 int32_t VerifyCommittedLayer( |
| 36 const ppapi::CompositorLayerData* old_layer, |
| 37 const ppapi::CompositorLayerData* new_layer) { |
| 38 if (old_layer && old_layer->type != new_layer->type) |
| 39 return PP_ERROR_BADARGUMENT; |
| 40 |
| 41 if (new_layer->type == ppapi::CompositorLayerData::TYPE_COLOR) |
| 42 return PP_OK; |
| 43 |
| 44 // Check texture layer. |
| 45 if (new_layer->type == ppapi::CompositorLayerData::TYPE_TEXTURE) { |
| 46 if (old_layer && new_layer->resource_id == old_layer->resource_id) { |
| 47 if (memcmp(new_layer->texture.mailbox, |
| 48 old_layer->texture.mailbox, |
| 49 sizeof(ppapi::CompositorLayerData::Mailbox)) != 0) { |
| 50 return PP_ERROR_BADARGUMENT; |
| 51 } |
| 52 } else { |
| 53 gpu::Mailbox gpu_mailbox; |
| 54 gpu_mailbox.SetName(new_layer->texture.mailbox); |
| 55 if (!gpu_mailbox.Verify()) |
| 56 return PP_ERROR_BADARGUMENT; |
| 57 } |
| 58 return PP_OK; |
| 59 } |
| 60 |
| 61 // Check image layer. |
| 62 if (new_layer->type == ppapi::CompositorLayerData::TYPE_IMAGE) { |
| 63 if (old_layer && new_layer->resource_id == old_layer->resource_id) { |
| 64 if (new_layer->image.host_resource != old_layer->image.host_resource) |
| 65 return PP_ERROR_BADARGUMENT; |
| 66 } else { |
| 67 EnterResourceNoLock<PPB_ImageData_API> enter( |
| 68 new_layer->image.host_resource, true); |
| 69 if (enter.failed()) |
| 70 return PP_ERROR_BADRESOURCE; |
| 71 |
| 72 // TODO(penghuang): support all kinds of image. |
| 73 PP_ImageDataDesc desc; |
| 74 if (enter.object()->Describe(&desc) != PP_TRUE || |
| 75 desc.stride != desc.size.width * 4 || |
| 76 desc.format != PP_IMAGEDATAFORMAT_RGBA_PREMUL) { |
| 77 return PP_ERROR_BADARGUMENT; |
| 78 } |
| 79 |
| 80 int handle; |
| 81 uint32_t byte_count; |
| 82 if (enter.object()->GetSharedMemory(&handle, &byte_count) != PP_OK) |
| 83 return PP_ERROR_FAILED; |
| 84 } |
| 85 return PP_OK; |
| 86 } |
| 87 return PP_ERROR_BADARGUMENT; |
| 88 } |
| 89 |
| 90 } // namespace |
| 91 |
| 92 PepperCompositorHost::PepperCompositorHost( |
| 93 RendererPpapiHost* host, |
| 94 PP_Instance instance, |
| 95 PP_Resource resource) |
| 96 : ResourceHost(host->GetPpapiHost(), instance, resource), |
| 97 bound_instance_(NULL), |
| 98 weak_factory_(this) { |
| 99 layer_ = cc::Layer::Create(); |
| 100 // TODO(penghuang): SetMasksToBounds() can be expensive if the layer is |
| 101 // transformed. Possibly better could be to explicitly clip the child layers |
| 102 // (by modifying their bounds). |
| 103 layer_->SetMasksToBounds(true); |
| 104 layer_->SetIsDrawable(true); |
| 105 } |
| 106 |
| 107 PepperCompositorHost::~PepperCompositorHost() { |
| 108 // Unbind from the instance when destroyed if we're still bound. |
| 109 if (bound_instance_) |
| 110 bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0); |
| 111 } |
| 112 |
| 113 bool PepperCompositorHost::BindToInstance( |
| 114 PepperPluginInstanceImpl* new_instance) { |
| 115 if (new_instance && new_instance->pp_instance() != pp_instance()) |
| 116 return false; // Can't bind other instance's contexts. |
| 117 if (bound_instance_ == new_instance) |
| 118 return true; // Rebinding the same device, nothing to do. |
| 119 if (bound_instance_ && new_instance) |
| 120 return false; // Can't change a bound device. |
| 121 bound_instance_ = new_instance; |
| 122 return true; |
| 123 } |
| 124 |
| 125 void PepperCompositorHost::ViewInitiatedPaint() { |
| 126 if (!commit_layers_reply_context_.is_valid()) |
| 127 return; |
| 128 host()->SendReply(commit_layers_reply_context_, |
| 129 PpapiPluginMsg_Compositor_CommitLayersReply()); |
| 130 commit_layers_reply_context_ = ppapi::host::ReplyMessageContext(); |
| 131 } |
| 132 |
| 133 void PepperCompositorHost::ViewFlushedPaint() {} |
| 134 |
| 135 void PepperCompositorHost::UpdateLayer( |
| 136 const scoped_refptr<cc::Layer>& layer, |
| 137 const ppapi::CompositorLayerData* old_layer, |
| 138 const ppapi::CompositorLayerData* new_layer) { |
| 139 DCHECK(!old_layer || new_layer->type == old_layer->type); |
| 140 |
| 141 // Always update properties on cc::Layer, because cc::Layer |
| 142 // will ignore any setting with unchanged value. |
| 143 layer->SetIsDrawable(true); |
| 144 layer->SetBlendMode(SkXfermode::kSrcOver_Mode); |
| 145 layer->SetOpacity(new_layer->opacity); |
| 146 layer->SetBounds(PP_ToGfxSize(new_layer->size)); |
| 147 layer->SetTransformOrigin(gfx::Point3F( |
| 148 new_layer->size.width / 2, new_layer->size.height / 2, 0.0f)); |
| 149 |
| 150 gfx::Transform transform(gfx::Transform::kSkipInitialization); |
| 151 transform.matrix().setColMajorf(new_layer->transform); |
| 152 layer->SetTransform(transform); |
| 153 |
| 154 // Consider a (0,0,0,0) rect as no clip rect. |
| 155 if (new_layer->clip_rect.point.x != 0 || |
| 156 new_layer->clip_rect.point.y != 0 || |
| 157 new_layer->clip_rect.size.width != 0 || |
| 158 new_layer->clip_rect.size.height != 0) { |
| 159 scoped_refptr<cc::Layer> clip_parent = layer->parent(); |
| 160 if (clip_parent == layer_) { |
| 161 // Create a clip parent layer, if it does not exist. |
| 162 clip_parent = cc::Layer::Create(); |
| 163 clip_parent->SetMasksToBounds(true); |
| 164 clip_parent->SetIsDrawable(true); |
| 165 layer_->ReplaceChild(layer, clip_parent); |
| 166 clip_parent->AddChild(layer); |
| 167 } |
| 168 gfx::Point position = PP_ToGfxPoint(new_layer->clip_rect.point); |
| 169 clip_parent->SetPosition(position); |
| 170 clip_parent->SetBounds(PP_ToGfxSize(new_layer->clip_rect.size)); |
| 171 layer->SetPosition(gfx::Point(-position.x(), -position.y())); |
| 172 } else if (layer->parent() != layer_) { |
| 173 // Remove the clip parent layer. |
| 174 layer_->ReplaceChild(layer->parent(), layer); |
| 175 layer->SetPosition(gfx::Point()); |
| 176 } |
| 177 |
| 178 if (new_layer->type == ppapi::CompositorLayerData::TYPE_COLOR) { |
| 179 layer->SetBackgroundColor(SkColorSetARGBMacro(new_layer->color.alpha * 255, |
| 180 new_layer->color.red * 255, |
| 181 new_layer->color.green * 255, |
| 182 new_layer->color.blue * 255)); |
| 183 } else if (new_layer->type == ppapi::CompositorLayerData::TYPE_TEXTURE) { |
| 184 scoped_refptr<cc::TextureLayer> texture_layer( |
| 185 static_cast<cc::TextureLayer*>(layer.get())); |
| 186 if (!old_layer || new_layer->resource_id != old_layer->resource_id) { |
| 187 gpu::Mailbox gpu_mailbox; |
| 188 gpu_mailbox.SetName(new_layer->texture.mailbox); |
| 189 DCHECK(gpu_mailbox.Verify()); |
| 190 cc::TextureMailbox mailbox(gpu_mailbox, |
| 191 GL_TEXTURE_2D, |
| 192 new_layer->texture.sync_point); |
| 193 texture_layer->SetTextureMailbox(mailbox, |
| 194 cc::SingleReleaseCallback::Create( |
| 195 base::Bind(&PepperCompositorHost::ResourceReleased, |
| 196 weak_factory_.GetWeakPtr(), |
| 197 new_layer->resource_id)));; |
| 198 } |
| 199 texture_layer->SetPremultipliedAlpha(new_layer->texture.premult_alpha); |
| 200 gfx::RectF rect = PP_ToGfxRectF(new_layer->texture.source_rect); |
| 201 texture_layer->SetUV(rect.origin(), rect.bottom_right()); |
| 202 } else if (new_layer->type == ppapi::CompositorLayerData::TYPE_IMAGE) { |
| 203 if (!old_layer || new_layer->resource_id != old_layer->resource_id) { |
| 204 scoped_refptr<cc::TextureLayer> image_layer( |
| 205 static_cast<cc::TextureLayer*>(layer.get())); |
| 206 EnterResourceNoLock<PPB_ImageData_API> enter( |
| 207 new_layer->image.host_resource, true); |
| 208 DCHECK(enter.succeeded()); |
| 209 PP_ImageDataDesc desc; |
| 210 { |
| 211 PP_Bool rv = enter.object()->Describe(&desc); |
| 212 DCHECK_EQ(rv, PP_TRUE); |
| 213 } |
| 214 int handle; |
| 215 uint32_t byte_count; |
| 216 { |
| 217 int32_t rv = enter.object()->GetSharedMemory(&handle, &byte_count); |
| 218 DCHECK_EQ(rv, PP_OK); |
| 219 } |
| 220 #if defined(OS_WIN) |
| 221 base::SharedMemoryHandle shm_handle = |
| 222 static_cast<base::SharedMemoryHandle>(_dup(handle)); |
| 223 #else |
| 224 base::SharedMemoryHandle shm_handle(dup(handle), false); |
| 225 #endif |
| 226 // Use a scoped_ptr to keep shared_memory alive until |
| 227 // PepperCompositorHost::ImageReleased is called. |
| 228 scoped_ptr<base::SharedMemory> shared_memory( |
| 229 new base::SharedMemory(shm_handle, true)); |
| 230 shared_memory->Map(desc.stride * desc.size.height); |
| 231 cc::TextureMailbox mailbox(shared_memory.get(), |
| 232 PP_ToGfxSize(desc.size)); |
| 233 image_layer->SetTextureMailbox(mailbox, |
| 234 cc::SingleReleaseCallback::Create( |
| 235 base::Bind(&PepperCompositorHost::ImageReleased, |
| 236 weak_factory_.GetWeakPtr(), |
| 237 new_layer->resource_id, |
| 238 base::Passed(&shared_memory)))); |
| 239 |
| 240 // ImageData is always premultiplied alpha. |
| 241 image_layer->SetPremultipliedAlpha(true); |
| 242 } |
| 243 } |
| 244 } |
| 245 |
| 246 void PepperCompositorHost::ResourceReleased(int32_t id, |
| 247 uint32_t sync_point, |
| 248 bool is_lost) { |
| 249 host()->SendUnsolicitedReply( |
| 250 pp_resource(), |
| 251 PpapiPluginMsg_Compositor_ReleaseResource(id, sync_point, is_lost)); |
| 252 } |
| 253 |
| 254 void PepperCompositorHost::ImageReleased( |
| 255 int32_t id, |
| 256 const scoped_ptr<base::SharedMemory>& shared_memory, |
| 257 uint32_t sync_point, |
| 258 bool is_lost) { |
| 259 ResourceReleased(id, sync_point, is_lost); |
| 260 } |
| 261 |
| 262 int32_t PepperCompositorHost::OnResourceMessageReceived( |
| 263 const IPC::Message& msg, |
| 264 HostMessageContext* context) { |
| 265 PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost, msg) |
| 266 PPAPI_DISPATCH_HOST_RESOURCE_CALL( |
| 267 PpapiHostMsg_Compositor_CommitLayers, OnHostMsgCommitLayers) |
| 268 PPAPI_END_MESSAGE_MAP() |
| 269 return ppapi::host::ResourceHost::OnResourceMessageReceived(msg, context); |
| 270 } |
| 271 |
| 272 bool PepperCompositorHost::IsCompositorHost() { |
| 273 return true; |
| 274 } |
| 275 |
| 276 int32_t PepperCompositorHost::OnHostMsgCommitLayers( |
| 277 HostMessageContext* context, |
| 278 const std::vector<ppapi::CompositorLayerData>& layers, |
| 279 bool reset) { |
| 280 // Do not support CommitLayers() on an unbounded compositor. |
| 281 if (!bound_instance_) |
| 282 return PP_ERROR_FAILED; |
| 283 |
| 284 if (commit_layers_reply_context_.is_valid()) |
| 285 return PP_ERROR_INPROGRESS; |
| 286 |
| 287 commit_layers_reply_context_ = context->MakeReplyMessageContext(); |
| 288 |
| 289 // Verfiy the layers first, if an error happens, we will return the error to |
| 290 // plugin and keep current layers set by the previous CommitLayers() |
| 291 // unchanged. |
| 292 for (size_t i = 0; i < layers.size(); ++i) { |
| 293 const ppapi::CompositorLayerData* old_layer = NULL; |
| 294 if (!reset && i < layers_.size()) |
| 295 old_layer = &layers_[i].pp_layer; |
| 296 int32_t rv = VerifyCommittedLayer(old_layer, &layers[i]); |
| 297 if (rv != PP_OK) |
| 298 return rv; |
| 299 } |
| 300 |
| 301 // ResetLayers() has been called, we need rebuild layer stack. |
| 302 if (reset) { |
| 303 layer_->RemoveAllChildren(); |
| 304 layers_.clear(); |
| 305 } |
| 306 |
| 307 for (size_t i = 0; i < layers.size(); ++i) { |
| 308 const ppapi::CompositorLayerData* pp_layer = &layers[i]; |
| 309 LayerData* data = i >= layers_.size() ? NULL : &layers_[i]; |
| 310 DCHECK(!data || data->cc_layer); |
| 311 scoped_refptr<cc::Layer> cc_layer = data ? data->cc_layer : NULL; |
| 312 ppapi::CompositorLayerData* old_layer = data ? &data->pp_layer : NULL; |
| 313 |
| 314 if (!cc_layer) { |
| 315 if (pp_layer->type == ppapi::CompositorLayerData::TYPE_COLOR) { |
| 316 cc_layer = cc::SolidColorLayer::Create(); |
| 317 } else if (pp_layer->type == ppapi::CompositorLayerData::TYPE_TEXTURE || |
| 318 pp_layer->type == ppapi::CompositorLayerData::TYPE_IMAGE) { |
| 319 cc_layer = cc::TextureLayer::CreateForMailbox(NULL); |
| 320 } |
| 321 layer_->AddChild(cc_layer); |
| 322 } |
| 323 |
| 324 UpdateLayer(cc_layer, old_layer, pp_layer); |
| 325 |
| 326 if (old_layer) { |
| 327 *old_layer = *pp_layer; |
| 328 } else { |
| 329 layers_.push_back(LayerData(cc_layer, *pp_layer)); |
| 330 } |
| 331 } |
| 332 |
| 333 return PP_OK_COMPLETIONPENDING; |
| 334 } |
| 335 |
| 336 } // namespace content |
OLD | NEW |