Index: ui/gl/gl_surface_glx.cc |
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc |
index a164f3f56c1793cf84c36821d12874659f372c59..da2d74c3cf7665115f65d94a9ec8142b8a784132 100644 |
--- a/ui/gl/gl_surface_glx.cc |
+++ b/ui/gl/gl_surface_glx.cc |
@@ -12,8 +12,13 @@ extern "C" { |
#include "base/debug/trace_event.h" |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
+#include "base/memory/weak_ptr.h" |
#include "base/message_loop.h" |
#include "base/process_util.h" |
+#include "base/synchronization/cancellation_flag.h" |
+#include "base/synchronization/lock.h" |
+#include "base/threading/non_thread_safe.h" |
+#include "base/threading/thread.h" |
#include "base/time.h" |
#include "third_party/mesa/MesaLib/include/GL/osmesa.h" |
#include "ui/base/x/x11_util.h" |
@@ -45,6 +50,310 @@ bool g_glx_oml_sync_control_supported = false; |
// always fails even though GLX_OML_sync_control is reported as being supported. |
bool g_glx_get_msc_rate_oml_supported = false; |
+bool g_glx_sgi_video_sync_supported = false; |
+ |
+class OMLSyncControlVSyncProvider |
+ : public gfx::NativeViewGLSurfaceGLX::VSyncProvider { |
+ public: |
+ explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window) |
+ : window_(window) { |
+ } |
+ |
+ virtual ~OMLSyncControlVSyncProvider() { } |
+ |
+ virtual void GetVSyncParameters( |
+ const GLSurface::UpdateVSyncCallback& callback) OVERRIDE { |
+ base::TimeTicks timebase; |
+ base::TimeDelta interval; |
+ |
+ // The actual clock used for the system time returned by glXGetSyncValuesOML |
+ // is unspecified. In practice, the clock used is likely to be either |
+ // CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the |
+ // current time according to both clocks, and assume that the returned time |
+ // was produced by the clock whose current time is closest to it, subject |
+ // to the restriction that the returned time must not be in the future |
+ // (since it is the time of a vblank that has already occurred). |
+ int64 system_time; |
+ int64 media_stream_counter; |
+ int64 swap_buffer_counter; |
+ if (!glXGetSyncValuesOML(g_display, window_, &system_time, |
+ &media_stream_counter, &swap_buffer_counter)) |
+ return; |
+ |
+ struct timespec real_time; |
+ struct timespec monotonic_time; |
+ clock_gettime(CLOCK_REALTIME, &real_time); |
+ clock_gettime(CLOCK_MONOTONIC, &monotonic_time); |
+ |
+ int64 real_time_in_microseconds = |
+ real_time.tv_sec * base::Time::kMicrosecondsPerSecond + |
+ real_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
+ int64 monotonic_time_in_microseconds = |
+ monotonic_time.tv_sec * base::Time::kMicrosecondsPerSecond + |
+ monotonic_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
+ |
+ if ((system_time > real_time_in_microseconds) && |
+ (system_time > monotonic_time_in_microseconds)) |
+ return; |
+ |
+ // We need the time according to CLOCK_MONOTONIC, so if we've been given |
+ // a time from CLOCK_REALTIME, we need to convert. |
+ bool time_conversion_needed = |
+ (system_time > monotonic_time_in_microseconds) || |
+ (real_time_in_microseconds - system_time < |
+ monotonic_time_in_microseconds - system_time); |
+ |
+ if (time_conversion_needed) { |
+ int64 time_difference = |
+ real_time_in_microseconds - monotonic_time_in_microseconds; |
+ timebase = base::TimeTicks::FromInternalValue( |
+ system_time - time_difference); |
+ } else { |
+ timebase = base::TimeTicks::FromInternalValue(system_time); |
+ } |
+ |
+ // On platforms where glXGetMscRateOML doesn't work, we fall back to the |
+ // assumption that we're displaying 60 frames per second. |
+ const int64 kDefaultIntervalTime = |
+ base::Time::kMicrosecondsPerSecond / 60; |
+ int64 interval_time = kDefaultIntervalTime; |
+ int32 numerator; |
+ int32 denominator; |
+ if (g_glx_get_msc_rate_oml_supported) { |
+ if (glXGetMscRateOML(g_display, window_, &numerator, &denominator)) { |
+ interval_time = |
+ (base::Time::kMicrosecondsPerSecond * denominator) / numerator; |
+ } else { |
+ // Once glXGetMscRateOML has been found to fail, don't try again, |
+ // since each failing call may spew an error message. |
+ g_glx_get_msc_rate_oml_supported = false; |
+ } |
+ } |
+ interval = base::TimeDelta::FromMicroseconds(interval_time); |
+ callback.Run(timebase, interval); |
+ } |
+ |
+ private: |
+ XID window_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider); |
+}; |
+ |
+class SGIVideoSyncThread |
+ : public base::Thread, |
+ public base::NonThreadSafe, |
+ public base::RefCounted<SGIVideoSyncThread> { |
+ public: |
+ static scoped_refptr<SGIVideoSyncThread> Create() { |
+ if (!g_video_sync_thread) { |
+ g_video_sync_thread = new SGIVideoSyncThread(); |
+ g_video_sync_thread->Start(); |
+ } |
+ return g_video_sync_thread; |
+ } |
+ |
+ private: |
+ friend class base::RefCounted<SGIVideoSyncThread>; |
+ |
+ SGIVideoSyncThread() : base::Thread("SGI_video_sync") { |
+ DCHECK(CalledOnValidThread()); |
+ } |
+ |
+ ~SGIVideoSyncThread() { |
+ DCHECK(CalledOnValidThread()); |
+ g_video_sync_thread = NULL; |
+ Stop(); |
+ } |
+ |
+ static SGIVideoSyncThread* g_video_sync_thread; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread); |
+}; |
+ |
+class SGIVideoSyncProviderThreadShim |
+ : public base::SupportsWeakPtr<SGIVideoSyncProviderThreadShim> { |
+ public: |
+ explicit SGIVideoSyncProviderThreadShim(XID window) |
+ : window_(window), |
+ context_(NULL), |
+ message_loop_(base::MessageLoopProxy::current()), |
+ cancel_vsync_flag_(), |
+ vsync_lock_() { |
+ // This ensures that creation of |window_| has occured when this shim |
+ // is executing in the same process as the call to create |window_|. |
+ XSync(::gfx::g_display, False); |
+ } |
+ |
+ base::CancellationFlag* cancel_vsync_flag() { |
+ return &cancel_vsync_flag_; |
+ } |
+ |
+ base::Lock* vsync_lock() { |
+ return &vsync_lock_; |
+ } |
+ |
+ void Initialize() { |
+ DCHECK(SGIVideoSyncProviderThreadShim::g_display); |
+ |
+ XWindowAttributes attributes; |
+ if (!XGetWindowAttributes(SGIVideoSyncProviderThreadShim::g_display, |
+ window_, &attributes)) { |
+ LOG(ERROR) << "XGetWindowAttributes failed for window " << |
+ window_ << "."; |
+ return; |
+ } |
+ |
+ XVisualInfo visual_info_template; |
+ visual_info_template.visualid = XVisualIDFromVisual(attributes.visual); |
+ |
+ int visual_info_count = 0; |
+ scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list( |
+ XGetVisualInfo(SGIVideoSyncProviderThreadShim::g_display, VisualIDMask, |
+ &visual_info_template, &visual_info_count)); |
+ |
+ DCHECK(visual_info_list.get()); |
+ if (visual_info_count == 0) { |
+ LOG(ERROR) << "No visual info for visual ID."; |
+ return; |
+ } |
+ |
+ context_ = glXCreateContext(SGIVideoSyncProviderThreadShim::g_display, |
+ visual_info_list.get(), |
+ NULL, |
+ True); |
+ |
+ DCHECK(NULL != context_); |
+ } |
+ |
+ void Destroy() { |
+ if (context_) { |
+ glXDestroyContext(SGIVideoSyncProviderThreadShim::g_display, context_); |
+ context_ = NULL; |
+ } |
+ delete this; |
+ } |
+ |
+ void GetVSyncParameters(const GLSurface::UpdateVSyncCallback& callback) { |
+ base::TimeTicks now; |
+ { |
+ // Don't allow |window_| destruction while we're probing vsync. |
+ base::AutoLock locked(vsync_lock_); |
+ |
+ if (!context_ || cancel_vsync_flag_.IsSet()) |
+ return; |
+ |
+ glXMakeCurrent(SGIVideoSyncProviderThreadShim::g_display, |
+ window_, context_); |
+ |
+ unsigned int retrace_count = 0; |
+ if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0) |
+ return; |
+ |
+ TRACE_EVENT_INSTANT0("gpu", "vblank"); |
+ now = base::TimeTicks::HighResNow(); |
+ |
+ glXMakeCurrent(SGIVideoSyncProviderThreadShim::g_display, 0, 0); |
+ } |
+ |
+ const int64 kDefaultIntervalTime = |
+ base::Time::kMicrosecondsPerSecond / 60; |
+ base::TimeDelta interval = |
+ base::TimeDelta::FromMicroseconds(kDefaultIntervalTime); |
+ |
+ message_loop_->PostTask(FROM_HERE, base::Bind(callback, now, interval)); |
+ } |
+ |
+ private: |
+ // For initialization of g_display in GLSurface::InitializeOneOff before |
+ // the sandbox goes up. |
+ friend class gfx::GLSurfaceGLX; |
+ |
+ virtual ~SGIVideoSyncProviderThreadShim() { |
+ } |
+ |
+ static Display* g_display; |
+ |
+ XID window_; |
+ GLXContext context_; |
+ |
+ scoped_refptr<base::MessageLoopProxy> message_loop_; |
+ |
+ base::CancellationFlag cancel_vsync_flag_; |
+ base::Lock vsync_lock_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim); |
+}; |
+ |
+class SGIVideoSyncVSyncProvider |
+ : public gfx::NativeViewGLSurfaceGLX::VSyncProvider, |
+ public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> { |
+ public: |
+ explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window) |
+ : vsync_thread_(SGIVideoSyncThread::Create()), |
+ shim_((new SGIVideoSyncProviderThreadShim(window))->AsWeakPtr()), |
+ cancel_vsync_flag_(shim_->cancel_vsync_flag()), |
+ vsync_lock_(shim_->vsync_lock()) { |
+ // The WeakPtr is bound to the SGIVideoSyncThread. We only use it for |
+ // PostTask. |
+ shim_->DetachFromThread(); |
+ vsync_thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SGIVideoSyncProviderThreadShim::Initialize, shim_)); |
+ } |
+ |
+ virtual ~SGIVideoSyncVSyncProvider() { |
+ { |
+ base::AutoLock locked(*vsync_lock_); |
+ cancel_vsync_flag_->Set(); |
+ } |
+ vsync_thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SGIVideoSyncProviderThreadShim::Destroy, shim_)); |
+ } |
+ |
+ virtual void GetVSyncParameters( |
+ const GLSurface::UpdateVSyncCallback& callback) OVERRIDE { |
+ // Only one outstanding request per surface. |
+ if (!pending_callback_) { |
+ pending_callback_.reset(new GLSurface::UpdateVSyncCallback(callback)); |
+ vsync_thread_->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters, |
+ shim_, base::Bind( |
+ &SGIVideoSyncVSyncProvider::PendingCallbackRunner, |
+ AsWeakPtr()))); |
+ } |
+ } |
+ |
+ private: |
+ void PendingCallbackRunner(const base::TimeTicks timebase, |
+ const base::TimeDelta interval) { |
+ DCHECK(pending_callback_); |
+ pending_callback_->Run(timebase, interval); |
+ pending_callback_.reset(); |
+ } |
+ |
+ scoped_refptr<SGIVideoSyncThread> vsync_thread_; |
+ base::WeakPtr<SGIVideoSyncProviderThreadShim> shim_; |
+ |
+ scoped_ptr<GLSurface::UpdateVSyncCallback> pending_callback_; |
+ |
+ // Raw pointers to sync primitives owned by the shim_. |
+ // These will only be referenced before we post a task to destroy |
+ // the shim_, so they are safe to access. |
+ base::CancellationFlag* cancel_vsync_flag_; |
+ base::Lock* vsync_lock_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider); |
+}; |
+ |
+SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL; |
+ |
+// In order to take advantage of GLX_SGI_video_sync, we need a display |
+// for use on a separate thread. We must allocate this before the sandbox |
+// goes up (rather than on-demand when we start the thread). |
+Display* SGIVideoSyncProviderThreadShim::g_display = NULL; |
+ |
} // namespace |
GLSurfaceGLX::GLSurfaceGLX() {} |
@@ -54,6 +363,10 @@ bool GLSurfaceGLX::InitializeOneOff() { |
if (initialized) |
return true; |
+ // SGIVideoSyncProviderShim (if instantiated) will issue X commands on |
+ // it's own thread. |
+ XInitThreads(); |
+ |
g_display = base::MessagePumpForUI::GetDefaultXDisplay(); |
if (!g_display) { |
LOG(ERROR) << "XOpenDisplay failed."; |
@@ -79,7 +392,11 @@ bool GLSurfaceGLX::InitializeOneOff() { |
g_glx_oml_sync_control_supported = |
HasGLXExtension("GLX_OML_sync_control"); |
g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported; |
+ g_glx_sgi_video_sync_supported = |
+ HasGLXExtension("GLX_SGI_video_sync"); |
+ if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported) |
+ SGIVideoSyncProviderThreadShim::g_display = XOpenDisplay(NULL); |
initialized = true; |
return true; |
@@ -128,6 +445,12 @@ bool NativeViewGLSurfaceGLX::Initialize() { |
return false; |
} |
size_ = gfx::Size(attributes.width, attributes.height); |
+ |
+ if (g_glx_oml_sync_control_supported) |
+ vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_)); |
+ else if (g_glx_sgi_video_sync_supported) |
+ vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_)); |
+ |
return true; |
} |
@@ -240,77 +563,10 @@ bool NativeViewGLSurfaceGLX::PostSubBuffer( |
return true; |
} |
-bool NativeViewGLSurfaceGLX::GetVSyncParameters(base::TimeTicks* timebase, |
- base::TimeDelta* interval) { |
- if (!g_glx_oml_sync_control_supported) |
- return false; |
- |
- // The actual clock used for the system time returned by glXGetSyncValuesOML |
- // is unspecified. In practice, the clock used is likely to be either |
- // CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the |
- // current time according to both clocks, and assume that the returned time |
- // was produced by the clock whose current time is closest to it, subject |
- // to the restriction that the returned time must not be in the future (since |
- // it is the time of a vblank that has already occurred). |
- int64 system_time; |
- int64 media_stream_counter; |
- int64 swap_buffer_counter; |
- if (!glXGetSyncValuesOML(g_display, window_, &system_time, |
- &media_stream_counter, &swap_buffer_counter)) |
- return false; |
- |
- struct timespec real_time; |
- struct timespec monotonic_time; |
- clock_gettime(CLOCK_REALTIME, &real_time); |
- clock_gettime(CLOCK_MONOTONIC, &monotonic_time); |
- |
- int64 real_time_in_microseconds = |
- real_time.tv_sec * base::Time::kMicrosecondsPerSecond + |
- real_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
- int64 monotonic_time_in_microseconds = |
- monotonic_time.tv_sec * base::Time::kMicrosecondsPerSecond + |
- monotonic_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
- |
- if ((system_time > real_time_in_microseconds) && |
- (system_time > monotonic_time_in_microseconds)) |
- return false; |
- |
- // We need the time according to CLOCK_MONOTONIC, so if we've been given |
- // a time from CLOCK_REALTIME, we need to convert. |
- bool time_conversion_needed = |
- (system_time > monotonic_time_in_microseconds) || |
- (real_time_in_microseconds - system_time < |
- monotonic_time_in_microseconds - system_time); |
- |
- if (time_conversion_needed) { |
- int64 time_difference = |
- real_time_in_microseconds - monotonic_time_in_microseconds; |
- *timebase = base::TimeTicks::FromInternalValue( |
- system_time - time_difference); |
- } else { |
- *timebase = base::TimeTicks::FromInternalValue(system_time); |
- } |
- |
- // On platforms where glXGetMscRateOML doesn't work, we fall back to the |
- // assumption that we're displaying 60 frames per second. |
- const int64 kDefaultIntervalTime = |
- base::Time::kMicrosecondsPerSecond / 60; |
- int64 interval_time = kDefaultIntervalTime; |
- int32 numerator; |
- int32 denominator; |
- if (g_glx_get_msc_rate_oml_supported) { |
- if (glXGetMscRateOML(g_display, window_, &numerator, &denominator)) { |
- interval_time = |
- (base::Time::kMicrosecondsPerSecond * denominator) / numerator; |
- } else { |
- // Once glXGetMscRateOML has been found to fail, don't try again, |
- // since each failing call may spew an error message. |
- g_glx_get_msc_rate_oml_supported = false; |
- } |
- } |
- |
- *interval = base::TimeDelta::FromMicroseconds(interval_time); |
- return true; |
+void NativeViewGLSurfaceGLX::GetVSyncParameters( |
+ const UpdateVSyncCallback& callback) { |
+ if (vsync_provider_) |
+ vsync_provider_->GetVSyncParameters(callback); |
} |
NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX() |