ext_framebuffer_object: setup max mipmap level to make fbo complete
[piglit.git] / tests / spec / glx_oml_sync_control / timing.c
blobab7258940f7cb2b0998948b4fb7401f9a6e7a15b
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);
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,
151 &result))
152 return PIGLIT_FAIL;
153 } else {
154 wait_for_msc(dpy, last_msc + 1, 0, 0, &last_ust, &last_msc,
155 &last_sbc, &result);
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,
171 target_msc, divisor,
172 msc_remainder, &new_ust, &new_msc,
173 &new_sbc, &result))
174 return PIGLIT_FAIL;
175 } else {
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");
186 return PIGLIT_FAIL;
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. */
197 new_ust = check_ust;
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. */
215 new_msc = check_msc;
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 "
228 "%"PRId64"\n",
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 "
236 "%"PRId64"\n",
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()) {
248 fprintf(stderr,
249 "no monotonic clock\n");
250 piglit_merge_result(&result,
251 PIGLIT_WARN);
252 } else {
253 update_stats(
254 &msc_wallclock_duration_stats,
255 (new_timestamp - last_timestamp)
256 / (delta_msc*1000));
261 expected_msc = target_msc;
262 if (!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
275 " MSCs early\n",
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"
290 " %"PRId64"\n",
291 i, new_msc % divisor, msc_remainder);
292 result = PIGLIT_FAIL;
295 last_ust = new_ust;
296 last_msc = new_msc;
297 last_sbc = new_sbc;
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"
324 " vblank\n",
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;
339 return result;
342 static unsigned int
343 parse_num_arg(int argc, char **argv, int j)
345 char *ptr;
346 unsigned int val;
348 if (j >= argc) {
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);
359 return val;
363 main(int argc, char **argv)
365 int j;
366 for (j = 1; j < argc; j++) {
367 if (!strcmp(argv[j], "-fullscreen")) {
368 fullscreen = true;
369 } else if (!strcmp(argv[j], "-waitformsc")) {
370 use_swapbuffers = false;
371 } else if (!strcmp(argv[j], "-divisor")) {
372 j++;
373 divisor = parse_num_arg(argc, argv, j);
374 } else if (!strcmp(argv[j], "-msc-delta")) {
375 j++;
376 target_msc_delta = parse_num_arg(argc, argv, j);
377 } else if (!strcmp(argv[j], "-auto")) {
378 piglit_automatic = true;
379 } else {
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);
397 if (!divisor) {
398 divisor=1;
400 piglit_automatic = true;
401 piglit_oml_sync_control_test_run(fullscreen, draw);
403 return 0;