quakeforge/libs/util/test/test-simd.c

340 lines
12 KiB
C
Raw Normal View History

#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "QF/simd/vec4d.h"
#include "QF/simd/vec4f.h"
#define right { 1, 0, 0 }
#define forward { 0, 1, 0 }
#define up { 0, 0, 1 }
#define one { 1, 1, 1, 1 }
#define half { 0.5, 0.5, 0.5, 0.5 }
#define zero { 0, 0, 0, 0 }
#define qident { 0, 0, 0, 1 }
#define qtest { 0.64, 0.48, 0, 0.6 }
#define nright { -1, 0, 0 }
#define nforward { 0, -1, 0 }
#define nup { 0, 0, -1 }
#define none { -1, -1, -1, -1 }
#define nqident { 0, 0, 0, -1 }
#define s05 0.70710678118654757
typedef struct {
vec4d_t (*op) (vec4d_t a, vec4d_t b);
vec4d_t a;
vec4d_t b;
vec4d_t expect;
vec4d_t ulp_errors;
} vec4d_test_t;
typedef struct {
vec4f_t (*op) (vec4f_t a, vec4f_t b);
vec4f_t a;
vec4f_t b;
vec4f_t expect;
vec4f_t ulp_errors;
} vec4f_test_t;
static vec4d_t tvtruncd (vec4d_t v, vec4d_t ignore)
{
return vtruncd (v);
}
static vec4d_t tvceild (vec4d_t v, vec4d_t ignore)
{
return vceild (v);
}
static vec4d_t tvfloord (vec4d_t v, vec4d_t ignore)
{
return vfloord (v);
}
static vec4d_t tqconjd (vec4d_t v, vec4d_t ignore)
{
return qconjd (v);
}
static vec4f_t tvtruncf (vec4f_t v, vec4f_t ignore)
{
return vtruncf (v);
}
static vec4f_t tvceilf (vec4f_t v, vec4f_t ignore)
{
return vceilf (v);
}
static vec4f_t tvfloorf (vec4f_t v, vec4f_t ignore)
{
return vfloorf (v);
}
static vec4f_t tqconjf (vec4f_t v, vec4f_t ignore)
{
return qconjf (v);
}
static vec4d_test_t vec4d_tests[] = {
// 3D dot products
{ dotd, right, right, one },
{ dotd, right, forward, zero },
{ dotd, right, up, zero },
{ dotd, forward, right, zero },
{ dotd, forward, forward, one },
{ dotd, forward, up, zero },
{ dotd, up, right, zero },
{ dotd, up, forward, zero },
{ dotd, up, up, one },
// one is 4D, so its self dot product is 4
{ dotd, one, one, { 4, 4, 4, 4} },
{ dotd, one, none, {-4, -4, -4, -4} },
// 3D cross products
{ crossd, right, right, zero },
{ crossd, right, forward, up },
{ crossd, right, up, nforward },
{ crossd, forward, right, nup },
{ crossd, forward, forward, zero },
{ crossd, forward, up, right },
{ crossd, up, right, forward },
{ crossd, up, forward, nright },
{ crossd, up, up, zero },
// double whammy tests: cross product with an angled vector and
// ensuring that a 4d vector (non-zero w component) does not affect
// the result, including the result's w component remaining zero.
{ crossd, right, one, { 0, -1, 1} },
{ crossd, forward, one, { 1, 0, -1} },
{ crossd, up, one, {-1, 1, 0} },
{ crossd, one, right, { 0, 1, -1} },
{ crossd, one, forward, {-1, 0, 1} },
{ crossd, one, up, { 1, -1, 0} },
// This one fails when optimizing with -mfma (which is why fma is not
// used): ulp errors in z and w
{ crossd, qtest, qtest, {0, 0, 0, 0} },
{ qmuld, qident, qident, qident },
{ qmuld, qident, right, right },
{ qmuld, qident, forward, forward },
{ qmuld, qident, up, up },
{ qmuld, right, qident, right },
{ qmuld, forward, qident, forward },
{ qmuld, up, qident, up },
{ qmuld, right, right, nqident },
{ qmuld, right, forward, up },
{ qmuld, right, up, nforward },
{ qmuld, forward, right, nup },
{ qmuld, forward, forward, nqident },
{ qmuld, forward, up, right },
{ qmuld, up, right, forward },
{ qmuld, up, forward, nright },
{ qmuld, up, up, nqident },
{ qmuld, one, one, { 2, 2, 2, -2 } },
{ qmuld, one, { 2, 2, 2, -2 }, { 0, 0, 0, -8 } },
// This one fails when optimizing with -mfma (which is why fma is not
// used): ulp error in z
{ qmuld, qtest, qtest, {0.768, 0.576, 0, -0.28} },
// The one vector is not unit (magnitude 2), so using it as a rotation
// quaternion results in scaling by 4. However, it still has the effect
// of rotating 120 degrees around the axis equidistant from the three
// orthogonal axes such that x->y->z->x
{ qvmuld, one, right, { 0, 4, 0, 0 } },
{ qvmuld, one, forward, { 0, 0, 4, 0 } },
{ qvmuld, one, up, { 4, 0, 0, 0 } },
{ qvmuld, one, {1,1,1,0}, { 4, 4, 4, 0 } },
{ qvmuld, one, one, { 4, 4, 4, -2 } },
// The half vector is unit.
{ qvmuld, half, right, forward },
{ qvmuld, half, forward, up },
{ qvmuld, half, up, right },
{ qvmuld, half, {1,1,1,0}, { 1, 1, 1, 0 } },
// one is a 4D vector and qvmuld is meant for 3D vectors. However, it
// seems that the vector's w has no effect on the 3d portion of the
// result, but the result's w is cosine of the full rotation angle
// scaled by quaternion magnitude and vector w
{ qvmuld, half, one, { 1, 1, 1, -0.5 } },
{ qvmuld, half, {2,2,2,2}, { 2, 2, 2, -1 } },
{ qvmuld, qtest, right, {0.5392, 0.6144, -0.576, 0} },
{ qvmuld, qtest, forward, {0.6144, 0.1808, 0.768, 0},
{0, -2.7e-17, 0, 0} },
{ qvmuld, qtest, up, {0.576, -0.768, -0.28, 0} },
{ qrotd, right, right, qident },
{ qrotd, right, forward, { 0, 0, s05, s05 },
{0, 0, -1.1e-16, 0} },
{ qrotd, right, up, { 0, -s05, 0, s05 },
{0, 1.1e-16, 0, 0} },
{ qrotd, forward, right, { 0, 0, -s05, s05 },
{0, 0, 1.1e-16, 0} },
{ qrotd, forward, forward, qident },
{ qrotd, forward, up, { s05, 0, 0, s05 },
{-1.1e-16, 0, 0, 0} },
{ qrotd, up, right, { 0, s05, 0, s05 },
{0, -1.1e-16, 0, 0} },
{ qrotd, up, forward, { -s05, 0, 0, s05 },
{ 1.1e-16, 0, 0, 0} },
{ qrotd, up, up, qident },
{ tvtruncd, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -1, -2 } },
{ tvceild, { 1.1, 2.9, -1.1, -2.9 }, {}, { 2, 3, -1, -2 } },
{ tvfloord, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -2, -3 } },
{ tqconjd, one, {}, { -1, -1, -1, 1 } },
};
#define num_vec4d_tests (sizeof (vec4d_tests) / (sizeof (vec4d_tests[0])))
static vec4f_test_t vec4f_tests[] = {
// 3D dot products
{ dotf, right, right, one },
{ dotf, right, forward, zero },
{ dotf, right, up, zero },
{ dotf, forward, right, zero },
{ dotf, forward, forward, one },
{ dotf, forward, up, zero },
{ dotf, up, right, zero },
{ dotf, up, forward, zero },
{ dotf, up, up, one },
// one is 4D, so its self dot product is 4
{ dotf, one, one, { 4, 4, 4, 4} },
{ dotf, one, none, {-4, -4, -4, -4} },
// 3D cross products
{ crossf, right, right, zero },
{ crossf, right, forward, up },
{ crossf, right, up, nforward },
{ crossf, forward, right, nup },
{ crossf, forward, forward, zero },
{ crossf, forward, up, right },
{ crossf, up, right, forward },
{ crossf, up, forward, nright },
{ crossf, up, up, zero },
// double whammy tests: cross product with an angled vector and
// ensuring that a 4d vector (non-zero w component) does not affect
// the result, including the result's w component remaining zero.
{ crossf, right, one, { 0, -1, 1} },
{ crossf, forward, one, { 1, 0, -1} },
{ crossf, up, one, {-1, 1, 0} },
{ crossf, one, right, { 0, 1, -1} },
{ crossf, one, forward, {-1, 0, 1} },
{ crossf, one, up, { 1, -1, 0} },
{ crossf, qtest, qtest, {0, 0, 0, 0} },
{ qmulf, qident, qident, qident },
{ qmulf, qident, right, right },
{ qmulf, qident, forward, forward },
{ qmulf, qident, up, up },
{ qmulf, right, qident, right },
{ qmulf, forward, qident, forward },
{ qmulf, up, qident, up },
{ qmulf, right, right, nqident },
{ qmulf, right, forward, up },
{ qmulf, right, up, nforward },
{ qmulf, forward, right, nup },
{ qmulf, forward, forward, nqident },
{ qmulf, forward, up, right },
{ qmulf, up, right, forward },
{ qmulf, up, forward, nright },
{ qmulf, up, up, nqident },
{ qmulf, one, one, { 2, 2, 2, -2 } },
{ qmulf, one, { 2, 2, 2, -2 }, { 0, 0, 0, -8 } },
{ qmulf, qtest, qtest, {0.768, 0.576, 0, -0.28},
{0, 6e-8, 0, 3e-8} },
// The one vector is not unit (magnitude 2), so using it as a rotation
// quaternion results in scaling by 4. However, it still has the effect
// of rotating 120 degrees around the axis equidistant from the three
// orthogonal axes such that x->y->z->x
{ qvmulf, one, right, { 0, 4, 0, 0 } },
{ qvmulf, one, forward, { 0, 0, 4, 0 } },
{ qvmulf, one, up, { 4, 0, 0, 0 } },
{ qvmulf, one, {1,1,1,0}, { 4, 4, 4, 0 } },
{ qvmulf, one, one, { 4, 4, 4, -2 } },
{ qvmulf, qtest, right, {0.5392, 0.6144, -0.576, 0},
{0, -5.9e-08, -6e-8, 0} },
{ qvmulf, qtest, forward, {0.6144, 0.1808, 0.768, 0},
{-5.9e-08, 1.5e-08, 0, 0} },
{ qvmulf, qtest, up, {0.576, -0.768, -0.28, 0},
{6e-8, 0, 3e-8, 0} },
{ qrotf, right, right, qident },
{ qrotf, right, forward, { 0, 0, s05, s05 } },
{ qrotf, right, up, { 0, -s05, 0, s05 } },
{ qrotf, forward, right, { 0, 0, -s05, s05 } },
{ qrotf, forward, forward, qident },
{ qrotf, forward, up, { s05, 0, 0, s05 } },
{ qrotf, up, right, { 0, s05, 0, s05 } },
{ qrotf, up, forward, { -s05, 0, 0, s05 } },
{ qrotf, up, up, qident },
{ tvtruncf, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -1, -2 } },
{ tvceilf, { 1.1, 2.9, -1.1, -2.9 }, {}, { 2, 3, -1, -2 } },
{ tvfloorf, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -2, -3 } },
{ tqconjf, one, {}, { -1, -1, -1, 1 } },
};
#define num_vec4f_tests (sizeof (vec4f_tests) / (sizeof (vec4f_tests[0])))
static int
run_vec4d_tests (void)
{
int ret = 0;
for (size_t i = 0; i < num_vec4d_tests; i++) {
__auto_type test = &vec4d_tests[i];
vec4d_t result = test->op (test->a, test->b);
vec4d_t expect = test->expect + test->ulp_errors;
vec4l_t res = result != expect;
if (res[0] || res[1] || res[2] || res[3]) {
ret |= 1;
printf ("\nrun_vec4d_tests\n");
printf ("a: " VEC4D_FMT "\n", VEC4_EXP(test->a));
printf ("b: " VEC4D_FMT "\n", VEC4_EXP(test->b));
printf ("r: " VEC4D_FMT "\n", VEC4_EXP(result));
printf ("t: " VEC4L_FMT "\n", VEC4_EXP(res));
printf ("E: " VEC4D_FMT "\n", VEC4_EXP(expect));
printf ("e: " VEC4D_FMT "\n", VEC4_EXP(test->expect));
printf ("u: " VEC4D_FMT "\n", VEC4_EXP(test->ulp_errors));
}
}
return ret;
}
static int
run_vec4f_tests (void)
{
int ret = 0;
for (size_t i = 0; i < num_vec4f_tests; i++) {
__auto_type test = &vec4f_tests[i];
vec4f_t result = test->op (test->a, test->b);
vec4f_t expect = test->expect + test->ulp_errors;
vec4i_t res = result != expect;
if (res[0] || res[1] || res[2] || res[3]) {
ret |= 1;
printf ("\nrun_vec4f_tests\n");
printf ("a: " VEC4F_FMT "\n", VEC4_EXP(test->a));
printf ("b: " VEC4F_FMT "\n", VEC4_EXP(test->b));
printf ("r: " VEC4F_FMT "\n", VEC4_EXP(result));
printf ("t: " VEC4I_FMT "\n", VEC4_EXP(res));
printf ("E: " VEC4F_FMT "\n", VEC4_EXP(expect));
printf ("e: " VEC4F_FMT "\n", VEC4_EXP(test->expect));
printf ("u: " VEC4F_FMT "\n", VEC4_EXP(test->ulp_errors));
}
}
return ret;
}
int
main (void)
{
int ret = 0;
ret |= run_vec4d_tests ();
ret |= run_vec4f_tests ();
return ret;
}