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/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 28namespace gfx { 29 30namespace { 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. 35class ScopedPtrXFree { 36 public: 37 void operator()(void* x) const { 38 ::XFree(x); 39 } 40}; 41 42Display* g_display; 43const char* g_glx_extensions = NULL; 44bool g_glx_context_create = false; 45bool g_glx_create_context_robustness_supported = false; 46bool g_glx_texture_from_pixmap_supported = false; 47bool 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. 52bool g_glx_get_msc_rate_oml_supported = false; 53 54bool g_glx_sgi_video_sync_supported = false; 55 56class 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 94class 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 125class 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 228class 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 295SGIVideoSyncThread* 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). 300Display* SGIVideoSyncProviderThreadShim::display_ = NULL; 301 302} // namespace 303 304GLSurfaceGLX::GLSurfaceGLX() {} 305 306bool 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 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 size_ = size; 417 return true; 418} 419 420bool NativeViewGLSurfaceGLX::IsOffscreen() { 421 return false; 422} 423 424bool NativeViewGLSurfaceGLX::SwapBuffers() { 425 glXSwapBuffers(g_display, window_); 426 return true; 427} 428 429gfx::Size NativeViewGLSurfaceGLX::GetSize() { 430 return size_; 431} 432 433void* NativeViewGLSurfaceGLX::GetHandle() { 434 return reinterpret_cast<void*>(window_); 435} 436 437std::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 446void* 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 507bool 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 514VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() { 515 return vsync_provider_.get(); 516} 517 518NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX() 519 : window_(0), 520 config_(NULL) { 521} 522 523NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() { 524 Destroy(); 525} 526 527PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size) 528 : size_(size), 529 config_(NULL), 530 pbuffer_(0) { 531} 532 533bool 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 582void PbufferGLSurfaceGLX::Destroy() { 583 if (pbuffer_) { 584 glXDestroyPbuffer(g_display, pbuffer_); 585 pbuffer_ = 0; 586 } 587 588 config_ = NULL; 589} 590 591bool PbufferGLSurfaceGLX::IsOffscreen() { 592 return true; 593} 594 595bool PbufferGLSurfaceGLX::SwapBuffers() { 596 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer."; 597 return false; 598} 599 600gfx::Size PbufferGLSurfaceGLX::GetSize() { 601 return size_; 602} 603 604void* PbufferGLSurfaceGLX::GetHandle() { 605 return reinterpret_cast<void*>(pbuffer_); 606} 607 608void* PbufferGLSurfaceGLX::GetConfig() { 609 return config_; 610} 611 612PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() { 613 Destroy(); 614} 615 616} // namespace gfx 617