browser_plugin_compositing_helper.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2013 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/browser_plugin/browser_plugin_compositing_helper.h" 6 7#include "cc/layers/delegated_renderer_layer.h" 8#include "cc/layers/solid_color_layer.h" 9#include "cc/layers/texture_layer.h" 10#include "cc/output/context_provider.h" 11#include "content/common/browser_plugin/browser_plugin_messages.h" 12#include "content/common/gpu/client/context_provider_command_buffer.h" 13#include "content/renderer/browser_plugin/browser_plugin_manager.h" 14#include "content/renderer/render_thread_impl.h" 15#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h" 16#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" 17#include "third_party/khronos/GLES2/gl2.h" 18#include "ui/gfx/size_conversions.h" 19#include "webkit/renderer/compositor_bindings/web_layer_impl.h" 20 21namespace content { 22 23BrowserPluginCompositingHelper::BrowserPluginCompositingHelper( 24 WebKit::WebPluginContainer* container, 25 BrowserPluginManager* manager, 26 int instance_id, 27 int host_routing_id) 28 : instance_id_(instance_id), 29 host_routing_id_(host_routing_id), 30 last_route_id_(0), 31 last_host_id_(0), 32 last_mailbox_valid_(false), 33 ack_pending_(true), 34 ack_pending_for_crashed_guest_(false), 35 container_(container), 36 browser_plugin_manager_(manager) { 37} 38 39BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() { 40} 41 42void BrowserPluginCompositingHelper::DidCommitCompositorFrame() { 43 if (!delegated_layer_ || !ack_pending_) 44 return; 45 46 cc::CompositorFrameAck ack; 47 delegated_layer_->TakeUnusedResourcesForChildCompositor(&ack.resources); 48 49 browser_plugin_manager_->Send( 50 new BrowserPluginHostMsg_CompositorFrameACK( 51 host_routing_id_, 52 instance_id_, 53 last_route_id_, 54 last_host_id_, 55 ack)); 56 57 ack_pending_ = false; 58} 59 60void BrowserPluginCompositingHelper::EnableCompositing(bool enable) { 61 if (enable && !background_layer_) { 62 background_layer_ = cc::SolidColorLayer::Create(); 63 background_layer_->SetMasksToBounds(true); 64 background_layer_->SetBackgroundColor( 65 SkColorSetARGBInline(255, 255, 255, 255)); 66 web_layer_.reset(new webkit::WebLayerImpl(background_layer_)); 67 } 68 69 container_->setWebLayer(enable ? web_layer_.get() : NULL); 70} 71 72void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerBounds( 73 const gfx::Size& new_size, 74 float device_scale_factor, 75 cc::Layer* layer) { 76 if (buffer_size_ != new_size) { 77 buffer_size_ = new_size; 78 // The container size is in DIP, so is the layer size. 79 // Buffer size is in physical pixels, so we need to adjust 80 // it by the device scale factor. 81 gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize( 82 gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor)); 83 layer->SetBounds(device_scale_adjusted_size); 84 } 85} 86 87// If we have a mailbox that was freed up from the compositor, 88// but we are not expected to return it to the guest renderer 89// via an ACK, we should free it because we now own it. 90// To free the mailbox memory, we need a context to consume it 91// into a texture ID and then delete this texture ID. 92// We use a shared graphics context accessible from the main 93// thread to do it. 94void BrowserPluginCompositingHelper::FreeMailboxMemory( 95 const std::string& mailbox_name, 96 unsigned sync_point) { 97 if (mailbox_name.empty()) 98 return; 99 100 scoped_refptr<cc::ContextProvider> context_provider = 101 RenderThreadImpl::current()->OffscreenContextProviderForMainThread(); 102 if (!context_provider) 103 return; 104 105 WebKit::WebGraphicsContext3D *context = context_provider->Context3d(); 106 // When a buffer is released from the compositor, we also get a 107 // sync point that specifies when in the command buffer 108 // it's safe to use it again. 109 // If the sync point is non-zero, we need to tell our context 110 // to wait until this sync point is reached before we can safely 111 // delete the buffer. 112 if (sync_point) 113 context->waitSyncPoint(sync_point); 114 115 unsigned texture_id = context->createTexture(); 116 context->bindTexture(GL_TEXTURE_2D, texture_id); 117 context->consumeTextureCHROMIUM( 118 GL_TEXTURE_2D, 119 reinterpret_cast<const int8*>(mailbox_name.data())); 120 context->deleteTexture(texture_id); 121} 122 123void BrowserPluginCompositingHelper::MailboxReleased( 124 const std::string& mailbox_name, 125 int gpu_route_id, 126 int gpu_host_id, 127 unsigned sync_point, 128 bool lost_resource) { 129 if (lost_resource) { 130 // Recurse with an empty mailbox if the one being released was lost. 131 MailboxReleased(std::string(), gpu_route_id, gpu_host_id, 0, false); 132 return; 133 } 134 135 // This means the GPU process crashed and we have nothing further to do. 136 // Nobody is expecting an ACK and the buffer doesn't need to be deleted 137 // because it went away with the GPU process. 138 if (last_host_id_ != gpu_host_id) 139 return; 140 141 // This means the guest crashed. 142 // Either ACK the last buffer, so texture transport could 143 // be destroyed of delete the mailbox if nobody wants it back. 144 if (last_route_id_ != gpu_route_id) { 145 if (!ack_pending_for_crashed_guest_) { 146 FreeMailboxMemory(mailbox_name, sync_point); 147 } else { 148 ack_pending_for_crashed_guest_ = false; 149 browser_plugin_manager_->Send( 150 new BrowserPluginHostMsg_BuffersSwappedACK( 151 host_routing_id_, 152 instance_id_, 153 gpu_route_id, 154 gpu_host_id, 155 mailbox_name, 156 sync_point)); 157 } 158 return; 159 } 160 161 // We need to send an ACK to TextureImageTransportSurface 162 // for every buffer it sends us. However, if a buffer is freed up from 163 // the compositor in cases like switching back to SW mode without a new 164 // buffer arriving, no ACK is needed and we destroy this buffer. 165 if (!ack_pending_) { 166 FreeMailboxMemory(mailbox_name, sync_point); 167 last_mailbox_valid_ = false; 168 return; 169 } 170 ack_pending_ = false; 171 browser_plugin_manager_->Send( 172 new BrowserPluginHostMsg_BuffersSwappedACK( 173 host_routing_id_, 174 instance_id_, 175 gpu_route_id, 176 gpu_host_id, 177 mailbox_name, 178 sync_point)); 179} 180 181void BrowserPluginCompositingHelper::OnContainerDestroy() { 182 if (container_) 183 container_->setWebLayer(NULL); 184 container_ = NULL; 185 186 texture_layer_ = NULL; 187 delegated_layer_ = NULL; 188 background_layer_ = NULL; 189 web_layer_.reset(); 190} 191 192void BrowserPluginCompositingHelper::OnBuffersSwapped( 193 const gfx::Size& size, 194 const std::string& mailbox_name, 195 int gpu_route_id, 196 int gpu_host_id, 197 float device_scale_factor) { 198 DCHECK(!delegated_layer_); 199 // If the guest crashed but the GPU process didn't, we may still have 200 // a transport surface waiting on an ACK, which we must send to 201 // avoid leaking. 202 if (last_route_id_ != gpu_route_id && last_host_id_ == gpu_host_id) 203 ack_pending_for_crashed_guest_ = ack_pending_; 204 205 // If these mismatch, we are either just starting up, GPU process crashed or 206 // guest renderer crashed. 207 // In this case, we are communicating with a new image transport 208 // surface and must ACK with the new ID's and an empty mailbox. 209 if (last_route_id_ != gpu_route_id || last_host_id_ != gpu_host_id) 210 last_mailbox_valid_ = false; 211 212 last_route_id_ = gpu_route_id; 213 last_host_id_ = gpu_host_id; 214 215 ack_pending_ = true; 216 // Browser plugin getting destroyed, do a fast ACK. 217 if (!background_layer_) { 218 MailboxReleased(mailbox_name, gpu_route_id, gpu_host_id, 0, false); 219 return; 220 } 221 222 if (!texture_layer_) { 223 texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL); 224 texture_layer_->SetIsDrawable(true); 225 texture_layer_->SetContentsOpaque(true); 226 227 background_layer_->AddChild(texture_layer_); 228 } 229 230 // The size of browser plugin container is not always equal to the size 231 // of the buffer that arrives here. This could be for a number of reasons, 232 // including autosize and a resize in progress. 233 // During resize, the container size changes first and then some time 234 // later, a new buffer with updated size will arrive. During this process, 235 // we need to make sure that things are still displayed pixel perfect. 236 // We accomplish this by modifying bounds of the texture layer only 237 // when a new buffer arrives. 238 // Visually, this will either display a smaller part of the buffer 239 // or introduce a gutter around it. 240 CheckSizeAndAdjustLayerBounds(size, 241 device_scale_factor, 242 texture_layer_.get()); 243 244 bool current_mailbox_valid = !mailbox_name.empty(); 245 if (!last_mailbox_valid_) { 246 MailboxReleased(std::string(), gpu_route_id, gpu_host_id, 0, false); 247 if (!current_mailbox_valid) 248 return; 249 } 250 251 cc::TextureMailbox::ReleaseCallback callback; 252 if (current_mailbox_valid) { 253 callback = base::Bind(&BrowserPluginCompositingHelper::MailboxReleased, 254 scoped_refptr<BrowserPluginCompositingHelper>(this), 255 mailbox_name, 256 gpu_route_id, 257 gpu_host_id); 258 } 259 texture_layer_->SetTextureMailbox(cc::TextureMailbox(mailbox_name, 260 callback)); 261 texture_layer_->SetNeedsDisplay(); 262 last_mailbox_valid_ = current_mailbox_valid; 263} 264 265void BrowserPluginCompositingHelper::OnCompositorFrameSwapped( 266 scoped_ptr<cc::CompositorFrame> frame, 267 int route_id, 268 int host_id) { 269 DCHECK(!texture_layer_); 270 if (!delegated_layer_) { 271 delegated_layer_ = cc::DelegatedRendererLayer::Create(NULL); 272 delegated_layer_->SetIsDrawable(true); 273 delegated_layer_->SetContentsOpaque(true); 274 275 background_layer_->AddChild(delegated_layer_); 276 } 277 278 cc::DelegatedFrameData *frame_data = frame->delegated_frame_data.get(); 279 if (!frame_data) 280 return; 281 282 CheckSizeAndAdjustLayerBounds( 283 frame_data->render_pass_list.back()->output_rect.size(), 284 frame->metadata.device_scale_factor, 285 delegated_layer_.get()); 286 287 delegated_layer_->SetFrameData(frame->delegated_frame_data.Pass()); 288 last_route_id_ = route_id; 289 last_host_id_ = host_id; 290 ack_pending_ = true; 291} 292 293void BrowserPluginCompositingHelper::UpdateVisibility(bool visible) { 294 if (texture_layer_) 295 texture_layer_->SetIsDrawable(visible); 296 if (delegated_layer_) 297 delegated_layer_->SetIsDrawable(visible); 298} 299 300} // namespace content 301