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