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/transfer_buffer.h" 27 #include "gpu/command_buffer/common/command_buffer.h" 28 #include "gpu/command_buffer/common/constants.h" 29 #include "ui/gfx/size.h" 30 #include "ui/gl/gl_image.h" 31 32 #if defined(OS_ANDROID) 33 #include "ui/gl/android/surface_texture.h" 34 #endif 35 36 namespace gpu { 37 38 namespace { 39 40 const int32 kCommandBufferSize = 1024 * 1024; 41 // TODO(kbr): make the transfer buffer size configurable via context 42 // creation attributes. 43 const size_t kStartTransferBufferSize = 4 * 1024 * 1024; 44 const size_t kMinTransferBufferSize = 1 * 256 * 1024; 45 const size_t kMaxTransferBufferSize = 16 * 1024 * 1024; 46 47 class GLInProcessContextImpl 48 : public GLInProcessContext, 49 public base::SupportsWeakPtr<GLInProcessContextImpl> { 50 public: 51 explicit GLInProcessContextImpl(); 52 virtual ~GLInProcessContextImpl(); 53 54 bool Initialize( 55 scoped_refptr<gfx::GLSurface> surface, 56 bool is_offscreen, 57 bool use_global_share_group, 58 GLInProcessContext* share_context, 59 gfx::AcceleratedWidget window, 60 const gfx::Size& size, 61 const GLInProcessContextAttribs& attribs, 62 gfx::GpuPreference gpu_preference, 63 const scoped_refptr<InProcessCommandBuffer::Service>& service); 64 65 // GLInProcessContext implementation: 66 virtual void SetContextLostCallback(const base::Closure& callback) OVERRIDE; 67 virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE; 68 69 #if defined(OS_ANDROID) 70 virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture( 71 uint32 stream_id) OVERRIDE; 72 #endif 73 74 private: 75 void Destroy(); 76 void OnContextLost(); 77 void OnSignalSyncPoint(const base::Closure& callback); 78 79 scoped_ptr<gles2::GLES2CmdHelper> gles2_helper_; 80 scoped_ptr<TransferBuffer> transfer_buffer_; 81 scoped_ptr<gles2::GLES2Implementation> gles2_implementation_; 82 scoped_ptr<InProcessCommandBuffer> command_buffer_; 83 84 bool context_lost_; 85 base::Closure context_lost_callback_; 86 87 DISALLOW_COPY_AND_ASSIGN(GLInProcessContextImpl); 88 }; 89 90 base::LazyInstance<base::Lock> g_all_shared_contexts_lock = 91 LAZY_INSTANCE_INITIALIZER; 92 base::LazyInstance<std::set<GLInProcessContextImpl*> > g_all_shared_contexts = 93 LAZY_INSTANCE_INITIALIZER; 94 95 GLInProcessContextImpl::GLInProcessContextImpl() 96 : context_lost_(false) {} 97 98 GLInProcessContextImpl::~GLInProcessContextImpl() { 99 { 100 base::AutoLock lock(g_all_shared_contexts_lock.Get()); 101 g_all_shared_contexts.Get().erase(this); 102 } 103 Destroy(); 104 } 105 106 gles2::GLES2Implementation* GLInProcessContextImpl::GetImplementation() { 107 return gles2_implementation_.get(); 108 } 109 110 void GLInProcessContextImpl::SetContextLostCallback( 111 const base::Closure& callback) { 112 context_lost_callback_ = callback; 113 } 114 115 void GLInProcessContextImpl::OnContextLost() { 116 context_lost_ = true; 117 if (!context_lost_callback_.is_null()) { 118 context_lost_callback_.Run(); 119 } 120 } 121 122 bool GLInProcessContextImpl::Initialize( 123 scoped_refptr<gfx::GLSurface> surface, 124 bool is_offscreen, 125 bool use_global_share_group, 126 GLInProcessContext* share_context, 127 gfx::AcceleratedWidget window, 128 const gfx::Size& size, 129 const GLInProcessContextAttribs& attribs, 130 gfx::GpuPreference gpu_preference, 131 const scoped_refptr<InProcessCommandBuffer::Service>& service) { 132 DCHECK(!use_global_share_group || !share_context); 133 DCHECK(size.width() >= 0 && size.height() >= 0); 134 135 // Changes to these values should also be copied to 136 // gpu/command_buffer/client/gl_in_process_context.cc and to 137 // content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h 138 const int32 ALPHA_SIZE = 0x3021; 139 const int32 BLUE_SIZE = 0x3022; 140 const int32 GREEN_SIZE = 0x3023; 141 const int32 RED_SIZE = 0x3024; 142 const int32 DEPTH_SIZE = 0x3025; 143 const int32 STENCIL_SIZE = 0x3026; 144 const int32 SAMPLES = 0x3031; 145 const int32 SAMPLE_BUFFERS = 0x3032; 146 const int32 NONE = 0x3038; 147 148 // Chromium-specific attributes 149 const int32 FAIL_IF_MAJOR_PERF_CAVEAT = 0x10002; 150 151 std::vector<int32> attrib_vector; 152 if (attribs.alpha_size >= 0) { 153 attrib_vector.push_back(ALPHA_SIZE); 154 attrib_vector.push_back(attribs.alpha_size); 155 } 156 if (attribs.blue_size >= 0) { 157 attrib_vector.push_back(BLUE_SIZE); 158 attrib_vector.push_back(attribs.blue_size); 159 } 160 if (attribs.green_size >= 0) { 161 attrib_vector.push_back(GREEN_SIZE); 162 attrib_vector.push_back(attribs.green_size); 163 } 164 if (attribs.red_size >= 0) { 165 attrib_vector.push_back(RED_SIZE); 166 attrib_vector.push_back(attribs.red_size); 167 } 168 if (attribs.depth_size >= 0) { 169 attrib_vector.push_back(DEPTH_SIZE); 170 attrib_vector.push_back(attribs.depth_size); 171 } 172 if (attribs.stencil_size >= 0) { 173 attrib_vector.push_back(STENCIL_SIZE); 174 attrib_vector.push_back(attribs.stencil_size); 175 } 176 if (attribs.samples >= 0) { 177 attrib_vector.push_back(SAMPLES); 178 attrib_vector.push_back(attribs.samples); 179 } 180 if (attribs.sample_buffers >= 0) { 181 attrib_vector.push_back(SAMPLE_BUFFERS); 182 attrib_vector.push_back(attribs.sample_buffers); 183 } 184 if (attribs.fail_if_major_perf_caveat > 0) { 185 attrib_vector.push_back(FAIL_IF_MAJOR_PERF_CAVEAT); 186 attrib_vector.push_back(attribs.fail_if_major_perf_caveat); 187 } 188 attrib_vector.push_back(NONE); 189 190 base::Closure wrapped_callback = 191 base::Bind(&GLInProcessContextImpl::OnContextLost, AsWeakPtr()); 192 command_buffer_.reset(new InProcessCommandBuffer(service)); 193 194 scoped_ptr<base::AutoLock> scoped_shared_context_lock; 195 scoped_refptr<gles2::ShareGroup> share_group; 196 InProcessCommandBuffer* share_command_buffer = NULL; 197 if (use_global_share_group) { 198 scoped_shared_context_lock.reset( 199 new base::AutoLock(g_all_shared_contexts_lock.Get())); 200 for (std::set<GLInProcessContextImpl*>::const_iterator it = 201 g_all_shared_contexts.Get().begin(); 202 it != g_all_shared_contexts.Get().end(); 203 it++) { 204 const GLInProcessContextImpl* context = *it; 205 if (!context->context_lost_) { 206 share_group = context->gles2_implementation_->share_group(); 207 share_command_buffer = context->command_buffer_.get(); 208 DCHECK(share_group); 209 DCHECK(share_command_buffer); 210 break; 211 } 212 } 213 } else if (share_context) { 214 GLInProcessContextImpl* impl = 215 static_cast<GLInProcessContextImpl*>(share_context); 216 share_group = impl->gles2_implementation_->share_group(); 217 share_command_buffer = impl->command_buffer_.get(); 218 DCHECK(share_group); 219 DCHECK(share_command_buffer); 220 } 221 222 if (!command_buffer_->Initialize(surface, 223 is_offscreen, 224 window, 225 size, 226 attrib_vector, 227 gpu_preference, 228 wrapped_callback, 229 share_command_buffer)) { 230 LOG(ERROR) << "Failed to initialize InProcessCommmandBuffer"; 231 return false; 232 } 233 234 // Create the GLES2 helper, which writes the command buffer protocol. 235 gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get())); 236 if (!gles2_helper_->Initialize(kCommandBufferSize)) { 237 LOG(ERROR) << "Failed to initialize GLES2CmdHelper"; 238 Destroy(); 239 return false; 240 } 241 242 // Create a transfer buffer. 243 transfer_buffer_.reset(new TransferBuffer(gles2_helper_.get())); 244 245 bool bind_generates_resources = false; 246 bool free_everything_when_invisible = false; 247 248 // Create the object exposing the OpenGL API. 249 gles2_implementation_.reset(new gles2::GLES2Implementation( 250 gles2_helper_.get(), 251 share_group, 252 transfer_buffer_.get(), 253 bind_generates_resources, 254 free_everything_when_invisible, 255 command_buffer_.get())); 256 257 if (use_global_share_group) { 258 g_all_shared_contexts.Get().insert(this); 259 scoped_shared_context_lock.reset(); 260 } 261 262 if (!gles2_implementation_->Initialize( 263 kStartTransferBufferSize, 264 kMinTransferBufferSize, 265 kMaxTransferBufferSize, 266 gles2::GLES2Implementation::kNoLimit)) { 267 return false; 268 } 269 270 return true; 271 } 272 273 void GLInProcessContextImpl::Destroy() { 274 if (gles2_implementation_) { 275 // First flush the context to ensure that any pending frees of resources 276 // are completed. Otherwise, if this context is part of a share group, 277 // those resources might leak. Also, any remaining side effects of commands 278 // issued on this context might not be visible to other contexts in the 279 // share group. 280 gles2_implementation_->Flush(); 281 282 gles2_implementation_.reset(); 283 } 284 285 transfer_buffer_.reset(); 286 gles2_helper_.reset(); 287 command_buffer_.reset(); 288 } 289 290 #if defined(OS_ANDROID) 291 scoped_refptr<gfx::SurfaceTexture> 292 GLInProcessContextImpl::GetSurfaceTexture(uint32 stream_id) { 293 return command_buffer_->GetSurfaceTexture(stream_id); 294 } 295 #endif 296 297 } // anonymous namespace 298 299 GLInProcessContextAttribs::GLInProcessContextAttribs() 300 : alpha_size(-1), 301 blue_size(-1), 302 green_size(-1), 303 red_size(-1), 304 depth_size(-1), 305 stencil_size(-1), 306 samples(-1), 307 sample_buffers(-1) {} 308 309 // static 310 GLInProcessContext* GLInProcessContext::CreateContext( 311 bool is_offscreen, 312 gfx::AcceleratedWidget window, 313 const gfx::Size& size, 314 bool share_resources, 315 const GLInProcessContextAttribs& attribs, 316 gfx::GpuPreference gpu_preference) { 317 scoped_ptr<GLInProcessContextImpl> context( 318 new GLInProcessContextImpl()); 319 if (!context->Initialize( 320 NULL /* surface */, 321 is_offscreen, 322 share_resources, 323 NULL, 324 window, 325 size, 326 attribs, 327 gpu_preference, 328 scoped_refptr<InProcessCommandBuffer::Service>())) 329 return NULL; 330 331 return context.release(); 332 } 333 334 // static 335 GLInProcessContext* GLInProcessContext::CreateWithSurface( 336 scoped_refptr<gfx::GLSurface> surface, 337 scoped_refptr<gpu::InProcessCommandBuffer::Service> service, 338 GLInProcessContext* share_context, 339 const GLInProcessContextAttribs& attribs, 340 gfx::GpuPreference gpu_preference) { 341 scoped_ptr<GLInProcessContextImpl> context( 342 new GLInProcessContextImpl()); 343 if (!context->Initialize( 344 surface, 345 surface->IsOffscreen(), 346 false, 347 share_context, 348 gfx::kNullAcceleratedWidget, 349 surface->GetSize(), 350 attribs, 351 gpu_preference, 352 service)) 353 return NULL; 354 355 return context.release(); 356 } 357 358 } // namespace gpu 359