1 // Copyright (c) 2012 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/ppb_graphics_3d_impl.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "content/common/gpu/client/command_buffer_proxy_impl.h" 12 #include "content/common/gpu/client/gpu_channel_host.h" 13 #include "content/public/common/content_switches.h" 14 #include "content/public/common/web_preferences.h" 15 #include "content/renderer/pepper/host_globals.h" 16 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" 17 #include "content/renderer/pepper/plugin_module.h" 18 #include "content/renderer/render_thread_impl.h" 19 #include "content/renderer/render_view_impl.h" 20 #include "gpu/command_buffer/client/gles2_implementation.h" 21 #include "ppapi/c/ppp_graphics_3d.h" 22 #include "ppapi/thunk/enter.h" 23 #include "third_party/WebKit/public/platform/WebString.h" 24 #include "third_party/WebKit/public/web/WebConsoleMessage.h" 25 #include "third_party/WebKit/public/web/WebDocument.h" 26 #include "third_party/WebKit/public/web/WebElement.h" 27 #include "third_party/WebKit/public/web/WebLocalFrame.h" 28 #include "third_party/WebKit/public/web/WebPluginContainer.h" 29 30 using ppapi::thunk::EnterResourceNoLock; 31 using ppapi::thunk::PPB_Graphics3D_API; 32 using blink::WebConsoleMessage; 33 using blink::WebLocalFrame; 34 using blink::WebPluginContainer; 35 using blink::WebString; 36 37 namespace content { 38 39 namespace { 40 const int32 kCommandBufferSize = 1024 * 1024; 41 const int32 kTransferBufferSize = 1024 * 1024; 42 43 } // namespace. 44 45 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance) 46 : PPB_Graphics3D_Shared(instance), 47 bound_to_instance_(false), 48 commit_pending_(false), 49 sync_point_(0), 50 has_alpha_(false), 51 command_buffer_(NULL), 52 weak_ptr_factory_(this) {} 53 54 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() { 55 DestroyGLES2Impl(); 56 if (command_buffer_) { 57 DCHECK(channel_.get()); 58 channel_->DestroyCommandBuffer(command_buffer_); 59 command_buffer_ = NULL; 60 } 61 62 channel_ = NULL; 63 } 64 65 // static 66 PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance, 67 PP_Resource share_context, 68 const int32_t* attrib_list) { 69 PPB_Graphics3D_API* share_api = NULL; 70 if (share_context) { 71 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true); 72 if (enter.failed()) 73 return 0; 74 share_api = enter.object(); 75 } 76 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d( 77 new PPB_Graphics3D_Impl(instance)); 78 if (!graphics_3d->Init(share_api, attrib_list)) 79 return 0; 80 return graphics_3d->GetReference(); 81 } 82 83 // static 84 PP_Resource PPB_Graphics3D_Impl::CreateRaw( 85 PP_Instance instance, 86 PP_Resource share_context, 87 const int32_t* attrib_list, 88 base::SharedMemoryHandle* shared_state_handle) { 89 PPB_Graphics3D_API* share_api = NULL; 90 if (share_context) { 91 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true); 92 if (enter.failed()) 93 return 0; 94 share_api = enter.object(); 95 } 96 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d( 97 new PPB_Graphics3D_Impl(instance)); 98 if (!graphics_3d->InitRaw(share_api, attrib_list, shared_state_handle)) 99 return 0; 100 return graphics_3d->GetReference(); 101 } 102 103 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) { 104 GetCommandBuffer()->SetGetBuffer(transfer_buffer_id); 105 return PP_TRUE; 106 } 107 108 scoped_refptr<gpu::Buffer> PPB_Graphics3D_Impl::CreateTransferBuffer( 109 uint32_t size, 110 int32_t* id) { 111 return GetCommandBuffer()->CreateTransferBuffer(size, id); 112 } 113 114 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) { 115 GetCommandBuffer()->DestroyTransferBuffer(id); 116 return PP_TRUE; 117 } 118 119 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) { 120 GetCommandBuffer()->Flush(put_offset); 121 return PP_TRUE; 122 } 123 124 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForTokenInRange( 125 int32_t start, 126 int32_t end) { 127 GetCommandBuffer()->WaitForTokenInRange(start, end); 128 return GetCommandBuffer()->GetLastState(); 129 } 130 131 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForGetOffsetInRange( 132 int32_t start, 133 int32_t end) { 134 GetCommandBuffer()->WaitForGetOffsetInRange(start, end); 135 return GetCommandBuffer()->GetLastState(); 136 } 137 138 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() { 139 return command_buffer_->InsertSyncPoint(); 140 } 141 142 uint32_t PPB_Graphics3D_Impl::InsertFutureSyncPoint() { 143 return command_buffer_->InsertFutureSyncPoint(); 144 } 145 146 void PPB_Graphics3D_Impl::RetireSyncPoint(uint32_t sync_point) { 147 return command_buffer_->RetireSyncPoint(sync_point); 148 } 149 150 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) { 151 bound_to_instance_ = bind; 152 return true; 153 } 154 155 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_; } 156 157 void PPB_Graphics3D_Impl::ViewInitiatedPaint() { 158 commit_pending_ = false; 159 160 if (HasPendingSwap()) 161 SwapBuffersACK(PP_OK); 162 } 163 164 void PPB_Graphics3D_Impl::ViewFlushedPaint() {} 165 166 int PPB_Graphics3D_Impl::GetCommandBufferRouteId() { 167 DCHECK(command_buffer_); 168 return command_buffer_->GetRouteID(); 169 } 170 171 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() { 172 return command_buffer_; 173 } 174 175 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() { 176 return command_buffer_; 177 } 178 179 int32 PPB_Graphics3D_Impl::DoSwapBuffers() { 180 DCHECK(command_buffer_); 181 // We do not have a GLES2 implementation when using an OOP proxy. 182 // The plugin-side proxy is responsible for adding the SwapBuffers command 183 // to the command buffer in that case. 184 if (gles2_impl()) 185 gles2_impl()->SwapBuffers(); 186 187 // Since the backing texture has been updated, a new sync point should be 188 // inserted. 189 sync_point_ = command_buffer_->InsertSyncPoint(); 190 191 if (bound_to_instance_) { 192 // If we are bound to the instance, we need to ask the compositor 193 // to commit our backing texture so that the graphics appears on the page. 194 // When the backing texture will be committed we get notified via 195 // ViewFlushedPaint(). 196 // 197 // Don't need to check for NULL from GetPluginInstance since when we're 198 // bound, we know our instance is valid. 199 HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture(); 200 commit_pending_ = true; 201 } else { 202 // Wait for the command to complete on the GPU to allow for throttling. 203 command_buffer_->Echo(base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers, 204 weak_ptr_factory_.GetWeakPtr())); 205 } 206 207 return PP_OK_COMPLETIONPENDING; 208 } 209 210 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context, 211 const int32_t* attrib_list) { 212 if (!InitRaw(share_context, attrib_list, NULL)) 213 return false; 214 215 gpu::gles2::GLES2Implementation* share_gles2 = NULL; 216 if (share_context) { 217 share_gles2 = 218 static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl(); 219 } 220 221 return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, share_gles2); 222 } 223 224 bool PPB_Graphics3D_Impl::InitRaw( 225 PPB_Graphics3D_API* share_context, 226 const int32_t* attrib_list, 227 base::SharedMemoryHandle* shared_state_handle) { 228 PepperPluginInstanceImpl* plugin_instance = 229 HostGlobals::Get()->GetInstance(pp_instance()); 230 if (!plugin_instance) 231 return false; 232 233 const WebPreferences& prefs = 234 static_cast<RenderViewImpl*>(plugin_instance->GetRenderView()) 235 ->webkit_preferences(); 236 // 3D access might be disabled or blacklisted. 237 if (!prefs.pepper_3d_enabled) 238 return false; 239 240 RenderThreadImpl* render_thread = RenderThreadImpl::current(); 241 if (!render_thread) 242 return false; 243 244 channel_ = render_thread->EstablishGpuChannelSync( 245 CAUSE_FOR_GPU_LAUNCH_PEPPERPLATFORMCONTEXT3DIMPL_INITIALIZE); 246 if (!channel_.get()) 247 return false; 248 249 gfx::Size surface_size; 250 std::vector<int32> attribs; 251 gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; 252 // TODO(alokp): Change GpuChannelHost::CreateOffscreenCommandBuffer() 253 // interface to accept width and height in the attrib_list so that 254 // we do not need to filter for width and height here. 255 if (attrib_list) { 256 for (const int32_t* attr = attrib_list; attr[0] != PP_GRAPHICS3DATTRIB_NONE; 257 attr += 2) { 258 switch (attr[0]) { 259 case PP_GRAPHICS3DATTRIB_WIDTH: 260 surface_size.set_width(attr[1]); 261 break; 262 case PP_GRAPHICS3DATTRIB_HEIGHT: 263 surface_size.set_height(attr[1]); 264 break; 265 case PP_GRAPHICS3DATTRIB_GPU_PREFERENCE: 266 gpu_preference = 267 (attr[1] == PP_GRAPHICS3DATTRIB_GPU_PREFERENCE_LOW_POWER) 268 ? gfx::PreferIntegratedGpu 269 : gfx::PreferDiscreteGpu; 270 break; 271 case PP_GRAPHICS3DATTRIB_ALPHA_SIZE: 272 has_alpha_ = attr[1] > 0; 273 // fall-through 274 default: 275 attribs.push_back(attr[0]); 276 attribs.push_back(attr[1]); 277 break; 278 } 279 } 280 attribs.push_back(PP_GRAPHICS3DATTRIB_NONE); 281 } 282 283 CommandBufferProxyImpl* share_buffer = NULL; 284 if (share_context) { 285 PPB_Graphics3D_Impl* share_graphics = 286 static_cast<PPB_Graphics3D_Impl*>(share_context); 287 share_buffer = share_graphics->command_buffer_; 288 } 289 290 command_buffer_ = channel_->CreateOffscreenCommandBuffer( 291 surface_size, share_buffer, attribs, GURL::EmptyGURL(), gpu_preference); 292 if (!command_buffer_) 293 return false; 294 if (!command_buffer_->Initialize()) 295 return false; 296 if (shared_state_handle) 297 *shared_state_handle = command_buffer_->GetSharedStateHandle(); 298 mailbox_ = gpu::Mailbox::Generate(); 299 if (!command_buffer_->ProduceFrontBuffer(mailbox_)) 300 return false; 301 sync_point_ = command_buffer_->InsertSyncPoint(); 302 303 command_buffer_->SetChannelErrorCallback(base::Bind( 304 &PPB_Graphics3D_Impl::OnContextLost, weak_ptr_factory_.GetWeakPtr())); 305 306 command_buffer_->SetOnConsoleMessageCallback(base::Bind( 307 &PPB_Graphics3D_Impl::OnConsoleMessage, weak_ptr_factory_.GetWeakPtr())); 308 return true; 309 } 310 311 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message, int id) { 312 if (!bound_to_instance_) 313 return; 314 WebPluginContainer* container = 315 HostGlobals::Get()->GetInstance(pp_instance())->container(); 316 if (!container) 317 return; 318 WebLocalFrame* frame = container->element().document().frame(); 319 if (!frame) 320 return; 321 WebConsoleMessage console_message = WebConsoleMessage( 322 WebConsoleMessage::LevelError, WebString(base::UTF8ToUTF16(message))); 323 frame->addMessageToConsole(console_message); 324 } 325 326 void PPB_Graphics3D_Impl::OnSwapBuffers() { 327 if (HasPendingSwap()) { 328 // If we're off-screen, no need to trigger and wait for compositing. 329 // Just send the swap-buffers ACK to the plugin immediately. 330 commit_pending_ = false; 331 SwapBuffersACK(PP_OK); 332 } 333 } 334 335 void PPB_Graphics3D_Impl::OnContextLost() { 336 // Don't need to check for NULL from GetPluginInstance since when we're 337 // bound, we know our instance is valid. 338 if (bound_to_instance_) { 339 HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(pp_instance(), 340 0); 341 } 342 343 // Send context lost to plugin. This may have been caused by a PPAPI call, so 344 // avoid re-entering. 345 base::MessageLoop::current()->PostTask( 346 FROM_HERE, 347 base::Bind(&PPB_Graphics3D_Impl::SendContextLost, 348 weak_ptr_factory_.GetWeakPtr())); 349 } 350 351 void PPB_Graphics3D_Impl::SendContextLost() { 352 // By the time we run this, the instance may have been deleted, or in the 353 // process of being deleted. Even in the latter case, we don't want to send a 354 // callback after DidDestroy. 355 PepperPluginInstanceImpl* instance = 356 HostGlobals::Get()->GetInstance(pp_instance()); 357 if (!instance || !instance->container()) 358 return; 359 360 // This PPB_Graphics3D_Impl could be deleted during the call to 361 // GetPluginInterface (which sends a sync message in some cases). We still 362 // send the Graphics3DContextLost to the plugin; the instance may care about 363 // that event even though this context has been destroyed. 364 PP_Instance this_pp_instance = pp_instance(); 365 const PPP_Graphics3D* ppp_graphics_3d = static_cast<const PPP_Graphics3D*>( 366 instance->module()->GetPluginInterface(PPP_GRAPHICS_3D_INTERFACE)); 367 // We have to check *again* that the instance exists, because it could have 368 // been deleted during GetPluginInterface(). Even the PluginModule could be 369 // deleted, but in that case, the instance should also be gone, so the 370 // GetInstance check covers both cases. 371 if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance)) 372 ppp_graphics_3d->Graphics3DContextLost(this_pp_instance); 373 } 374 375 } // namespace content 376