glx-oml-sync-control-timing: Refactor helper functions for swap / wait
[piglit.git] / tests / spec / glx_oml_sync_control / timing.c
blobec400ca0794160e632371ff2d206fc7aa5c04a59
1 /*
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
13 * Software.
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
21 * IN THE SOFTWARE.
24 /**
25 * \file timing.c
26 * Validates that OML Sync Control implementation actually syncs to vertical
27 * retrace
30 #include "piglit-util-gl.h"
31 #include "piglit-glx-util.h"
32 #include "common.h"
34 * TODO: varying MSC deltas enumerated as arguments
35 * TODO: As a different test, create two drawables and verify they have
36 * independent SBC
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;
45 struct stats {
46 unsigned int n;
47 double mean;
48 double M2;
51 static void update_stats(struct stats *stats, double val) {
52 double delta = val - stats->mean;
53 stats->n += 1;
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));
62 static bool
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,
72 remainder);
73 if (*target_sbc <= 0) {
74 fprintf(stderr, "SwapBuffersMscOML failed\n");
75 return false;
78 if (*target_sbc != last_sbc + 1) {
79 fprintf(stderr,
80 "glXSwapBuffersMscOML calculated the wrong target sbc: "
81 "expected %"PRId64" but got %"PRId64"\n", last_sbc + 1,
82 *target_sbc);
83 *result = PIGLIT_FAIL;
86 if (!glXWaitForSbcOML(dpy, win, *target_sbc, new_ust, new_msc,
87 new_sbc)) {
88 fprintf(stderr, "glXWaitForSbcOML failed\n");
89 *result = PIGLIT_FAIL;
92 return true;
95 static void
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
108 draw(Display *dpy)
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;
117 unsigned int i;
119 if (!glXGetSyncValuesOML(dpy, win, &last_ust, &last_msc, &last_sbc)) {
120 fprintf(stderr, "Initial glXGetSyncValuesOML failed\n");
121 return PIGLIT_FAIL;
124 /* Check that the window is fresh */
125 if (last_sbc != 0) {
126 fprintf(stderr, "Initial SBC for the window should be 0, was "
127 "%" PRId64 "\n",
128 last_sbc);
129 piglit_merge_result(&result, PIGLIT_WARN);
132 if (!glXGetMscRateOML(dpy, win, &rate_num, &rate_den)) {
133 fprintf(stderr,
134 "glXGetMscRateOML failed, can't test MSC duration\n");
135 piglit_merge_result(&result, PIGLIT_WARN);
136 } else {
137 expected_msc_wallclock_duration = 1e6 * rate_den / rate_num;
140 piglit_set_timeout(5, PIGLIT_FAIL);
143 for (i = 0; i < loops; i++) {
144 int64_t new_ust = 0xd0, new_msc = 0xd0, new_sbc = 0xd0;
145 int64_t check_ust = 0xd0, check_msc = 0xd0, check_sbc = 0xd0;
146 int64_t new_timestamp; /* in nano seconds */
147 int64_t expected_msc, target_sbc;
148 int64_t target_msc = 0;
150 if (target_msc_delta) {
151 target_msc = last_msc + target_msc_delta;
154 if (use_swapbuffers) {
155 if (!swap_buffers_msc(dpy, last_sbc, &target_sbc,
156 target_msc, divisor,
157 msc_remainder, &new_ust, &new_msc,
158 &new_sbc, &result))
159 return PIGLIT_FAIL;
160 } else {
161 target_sbc = last_sbc;
162 wait_for_msc(dpy, target_msc, divisor, msc_remainder,
163 &new_ust, &new_msc, &new_sbc, &result);
165 new_timestamp = piglit_time_get_nano();
167 if (!glXGetSyncValuesOML(dpy, win,
168 &check_ust, &check_msc, &check_sbc))
170 fprintf(stderr, "Follow-up GetSyncValuesOML failed\n");
171 return PIGLIT_FAIL;
174 if (new_ust < last_ust) {
175 fprintf(stderr, "iteration %u: non-monotonic UST went "
176 "backward by %"PRId64" during Wait\n",
177 i, last_ust - new_ust);
178 result = PIGLIT_FAIL;
179 /* Wait returned something bogus, but GetSyncValues
180 * usually works, so try evaluating the rest of the
181 * tests using the check values. */
182 new_ust = check_ust;
185 if (check_ust < new_ust) {
186 fprintf(stderr, "iteration %u: non-monotonic UST went "
187 "backward by %"PRId64" across GetSyncValues\n",
188 i, last_ust - check_ust);
189 result = PIGLIT_FAIL;
192 if (new_msc < last_msc) {
193 fprintf(stderr, "iteration %u: non-monotonic MSC went "
194 "backward by %"PRId64" during Wait\n",
195 i, last_msc - new_msc);
196 result = PIGLIT_FAIL;
197 /* Wait returned something bogus, but GetSyncValues
198 * usually works, so try evaluating the rest of the
199 * tests using the check values. */
200 new_msc = check_msc;
203 if (check_msc < new_msc) {
204 fprintf(stderr, "iteration %u: non-monotonic MSC went "
205 "backward by %"PRId64" across GetSyncValues\n",
206 i, last_msc - check_msc);
207 result = PIGLIT_FAIL;
210 if (new_sbc != target_sbc) {
211 fprintf(stderr, "iteration %u: Wait should have "
212 "returned at SBC %"PRId64" but returned at "
213 "%"PRId64"\n",
214 i, target_sbc, new_sbc);
215 result = PIGLIT_FAIL;
218 if (check_sbc != new_sbc) {
219 fprintf(stderr, "iteration %u: GetSyncValues "
220 "returned SBC %"PRId64" but Wait returned "
221 "%"PRId64"\n",
222 i, check_sbc, new_sbc);
223 result = PIGLIT_FAIL;
226 if (new_msc > last_msc) {
227 int64_t delta_msc = new_msc - last_msc;
228 update_stats(&msc_ust_duration_stats,
229 (new_ust - last_ust) / delta_msc);
231 if (last_timestamp >= 0) {
232 if (!piglit_time_is_monotonic()) {
233 fprintf(stderr,
234 "no monotonic clock\n");
235 piglit_merge_result(&result,
236 PIGLIT_WARN);
237 } else {
238 update_stats(
239 &msc_wallclock_duration_stats,
240 (new_timestamp - last_timestamp)
241 / (delta_msc*1000));
246 expected_msc = target_msc;
247 if (!target_msc) {
248 /* If there is a divisor, the expected MSC is the
249 * next MSC after last_msc such that
250 * MSC % divisor == remainder
252 int64_t last_remainder = last_msc % divisor;
253 expected_msc = last_msc - last_remainder + msc_remainder;
254 if (expected_msc <= last_msc)
255 expected_msc += divisor;
258 if (new_msc < expected_msc) {
259 fprintf(stderr, "iteration %u woke up %"PRId64
260 " MSCs early\n",
261 i, expected_msc - new_msc);
262 result = PIGLIT_FAIL;
265 if (new_msc > expected_msc) {
266 fprintf(stderr, "iteration %u woke up %"PRId64
267 " MSCs later than expected\n",
268 i, new_msc - expected_msc);
269 piglit_merge_result(&result, PIGLIT_WARN);
272 if (new_msc % divisor != msc_remainder) {
273 fprintf(stderr, "iteration %u woke up at wrong MSC"
274 " remainder %"PRId64", not requested remainder"
275 " %"PRId64"\n",
276 i, new_msc % divisor, msc_remainder);
277 result = PIGLIT_FAIL;
280 last_ust = new_ust;
281 last_msc = new_msc;
282 last_sbc = new_sbc;
283 last_timestamp = new_timestamp;
286 if (msc_ust_duration_stats.n < 2) {
287 fprintf(stderr, "Not enough UST timing samples\n");
288 piglit_merge_result(&result, PIGLIT_WARN);
289 } else if (expected_msc_wallclock_duration > 0.0) {
290 double apparent_ust_rate = msc_ust_duration_stats.mean /
291 expected_msc_wallclock_duration;
292 if (get_stddev(&msc_ust_duration_stats) /
293 apparent_ust_rate > 100)
295 fprintf(stderr, "UST duration per MSC is surprisingly"
296 " variable (stddev %f USTs), but then it only"
297 " has to be monotonic\n",
298 get_stddev(&msc_ust_duration_stats));
299 piglit_merge_result(&result, PIGLIT_WARN);
303 if (msc_wallclock_duration_stats.n < 2) {
304 fprintf(stderr, "Not enough wallclock timing samples\n");
305 piglit_merge_result(&result, PIGLIT_WARN);
306 } else if (get_stddev(&msc_wallclock_duration_stats) > 1000) {
307 fprintf(stderr, "Wallclock time between MSCs has stddev > 1ms"
308 " (%fus), driver is probably not syncing to"
309 " vblank\n",
310 get_stddev(&msc_wallclock_duration_stats));
311 result = PIGLIT_FAIL;
312 } else if (expected_msc_wallclock_duration > 0.0) {
313 if (fabs(expected_msc_wallclock_duration -
314 msc_wallclock_duration_stats.mean) > 50)
316 fprintf(stderr, "Wallclock time between MSCs %fus"
317 " does not match glXGetMscRateOML %fus\n",
318 msc_wallclock_duration_stats.mean,
319 expected_msc_wallclock_duration);
320 result = PIGLIT_FAIL;
324 return result;
327 static unsigned int
328 parse_num_arg(int argc, char **argv, int j)
330 char *ptr;
331 unsigned int val;
333 if (j >= argc) {
334 fprintf(stderr, "%s requires an argument\n", argv[j - 1]);
335 piglit_report_result(PIGLIT_FAIL);
338 val = strtoul(argv[j], &ptr, 0);
339 if (!val || *ptr != '\0') {
340 fprintf(stderr, "%s requires an argument\n", argv[j - 1]);
341 piglit_report_result(PIGLIT_FAIL);
344 return val;
348 main(int argc, char **argv)
350 int j;
351 for (j = 1; j < argc; j++) {
352 if (!strcmp(argv[j], "-fullscreen")) {
353 fullscreen = true;
354 } else if (!strcmp(argv[j], "-waitformsc")) {
355 use_swapbuffers = false;
356 } else if (!strcmp(argv[j], "-divisor")) {
357 j++;
358 divisor = parse_num_arg(argc, argv, j);
359 } else if (!strcmp(argv[j], "-msc-delta")) {
360 j++;
361 target_msc_delta = parse_num_arg(argc, argv, j);
362 } else if (!strcmp(argv[j], "-auto")) {
363 piglit_automatic = true;
364 } else {
365 fprintf(stderr, "unsupported option %s\n", argv[j]);
366 piglit_report_result(PIGLIT_FAIL);
370 if (divisor && target_msc_delta) {
371 fprintf(stderr, "this test doesn't support using both "
372 "-divisor and -msc-delta\n");
373 piglit_report_result(PIGLIT_FAIL);
376 if (!use_swapbuffers && !divisor && !target_msc_delta) {
377 fprintf(stderr, "when using -waitformsc, this test requires "
378 "either -divisor or -msc-delta\n");
379 piglit_report_result(PIGLIT_FAIL);
382 if (!divisor) {
383 divisor=1;
385 piglit_automatic = true;
386 piglit_oml_sync_control_test_run(fullscreen, draw);
388 return 0;