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/public/common/content_switches.h" 12 #include "content/renderer/pepper/host_globals.h" 13 #include "content/renderer/pepper/pepper_platform_context_3d.h" 14 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" 15 #include "content/renderer/pepper/plugin_module.h" 16 #include "content/renderer/render_view_impl.h" 17 #include "gpu/command_buffer/client/gles2_implementation.h" 18 #include "ppapi/c/ppp_graphics_3d.h" 19 #include "ppapi/thunk/enter.h" 20 #include "third_party/WebKit/public/platform/WebString.h" 21 #include "third_party/WebKit/public/web/WebConsoleMessage.h" 22 #include "third_party/WebKit/public/web/WebDocument.h" 23 #include "third_party/WebKit/public/web/WebElement.h" 24 #include "third_party/WebKit/public/web/WebFrame.h" 25 #include "third_party/WebKit/public/web/WebPluginContainer.h" 26 #include "webkit/common/webpreferences.h" 27 28 using ppapi::thunk::EnterResourceNoLock; 29 using ppapi::thunk::PPB_Graphics3D_API; 30 using blink::WebConsoleMessage; 31 using blink::WebFrame; 32 using blink::WebPluginContainer; 33 using blink::WebString; 34 35 namespace content { 36 37 namespace { 38 const int32 kCommandBufferSize = 1024 * 1024; 39 const int32 kTransferBufferSize = 1024 * 1024; 40 41 PP_Bool ShmToHandle(base::SharedMemory* shm, 42 size_t size, 43 int* shm_handle, 44 uint32_t* shm_size) { 45 if (!shm || !shm_handle || !shm_size) 46 return PP_FALSE; 47 #if defined(OS_POSIX) 48 *shm_handle = shm->handle().fd; 49 #elif defined(OS_WIN) 50 *shm_handle = reinterpret_cast<int>(shm->handle()); 51 #else 52 #error "Platform not supported." 53 #endif 54 *shm_size = size; 55 return PP_TRUE; 56 } 57 58 } // namespace. 59 60 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance) 61 : PPB_Graphics3D_Shared(instance), 62 bound_to_instance_(false), 63 commit_pending_(false), 64 weak_ptr_factory_(this) { 65 } 66 67 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() { 68 DestroyGLES2Impl(); 69 } 70 71 // static 72 PP_Bool PPB_Graphics3D_Impl::IsGpuBlacklisted() { 73 CommandLine* command_line = CommandLine::ForCurrentProcess(); 74 if (command_line) 75 return PP_FromBool(command_line->HasSwitch(switches::kDisablePepper3d)); 76 return PP_TRUE; 77 } 78 79 // static 80 PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance, 81 PP_Resource share_context, 82 const int32_t* attrib_list) { 83 PPB_Graphics3D_API* share_api = NULL; 84 if (IsGpuBlacklisted()) 85 return 0; 86 if (share_context) { 87 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true); 88 if (enter.failed()) 89 return 0; 90 share_api = enter.object(); 91 } 92 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d( 93 new PPB_Graphics3D_Impl(instance)); 94 if (!graphics_3d->Init(share_api, attrib_list)) 95 return 0; 96 return graphics_3d->GetReference(); 97 } 98 99 // static 100 PP_Resource PPB_Graphics3D_Impl::CreateRaw(PP_Instance instance, 101 PP_Resource share_context, 102 const int32_t* attrib_list) { 103 PPB_Graphics3D_API* share_api = NULL; 104 if (IsGpuBlacklisted()) 105 return 0; 106 if (share_context) { 107 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true); 108 if (enter.failed()) 109 return 0; 110 share_api = enter.object(); 111 } 112 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d( 113 new PPB_Graphics3D_Impl(instance)); 114 if (!graphics_3d->InitRaw(share_api, attrib_list)) 115 return 0; 116 return graphics_3d->GetReference(); 117 } 118 119 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) { 120 GetCommandBuffer()->SetGetBuffer(transfer_buffer_id); 121 return PP_TRUE; 122 } 123 124 gpu::CommandBuffer::State PPB_Graphics3D_Impl::GetState() { 125 return GetCommandBuffer()->GetState(); 126 } 127 128 int32_t PPB_Graphics3D_Impl::CreateTransferBuffer(uint32_t size) { 129 int32_t id = -1; 130 GetCommandBuffer()->CreateTransferBuffer(size, &id); 131 return id; 132 } 133 134 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) { 135 GetCommandBuffer()->DestroyTransferBuffer(id); 136 return PP_TRUE; 137 } 138 139 PP_Bool PPB_Graphics3D_Impl::GetTransferBuffer(int32_t id, 140 int* shm_handle, 141 uint32_t* shm_size) { 142 gpu::Buffer buffer = GetCommandBuffer()->GetTransferBuffer(id); 143 return ShmToHandle(buffer.shared_memory, buffer.size, shm_handle, shm_size); 144 } 145 146 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) { 147 GetCommandBuffer()->Flush(put_offset); 148 return PP_TRUE; 149 } 150 151 gpu::CommandBuffer::State PPB_Graphics3D_Impl::FlushSync(int32_t put_offset) { 152 gpu::CommandBuffer::State state = GetCommandBuffer()->GetState(); 153 return GetCommandBuffer()->FlushSync(put_offset, state.get_offset); 154 } 155 156 gpu::CommandBuffer::State PPB_Graphics3D_Impl::FlushSyncFast( 157 int32_t put_offset, 158 int32_t last_known_get) { 159 return GetCommandBuffer()->FlushSync(put_offset, last_known_get); 160 } 161 162 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() { 163 return platform_context_->GetGpuControl()->InsertSyncPoint(); 164 } 165 166 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) { 167 bound_to_instance_ = bind; 168 return true; 169 } 170 171 bool PPB_Graphics3D_Impl::IsOpaque() { 172 return platform_context_->IsOpaque(); 173 } 174 175 void PPB_Graphics3D_Impl::ViewInitiatedPaint() { 176 commit_pending_ = false; 177 178 if (HasPendingSwap()) 179 SwapBuffersACK(PP_OK); 180 } 181 182 void PPB_Graphics3D_Impl::ViewFlushedPaint() { 183 } 184 185 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() { 186 return platform_context_->GetCommandBuffer(); 187 } 188 189 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() { 190 return platform_context_->GetGpuControl(); 191 } 192 193 int32 PPB_Graphics3D_Impl::DoSwapBuffers() { 194 // We do not have a GLES2 implementation when using an OOP proxy. 195 // The plugin-side proxy is responsible for adding the SwapBuffers command 196 // to the command buffer in that case. 197 if (gles2_impl()) 198 gles2_impl()->SwapBuffers(); 199 200 // Since the backing texture has been updated, a new sync point should be 201 // inserted. 202 platform_context_->InsertSyncPointForBackingMailbox(); 203 204 if (bound_to_instance_) { 205 // If we are bound to the instance, we need to ask the compositor 206 // to commit our backing texture so that the graphics appears on the page. 207 // When the backing texture will be committed we get notified via 208 // ViewFlushedPaint(). 209 // 210 // Don't need to check for NULL from GetPluginInstance since when we're 211 // bound, we know our instance is valid. 212 HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture(); 213 commit_pending_ = true; 214 } else { 215 // Wait for the command to complete on the GPU to allow for throttling. 216 platform_context_->Echo(base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers, 217 weak_ptr_factory_.GetWeakPtr())); 218 } 219 220 221 return PP_OK_COMPLETIONPENDING; 222 } 223 224 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context, 225 const int32_t* attrib_list) { 226 if (!InitRaw(share_context, attrib_list)) 227 return false; 228 229 gpu::CommandBuffer* command_buffer = GetCommandBuffer(); 230 if (!command_buffer->Initialize()) 231 return false; 232 233 gpu::gles2::GLES2Implementation* share_gles2 = NULL; 234 if (share_context) { 235 share_gles2 = 236 static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl(); 237 } 238 239 return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, 240 share_gles2); 241 } 242 243 bool PPB_Graphics3D_Impl::InitRaw(PPB_Graphics3D_API* share_context, 244 const int32_t* attrib_list) { 245 PepperPluginInstanceImpl* plugin_instance = 246 HostGlobals::Get()->GetInstance(pp_instance()); 247 if (!plugin_instance) 248 return false; 249 250 PlatformContext3D* share_platform_context = NULL; 251 if (share_context) { 252 PPB_Graphics3D_Impl* share_graphics = 253 static_cast<PPB_Graphics3D_Impl*>(share_context); 254 share_platform_context = share_graphics->platform_context(); 255 } 256 257 // If accelerated compositing of plugins is disabled, fail to create a 3D 258 // context, because it won't be visible. This allows graceful fallback in the 259 // modules. 260 const WebPreferences& prefs = static_cast<RenderViewImpl*>(plugin_instance-> 261 GetRenderView())->webkit_preferences(); 262 if (!prefs.accelerated_compositing_for_plugins_enabled) 263 return false; 264 265 platform_context_.reset(new PlatformContext3D); 266 if (!platform_context_) 267 return false; 268 269 if (!platform_context_->Init(attrib_list, share_platform_context)) 270 return false; 271 272 platform_context_->SetContextLostCallback( 273 base::Bind(&PPB_Graphics3D_Impl::OnContextLost, 274 weak_ptr_factory_.GetWeakPtr())); 275 276 platform_context_->SetOnConsoleMessageCallback( 277 base::Bind(&PPB_Graphics3D_Impl::OnConsoleMessage, 278 weak_ptr_factory_.GetWeakPtr())); 279 return true; 280 } 281 282 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message, 283 int id) { 284 if (!bound_to_instance_) 285 return; 286 WebPluginContainer* container = 287 HostGlobals::Get()->GetInstance(pp_instance())->container(); 288 if (!container) 289 return; 290 WebFrame* frame = container->element().document().frame(); 291 if (!frame) 292 return; 293 WebConsoleMessage console_message = WebConsoleMessage( 294 WebConsoleMessage::LevelError, WebString(UTF8ToUTF16(message))); 295 frame->addMessageToConsole(console_message); 296 } 297 298 void PPB_Graphics3D_Impl::OnSwapBuffers() { 299 if (HasPendingSwap()) { 300 // If we're off-screen, no need to trigger and wait for compositing. 301 // Just send the swap-buffers ACK to the plugin immediately. 302 commit_pending_ = false; 303 SwapBuffersACK(PP_OK); 304 } 305 } 306 307 void PPB_Graphics3D_Impl::OnContextLost() { 308 // Don't need to check for NULL from GetPluginInstance since when we're 309 // bound, we know our instance is valid. 310 if (bound_to_instance_) { 311 HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics( 312 pp_instance(), 0); 313 } 314 315 // Send context lost to plugin. This may have been caused by a PPAPI call, so 316 // avoid re-entering. 317 base::MessageLoop::current()->PostTask( 318 FROM_HERE, 319 base::Bind(&PPB_Graphics3D_Impl::SendContextLost, 320 weak_ptr_factory_.GetWeakPtr())); 321 } 322 323 void PPB_Graphics3D_Impl::SendContextLost() { 324 // By the time we run this, the instance may have been deleted, or in the 325 // process of being deleted. Even in the latter case, we don't want to send a 326 // callback after DidDestroy. 327 PepperPluginInstanceImpl* instance = 328 HostGlobals::Get()->GetInstance(pp_instance()); 329 if (!instance || !instance->container()) 330 return; 331 332 // This PPB_Graphics3D_Impl could be deleted during the call to 333 // GetPluginInterface (which sends a sync message in some cases). We still 334 // send the Graphics3DContextLost to the plugin; the instance may care about 335 // that event even though this context has been destroyed. 336 PP_Instance this_pp_instance = pp_instance(); 337 const PPP_Graphics3D* ppp_graphics_3d = 338 static_cast<const PPP_Graphics3D*>( 339 instance->module()->GetPluginInterface( 340 PPP_GRAPHICS_3D_INTERFACE)); 341 // We have to check *again* that the instance exists, because it could have 342 // been deleted during GetPluginInterface(). Even the PluginModule could be 343 // deleted, but in that case, the instance should also be gone, so the 344 // GetInstance check covers both cases. 345 if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance)) 346 ppp_graphics_3d->Graphics3DContextLost(this_pp_instance); 347 } 348 349 } // namespace content 350