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/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" 6 7#include "third_party/khronos/GLES2/gl2.h" 8#ifndef GL_GLEXT_PROTOTYPES 9#define GL_GLEXT_PROTOTYPES 1 10#endif 11#include "third_party/khronos/GLES2/gl2ext.h" 12 13#include <algorithm> 14#include <map> 15 16#include "base/atomicops.h" 17#include "base/bind.h" 18#include "base/command_line.h" 19#include "base/debug/trace_event.h" 20#include "base/lazy_instance.h" 21#include "base/logging.h" 22#include "base/message_loop/message_loop.h" 23#include "base/metrics/field_trial.h" 24#include "base/metrics/histogram.h" 25#include "content/common/gpu/client/gpu_channel_host.h" 26#include "content/public/common/content_constants.h" 27#include "content/public/common/content_switches.h" 28#include "gpu/GLES2/gl2extchromium.h" 29#include "gpu/command_buffer/client/gles2_cmd_helper.h" 30#include "gpu/command_buffer/client/gles2_implementation.h" 31#include "gpu/command_buffer/client/gles2_trace_implementation.h" 32#include "gpu/command_buffer/client/transfer_buffer.h" 33#include "gpu/command_buffer/common/constants.h" 34#include "gpu/command_buffer/common/gpu_memory_allocation.h" 35#include "gpu/command_buffer/common/mailbox.h" 36#include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h" 37#include "third_party/skia/include/core/SkTypes.h" 38 39namespace content { 40 41namespace { 42 43static base::LazyInstance<base::Lock>::Leaky 44 g_default_share_groups_lock = LAZY_INSTANCE_INITIALIZER; 45 46typedef std::map<GpuChannelHost*, 47 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> > 48 ShareGroupMap; 49static base::LazyInstance<ShareGroupMap> g_default_share_groups = 50 LAZY_INSTANCE_INITIALIZER; 51 52scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> 53 GetDefaultShareGroupForHost(GpuChannelHost* host) { 54 base::AutoLock lock(g_default_share_groups_lock.Get()); 55 56 ShareGroupMap& share_groups = g_default_share_groups.Get(); 57 ShareGroupMap::iterator it = share_groups.find(host); 58 if (it == share_groups.end()) { 59 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> group = 60 new WebGraphicsContext3DCommandBufferImpl::ShareGroup(); 61 share_groups[host] = group; 62 return group; 63 } 64 return it->second; 65} 66 67} // namespace anonymous 68 69WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits::SharedMemoryLimits() 70 : command_buffer_size(kDefaultCommandBufferSize), 71 start_transfer_buffer_size(kDefaultStartTransferBufferSize), 72 min_transfer_buffer_size(kDefaultMinTransferBufferSize), 73 max_transfer_buffer_size(kDefaultMaxTransferBufferSize), 74 mapped_memory_reclaim_limit(gpu::gles2::GLES2Implementation::kNoLimit) {} 75 76WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() { 77} 78 79WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() { 80 DCHECK(contexts_.empty()); 81} 82 83WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl( 84 int surface_id, 85 const GURL& active_url, 86 GpuChannelHost* host, 87 const Attributes& attributes, 88 bool lose_context_when_out_of_memory, 89 const SharedMemoryLimits& limits, 90 WebGraphicsContext3DCommandBufferImpl* share_context) 91 : lose_context_when_out_of_memory_(lose_context_when_out_of_memory), 92 attributes_(attributes), 93 visible_(false), 94 host_(host), 95 surface_id_(surface_id), 96 active_url_(active_url), 97 gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu 98 : gfx::PreferIntegratedGpu), 99 mem_limits_(limits), 100 weak_ptr_factory_(this) { 101 if (share_context) { 102 DCHECK(!attributes_.shareResources); 103 share_group_ = share_context->share_group_; 104 } else { 105 share_group_ = attributes_.shareResources 106 ? GetDefaultShareGroupForHost(host) 107 : scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>( 108 new ShareGroup()); 109 } 110} 111 112WebGraphicsContext3DCommandBufferImpl:: 113 ~WebGraphicsContext3DCommandBufferImpl() { 114 if (real_gl_) { 115 real_gl_->SetErrorMessageCallback(NULL); 116 } 117 118 Destroy(); 119} 120 121bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() { 122 if (initialized_) 123 return true; 124 125 if (initialize_failed_) 126 return false; 127 128 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL"); 129 130 if (!CreateContext(surface_id_ != 0)) { 131 Destroy(); 132 initialize_failed_ = true; 133 return false; 134 } 135 136 if (gl_ && attributes_.webGL) 137 gl_->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation"); 138 139 command_buffer_->SetChannelErrorCallback( 140 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost, 141 weak_ptr_factory_.GetWeakPtr())); 142 143 command_buffer_->SetOnConsoleMessageCallback( 144 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnErrorMessage, 145 weak_ptr_factory_.GetWeakPtr())); 146 147 real_gl_->SetErrorMessageCallback(getErrorMessageCallback()); 148 149 visible_ = true; 150 initialized_ = true; 151 return true; 152} 153 154bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer( 155 bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) { 156 if (!host_.get()) 157 return false; 158 159 CommandBufferProxyImpl* share_group_command_buffer = NULL; 160 161 if (share_context) { 162 share_group_command_buffer = share_context->command_buffer_.get(); 163 } 164 165 ::gpu::gles2::ContextCreationAttribHelper attribs_for_gles2; 166 ConvertAttributes(attributes_, &attribs_for_gles2); 167 attribs_for_gles2.lose_context_when_out_of_memory = 168 lose_context_when_out_of_memory_; 169 DCHECK(attribs_for_gles2.buffer_preserved); 170 std::vector<int32> attribs; 171 attribs_for_gles2.Serialize(&attribs); 172 173 // Create a proxy to a command buffer in the GPU process. 174 if (onscreen) { 175 command_buffer_.reset(host_->CreateViewCommandBuffer( 176 surface_id_, 177 share_group_command_buffer, 178 attribs, 179 active_url_, 180 gpu_preference_)); 181 } else { 182 command_buffer_.reset(host_->CreateOffscreenCommandBuffer( 183 gfx::Size(1, 1), 184 share_group_command_buffer, 185 attribs, 186 active_url_, 187 gpu_preference_)); 188 } 189 190 if (!command_buffer_) { 191 DLOG(ERROR) << "GpuChannelHost failed to create command buffer."; 192 return false; 193 } 194 195 DVLOG_IF(1, gpu::error::IsError(command_buffer_->GetLastError())) 196 << "Context dead on arrival. Last error: " 197 << command_buffer_->GetLastError(); 198 // Initialize the command buffer. 199 bool result = command_buffer_->Initialize(); 200 LOG_IF(ERROR, !result) << "CommandBufferProxy::Initialize failed."; 201 return result; 202} 203 204bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen) { 205 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext"); 206 scoped_refptr<gpu::gles2::ShareGroup> gles2_share_group; 207 208 scoped_ptr<base::AutoLock> share_group_lock; 209 bool add_to_share_group = false; 210 if (!command_buffer_) { 211 WebGraphicsContext3DCommandBufferImpl* share_context = NULL; 212 213 share_group_lock.reset(new base::AutoLock(share_group_->lock())); 214 share_context = share_group_->GetAnyContextLocked(); 215 216 if (!InitializeCommandBuffer(onscreen, share_context)) { 217 LOG(ERROR) << "Failed to initialize command buffer."; 218 return false; 219 } 220 221 if (share_context) 222 gles2_share_group = share_context->GetImplementation()->share_group(); 223 224 add_to_share_group = true; 225 } 226 227 // Create the GLES2 helper, which writes the command buffer protocol. 228 gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_.get())); 229 if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) { 230 LOG(ERROR) << "Failed to initialize GLES2CmdHelper."; 231 return false; 232 } 233 234 if (attributes_.noAutomaticFlushes) 235 gles2_helper_->SetAutomaticFlushes(false); 236 // Create a transfer buffer used to copy resources between the renderer 237 // process and the GPU process. 238 transfer_buffer_ .reset(new gpu::TransferBuffer(gles2_helper_.get())); 239 240 DCHECK(host_.get()); 241 242 // Create the object exposing the OpenGL API. 243 bool bind_generates_resources = false; 244 real_gl_.reset( 245 new gpu::gles2::GLES2Implementation(gles2_helper_.get(), 246 gles2_share_group.get(), 247 transfer_buffer_.get(), 248 bind_generates_resources, 249 lose_context_when_out_of_memory_, 250 command_buffer_.get())); 251 setGLInterface(real_gl_.get()); 252 253 if (!real_gl_->Initialize( 254 mem_limits_.start_transfer_buffer_size, 255 mem_limits_.min_transfer_buffer_size, 256 mem_limits_.max_transfer_buffer_size, 257 mem_limits_.mapped_memory_reclaim_limit)) { 258 LOG(ERROR) << "Failed to initialize GLES2Implementation."; 259 return false; 260 } 261 262 if (add_to_share_group) 263 share_group_->AddContextLocked(this); 264 265 if (CommandLine::ForCurrentProcess()->HasSwitch( 266 switches::kEnableGpuClientTracing)) { 267 trace_gl_.reset(new gpu::gles2::GLES2TraceImplementation(GetGLInterface())); 268 setGLInterface(trace_gl_.get()); 269 } 270 return true; 271} 272 273bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread() { 274 if (!MaybeInitializeGL()) { 275 DLOG(ERROR) << "Failed to initialize context."; 276 return false; 277 } 278 if (gpu::error::IsError(command_buffer_->GetLastError())) { 279 LOG(ERROR) << "Context dead on arrival. Last error: " 280 << command_buffer_->GetLastError(); 281 return false; 282 } 283 284 return true; 285} 286 287void WebGraphicsContext3DCommandBufferImpl::Destroy() { 288 share_group_->RemoveContext(this); 289 290 gpu::gles2::GLES2Interface* gl = GetGLInterface(); 291 if (gl) { 292 // First flush the context to ensure that any pending frees of resources 293 // are completed. Otherwise, if this context is part of a share group, 294 // those resources might leak. Also, any remaining side effects of commands 295 // issued on this context might not be visible to other contexts in the 296 // share group. 297 gl->Flush(); 298 setGLInterface(NULL); 299 } 300 301 trace_gl_.reset(); 302 real_gl_.reset(); 303 transfer_buffer_.reset(); 304 gles2_helper_.reset(); 305 real_gl_.reset(); 306 307 if (command_buffer_) { 308 if (host_.get()) 309 host_->DestroyCommandBuffer(command_buffer_.release()); 310 command_buffer_.reset(); 311 } 312 313 host_ = NULL; 314} 315 316gpu::ContextSupport* 317WebGraphicsContext3DCommandBufferImpl::GetContextSupport() { 318 return real_gl_.get(); 319} 320 321bool WebGraphicsContext3DCommandBufferImpl::isContextLost() { 322 return initialize_failed_ || 323 (command_buffer_ && IsCommandBufferContextLost()) || 324 context_lost_reason_ != GL_NO_ERROR; 325} 326 327WGC3Denum WebGraphicsContext3DCommandBufferImpl::getGraphicsResetStatusARB() { 328 if (IsCommandBufferContextLost() && 329 context_lost_reason_ == GL_NO_ERROR) { 330 return GL_UNKNOWN_CONTEXT_RESET_ARB; 331 } 332 333 return context_lost_reason_; 334} 335 336bool WebGraphicsContext3DCommandBufferImpl::IsCommandBufferContextLost() { 337 // If the channel shut down unexpectedly, let that supersede the 338 // command buffer's state. 339 if (host_.get() && host_->IsLost()) 340 return true; 341 gpu::CommandBuffer::State state = command_buffer_->GetLastState(); 342 return state.error == gpu::error::kLostContext; 343} 344 345// static 346WebGraphicsContext3DCommandBufferImpl* 347WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext( 348 GpuChannelHost* host, 349 const WebGraphicsContext3D::Attributes& attributes, 350 bool lose_context_when_out_of_memory, 351 const GURL& active_url, 352 const SharedMemoryLimits& limits, 353 WebGraphicsContext3DCommandBufferImpl* share_context) { 354 if (!host) 355 return NULL; 356 357 if (share_context && share_context->IsCommandBufferContextLost()) 358 return NULL; 359 360 return new WebGraphicsContext3DCommandBufferImpl( 361 0, 362 active_url, 363 host, 364 attributes, 365 lose_context_when_out_of_memory, 366 limits, 367 share_context); 368} 369 370namespace { 371 372WGC3Denum convertReason(gpu::error::ContextLostReason reason) { 373 switch (reason) { 374 case gpu::error::kGuilty: 375 return GL_GUILTY_CONTEXT_RESET_ARB; 376 case gpu::error::kInnocent: 377 return GL_INNOCENT_CONTEXT_RESET_ARB; 378 case gpu::error::kUnknown: 379 return GL_UNKNOWN_CONTEXT_RESET_ARB; 380 } 381 382 NOTREACHED(); 383 return GL_UNKNOWN_CONTEXT_RESET_ARB; 384} 385 386} // anonymous namespace 387 388void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() { 389 context_lost_reason_ = convertReason( 390 command_buffer_->GetLastState().context_lost_reason); 391 if (context_lost_callback_) { 392 context_lost_callback_->onContextLost(); 393 } 394 395 share_group_->RemoveAllContexts(); 396 397 DCHECK(host_.get()); 398 { 399 base::AutoLock lock(g_default_share_groups_lock.Get()); 400 g_default_share_groups.Get().erase(host_.get()); 401 } 402} 403 404} // namespace content 405