| 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()
|
|
|