2 * Copyright © 2014 The TOVA Company
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 * Validates that OML Sync Control implementation actually syncs to vertical
30 #include "piglit-util-gl.h"
31 #include "piglit-glx-util.h"
34 * TODO: varying MSC deltas enumerated as arguments
35 * TODO: As a different test, create two drawables and verify they have
38 static bool fullscreen
;
39 static bool use_swapbuffers
= true;
40 static int64_t target_msc_delta
;
41 static int64_t divisor
;
42 static const int64_t msc_remainder
= 0;
43 static const unsigned int loops
= 10;
51 static void update_stats(struct stats
*stats
, double val
) {
52 double delta
= val
- stats
->mean
;
54 stats
->mean
+= delta
/ stats
->n
;
55 stats
->M2
+= delta
* (val
- stats
->mean
);
58 static double get_stddev(struct stats
*stats
) {
59 return sqrt(stats
->M2
/ (stats
->n
- 1));
63 swap_buffers_msc(Display
*dpy
, int64_t last_sbc
, int64_t *target_sbc
,
64 int64_t target_msc
, int64_t divisor
, int64_t remainder
,
65 int64_t *new_ust
, int64_t *new_msc
, int64_t *new_sbc
,
66 enum piglit_result
*result
)
68 glClearColor(0.0, 1.0, 0.0, 0.0);
69 glClear(GL_COLOR_BUFFER_BIT
);
71 *target_sbc
= glXSwapBuffersMscOML(dpy
, win
, target_msc
, divisor
,
73 if (*target_sbc
<= 0) {
74 fprintf(stderr
, "SwapBuffersMscOML failed\n");
78 if (*target_sbc
!= last_sbc
+ 1) {
80 "glXSwapBuffersMscOML calculated the wrong target sbc: "
81 "expected %"PRId64
" but got %"PRId64
"\n", last_sbc
+ 1,
83 *result
= PIGLIT_FAIL
;
86 if (!glXWaitForSbcOML(dpy
, win
, *target_sbc
, new_ust
, new_msc
,
88 fprintf(stderr
, "glXWaitForSbcOML failed\n");
89 *result
= PIGLIT_FAIL
;
96 wait_for_msc(Display
*dpy
, int64_t target_msc
, int64_t divisor
,
97 int64_t remainder
, int64_t *new_ust
, int64_t *new_msc
,
98 int64_t *new_sbc
, enum piglit_result
*result
)
100 if (!glXWaitForMscOML(dpy
, win
, target_msc
, divisor
, remainder
,
101 new_ust
, new_msc
, new_sbc
)) {
102 fprintf(stderr
, "glXWaitForMscOML failed\n");
103 *result
= PIGLIT_FAIL
;
107 static enum piglit_result
110 enum piglit_result result
= PIGLIT_PASS
;
111 int64_t last_ust
= 0xd0, last_msc
= 0xd0, last_sbc
= 0xd0;
112 int64_t last_timestamp
= -1; /* in nano seconds */
113 struct stats msc_wallclock_duration_stats
= {};
114 struct stats msc_ust_duration_stats
= {};
115 double expected_msc_wallclock_duration
= 0.0;
116 int32_t rate_num
, rate_den
;
119 if (!glXGetSyncValuesOML(dpy
, win
, &last_ust
, &last_msc
, &last_sbc
)) {
120 fprintf(stderr
, "Initial glXGetSyncValuesOML failed\n");
124 /* Check that the window is fresh */
126 fprintf(stderr
, "Initial SBC for the window should be 0, was "
129 piglit_merge_result(&result
, PIGLIT_WARN
);
132 if (!glXGetMscRateOML(dpy
, win
, &rate_num
, &rate_den
)) {
134 "glXGetMscRateOML failed, can't test MSC duration\n");
135 piglit_merge_result(&result
, PIGLIT_WARN
);
137 expected_msc_wallclock_duration
= 1e6
* rate_den
/ rate_num
;
140 piglit_set_timeout(5, PIGLIT_FAIL
);
142 /* Perform a swap / wait once before the loop. Without this, the first
143 * loop iteration can start at any point of the display refresh cycle.
144 * If we called glXGetSyncValuesOML shortly before a vertical blank
145 * period, it may be too late for the first iteration swap / wait to
146 * hit the target MSC, which would result in at least a warning.
148 if (use_swapbuffers
) {
149 if (!swap_buffers_msc(dpy
, last_sbc
, &last_sbc
, last_msc
+ 1,
150 0, 0, &last_ust
, &last_msc
, &last_sbc
,
154 wait_for_msc(dpy
, last_msc
+ 1, 0, 0, &last_ust
, &last_msc
,
158 for (i
= 0; i
< loops
; i
++) {
159 int64_t new_ust
= 0xd0, new_msc
= 0xd0, new_sbc
= 0xd0;
160 int64_t check_ust
= 0xd0, check_msc
= 0xd0, check_sbc
= 0xd0;
161 int64_t new_timestamp
; /* in nano seconds */
162 int64_t expected_msc
, target_sbc
;
163 int64_t target_msc
= 0;
165 if (target_msc_delta
) {
166 target_msc
= last_msc
+ target_msc_delta
;
169 if (use_swapbuffers
) {
170 if (!swap_buffers_msc(dpy
, last_sbc
, &target_sbc
,
172 msc_remainder
, &new_ust
, &new_msc
,
176 target_sbc
= last_sbc
;
177 wait_for_msc(dpy
, target_msc
, divisor
, msc_remainder
,
178 &new_ust
, &new_msc
, &new_sbc
, &result
);
180 new_timestamp
= piglit_time_get_nano();
182 if (!glXGetSyncValuesOML(dpy
, win
,
183 &check_ust
, &check_msc
, &check_sbc
))
185 fprintf(stderr
, "Follow-up GetSyncValuesOML failed\n");
189 if (new_ust
< last_ust
) {
190 fprintf(stderr
, "iteration %u: non-monotonic UST went "
191 "backward by %"PRId64
" during Wait\n",
192 i
, last_ust
- new_ust
);
193 result
= PIGLIT_FAIL
;
194 /* Wait returned something bogus, but GetSyncValues
195 * usually works, so try evaluating the rest of the
196 * tests using the check values. */
200 if (check_ust
< new_ust
) {
201 fprintf(stderr
, "iteration %u: non-monotonic UST went "
202 "backward by %"PRId64
" across GetSyncValues\n",
203 i
, last_ust
- check_ust
);
204 result
= PIGLIT_FAIL
;
207 if (new_msc
< last_msc
) {
208 fprintf(stderr
, "iteration %u: non-monotonic MSC went "
209 "backward by %"PRId64
" during Wait\n",
210 i
, last_msc
- new_msc
);
211 result
= PIGLIT_FAIL
;
212 /* Wait returned something bogus, but GetSyncValues
213 * usually works, so try evaluating the rest of the
214 * tests using the check values. */
218 if (check_msc
< new_msc
) {
219 fprintf(stderr
, "iteration %u: non-monotonic MSC went "
220 "backward by %"PRId64
" across GetSyncValues\n",
221 i
, last_msc
- check_msc
);
222 result
= PIGLIT_FAIL
;
225 if (new_sbc
!= target_sbc
) {
226 fprintf(stderr
, "iteration %u: Wait should have "
227 "returned at SBC %"PRId64
" but returned at "
229 i
, target_sbc
, new_sbc
);
230 result
= PIGLIT_FAIL
;
233 if (check_sbc
!= new_sbc
) {
234 fprintf(stderr
, "iteration %u: GetSyncValues "
235 "returned SBC %"PRId64
" but Wait returned "
237 i
, check_sbc
, new_sbc
);
238 result
= PIGLIT_FAIL
;
241 if (new_msc
> last_msc
) {
242 int64_t delta_msc
= new_msc
- last_msc
;
243 update_stats(&msc_ust_duration_stats
,
244 (new_ust
- last_ust
) / delta_msc
);
246 if (last_timestamp
>= 0) {
247 if (!piglit_time_is_monotonic()) {
249 "no monotonic clock\n");
250 piglit_merge_result(&result
,
254 &msc_wallclock_duration_stats
,
255 (new_timestamp
- last_timestamp
)
261 expected_msc
= target_msc
;
263 /* If there is a divisor, the expected MSC is the
264 * next MSC after last_msc such that
265 * MSC % divisor == remainder
267 int64_t last_remainder
= last_msc
% divisor
;
268 expected_msc
= last_msc
- last_remainder
+ msc_remainder
;
269 if (expected_msc
<= last_msc
)
270 expected_msc
+= divisor
;
273 if (new_msc
< expected_msc
) {
274 fprintf(stderr
, "iteration %u woke up %"PRId64
276 i
, expected_msc
- new_msc
);
277 result
= PIGLIT_FAIL
;
280 if (new_msc
> expected_msc
) {
281 fprintf(stderr
, "iteration %u woke up %"PRId64
282 " MSCs later than expected\n",
283 i
, new_msc
- expected_msc
);
284 piglit_merge_result(&result
, PIGLIT_WARN
);
287 if (new_msc
% divisor
!= msc_remainder
) {
288 fprintf(stderr
, "iteration %u woke up at wrong MSC"
289 " remainder %"PRId64
", not requested remainder"
291 i
, new_msc
% divisor
, msc_remainder
);
292 result
= PIGLIT_FAIL
;
298 last_timestamp
= new_timestamp
;
301 if (msc_ust_duration_stats
.n
< 2) {
302 fprintf(stderr
, "Not enough UST timing samples\n");
303 piglit_merge_result(&result
, PIGLIT_WARN
);
304 } else if (expected_msc_wallclock_duration
> 0.0) {
305 double apparent_ust_rate
= msc_ust_duration_stats
.mean
/
306 expected_msc_wallclock_duration
;
307 if (get_stddev(&msc_ust_duration_stats
) /
308 apparent_ust_rate
> 100)
310 fprintf(stderr
, "UST duration per MSC is surprisingly"
311 " variable (stddev %f USTs), but then it only"
312 " has to be monotonic\n",
313 get_stddev(&msc_ust_duration_stats
));
314 piglit_merge_result(&result
, PIGLIT_WARN
);
318 if (msc_wallclock_duration_stats
.n
< 2) {
319 fprintf(stderr
, "Not enough wallclock timing samples\n");
320 piglit_merge_result(&result
, PIGLIT_WARN
);
321 } else if (get_stddev(&msc_wallclock_duration_stats
) > 1000) {
322 fprintf(stderr
, "Wallclock time between MSCs has stddev > 1ms"
323 " (%fus), driver is probably not syncing to"
325 get_stddev(&msc_wallclock_duration_stats
));
326 result
= PIGLIT_FAIL
;
327 } else if (expected_msc_wallclock_duration
> 0.0) {
328 if (fabs(expected_msc_wallclock_duration
-
329 msc_wallclock_duration_stats
.mean
) > 50)
331 fprintf(stderr
, "Wallclock time between MSCs %fus"
332 " does not match glXGetMscRateOML %fus\n",
333 msc_wallclock_duration_stats
.mean
,
334 expected_msc_wallclock_duration
);
335 result
= PIGLIT_FAIL
;
343 parse_num_arg(int argc
, char **argv
, int j
)
349 fprintf(stderr
, "%s requires an argument\n", argv
[j
- 1]);
350 piglit_report_result(PIGLIT_FAIL
);
353 val
= strtoul(argv
[j
], &ptr
, 0);
354 if (!val
|| *ptr
!= '\0') {
355 fprintf(stderr
, "%s requires an argument\n", argv
[j
- 1]);
356 piglit_report_result(PIGLIT_FAIL
);
363 main(int argc
, char **argv
)
366 for (j
= 1; j
< argc
; j
++) {
367 if (!strcmp(argv
[j
], "-fullscreen")) {
369 } else if (!strcmp(argv
[j
], "-waitformsc")) {
370 use_swapbuffers
= false;
371 } else if (!strcmp(argv
[j
], "-divisor")) {
373 divisor
= parse_num_arg(argc
, argv
, j
);
374 } else if (!strcmp(argv
[j
], "-msc-delta")) {
376 target_msc_delta
= parse_num_arg(argc
, argv
, j
);
377 } else if (!strcmp(argv
[j
], "-auto")) {
378 piglit_automatic
= true;
380 fprintf(stderr
, "unsupported option %s\n", argv
[j
]);
381 piglit_report_result(PIGLIT_FAIL
);
385 if (divisor
&& target_msc_delta
) {
386 fprintf(stderr
, "this test doesn't support using both "
387 "-divisor and -msc-delta\n");
388 piglit_report_result(PIGLIT_FAIL
);
391 if (!use_swapbuffers
&& !divisor
&& !target_msc_delta
) {
392 fprintf(stderr
, "when using -waitformsc, this test requires "
393 "either -divisor or -msc-delta\n");
394 piglit_report_result(PIGLIT_FAIL
);
400 piglit_automatic
= true;
401 piglit_oml_sync_control_test_run(fullscreen
, draw
);