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