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 "gpu/command_buffer/client/gl_in_process_context.h" 6 7 #include <set> 8 #include <utility> 9 #include <vector> 10 11 #include <GLES2/gl2.h> 12 #ifndef GL_GLEXT_PROTOTYPES 13 #define GL_GLEXT_PROTOTYPES 1 14 #endif 15 #include <GLES2/gl2ext.h> 16 #include <GLES2/gl2extchromium.h> 17 18 #include "base/bind.h" 19 #include "base/bind_helpers.h" 20 #include "base/lazy_instance.h" 21 #include "base/logging.h" 22 #include "base/memory/scoped_ptr.h" 23 #include "base/memory/weak_ptr.h" 24 #include "base/message_loop/message_loop.h" 25 #include "gpu/command_buffer/client/gles2_implementation.h" 26 #include "gpu/command_buffer/client/gpu_memory_buffer_factory.h" 27 #include "gpu/command_buffer/client/image_factory.h" 28 #include "gpu/command_buffer/client/transfer_buffer.h" 29 #include "gpu/command_buffer/common/command_buffer.h" 30 #include "gpu/command_buffer/common/constants.h" 31 #include "gpu/command_buffer/service/in_process_command_buffer.h" 32 #include "ui/gfx/size.h" 33 #include "ui/gl/gl_image.h" 34 35 #if defined(OS_ANDROID) 36 #include "ui/gl/android/surface_texture_bridge.h" 37 #endif 38 39 namespace gpu { 40 41 namespace { 42 43 const int32 kCommandBufferSize = 1024 * 1024; 44 // TODO(kbr): make the transfer buffer size configurable via context 45 // creation attributes. 46 const size_t kStartTransferBufferSize = 4 * 1024 * 1024; 47 const size_t kMinTransferBufferSize = 1 * 256 * 1024; 48 const size_t kMaxTransferBufferSize = 16 * 1024 * 1024; 49 50 static GpuMemoryBufferFactory* g_gpu_memory_buffer_factory = NULL; 51 52 class GLInProcessContextImpl 53 : public GLInProcessContext, 54 public gles2::ImageFactory, 55 public base::SupportsWeakPtr<GLInProcessContextImpl> { 56 public: 57 explicit GLInProcessContextImpl(); 58 virtual ~GLInProcessContextImpl(); 59 60 bool Initialize(scoped_refptr<gfx::GLSurface> surface, 61 bool is_offscreen, 62 bool share_resources, 63 gfx::AcceleratedWidget window, 64 const gfx::Size& size, 65 const char* allowed_extensions, 66 const GLInProcessContextAttribs& attribs, 67 gfx::GpuPreference gpu_preference); 68 69 // GLInProcessContext implementation: 70 virtual void SetContextLostCallback(const base::Closure& callback) OVERRIDE; 71 virtual void SignalSyncPoint(unsigned sync_point, 72 const base::Closure& callback) OVERRIDE; 73 virtual void SignalQuery(unsigned query, const base::Closure& callback) 74 OVERRIDE; 75 virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE; 76 77 // ImageFactory implementation: 78 virtual scoped_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer( 79 int width, int height, GLenum internalformat, 80 unsigned* image_id) OVERRIDE; 81 virtual void DeleteGpuMemoryBuffer(unsigned image_id) OVERRIDE; 82 83 #if defined(OS_ANDROID) 84 virtual scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture( 85 uint32 stream_id) OVERRIDE; 86 #endif 87 88 private: 89 void Destroy(); 90 void PollQueryCallbacks(); 91 void CallQueryCallback(size_t index); 92 void OnContextLost(); 93 void OnSignalSyncPoint(const base::Closure& callback); 94 95 scoped_ptr<gles2::GLES2CmdHelper> gles2_helper_; 96 scoped_ptr<TransferBuffer> transfer_buffer_; 97 scoped_ptr<gles2::GLES2Implementation> gles2_implementation_; 98 scoped_ptr<InProcessCommandBuffer> command_buffer_; 99 100 typedef std::pair<unsigned, base::Closure> QueryCallback; 101 std::vector<QueryCallback> query_callbacks_; 102 103 unsigned int share_group_id_; 104 bool context_lost_; 105 base::Closure context_lost_callback_; 106 107 DISALLOW_COPY_AND_ASSIGN(GLInProcessContextImpl); 108 }; 109 110 base::LazyInstance<base::Lock> g_all_shared_contexts_lock = 111 LAZY_INSTANCE_INITIALIZER; 112 base::LazyInstance<std::set<GLInProcessContextImpl*> > g_all_shared_contexts = 113 LAZY_INSTANCE_INITIALIZER; 114 115 size_t SharedContextCount() { 116 base::AutoLock lock(g_all_shared_contexts_lock.Get()); 117 return g_all_shared_contexts.Get().size(); 118 } 119 120 scoped_ptr<gfx::GpuMemoryBuffer> GLInProcessContextImpl::CreateGpuMemoryBuffer( 121 int width, int height, GLenum internalformat, unsigned int* image_id) { 122 scoped_ptr<gfx::GpuMemoryBuffer> buffer( 123 g_gpu_memory_buffer_factory->CreateGpuMemoryBuffer(width, 124 height, 125 internalformat)); 126 if (!buffer) 127 return scoped_ptr<gfx::GpuMemoryBuffer>(); 128 129 *image_id = command_buffer_->CreateImageForGpuMemoryBuffer( 130 buffer->GetHandle(), gfx::Size(width, height)); 131 return buffer.Pass(); 132 } 133 134 void GLInProcessContextImpl::DeleteGpuMemoryBuffer(unsigned int image_id) { 135 command_buffer_->RemoveImage(image_id); 136 } 137 138 GLInProcessContextImpl::GLInProcessContextImpl() 139 : share_group_id_(0), context_lost_(false) {} 140 141 GLInProcessContextImpl::~GLInProcessContextImpl() { 142 { 143 base::AutoLock lock(g_all_shared_contexts_lock.Get()); 144 g_all_shared_contexts.Get().erase(this); 145 } 146 Destroy(); 147 } 148 149 void GLInProcessContextImpl::SignalSyncPoint(unsigned sync_point, 150 const base::Closure& callback) { 151 DCHECK(!callback.is_null()); 152 base::Closure wrapped_callback = base::Bind( 153 &GLInProcessContextImpl::OnSignalSyncPoint, AsWeakPtr(), callback); 154 command_buffer_->SignalSyncPoint(sync_point, wrapped_callback); 155 } 156 157 gles2::GLES2Implementation* GLInProcessContextImpl::GetImplementation() { 158 return gles2_implementation_.get(); 159 } 160 161 void GLInProcessContextImpl::SetContextLostCallback( 162 const base::Closure& callback) { 163 context_lost_callback_ = callback; 164 } 165 166 void GLInProcessContextImpl::OnContextLost() { 167 context_lost_ = true; 168 if (!context_lost_callback_.is_null()) { 169 context_lost_callback_.Run(); 170 } 171 } 172 173 void GLInProcessContextImpl::OnSignalSyncPoint(const base::Closure& callback) { 174 // TODO: Should it always trigger callbacks? 175 if (!context_lost_) 176 callback.Run(); 177 } 178 179 bool GLInProcessContextImpl::Initialize( 180 scoped_refptr<gfx::GLSurface> surface, 181 bool is_offscreen, 182 bool share_resources, 183 gfx::AcceleratedWidget window, 184 const gfx::Size& size, 185 const char* allowed_extensions, 186 const GLInProcessContextAttribs& attribs, 187 gfx::GpuPreference gpu_preference) { 188 DCHECK(size.width() >= 0 && size.height() >= 0); 189 190 const int32 ALPHA_SIZE = 0x3021; 191 const int32 BLUE_SIZE = 0x3022; 192 const int32 GREEN_SIZE = 0x3023; 193 const int32 RED_SIZE = 0x3024; 194 const int32 DEPTH_SIZE = 0x3025; 195 const int32 STENCIL_SIZE = 0x3026; 196 const int32 SAMPLES = 0x3031; 197 const int32 SAMPLE_BUFFERS = 0x3032; 198 const int32 NONE = 0x3038; 199 200 std::vector<int32> attrib_vector; 201 if (attribs.alpha_size >= 0) { 202 attrib_vector.push_back(ALPHA_SIZE); 203 attrib_vector.push_back(attribs.alpha_size); 204 } 205 if (attribs.blue_size >= 0) { 206 attrib_vector.push_back(BLUE_SIZE); 207 attrib_vector.push_back(attribs.blue_size); 208 } 209 if (attribs.green_size >= 0) { 210 attrib_vector.push_back(GREEN_SIZE); 211 attrib_vector.push_back(attribs.green_size); 212 } 213 if (attribs.red_size >= 0) { 214 attrib_vector.push_back(RED_SIZE); 215 attrib_vector.push_back(attribs.red_size); 216 } 217 if (attribs.depth_size >= 0) { 218 attrib_vector.push_back(DEPTH_SIZE); 219 attrib_vector.push_back(attribs.depth_size); 220 } 221 if (attribs.stencil_size >= 0) { 222 attrib_vector.push_back(STENCIL_SIZE); 223 attrib_vector.push_back(attribs.stencil_size); 224 } 225 if (attribs.samples >= 0) { 226 attrib_vector.push_back(SAMPLES); 227 attrib_vector.push_back(attribs.samples); 228 } 229 if (attribs.sample_buffers >= 0) { 230 attrib_vector.push_back(SAMPLE_BUFFERS); 231 attrib_vector.push_back(attribs.sample_buffers); 232 } 233 attrib_vector.push_back(NONE); 234 235 base::Closure wrapped_callback = 236 base::Bind(&GLInProcessContextImpl::OnContextLost, AsWeakPtr()); 237 command_buffer_.reset(new InProcessCommandBuffer()); 238 239 scoped_ptr<base::AutoLock> scoped_shared_context_lock; 240 scoped_refptr<gles2::ShareGroup> share_group; 241 if (share_resources) { 242 scoped_shared_context_lock.reset( 243 new base::AutoLock(g_all_shared_contexts_lock.Get())); 244 for (std::set<GLInProcessContextImpl*>::const_iterator it = 245 g_all_shared_contexts.Get().begin(); 246 it != g_all_shared_contexts.Get().end(); 247 it++) { 248 const GLInProcessContextImpl* context = *it; 249 if (!context->context_lost_) { 250 share_group = context->gles2_implementation_->share_group(); 251 DCHECK(share_group); 252 share_group_id_ = context->share_group_id_; 253 break; 254 } 255 share_group_id_ = std::max(share_group_id_, context->share_group_id_); 256 } 257 if (!share_group && !++share_group_id_) 258 ++share_group_id_; 259 } 260 if (!command_buffer_->Initialize(surface, 261 is_offscreen, 262 share_resources, 263 window, 264 size, 265 allowed_extensions, 266 attrib_vector, 267 gpu_preference, 268 wrapped_callback, 269 share_group_id_)) { 270 LOG(INFO) << "Failed to initialize InProcessCommmandBuffer"; 271 return false; 272 } 273 274 // Create the GLES2 helper, which writes the command buffer protocol. 275 gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get())); 276 if (!gles2_helper_->Initialize(kCommandBufferSize)) { 277 LOG(INFO) << "Failed to initialize GLES2CmdHelper"; 278 Destroy(); 279 return false; 280 } 281 282 // Create a transfer buffer. 283 transfer_buffer_.reset(new TransferBuffer(gles2_helper_.get())); 284 285 // Create the object exposing the OpenGL API. 286 gles2_implementation_.reset(new gles2::GLES2Implementation( 287 gles2_helper_.get(), 288 share_group, 289 transfer_buffer_.get(), 290 false, 291 this)); 292 293 if (share_resources) { 294 g_all_shared_contexts.Get().insert(this); 295 scoped_shared_context_lock.reset(); 296 } 297 298 if (!gles2_implementation_->Initialize( 299 kStartTransferBufferSize, 300 kMinTransferBufferSize, 301 kMaxTransferBufferSize)) { 302 return false; 303 } 304 305 return true; 306 } 307 308 void GLInProcessContextImpl::Destroy() { 309 while (!query_callbacks_.empty()) { 310 CallQueryCallback(0); 311 } 312 313 if (gles2_implementation_) { 314 // First flush the context to ensure that any pending frees of resources 315 // are completed. Otherwise, if this context is part of a share group, 316 // those resources might leak. Also, any remaining side effects of commands 317 // issued on this context might not be visible to other contexts in the 318 // share group. 319 gles2_implementation_->Flush(); 320 321 gles2_implementation_.reset(); 322 } 323 324 transfer_buffer_.reset(); 325 gles2_helper_.reset(); 326 command_buffer_.reset(); 327 } 328 329 void GLInProcessContextImpl::CallQueryCallback(size_t index) { 330 DCHECK_LT(index, query_callbacks_.size()); 331 QueryCallback query_callback = query_callbacks_[index]; 332 query_callbacks_[index] = query_callbacks_.back(); 333 query_callbacks_.pop_back(); 334 query_callback.second.Run(); 335 } 336 337 // TODO(sievers): Move this to the service side 338 void GLInProcessContextImpl::PollQueryCallbacks() { 339 for (size_t i = 0; i < query_callbacks_.size();) { 340 unsigned query = query_callbacks_[i].first; 341 GLuint param = 0; 342 gles2::GLES2Implementation* gl = GetImplementation(); 343 if (gl->IsQueryEXT(query)) { 344 gl->GetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, ¶m); 345 } else { 346 param = 1; 347 } 348 if (param) { 349 CallQueryCallback(i); 350 } else { 351 i++; 352 } 353 } 354 if (!query_callbacks_.empty()) { 355 base::MessageLoop::current()->PostDelayedTask( 356 FROM_HERE, 357 base::Bind(&GLInProcessContextImpl::PollQueryCallbacks, 358 this->AsWeakPtr()), 359 base::TimeDelta::FromMilliseconds(5)); 360 } 361 } 362 363 void GLInProcessContextImpl::SignalQuery( 364 unsigned query, 365 const base::Closure& callback) { 366 query_callbacks_.push_back(std::make_pair(query, callback)); 367 // If size > 1, there is already a poll callback pending. 368 if (query_callbacks_.size() == 1) { 369 PollQueryCallbacks(); 370 } 371 } 372 373 #if defined(OS_ANDROID) 374 scoped_refptr<gfx::SurfaceTextureBridge> 375 GLInProcessContextImpl::GetSurfaceTexture(uint32 stream_id) { 376 return command_buffer_->GetSurfaceTexture(stream_id); 377 } 378 #endif 379 380 } // anonymous namespace 381 382 GLInProcessContextAttribs::GLInProcessContextAttribs() 383 : alpha_size(-1), 384 blue_size(-1), 385 green_size(-1), 386 red_size(-1), 387 depth_size(-1), 388 stencil_size(-1), 389 samples(-1), 390 sample_buffers(-1) {} 391 392 // static 393 GLInProcessContext* GLInProcessContext::CreateContext( 394 bool is_offscreen, 395 gfx::AcceleratedWidget window, 396 const gfx::Size& size, 397 bool share_resources, 398 const char* allowed_extensions, 399 const GLInProcessContextAttribs& attribs, 400 gfx::GpuPreference gpu_preference) { 401 scoped_ptr<GLInProcessContextImpl> context( 402 new GLInProcessContextImpl()); 403 if (!context->Initialize( 404 NULL /* surface */, 405 is_offscreen, 406 share_resources, 407 window, 408 size, 409 allowed_extensions, 410 attribs, 411 gpu_preference)) 412 return NULL; 413 414 return context.release(); 415 } 416 417 // static 418 GLInProcessContext* GLInProcessContext::CreateWithSurface( 419 scoped_refptr<gfx::GLSurface> surface, 420 bool share_resources, 421 const char* allowed_extensions, 422 const GLInProcessContextAttribs& attribs, 423 gfx::GpuPreference gpu_preference) { 424 scoped_ptr<GLInProcessContextImpl> context( 425 new GLInProcessContextImpl()); 426 if (!context->Initialize( 427 surface, 428 surface->IsOffscreen(), 429 share_resources, 430 gfx::kNullAcceleratedWidget, 431 surface->GetSize(), 432 allowed_extensions, 433 attribs, 434 gpu_preference)) 435 return NULL; 436 437 return context.release(); 438 } 439 440 // static 441 void GLInProcessContext::SetGpuMemoryBufferFactory( 442 GpuMemoryBufferFactory* factory) { 443 DCHECK_EQ(0u, SharedContextCount()); 444 g_gpu_memory_buffer_factory = factory; 445 } 446 447 } // namespace gpu 448