gl_surface_glx.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 5extern "C" { 6#include <X11/Xlib.h> 7} 8 9#include "ui/gl/gl_surface_glx.h" 10 11#include "base/basictypes.h" 12#include "base/debug/trace_event.h" 13#include "base/logging.h" 14#include "base/memory/scoped_ptr.h" 15#include "base/memory/weak_ptr.h" 16#include "base/message_loop.h" 17#include "base/process_util.h" 18#include "base/synchronization/cancellation_flag.h" 19#include "base/synchronization/lock.h" 20#include "base/threading/non_thread_safe.h" 21#include "base/threading/thread.h" 22#include "base/time.h" 23#include "third_party/mesa/MesaLib/include/GL/osmesa.h" 24#include "ui/base/x/x11_util.h" 25#include "ui/gl/gl_bindings.h" 26#include "ui/gl/gl_implementation.h" 27#include "ui/gl/vsync_provider.h" 28 29namespace gfx { 30 31namespace { 32 33// scoped_ptr functor for XFree(). Use as follows: 34// scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> foo(...); 35// where "XVisualInfo" is any X type that is freed with XFree. 36class ScopedPtrXFree { 37 public: 38 void operator()(void* x) const { 39 ::XFree(x); 40 } 41}; 42 43Display* g_display; 44const char* g_glx_extensions = NULL; 45bool g_glx_context_create = false; 46bool g_glx_create_context_robustness_supported = false; 47bool g_glx_texture_from_pixmap_supported = false; 48bool g_glx_oml_sync_control_supported = false; 49 50// Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a 51// whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML 52// always fails even though GLX_OML_sync_control is reported as being supported. 53bool g_glx_get_msc_rate_oml_supported = false; 54 55bool g_glx_sgi_video_sync_supported = false; 56 57class OMLSyncControlVSyncProvider 58 : public gfx::SyncControlVSyncProvider { 59 public: 60 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window) 61 : SyncControlVSyncProvider(), 62 window_(window) { 63 } 64 65 virtual ~OMLSyncControlVSyncProvider() { } 66 67 protected: 68 virtual bool GetSyncValues(int64* system_time, 69 int64* media_stream_counter, 70 int64* swap_buffer_counter) OVERRIDE { 71 return glXGetSyncValuesOML(g_display, window_, system_time, 72 media_stream_counter, swap_buffer_counter); 73 } 74 75 virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE { 76 if (!g_glx_get_msc_rate_oml_supported) 77 return false; 78 79 if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) { 80 // Once glXGetMscRateOML has been found to fail, don't try again, 81 // since each failing call may spew an error message. 82 g_glx_get_msc_rate_oml_supported = false; 83 return false; 84 } 85 86 return true; 87 } 88 89 private: 90 XID window_; 91 92 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider); 93}; 94 95class SGIVideoSyncThread 96 : public base::Thread, 97 public base::NonThreadSafe, 98 public base::RefCounted<SGIVideoSyncThread> { 99 public: 100 static scoped_refptr<SGIVideoSyncThread> Create() { 101 if (!g_video_sync_thread) { 102 g_video_sync_thread = new SGIVideoSyncThread(); 103 g_video_sync_thread->Start(); 104 } 105 return g_video_sync_thread; 106 } 107 108 private: 109 friend class base::RefCounted<SGIVideoSyncThread>; 110 111 SGIVideoSyncThread() : base::Thread("SGI_video_sync") { 112 DCHECK(CalledOnValidThread()); 113 } 114 115 virtual ~SGIVideoSyncThread() { 116 DCHECK(CalledOnValidThread()); 117 g_video_sync_thread = NULL; 118 Stop(); 119 } 120 121 static SGIVideoSyncThread* g_video_sync_thread; 122 123 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread); 124}; 125 126class SGIVideoSyncProviderThreadShim 127 : public base::SupportsWeakPtr<SGIVideoSyncProviderThreadShim> { 128 public: 129 explicit SGIVideoSyncProviderThreadShim(XID window) 130 : window_(window), 131 context_(NULL), 132 message_loop_(base::MessageLoopProxy::current()), 133 cancel_vsync_flag_(), 134 vsync_lock_() { 135 // This ensures that creation of |window_| has occured when this shim 136 // is executing in the same process as the call to create |window_|. 137 XSync(g_display, False); 138 } 139 140 base::CancellationFlag* cancel_vsync_flag() { 141 return &cancel_vsync_flag_; 142 } 143 144 base::Lock* vsync_lock() { 145 return &vsync_lock_; 146 } 147 148 void Initialize() { 149 DCHECK(display_); 150 151 XWindowAttributes attributes; 152 if (!XGetWindowAttributes(display_, window_, &attributes)) { 153 LOG(ERROR) << "XGetWindowAttributes failed for window " << 154 window_ << "."; 155 return; 156 } 157 158 XVisualInfo visual_info_template; 159 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual); 160 161 int visual_info_count = 0; 162 scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list( 163 XGetVisualInfo(display_, VisualIDMask, 164 &visual_info_template, &visual_info_count)); 165 166 DCHECK(visual_info_list.get()); 167 if (visual_info_count == 0) { 168 LOG(ERROR) << "No visual info for visual ID."; 169 return; 170 } 171 172 context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True); 173 174 DCHECK(NULL != context_); 175 } 176 177 void Destroy() { 178 if (context_) { 179 glXDestroyContext(display_, context_); 180 context_ = NULL; 181 } 182 delete this; 183 } 184 185 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) { 186 base::TimeTicks now; 187 { 188 // Don't allow |window_| destruction while we're probing vsync. 189 base::AutoLock locked(vsync_lock_); 190 191 if (!context_ || cancel_vsync_flag_.IsSet()) 192 return; 193 194 glXMakeCurrent(display_, window_, context_); 195 196 unsigned int retrace_count = 0; 197 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0) 198 return; 199 200 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD); 201 now = base::TimeTicks::HighResNow(); 202 203 glXMakeCurrent(display_, 0, 0); 204 } 205 206 const base::TimeDelta kDefaultInterval = 207 base::TimeDelta::FromSeconds(1) / 60; 208 209 message_loop_->PostTask( 210 FROM_HERE, base::Bind(callback, now, kDefaultInterval)); 211 } 212 213 private: 214 // For initialization of display_ in GLSurface::InitializeOneOff before 215 // the sandbox goes up. 216 friend class gfx::GLSurfaceGLX; 217 218 virtual ~SGIVideoSyncProviderThreadShim() { 219 } 220 221 static Display* display_; 222 223 XID window_; 224 GLXContext context_; 225 226 scoped_refptr<base::MessageLoopProxy> message_loop_; 227 228 base::CancellationFlag cancel_vsync_flag_; 229 base::Lock vsync_lock_; 230 231 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim); 232}; 233 234class SGIVideoSyncVSyncProvider 235 : public gfx::VSyncProvider, 236 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> { 237 public: 238 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window) 239 : vsync_thread_(SGIVideoSyncThread::Create()), 240 shim_((new SGIVideoSyncProviderThreadShim(window))->AsWeakPtr()), 241 cancel_vsync_flag_(shim_->cancel_vsync_flag()), 242 vsync_lock_(shim_->vsync_lock()) { 243 // The WeakPtr is bound to the SGIVideoSyncThread. We only use it for 244 // PostTask. 245 shim_->DetachFromThread(); 246 vsync_thread_->message_loop()->PostTask( 247 FROM_HERE, 248 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize, shim_)); 249 } 250 251 virtual ~SGIVideoSyncVSyncProvider() { 252 { 253 base::AutoLock locked(*vsync_lock_); 254 cancel_vsync_flag_->Set(); 255 } 256 vsync_thread_->message_loop()->PostTask( 257 FROM_HERE, 258 base::Bind(&SGIVideoSyncProviderThreadShim::Destroy, shim_)); 259 } 260 261 virtual void GetVSyncParameters( 262 const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE { 263 // Only one outstanding request per surface. 264 if (!pending_callback_) { 265 pending_callback_.reset( 266 new VSyncProvider::UpdateVSyncCallback(callback)); 267 vsync_thread_->message_loop()->PostTask( 268 FROM_HERE, 269 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters, 270 shim_, base::Bind( 271 &SGIVideoSyncVSyncProvider::PendingCallbackRunner, 272 AsWeakPtr()))); 273 } 274 } 275 276 private: 277 void PendingCallbackRunner(const base::TimeTicks timebase, 278 const base::TimeDelta interval) { 279 DCHECK(pending_callback_); 280 pending_callback_->Run(timebase, interval); 281 pending_callback_.reset(); 282 } 283 284 scoped_refptr<SGIVideoSyncThread> vsync_thread_; 285 base::WeakPtr<SGIVideoSyncProviderThreadShim> shim_; 286 287 scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_; 288 289 // Raw pointers to sync primitives owned by the shim_. 290 // These will only be referenced before we post a task to destroy 291 // the shim_, so they are safe to access. 292 base::CancellationFlag* cancel_vsync_flag_; 293 base::Lock* vsync_lock_; 294 295 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider); 296}; 297 298SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL; 299 300// In order to take advantage of GLX_SGI_video_sync, we need a display 301// for use on a separate thread. We must allocate this before the sandbox 302// goes up (rather than on-demand when we start the thread). 303Display* SGIVideoSyncProviderThreadShim::display_ = NULL; 304 305} // namespace 306 307GLSurfaceGLX::GLSurfaceGLX() {} 308 309bool GLSurfaceGLX::InitializeOneOff() { 310 static bool initialized = false; 311 if (initialized) 312 return true; 313 314 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on 315 // it's own thread. 316 XInitThreads(); 317 318 g_display = base::MessagePumpForUI::GetDefaultXDisplay(); 319 if (!g_display) { 320 LOG(ERROR) << "XOpenDisplay failed."; 321 return false; 322 } 323 324 int major, minor; 325 if (!glXQueryVersion(g_display, &major, &minor)) { 326 LOG(ERROR) << "glxQueryVersion failed"; 327 return false; 328 } 329 330 if (major == 1 && minor < 3) { 331 LOG(ERROR) << "GLX 1.3 or later is required."; 332 return false; 333 } 334 335 g_glx_extensions = glXQueryExtensionsString(g_display, 0); 336 g_glx_context_create = 337 HasGLXExtension("GLX_ARB_create_context"); 338 g_glx_create_context_robustness_supported = 339 HasGLXExtension("GLX_ARB_create_context_robustness"); 340 g_glx_texture_from_pixmap_supported = 341 HasGLXExtension("GLX_EXT_texture_from_pixmap"); 342 g_glx_oml_sync_control_supported = 343 HasGLXExtension("GLX_OML_sync_control"); 344 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported; 345 g_glx_sgi_video_sync_supported = 346 HasGLXExtension("GLX_SGI_video_sync"); 347 348 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported) 349 SGIVideoSyncProviderThreadShim::display_ = XOpenDisplay(NULL); 350 351 initialized = true; 352 return true; 353} 354 355// static 356const char* GLSurfaceGLX::GetGLXExtensions() { 357 return g_glx_extensions; 358} 359 360// static 361bool GLSurfaceGLX::HasGLXExtension(const char* name) { 362 return ExtensionsContain(GetGLXExtensions(), name); 363} 364 365// static 366bool GLSurfaceGLX::IsCreateContextSupported() { 367 return g_glx_context_create; 368} 369 370// static 371bool GLSurfaceGLX::IsCreateContextRobustnessSupported() { 372 return g_glx_create_context_robustness_supported; 373} 374 375// static 376bool GLSurfaceGLX::IsTextureFromPixmapSupported() { 377 return g_glx_texture_from_pixmap_supported; 378} 379 380// static 381bool GLSurfaceGLX::IsOMLSyncControlSupported() { 382 return g_glx_oml_sync_control_supported; 383} 384 385void* GLSurfaceGLX::GetDisplay() { 386 return g_display; 387} 388 389GLSurfaceGLX::~GLSurfaceGLX() {} 390 391NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window) 392 : window_(window), 393 config_(NULL) { 394} 395 396bool NativeViewGLSurfaceGLX::Initialize() { 397 XWindowAttributes attributes; 398 if (!XGetWindowAttributes(g_display, window_, &attributes)) { 399 LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << "."; 400 return false; 401 } 402 size_ = gfx::Size(attributes.width, attributes.height); 403 404 if (g_glx_oml_sync_control_supported) 405 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_)); 406 else if (g_glx_sgi_video_sync_supported) 407 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_)); 408 409 return true; 410} 411 412void NativeViewGLSurfaceGLX::Destroy() { 413} 414 415bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) { 416 // On Intel drivers, the frame buffer won't be resize until the next swap. If 417 // we only do PostSubBuffer, then we're stuck in the old size. Force a swap 418 // now. 419 if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer && size_ != size) 420 SwapBuffers(); 421 size_ = size; 422 return true; 423} 424 425bool NativeViewGLSurfaceGLX::IsOffscreen() { 426 return false; 427} 428 429bool NativeViewGLSurfaceGLX::SwapBuffers() { 430 glXSwapBuffers(g_display, window_); 431 // For latency_tests.cc: 432 UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete", 433 TRACE_EVENT_SCOPE_THREAD); 434 return true; 435} 436 437gfx::Size NativeViewGLSurfaceGLX::GetSize() { 438 return size_; 439} 440 441void* NativeViewGLSurfaceGLX::GetHandle() { 442 return reinterpret_cast<void*>(window_); 443} 444 445std::string NativeViewGLSurfaceGLX::GetExtensions() { 446 std::string extensions = GLSurface::GetExtensions(); 447 if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer) { 448 extensions += extensions.empty() ? "" : " "; 449 extensions += "GL_CHROMIUM_post_sub_buffer"; 450 } 451 return extensions; 452} 453 454void* NativeViewGLSurfaceGLX::GetConfig() { 455 if (!config_) { 456 // This code path is expensive, but we only take it when 457 // attempting to use GLX_ARB_create_context_robustness, in which 458 // case we need a GLXFBConfig for the window in order to create a 459 // context for it. 460 // 461 // TODO(kbr): this is not a reliable code path. On platforms which 462 // support it, we should use glXChooseFBConfig in the browser 463 // process to choose the FBConfig and from there the X Visual to 464 // use when creating the window in the first place. Then we can 465 // pass that FBConfig down rather than attempting to reconstitute 466 // it. 467 468 XWindowAttributes attributes; 469 if (!XGetWindowAttributes( 470 g_display, 471 window_, 472 &attributes)) { 473 LOG(ERROR) << "XGetWindowAttributes failed for window " << 474 window_ << "."; 475 return NULL; 476 } 477 478 int visual_id = XVisualIDFromVisual(attributes.visual); 479 480 int num_elements = 0; 481 scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs( 482 glXGetFBConfigs(g_display, 483 DefaultScreen(g_display), 484 &num_elements)); 485 if (!configs.get()) { 486 LOG(ERROR) << "glXGetFBConfigs failed."; 487 return NULL; 488 } 489 if (!num_elements) { 490 LOG(ERROR) << "glXGetFBConfigs returned 0 elements."; 491 return NULL; 492 } 493 bool found = false; 494 int i; 495 for (i = 0; i < num_elements; ++i) { 496 int value; 497 if (glXGetFBConfigAttrib( 498 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) { 499 LOG(ERROR) << "glXGetFBConfigAttrib failed."; 500 return NULL; 501 } 502 if (value == visual_id) { 503 found = true; 504 break; 505 } 506 } 507 if (found) { 508 config_ = configs.get()[i]; 509 } 510 } 511 512 return config_; 513} 514 515bool NativeViewGLSurfaceGLX::PostSubBuffer( 516 int x, int y, int width, int height) { 517 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer); 518 glXCopySubBufferMESA(g_display, window_, x, y, width, height); 519 return true; 520} 521 522VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() { 523 return vsync_provider_.get(); 524} 525 526NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX() 527 : window_(0), 528 config_(NULL) { 529} 530 531NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() { 532 Destroy(); 533} 534 535PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size) 536 : size_(size), 537 config_(NULL), 538 pbuffer_(0) { 539} 540 541bool PbufferGLSurfaceGLX::Initialize() { 542 DCHECK(!pbuffer_); 543 544 static const int config_attributes[] = { 545 GLX_BUFFER_SIZE, 32, 546 GLX_ALPHA_SIZE, 8, 547 GLX_BLUE_SIZE, 8, 548 GLX_GREEN_SIZE, 8, 549 GLX_RED_SIZE, 8, 550 GLX_RENDER_TYPE, GLX_RGBA_BIT, 551 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, 552 GLX_DOUBLEBUFFER, False, 553 0 554 }; 555 556 int num_elements = 0; 557 scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs( 558 glXChooseFBConfig(g_display, 559 DefaultScreen(g_display), 560 config_attributes, 561 &num_elements)); 562 if (!configs.get()) { 563 LOG(ERROR) << "glXChooseFBConfig failed."; 564 return false; 565 } 566 if (!num_elements) { 567 LOG(ERROR) << "glXChooseFBConfig returned 0 elements."; 568 return false; 569 } 570 571 config_ = configs.get()[0]; 572 573 const int pbuffer_attributes[] = { 574 GLX_PBUFFER_WIDTH, size_.width(), 575 GLX_PBUFFER_HEIGHT, size_.height(), 576 0 577 }; 578 pbuffer_ = glXCreatePbuffer(g_display, 579 static_cast<GLXFBConfig>(config_), 580 pbuffer_attributes); 581 if (!pbuffer_) { 582 Destroy(); 583 LOG(ERROR) << "glXCreatePbuffer failed."; 584 return false; 585 } 586 587 return true; 588} 589 590void PbufferGLSurfaceGLX::Destroy() { 591 if (pbuffer_) { 592 glXDestroyPbuffer(g_display, pbuffer_); 593 pbuffer_ = 0; 594 } 595 596 config_ = NULL; 597} 598 599bool PbufferGLSurfaceGLX::IsOffscreen() { 600 return true; 601} 602 603bool PbufferGLSurfaceGLX::SwapBuffers() { 604 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer."; 605 return false; 606} 607 608gfx::Size PbufferGLSurfaceGLX::GetSize() { 609 return size_; 610} 611 612void* PbufferGLSurfaceGLX::GetHandle() { 613 return reinterpret_cast<void*>(pbuffer_); 614} 615 616void* PbufferGLSurfaceGLX::GetConfig() { 617 return config_; 618} 619 620PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() { 621 Destroy(); 622} 623 624} // namespace gfx 625