Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: ui/gl/gl_surface_glx.cc

Issue 11359114: Use GLX_SGI_video_sync to get a vblank signal. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: "" Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« ui/gl/gl_surface.h ('K') | « ui/gl/gl_surface_glx.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 extern "C" { 5 extern "C" {
6 #include <X11/Xlib.h> 6 #include <X11/Xlib.h>
7 } 7 }
8 8
9 #include "ui/gl/gl_surface_glx.h" 9 #include "ui/gl/gl_surface_glx.h"
10 10
11 #include "base/basictypes.h" 11 #include "base/basictypes.h"
12 #include "base/debug/trace_event.h" 12 #include "base/debug/trace_event.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h" 14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/weak_ptr.h"
15 #include "base/message_loop.h" 16 #include "base/message_loop.h"
16 #include "base/process_util.h" 17 #include "base/process_util.h"
18 #include "base/threading/non_thread_safe.h"
19 #include "base/threading/thread.h"
17 #include "base/time.h" 20 #include "base/time.h"
18 #include "third_party/mesa/MesaLib/include/GL/osmesa.h" 21 #include "third_party/mesa/MesaLib/include/GL/osmesa.h"
19 #include "ui/base/x/x11_util.h" 22 #include "ui/base/x/x11_util.h"
20 #include "ui/gl/gl_bindings.h" 23 #include "ui/gl/gl_bindings.h"
21 #include "ui/gl/gl_implementation.h" 24 #include "ui/gl/gl_implementation.h"
22 25
23 namespace gfx { 26 namespace gfx {
24 27
25 namespace { 28 namespace {
26 29
(...skipping 11 matching lines...) Expand all
38 const char* g_glx_extensions = NULL; 41 const char* g_glx_extensions = NULL;
39 bool g_glx_create_context_robustness_supported = false; 42 bool g_glx_create_context_robustness_supported = false;
40 bool g_glx_texture_from_pixmap_supported = false; 43 bool g_glx_texture_from_pixmap_supported = false;
41 bool g_glx_oml_sync_control_supported = false; 44 bool g_glx_oml_sync_control_supported = false;
42 45
43 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a 46 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
44 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML 47 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
45 // always fails even though GLX_OML_sync_control is reported as being supported. 48 // always fails even though GLX_OML_sync_control is reported as being supported.
46 bool g_glx_get_msc_rate_oml_supported = false; 49 bool g_glx_get_msc_rate_oml_supported = false;
47 50
51 bool g_glx_sgi_video_sync_supported = false;
52
53 class OMLSyncControlVSyncProvider
54 : public gfx::NativeViewGLSurfaceGLX::VSyncProvider {
55 public:
56 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
57 : window_(window) {
58 }
59
60 virtual ~OMLSyncControlVSyncProvider() { }
61
62 virtual void GetVSyncParameters(
63 const GLSurface::UpdateVSyncCallback& callback) OVERRIDE {
64 base::TimeTicks timebase;
65 base::TimeDelta interval;
66
67 // The actual clock used for the system time returned by glXGetSyncValuesOML
68 // is unspecified. In practice, the clock used is likely to be either
69 // CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the
70 // current time according to both clocks, and assume that the returned time
71 // was produced by the clock whose current time is closest to it, subject
72 // to the restriction that the returned time must not be in the future
73 // (since it is the time of a vblank that has already occurred).
74 int64 system_time;
75 int64 media_stream_counter;
76 int64 swap_buffer_counter;
77 if (!glXGetSyncValuesOML(g_display, window_, &system_time,
78 &media_stream_counter, &swap_buffer_counter))
79 return;
80
81 struct timespec real_time;
82 struct timespec monotonic_time;
83 clock_gettime(CLOCK_REALTIME, &real_time);
84 clock_gettime(CLOCK_MONOTONIC, &monotonic_time);
85
86 int64 real_time_in_microseconds =
87 real_time.tv_sec * base::Time::kMicrosecondsPerSecond +
88 real_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
89 int64 monotonic_time_in_microseconds =
90 monotonic_time.tv_sec * base::Time::kMicrosecondsPerSecond +
91 monotonic_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
92
93 if ((system_time > real_time_in_microseconds) &&
94 (system_time > monotonic_time_in_microseconds))
95 return;
96
97 // We need the time according to CLOCK_MONOTONIC, so if we've been given
98 // a time from CLOCK_REALTIME, we need to convert.
99 bool time_conversion_needed =
100 (system_time > monotonic_time_in_microseconds) ||
101 (real_time_in_microseconds - system_time <
102 monotonic_time_in_microseconds - system_time);
103
104 if (time_conversion_needed) {
105 int64 time_difference =
106 real_time_in_microseconds - monotonic_time_in_microseconds;
107 timebase = base::TimeTicks::FromInternalValue(
108 system_time - time_difference);
109 } else {
110 timebase = base::TimeTicks::FromInternalValue(system_time);
111 }
112
113 // On platforms where glXGetMscRateOML doesn't work, we fall back to the
114 // assumption that we're displaying 60 frames per second.
115 const int64 kDefaultIntervalTime =
116 base::Time::kMicrosecondsPerSecond / 60;
117 int64 interval_time = kDefaultIntervalTime;
118 int32 numerator;
119 int32 denominator;
120 if (g_glx_get_msc_rate_oml_supported) {
121 if (glXGetMscRateOML(g_display, window_, &numerator, &denominator)) {
122 interval_time =
123 (base::Time::kMicrosecondsPerSecond * denominator) / numerator;
124 } else {
125 // Once glXGetMscRateOML has been found to fail, don't try again,
126 // since each failing call may spew an error message.
127 g_glx_get_msc_rate_oml_supported = false;
128 }
129 }
130 interval = base::TimeDelta::FromMicroseconds(interval_time);
131 callback.Run(timebase, interval);
132 }
133
134 private:
135 XID window_;
136
137 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
138 };
139
140 class SGIVideoSyncThread
141 : public base::Thread,
142 public base::NonThreadSafe,
143 public base::RefCounted<SGIVideoSyncThread> {
144 public:
145 static scoped_refptr<SGIVideoSyncThread> Create() {
146 if (!g_video_sync_thread) {
147 g_video_sync_thread = new SGIVideoSyncThread();
148 g_video_sync_thread->Start();
149 }
150 return g_video_sync_thread;
151 }
152
153 ~SGIVideoSyncThread() {
154 DCHECK(CalledOnValidThread());
155 g_video_sync_thread = NULL;
156 Stop();
157 }
158
159 private:
160 friend class base::RefCounted<SGIVideoSyncThread>;
161
162 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
163 DCHECK(CalledOnValidThread());
164 }
165
166 static SGIVideoSyncThread* g_video_sync_thread;
167
168 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
169 };
170
171 class SGIVideoSyncProviderThreadShim
172 : public base::SupportsWeakPtr<SGIVideoSyncProviderThreadShim> {
173 public:
174 explicit SGIVideoSyncProviderThreadShim(XID window)
175 : window_(window),
176 context_(NULL),
177 message_loop_(base::MessageLoopProxy::current()) {
178 }
179
180 void Initialize() {
181 DCHECK(SGIVideoSyncProviderThreadShim::g_display);
182
183 XWindowAttributes attributes;
184 if (!XGetWindowAttributes(SGIVideoSyncProviderThreadShim::g_display,
185 window_, &attributes)) {
piman 2012/11/08 23:51:16 It makes me somewhat uncomfortable to use the wind
jonathan.backer 2012/11/09 17:02:35 FWIW, this is not much worse than the current situ
piman 2012/11/09 18:08:05 On Aura we have a round trip to X right after we c
186 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
187 window_ << ".";
188 return;
189 }
190
191 XVisualInfo visual_info_template;
192 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
193
194 int visual_info_count = 0;
195 scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
196 XGetVisualInfo(SGIVideoSyncProviderThreadShim::g_display, VisualIDMask,
197 &visual_info_template, &visual_info_count));
198
199 DCHECK(visual_info_list.get());
200 if (visual_info_count == 0) {
201 LOG(ERROR) << "No visual info for visual ID.";
202 return;
203 }
204
205 context_ = glXCreateContext(SGIVideoSyncProviderThreadShim::g_display,
206 visual_info_list.get(),
207 NULL,
208 True);
209
210 DCHECK(NULL != context_);
211 }
212
213 void Destroy() {
214 if (context_) {
215 glXDestroyContext(SGIVideoSyncProviderThreadShim::g_display, context_);
216 context_ = NULL;
217 }
218 delete this;
219 }
220
221 void GetVSyncParameters(const GLSurface::UpdateVSyncCallback& callback) {
222 if (!context_)
223 return;
224
225 glXMakeCurrent(SGIVideoSyncProviderThreadShim::g_display,
226 window_, context_);
piman 2012/11/08 23:51:16 How do we know that the window hasn't been destroy
jonathan.backer 2012/11/09 17:02:35 Good point. I've added a cancellation flag and a l
227
228 unsigned int retrace_count = 0;
229 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
230 return;
231
232 TRACE_EVENT_INSTANT0("gpu", "vblank");
233 base::TimeTicks now = base::TimeTicks::HighResNow();
234
235 const int64 kDefaultIntervalTime =
236 base::Time::kMicrosecondsPerSecond / 60;
237 base::TimeDelta interval =
238 base::TimeDelta::FromMicroseconds(kDefaultIntervalTime);
239
240 message_loop_->PostTask(FROM_HERE, base::Bind(callback, now, interval));
241 }
242
243 private:
244 // For initialization of g_display in GLSurface::InitializeOneOff before
245 // the sandbox goes up.
246 friend class gfx::GLSurfaceGLX;
247
248 virtual ~SGIVideoSyncProviderThreadShim() {
249 }
250
251 static Display* g_display;
252
253 XID window_;
254 GLXContext context_;
255
256 scoped_refptr<base::MessageLoopProxy> message_loop_;
257
258 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
259 };
260
261 class SGIVideoSyncVSyncProvider
262 : public gfx::NativeViewGLSurfaceGLX::VSyncProvider,
263 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
264 public:
265 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
266 : vsync_thread_(SGIVideoSyncThread::Create()),
267 shim_((new SGIVideoSyncProviderThreadShim(window))->AsWeakPtr()) {
268 // The WeakPtr is bound to the SGIVideoSyncThread. We only use it for
269 // PostTask.
270 shim_->DetachFromThread();
271 vsync_thread_->message_loop()->PostTask(
272 FROM_HERE,
273 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize, shim_));
274 }
275
276 virtual ~SGIVideoSyncVSyncProvider() {
277 vsync_thread_->message_loop()->PostTask(
278 FROM_HERE,
279 base::Bind(&SGIVideoSyncProviderThreadShim::Destroy, shim_));
280 }
281
282 virtual void GetVSyncParameters(
283 const GLSurface::UpdateVSyncCallback& callback) OVERRIDE {
284 // Only one outstanding request per surface.
285 if (!pending_callback_) {
286 pending_callback_.reset(new GLSurface::UpdateVSyncCallback(callback));
287 vsync_thread_->message_loop()->PostTask(
288 FROM_HERE,
289 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
290 shim_, base::Bind(
291 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
292 AsWeakPtr())));
293 }
294 }
295
296 private:
297 void PendingCallbackRunner(const base::TimeTicks timebase,
298 const base::TimeDelta interval) {
299 DCHECK(pending_callback_);
300 pending_callback_->Run(timebase, interval);
301 pending_callback_.reset();
302 }
303
304 scoped_refptr<SGIVideoSyncThread> vsync_thread_;
305 base::WeakPtr<SGIVideoSyncProviderThreadShim> shim_;
306
307 scoped_ptr<GLSurface::UpdateVSyncCallback> pending_callback_;
308
309 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
310 };
311
312 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
313
314 // In order to take advantage of GLX_SGI_video_sync, we need a display
315 // for use on a separate thread. We must allocate this before the sandbox
316 // goes up (rather than on-demand when we start the thread).
317 Display* SGIVideoSyncProviderThreadShim::g_display = NULL;
318
48 } // namespace 319 } // namespace
49 320
50 GLSurfaceGLX::GLSurfaceGLX() {} 321 GLSurfaceGLX::GLSurfaceGLX() {}
51 322
52 bool GLSurfaceGLX::InitializeOneOff() { 323 bool GLSurfaceGLX::InitializeOneOff() {
53 static bool initialized = false; 324 static bool initialized = false;
54 if (initialized) 325 if (initialized)
55 return true; 326 return true;
56 327
57 g_display = base::MessagePumpForUI::GetDefaultXDisplay(); 328 g_display = base::MessagePumpForUI::GetDefaultXDisplay();
(...skipping 14 matching lines...) Expand all
72 } 343 }
73 344
74 g_glx_extensions = glXQueryExtensionsString(g_display, 0); 345 g_glx_extensions = glXQueryExtensionsString(g_display, 0);
75 g_glx_create_context_robustness_supported = 346 g_glx_create_context_robustness_supported =
76 HasGLXExtension("GLX_ARB_create_context_robustness"); 347 HasGLXExtension("GLX_ARB_create_context_robustness");
77 g_glx_texture_from_pixmap_supported = 348 g_glx_texture_from_pixmap_supported =
78 HasGLXExtension("GLX_EXT_texture_from_pixmap"); 349 HasGLXExtension("GLX_EXT_texture_from_pixmap");
79 g_glx_oml_sync_control_supported = 350 g_glx_oml_sync_control_supported =
80 HasGLXExtension("GLX_OML_sync_control"); 351 HasGLXExtension("GLX_OML_sync_control");
81 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported; 352 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
353 g_glx_sgi_video_sync_supported =
354 HasGLXExtension("GLX_SGI_video_sync");
82 355
356 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
357 SGIVideoSyncProviderThreadShim::g_display = XOpenDisplay(NULL);
83 358
84 initialized = true; 359 initialized = true;
85 return true; 360 return true;
86 } 361 }
87 362
88 // static 363 // static
89 const char* GLSurfaceGLX::GetGLXExtensions() { 364 const char* GLSurfaceGLX::GetGLXExtensions() {
90 return g_glx_extensions; 365 return g_glx_extensions;
91 } 366 }
92 367
(...skipping 19 matching lines...) Expand all
112 387
113 void* GLSurfaceGLX::GetDisplay() { 388 void* GLSurfaceGLX::GetDisplay() {
114 return g_display; 389 return g_display;
115 } 390 }
116 391
117 GLSurfaceGLX::~GLSurfaceGLX() {} 392 GLSurfaceGLX::~GLSurfaceGLX() {}
118 393
119 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window) 394 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
120 : window_(window), 395 : window_(window),
121 config_(NULL) { 396 config_(NULL) {
397 if (g_glx_oml_sync_control_supported)
398 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_));
399 else if (g_glx_sgi_video_sync_supported)
400 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_));
122 } 401 }
123 402
124 bool NativeViewGLSurfaceGLX::Initialize() { 403 bool NativeViewGLSurfaceGLX::Initialize() {
125 XWindowAttributes attributes; 404 XWindowAttributes attributes;
126 if (!XGetWindowAttributes(g_display, window_, &attributes)) { 405 if (!XGetWindowAttributes(g_display, window_, &attributes)) {
127 LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << "."; 406 LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
128 return false; 407 return false;
129 } 408 }
130 size_ = gfx::Size(attributes.width, attributes.height); 409 size_ = gfx::Size(attributes.width, attributes.height);
131 return true; 410 return true;
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 return config_; 512 return config_;
234 } 513 }
235 514
236 bool NativeViewGLSurfaceGLX::PostSubBuffer( 515 bool NativeViewGLSurfaceGLX::PostSubBuffer(
237 int x, int y, int width, int height) { 516 int x, int y, int width, int height) {
238 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer); 517 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
239 glXCopySubBufferMESA(g_display, window_, x, y, width, height); 518 glXCopySubBufferMESA(g_display, window_, x, y, width, height);
240 return true; 519 return true;
241 } 520 }
242 521
243 bool NativeViewGLSurfaceGLX::GetVSyncParameters(base::TimeTicks* timebase, 522 void NativeViewGLSurfaceGLX::GetVSyncParameters(
244 base::TimeDelta* interval) { 523 const UpdateVSyncCallback& callback) {
245 if (!g_glx_oml_sync_control_supported) 524 if (vsync_provider_)
246 return false; 525 vsync_provider_->GetVSyncParameters(callback);
247
248 // The actual clock used for the system time returned by glXGetSyncValuesOML
249 // is unspecified. In practice, the clock used is likely to be either
250 // CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the
251 // current time according to both clocks, and assume that the returned time
252 // was produced by the clock whose current time is closest to it, subject
253 // to the restriction that the returned time must not be in the future (since
254 // it is the time of a vblank that has already occurred).
255 int64 system_time;
256 int64 media_stream_counter;
257 int64 swap_buffer_counter;
258 if (!glXGetSyncValuesOML(g_display, window_, &system_time,
259 &media_stream_counter, &swap_buffer_counter))
260 return false;
261
262 struct timespec real_time;
263 struct timespec monotonic_time;
264 clock_gettime(CLOCK_REALTIME, &real_time);
265 clock_gettime(CLOCK_MONOTONIC, &monotonic_time);
266
267 int64 real_time_in_microseconds =
268 real_time.tv_sec * base::Time::kMicrosecondsPerSecond +
269 real_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
270 int64 monotonic_time_in_microseconds =
271 monotonic_time.tv_sec * base::Time::kMicrosecondsPerSecond +
272 monotonic_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
273
274 if ((system_time > real_time_in_microseconds) &&
275 (system_time > monotonic_time_in_microseconds))
276 return false;
277
278 // We need the time according to CLOCK_MONOTONIC, so if we've been given
279 // a time from CLOCK_REALTIME, we need to convert.
280 bool time_conversion_needed =
281 (system_time > monotonic_time_in_microseconds) ||
282 (real_time_in_microseconds - system_time <
283 monotonic_time_in_microseconds - system_time);
284
285 if (time_conversion_needed) {
286 int64 time_difference =
287 real_time_in_microseconds - monotonic_time_in_microseconds;
288 *timebase = base::TimeTicks::FromInternalValue(
289 system_time - time_difference);
290 } else {
291 *timebase = base::TimeTicks::FromInternalValue(system_time);
292 }
293
294 // On platforms where glXGetMscRateOML doesn't work, we fall back to the
295 // assumption that we're displaying 60 frames per second.
296 const int64 kDefaultIntervalTime =
297 base::Time::kMicrosecondsPerSecond / 60;
298 int64 interval_time = kDefaultIntervalTime;
299 int32 numerator;
300 int32 denominator;
301 if (g_glx_get_msc_rate_oml_supported) {
302 if (glXGetMscRateOML(g_display, window_, &numerator, &denominator)) {
303 interval_time =
304 (base::Time::kMicrosecondsPerSecond * denominator) / numerator;
305 } else {
306 // Once glXGetMscRateOML has been found to fail, don't try again,
307 // since each failing call may spew an error message.
308 g_glx_get_msc_rate_oml_supported = false;
309 }
310 }
311
312 *interval = base::TimeDelta::FromMicroseconds(interval_time);
313 return true;
314 } 526 }
315 527
316 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX() 528 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
317 : window_(0), 529 : window_(0),
318 config_(NULL) { 530 config_(NULL) {
319 } 531 }
320 532
321 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() { 533 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
322 Destroy(); 534 Destroy();
323 } 535 }
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 617
406 void* PbufferGLSurfaceGLX::GetConfig() { 618 void* PbufferGLSurfaceGLX::GetConfig() {
407 return config_; 619 return config_;
408 } 620 }
409 621
410 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() { 622 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
411 Destroy(); 623 Destroy();
412 } 624 }
413 625
414 } // namespace gfx 626 } // namespace gfx
OLDNEW
« ui/gl/gl_surface.h ('K') | « ui/gl/gl_surface_glx.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698