| OLD | NEW |
| (Empty) |
| 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/gpu/renderer_gl_context.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/debug/trace_event.h" | |
| 9 #include "base/lazy_instance.h" | |
| 10 #include "base/memory/ref_counted.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/memory/singleton.h" | |
| 13 #include "base/memory/weak_ptr.h" | |
| 14 #include "base/shared_memory.h" | |
| 15 #include "content/common/view_messages.h" | |
| 16 #include "content/renderer/gpu/command_buffer_proxy.h" | |
| 17 #include "content/renderer/gpu/gpu_channel_host.h" | |
| 18 #include "content/renderer/render_widget.h" | |
| 19 #include "googleurl/src/gurl.h" | |
| 20 #include "ipc/ipc_channel_handle.h" | |
| 21 | |
| 22 #if defined(ENABLE_GPU) | |
| 23 #include "gpu/command_buffer/client/gles2_cmd_helper.h" | |
| 24 #include "gpu/command_buffer/client/gles2_implementation.h" | |
| 25 #include "gpu/command_buffer/client/gles2_lib.h" | |
| 26 #include "gpu/command_buffer/client/transfer_buffer.h" | |
| 27 #include "gpu/command_buffer/common/constants.h" | |
| 28 #endif // ENABLE_GPU | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 const int32 kCommandBufferSize = 1024 * 1024; | |
| 33 // TODO(kbr): make the transfer buffer size configurable via context | |
| 34 // creation attributes. | |
| 35 const size_t kStartTransferBufferSize = 1 * 1024 * 1024; | |
| 36 const size_t kMinTransferBufferSize = 1 * 256 * 1024; | |
| 37 const size_t kMaxTransferBufferSize = 16 * 1024 * 1024; | |
| 38 | |
| 39 // Singleton used to initialize and terminate the gles2 library. | |
| 40 class GLES2Initializer { | |
| 41 public: | |
| 42 GLES2Initializer() { | |
| 43 gles2::Initialize(); | |
| 44 } | |
| 45 | |
| 46 ~GLES2Initializer() { | |
| 47 gles2::Terminate(); | |
| 48 } | |
| 49 | |
| 50 private: | |
| 51 DISALLOW_COPY_AND_ASSIGN(GLES2Initializer); | |
| 52 }; | |
| 53 | |
| 54 //////////////////////////////////////////////////////////////////////////////// | |
| 55 | |
| 56 base::LazyInstance<GLES2Initializer> g_gles2_initializer = | |
| 57 LAZY_INSTANCE_INITIALIZER; | |
| 58 | |
| 59 //////////////////////////////////////////////////////////////////////////////// | |
| 60 | |
| 61 #if defined(ENABLE_GPU) | |
| 62 RendererGLContext::ContextLostReason ConvertReason( | |
| 63 gpu::error::ContextLostReason reason) { | |
| 64 switch (reason) { | |
| 65 case gpu::error::kGuilty: | |
| 66 return RendererGLContext::kGuilty; | |
| 67 case gpu::error::kInnocent: | |
| 68 return RendererGLContext::kInnocent; | |
| 69 case gpu::error::kUnknown: | |
| 70 return RendererGLContext::kUnknown; | |
| 71 } | |
| 72 NOTREACHED(); | |
| 73 return RendererGLContext::kUnknown; | |
| 74 } | |
| 75 #endif | |
| 76 | |
| 77 } // namespace | |
| 78 | |
| 79 RendererGLContext::~RendererGLContext() { | |
| 80 Destroy(); | |
| 81 } | |
| 82 | |
| 83 RendererGLContext* RendererGLContext::CreateViewContext( | |
| 84 GpuChannelHost* channel, | |
| 85 int32 surface_id, | |
| 86 RendererGLContext* share_group, | |
| 87 const char* allowed_extensions, | |
| 88 const int32* attrib_list, | |
| 89 const GURL& active_url, | |
| 90 gfx::GpuPreference gpu_preference) { | |
| 91 #if defined(ENABLE_GPU) | |
| 92 scoped_ptr<RendererGLContext> context(new RendererGLContext(channel)); | |
| 93 if (!context->Initialize( | |
| 94 true, | |
| 95 surface_id, | |
| 96 gfx::Size(), | |
| 97 share_group, | |
| 98 allowed_extensions, | |
| 99 attrib_list, | |
| 100 active_url, | |
| 101 gpu_preference)) | |
| 102 return NULL; | |
| 103 | |
| 104 return context.release(); | |
| 105 #else | |
| 106 return NULL; | |
| 107 #endif | |
| 108 } | |
| 109 | |
| 110 RendererGLContext* RendererGLContext::CreateOffscreenContext( | |
| 111 GpuChannelHost* channel, | |
| 112 const gfx::Size& size, | |
| 113 RendererGLContext* share_group, | |
| 114 const char* allowed_extensions, | |
| 115 const int32* attrib_list, | |
| 116 const GURL& active_url, | |
| 117 gfx::GpuPreference gpu_preference) { | |
| 118 #if defined(ENABLE_GPU) | |
| 119 scoped_ptr<RendererGLContext> context(new RendererGLContext(channel)); | |
| 120 if (!context->Initialize( | |
| 121 false, | |
| 122 0, | |
| 123 size, | |
| 124 share_group, | |
| 125 allowed_extensions, | |
| 126 attrib_list, | |
| 127 active_url, | |
| 128 gpu_preference)) | |
| 129 return NULL; | |
| 130 | |
| 131 return context.release(); | |
| 132 #else | |
| 133 return NULL; | |
| 134 #endif | |
| 135 } | |
| 136 | |
| 137 bool RendererGLContext::SetParent(RendererGLContext* new_parent) { | |
| 138 if (parent_.get() == new_parent) | |
| 139 return true; | |
| 140 | |
| 141 // Allocate a texture ID with respect to the parent and change the parent. | |
| 142 uint32 new_parent_texture_id = 0; | |
| 143 if (command_buffer_) { | |
| 144 if (new_parent) { | |
| 145 TRACE_EVENT0("gpu", "RendererGLContext::SetParent::flushParent"); | |
| 146 // Flush any remaining commands in the parent context to make sure the | |
| 147 // texture id accounting stays consistent. | |
| 148 int32 token = new_parent->gles2_helper_->InsertToken(); | |
| 149 new_parent->gles2_helper_->WaitForToken(token); | |
| 150 new_parent_texture_id = | |
| 151 new_parent->gles2_implementation_->MakeTextureId(); | |
| 152 | |
| 153 if (!command_buffer_->SetParent(new_parent->command_buffer_, | |
| 154 new_parent_texture_id)) { | |
| 155 new_parent->gles2_implementation_->FreeTextureId(parent_texture_id_); | |
| 156 return false; | |
| 157 } | |
| 158 } else { | |
| 159 if (!command_buffer_->SetParent(NULL, 0)) | |
| 160 return false; | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 // Free the previous parent's texture ID. | |
| 165 if (parent_.get() && parent_texture_id_ != 0) { | |
| 166 // Flush any remaining commands in the parent context to make sure the | |
| 167 // texture id accounting stays consistent. | |
| 168 gpu::gles2::GLES2Implementation* parent_gles2 = | |
| 169 parent_->GetImplementation(); | |
| 170 parent_gles2->helper()->CommandBufferHelper::Finish(); | |
| 171 parent_gles2->FreeTextureId(parent_texture_id_); | |
| 172 } | |
| 173 | |
| 174 if (new_parent) { | |
| 175 parent_ = new_parent->AsWeakPtr(); | |
| 176 parent_texture_id_ = new_parent_texture_id; | |
| 177 } else { | |
| 178 parent_.reset(); | |
| 179 parent_texture_id_ = 0; | |
| 180 } | |
| 181 | |
| 182 return true; | |
| 183 } | |
| 184 | |
| 185 uint32 RendererGLContext::GetParentTextureId() { | |
| 186 return parent_texture_id_; | |
| 187 } | |
| 188 | |
| 189 uint32 RendererGLContext::CreateParentTexture(const gfx::Size& size) { | |
| 190 uint32 texture_id = 0; | |
| 191 gles2_implementation_->GenTextures(1, &texture_id); | |
| 192 gles2_implementation_->Flush(); | |
| 193 return texture_id; | |
| 194 } | |
| 195 | |
| 196 void RendererGLContext::DeleteParentTexture(uint32 texture) { | |
| 197 gles2_implementation_->DeleteTextures(1, &texture); | |
| 198 } | |
| 199 | |
| 200 void RendererGLContext::SetContextLostCallback( | |
| 201 const base::Callback<void (ContextLostReason)>& callback) { | |
| 202 context_lost_callback_ = callback; | |
| 203 } | |
| 204 | |
| 205 bool RendererGLContext::MakeCurrent(RendererGLContext* context) { | |
| 206 if (context) { | |
| 207 DCHECK(context->CalledOnValidThread()); | |
| 208 gles2::SetGLContext(context->gles2_implementation_); | |
| 209 | |
| 210 // Don't request latest error status from service. Just use the locally | |
| 211 // cached information from the last flush. | |
| 212 // TODO(apatrick): I'm not sure if this should actually change the | |
| 213 // current context if it fails. For now it gets changed even if it fails | |
| 214 // because making GL calls with a NULL context crashes. | |
| 215 if (context->command_buffer_->GetLastState().error != gpu::error::kNoError) | |
| 216 return false; | |
| 217 } else { | |
| 218 gles2::SetGLContext(NULL); | |
| 219 } | |
| 220 | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 224 bool RendererGLContext::SwapBuffers() { | |
| 225 TRACE_EVENT1("gpu", "RendererGLContext::SwapBuffers", "frame", frame_number_); | |
| 226 frame_number_++; | |
| 227 | |
| 228 // Don't request latest error status from service. Just use the locally cached | |
| 229 // information from the last flush. | |
| 230 if (command_buffer_->GetLastState().error != gpu::error::kNoError) | |
| 231 return false; | |
| 232 | |
| 233 gles2_implementation_->SwapBuffers(); | |
| 234 | |
| 235 return true; | |
| 236 } | |
| 237 | |
| 238 bool RendererGLContext::Echo(const base::Closure& task) { | |
| 239 return command_buffer_->Echo(task); | |
| 240 } | |
| 241 | |
| 242 RendererGLContext::Error RendererGLContext::GetError() { | |
| 243 gpu::CommandBuffer::State state = command_buffer_->GetState(); | |
| 244 if (state.error == gpu::error::kNoError) { | |
| 245 Error old_error = last_error_; | |
| 246 last_error_ = SUCCESS; | |
| 247 return old_error; | |
| 248 } else { | |
| 249 // All command buffer errors are unrecoverable. The error is treated as a | |
| 250 // lost context: destroy the context and create another one. | |
| 251 return CONTEXT_LOST; | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 bool RendererGLContext::IsCommandBufferContextLost() { | |
| 256 // If the channel shut down unexpectedly, let that supersede the | |
| 257 // command buffer's state. | |
| 258 if (channel_->state() == GpuChannelHost::kLost) | |
| 259 return true; | |
| 260 gpu::CommandBuffer::State state = command_buffer_->GetLastState(); | |
| 261 return state.error == gpu::error::kLostContext; | |
| 262 } | |
| 263 | |
| 264 CommandBufferProxy* RendererGLContext::GetCommandBufferProxy() { | |
| 265 return command_buffer_; | |
| 266 } | |
| 267 | |
| 268 bool RendererGLContext::SetSurfaceVisible(bool visible) { | |
| 269 return GetCommandBufferProxy()->SetSurfaceVisible(visible); | |
| 270 } | |
| 271 | |
| 272 // TODO(gman): Remove This | |
| 273 void RendererGLContext::DisableShaderTranslation() { | |
| 274 NOTREACHED(); | |
| 275 } | |
| 276 | |
| 277 gpu::gles2::GLES2Implementation* RendererGLContext::GetImplementation() { | |
| 278 return gles2_implementation_; | |
| 279 } | |
| 280 | |
| 281 RendererGLContext::RendererGLContext(GpuChannelHost* channel) | |
| 282 : channel_(channel), | |
| 283 parent_(base::WeakPtr<RendererGLContext>()), | |
| 284 parent_texture_id_(0), | |
| 285 command_buffer_(NULL), | |
| 286 gles2_helper_(NULL), | |
| 287 transfer_buffer_(NULL), | |
| 288 gles2_implementation_(NULL), | |
| 289 last_error_(SUCCESS), | |
| 290 frame_number_(0) { | |
| 291 DCHECK(channel); | |
| 292 } | |
| 293 | |
| 294 bool RendererGLContext::Initialize(bool onscreen, | |
| 295 int32 surface_id, | |
| 296 const gfx::Size& size, | |
| 297 RendererGLContext* share_group, | |
| 298 const char* allowed_extensions, | |
| 299 const int32* attrib_list, | |
| 300 const GURL& active_url, | |
| 301 gfx::GpuPreference gpu_preference) { | |
| 302 DCHECK(CalledOnValidThread()); | |
| 303 DCHECK(size.width() >= 0 && size.height() >= 0); | |
| 304 TRACE_EVENT2("gpu", "RendererGLContext::Initialize", | |
| 305 "on_screen", onscreen, "num_pixels", size.GetArea()); | |
| 306 | |
| 307 if (channel_->state() != GpuChannelHost::kConnected) | |
| 308 return false; | |
| 309 | |
| 310 // Ensure the gles2 library is initialized first in a thread safe way. | |
| 311 g_gles2_initializer.Get(); | |
| 312 | |
| 313 bool share_resources = true; | |
| 314 bool bind_generates_resources = true; | |
| 315 std::vector<int32> attribs; | |
| 316 while (attrib_list) { | |
| 317 int32 attrib = *attrib_list++; | |
| 318 switch (attrib) { | |
| 319 // Known attributes | |
| 320 case ALPHA_SIZE: | |
| 321 case BLUE_SIZE: | |
| 322 case GREEN_SIZE: | |
| 323 case RED_SIZE: | |
| 324 case DEPTH_SIZE: | |
| 325 case STENCIL_SIZE: | |
| 326 case SAMPLES: | |
| 327 case SAMPLE_BUFFERS: | |
| 328 attribs.push_back(attrib); | |
| 329 attribs.push_back(*attrib_list++); | |
| 330 break; | |
| 331 case SHARE_RESOURCES: | |
| 332 share_resources = !!(*attrib_list++); | |
| 333 break; | |
| 334 case BIND_GENERATES_RESOURCES: | |
| 335 bind_generates_resources = !!(*attrib_list++); | |
| 336 break; | |
| 337 case NONE: | |
| 338 attribs.push_back(attrib); | |
| 339 attrib_list = NULL; | |
| 340 break; | |
| 341 default: | |
| 342 last_error_ = BAD_ATTRIBUTE; | |
| 343 attribs.push_back(NONE); | |
| 344 attrib_list = NULL; | |
| 345 break; | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 // Create a proxy to a command buffer in the GPU process. | |
| 350 if (onscreen) { | |
| 351 TRACE_EVENT0("gpu", | |
| 352 "RendererGLContext::Initialize::CreateViewCommandBuffer"); | |
| 353 command_buffer_ = channel_->CreateViewCommandBuffer( | |
| 354 surface_id, | |
| 355 share_group ? share_group->command_buffer_ : NULL, | |
| 356 allowed_extensions, | |
| 357 attribs, | |
| 358 active_url, | |
| 359 gpu_preference); | |
| 360 } else { | |
| 361 command_buffer_ = channel_->CreateOffscreenCommandBuffer( | |
| 362 size, | |
| 363 share_group ? share_group->command_buffer_ : NULL, | |
| 364 allowed_extensions, | |
| 365 attribs, | |
| 366 active_url, | |
| 367 gpu_preference); | |
| 368 } | |
| 369 if (!command_buffer_) { | |
| 370 Destroy(); | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 { | |
| 375 TRACE_EVENT0("gpu", | |
| 376 "RendererGLContext::Initialize::InitializeCommandBuffer"); | |
| 377 // Initiaize the command buffer. | |
| 378 if (!command_buffer_->Initialize()) { | |
| 379 Destroy(); | |
| 380 return false; | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 command_buffer_->SetChannelErrorCallback( | |
| 385 base::Bind(&RendererGLContext::OnContextLost, base::Unretained(this))); | |
| 386 | |
| 387 // Create the GLES2 helper, which writes the command buffer protocol. | |
| 388 gles2_helper_ = new gpu::gles2::GLES2CmdHelper(command_buffer_); | |
| 389 if (!gles2_helper_->Initialize(kCommandBufferSize)) { | |
| 390 Destroy(); | |
| 391 return false; | |
| 392 } | |
| 393 | |
| 394 { | |
| 395 TRACE_EVENT0("gpu", "RendererGLContext::Initialize::CreateTransferBuffer"); | |
| 396 // Create a transfer buffer used to copy resources between the renderer | |
| 397 // process and the GPU process. | |
| 398 transfer_buffer_ = new gpu::TransferBuffer(gles2_helper_); | |
| 399 } | |
| 400 | |
| 401 // Create the object exposing the OpenGL API. | |
| 402 gles2_implementation_ = new gpu::gles2::GLES2Implementation( | |
| 403 gles2_helper_, | |
| 404 transfer_buffer_, | |
| 405 share_resources, | |
| 406 bind_generates_resources); | |
| 407 | |
| 408 if (!gles2_implementation_->Initialize( | |
| 409 kStartTransferBufferSize, | |
| 410 kMinTransferBufferSize, | |
| 411 kMaxTransferBufferSize)) { | |
| 412 Destroy(); | |
| 413 return false; | |
| 414 } | |
| 415 | |
| 416 return true; | |
| 417 } | |
| 418 | |
| 419 void RendererGLContext::Destroy() { | |
| 420 TRACE_EVENT0("gpu", "RendererGLContext::Destroy"); | |
| 421 DCHECK(CalledOnValidThread()); | |
| 422 SetParent(NULL); | |
| 423 | |
| 424 if (gles2_implementation_) { | |
| 425 // First flush the context to ensure that any pending frees of resources | |
| 426 // are completed. Otherwise, if this context is part of a share group, | |
| 427 // those resources might leak. Also, any remaining side effects of commands | |
| 428 // issued on this context might not be visible to other contexts in the | |
| 429 // share group. | |
| 430 gles2_implementation_->Flush(); | |
| 431 | |
| 432 delete gles2_implementation_; | |
| 433 gles2_implementation_ = NULL; | |
| 434 } | |
| 435 | |
| 436 if (transfer_buffer_) { | |
| 437 delete transfer_buffer_; | |
| 438 transfer_buffer_ = NULL; | |
| 439 } | |
| 440 | |
| 441 delete gles2_helper_; | |
| 442 gles2_helper_ = NULL; | |
| 443 | |
| 444 if (channel_ && command_buffer_) { | |
| 445 channel_->DestroyCommandBuffer(command_buffer_); | |
| 446 command_buffer_ = NULL; | |
| 447 } | |
| 448 | |
| 449 channel_ = NULL; | |
| 450 } | |
| 451 | |
| 452 void RendererGLContext::OnContextLost() { | |
| 453 if (!context_lost_callback_.is_null()) { | |
| 454 RendererGLContext::ContextLostReason reason = kUnknown; | |
| 455 if (command_buffer_) { | |
| 456 reason = ConvertReason( | |
| 457 command_buffer_->GetLastState().context_lost_reason); | |
| 458 } | |
| 459 context_lost_callback_.Run(reason); | |
| 460 } | |
| 461 } | |
| OLD | NEW |