blob: 8e9d13f1acfface5513ba83e0bca670bc90e637d [file] [log] [blame]
Pekka Paalanen4520d5c2012-01-16 15:04:28 +02001/*
2 * Copyright © 2012 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <GLES2/gl2.h>
26#include <math.h>
27#include <unistd.h>
28#include <signal.h>
29#include <time.h>
30
31#include "matrix.h"
32
Pekka Paalanend1f0ab62012-01-20 10:47:57 +020033struct inverse_matrix {
34 double LU[16]; /* column-major */
35 unsigned perm[4]; /* permutation */
36};
37
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020038static struct timespec begin_time;
39
40static void
41reset_timer(void)
42{
43 clock_gettime(CLOCK_MONOTONIC, &begin_time);
44}
45
46static double
47read_timer(void)
48{
49 struct timespec t;
50
51 clock_gettime(CLOCK_MONOTONIC, &t);
52 return (double)(t.tv_sec - begin_time.tv_sec) +
53 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
54}
55
56static double
57det3x3(const GLfloat *c0, const GLfloat *c1, const GLfloat *c2)
58{
59 return (double)
60 c0[0] * c1[1] * c2[2] +
61 c1[0] * c2[1] * c0[2] +
62 c2[0] * c0[1] * c1[2] -
63 c0[2] * c1[1] * c2[0] -
64 c1[2] * c2[1] * c0[0] -
65 c2[2] * c0[1] * c1[0];
66}
67
68static double
69determinant(const struct weston_matrix *m)
70{
71 double det = 0;
72#if 1
73 /* develop on last row */
74 det -= m->d[3 + 0 * 4] * det3x3(&m->d[4], &m->d[8], &m->d[12]);
75 det += m->d[3 + 1 * 4] * det3x3(&m->d[0], &m->d[8], &m->d[12]);
76 det -= m->d[3 + 2 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[12]);
77 det += m->d[3 + 3 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[8]);
78#else
79 /* develop on first row */
80 det += m->d[0 + 0 * 4] * det3x3(&m->d[5], &m->d[9], &m->d[13]);
81 det -= m->d[0 + 1 * 4] * det3x3(&m->d[1], &m->d[9], &m->d[13]);
82 det += m->d[0 + 2 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[13]);
83 det -= m->d[0 + 3 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[9]);
84#endif
85 return det;
86}
87
88static void
Pekka Paalanend1f0ab62012-01-20 10:47:57 +020089print_permutation_matrix(const struct inverse_matrix *m)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020090{
Pekka Paalanend1f0ab62012-01-20 10:47:57 +020091 const unsigned *p = m->perm;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +020092 const char *row[4] = {
93 "1 0 0 0\n",
94 "0 1 0 0\n",
95 "0 0 1 0\n",
96 "0 0 0 1\n"
97 };
98
99 printf(" P =\n%s%s%s%s", row[p[0]], row[p[1]], row[p[2]], row[p[3]]);
100}
101
102static void
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200103print_LU_decomposition(const struct inverse_matrix *m)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200104{
105 unsigned r, c;
106
107 printf(" L "
108 " U\n");
109 for (r = 0; r < 4; ++r) {
110 double v;
111
112 for (c = 0; c < 4; ++c) {
113 if (c < r)
114 v = m->LU[r + c * 4];
115 else if (c == r)
116 v = 1.0;
117 else
118 v = 0.0;
119 printf(" %12.6f", v);
120 }
121
122 printf(" | ");
123
124 for (c = 0; c < 4; ++c) {
125 if (c >= r)
126 v = m->LU[r + c * 4];
127 else
128 v = 0.0;
129 printf(" %12.6f", v);
130 }
131 printf("\n");
132 }
133}
134
135static void
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200136print_inverse_data_matrix(const struct inverse_matrix *m)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200137{
138 unsigned r, c;
139
140 for (r = 0; r < 4; ++r) {
141 for (c = 0; c < 4; ++c)
142 printf(" %12.6f", m->LU[r + c * 4]);
143 printf("\n");
144 }
145
146 printf("permutation: ");
147 for (r = 0; r < 4; ++r)
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200148 printf(" %u", m->perm[r]);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200149 printf("\n");
150}
151
152static void
153print_matrix(const struct weston_matrix *m)
154{
155 unsigned r, c;
156
157 for (r = 0; r < 4; ++r) {
158 for (c = 0; c < 4; ++c)
159 printf(" %14.6e", m->d[r + c * 4]);
160 printf("\n");
161 }
162}
163
164static double
165frand(void)
166{
167 double r = random();
168 return r / (double)(RAND_MAX / 2) - 1.0f;
169}
170
171static void
172randomize_matrix(struct weston_matrix *m)
173{
174 unsigned i;
175 for (i = 0; i < 16; ++i)
176#if 1
177 m->d[i] = frand() * exp(10.0 * frand());
178#else
179 m->d[i] = frand();
180#endif
181}
182
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200183/* Take a matrix, compute inverse, multiply together
184 * and subtract the identity matrix to get the error matrix.
185 * Return the largest absolute value from the error matrix.
186 */
187static double
188test_inverse(struct weston_matrix *m)
189{
190 unsigned i;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200191 struct inverse_matrix q;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200192 double errsup = 0.0;
193
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200194 if (matrix_invert(q.LU, q.perm, m) != 0)
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200195 return INFINITY;
196
197 for (i = 0; i < 4; ++i)
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200198 inverse_transform(q.LU, q.perm, &m->d[i * 4]);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200199
200 m->d[0] -= 1.0f;
201 m->d[5] -= 1.0f;
202 m->d[10] -= 1.0f;
203 m->d[15] -= 1.0f;
204
205 for (i = 0; i < 16; ++i) {
206 double err = fabs(m->d[i]);
207 if (err > errsup)
208 errsup = err;
209 }
210
211 return errsup;
212}
213
214enum {
215 TEST_OK,
216 TEST_NOT_INVERTIBLE_OK,
217 TEST_FAIL,
218 TEST_COUNT
219};
220
221static int
222test(void)
223{
224 struct weston_matrix m;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200225 double det, errsup;
226
227 randomize_matrix(&m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200228 det = determinant(&m);
229
230 errsup = test_inverse(&m);
231 if (errsup < 1e-6)
232 return TEST_OK;
233
234 if (fabs(det) < 1e-5 && isinf(errsup))
235 return TEST_NOT_INVERTIBLE_OK;
236
237 printf("test fail, det: %g, error sup: %g\n", det, errsup);
Kristian Høgsberg010f98b2012-02-23 17:30:45 -0500238
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200239 return TEST_FAIL;
240}
241
242static int running;
243static void
244stopme(int n)
245{
246 running = 0;
247}
248
249static void
250test_loop_precision(void)
251{
252 int counts[TEST_COUNT] = { 0 };
253
254 printf("\nRunning a test loop for 10 seconds...\n");
255 running = 1;
256 alarm(10);
257 while (running) {
258 counts[test()]++;
259 }
260
261 printf("tests: %d ok, %d not invertible but ok, %d failed.\n"
262 "Total: %d iterations.\n",
263 counts[TEST_OK], counts[TEST_NOT_INVERTIBLE_OK],
264 counts[TEST_FAIL],
265 counts[TEST_OK] + counts[TEST_NOT_INVERTIBLE_OK] +
266 counts[TEST_FAIL]);
267}
268
269static void __attribute__((noinline))
270test_loop_speed_matrixvector(void)
271{
272 struct weston_matrix m;
273 struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } };
274 unsigned long count = 0;
275 double t;
276
277 printf("\nRunning 3 s test on weston_matrix_transform()...\n");
278
279 weston_matrix_init(&m);
280
281 running = 1;
282 alarm(3);
283 reset_timer();
284 while (running) {
285 weston_matrix_transform(&m, &v);
286 count++;
287 }
288 t = read_timer();
289
290 printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n",
291 count, t, 1e9 * t / count);
292}
293
294static void __attribute__((noinline))
295test_loop_speed_inversetransform(void)
296{
297 struct weston_matrix m;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200298 struct inverse_matrix inv;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200299 struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } };
300 unsigned long count = 0;
301 double t;
302
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200303 printf("\nRunning 3 s test on inverse_transform()...\n");
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200304
305 weston_matrix_init(&m);
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200306 matrix_invert(inv.LU, inv.perm, &m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200307
308 running = 1;
309 alarm(3);
310 reset_timer();
311 while (running) {
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200312 inverse_transform(inv.LU, inv.perm, v.f);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200313 count++;
314 }
315 t = read_timer();
316
317 printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n",
318 count, t, 1e9 * t / count);
319}
320
321static void __attribute__((noinline))
322test_loop_speed_invert(void)
323{
324 struct weston_matrix m;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200325 struct inverse_matrix inv;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200326 unsigned long count = 0;
327 double t;
328
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200329 printf("\nRunning 3 s test on matrix_invert()...\n");
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200330
331 weston_matrix_init(&m);
332
333 running = 1;
334 alarm(3);
335 reset_timer();
336 while (running) {
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200337 matrix_invert(inv.LU, inv.perm, &m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200338 count++;
339 }
340 t = read_timer();
341
342 printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n",
343 count, t, 1e9 * t / count);
344}
345
346static void __attribute__((noinline))
347test_loop_speed_invert_explicit(void)
348{
349 struct weston_matrix m;
350 unsigned long count = 0;
351 double t;
352
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200353 printf("\nRunning 3 s test on weston_matrix_invert()...\n");
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200354
355 weston_matrix_init(&m);
356
357 running = 1;
358 alarm(3);
359 reset_timer();
360 while (running) {
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200361 weston_matrix_invert(&m, &m);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200362 count++;
363 }
364 t = read_timer();
365
366 printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n",
367 count, t, 1e9 * t / count);
368}
369
370int main(void)
371{
372 struct sigaction ding;
373 struct weston_matrix M;
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200374 struct inverse_matrix Q;
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200375 int ret;
376 double errsup;
377 double det;
378
379 ding.sa_handler = stopme;
380 sigemptyset(&ding.sa_mask);
381 ding.sa_flags = 0;
382 sigaction(SIGALRM, &ding, NULL);
383
384 srandom(13);
385
386 M.d[0] = 3.0; M.d[4] = 17.0; M.d[8] = 10.0; M.d[12] = 0.0;
387 M.d[1] = 2.0; M.d[5] = 4.0; M.d[9] = -2.0; M.d[13] = 0.0;
388 M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0;
389 M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0;
390
Pekka Paalanend1f0ab62012-01-20 10:47:57 +0200391 ret = matrix_invert(Q.LU, Q.perm, &M);
Pekka Paalanen4520d5c2012-01-16 15:04:28 +0200392 printf("ret = %d\n", ret);
393 printf("det = %g\n\n", determinant(&M));
394
395 if (ret != 0)
396 return 1;
397
398 print_inverse_data_matrix(&Q);
399 printf("P * A = L * U\n");
400 print_permutation_matrix(&Q);
401 print_LU_decomposition(&Q);
402
403
404 printf("a random matrix:\n");
405 randomize_matrix(&M);
406 det = determinant(&M);
407 print_matrix(&M);
408 errsup = test_inverse(&M);
409 printf("\nThe matrix multiplied by its inverse, error:\n");
410 print_matrix(&M);
411 printf("max abs error: %g, original determinant %g\n", errsup, det);
412
413 test_loop_precision();
414 test_loop_speed_matrixvector();
415 test_loop_speed_inversetransform();
416 test_loop_speed_invert();
417 test_loop_speed_invert_explicit();
418
419 return 0;
420}