# BST-TIMEOUT: 180 . "$BST_LIB" # glm 0.9.9.8+0.9.9.9+git (SRCREV efec5db0, upstream master) -- OpenGL # Mathematics: the vector, matrix and quaternion types that mirror GLSL, plus # the transform and geometry functions built on them. Header-only: the port # ships 427 files under /usr/include/glm, glm.pc and the find_package(glm) # cmake package. No archive, no shared object, no CLI. # # THIS TARGET HAS NO GPU AND NONE IS IMPLIED. What ships is the maths header # set. Nothing here renders anything and nothing here talks to a driver, so a # reader should not take the presence of a library named "OpenGL Mathematics" # as graphics enablement. The glm entry in docs/brintos-test-plan.md says the # same in one line. That entry is cited by SUBJECT and not by number on # purpose: entry numbers are assigned at merge time and several ports land in # parallel, so a number written into a SHIPPED file goes stale the moment # another port lands first -- and correcting it would mean rebuilding the # image to keep the shipped bytes matching their own source. # # WHAT IS ACTUALLY TRUE OF THIS PORT, against its three batch siblings. cli11 # (76-cli11.test) and exprtk (81-exprtk.test) ship headers that CANNOT be # compiled on this arm: both are ordinary C++ written for a toolchain with # exceptions, and both have to assert their own failure. libeigen # (76-libeigen.test) compiles because upstream routes every throw through its # own EIGEN_THROW guard, which reads the COMPILER's exceptions state. GLM needs # neither an opt-in nor a guard: there is no `throw`, no `try` and no `catch` # anywhere under glm/ -- measured on this SRCREV, the words occur three times # and every one is inside a comment. It compiles, it runs, and target code can # be built against it today. # # SECTION 3 IS THE TRIPWIRE, and it is the section to read first if this suite # ever goes red. It asserts the compile-time state the numbers rest on. A red # there means the toolchain moved under the port -- which is not necessarily # bad news, but every number below was measured under that state and would have # to be re-measured. # # WHY THERE IS A PROBE. A header-only port ships bytes NO COMPILER ON THIS ARM # NEED EVER HAVE LOOKED AT, so "the files are present" is not merely thin -- it # is compatible with headers that do not compile, and for a MATHS library it is # also compatible with headers that compile and return confident wrong numbers. # The recipe therefore builds glm-probe from ${S} during the port, which makes # do_compile the port's first consumer, and the probe RUNS here. The two halves # answer different questions: # # * sections 1-2 assert the PAYLOAD -- that the headers arrived, that the # integration surface resolves under the image's own pkgconf, and that # every version the port states agrees with every other; # * sections 3-5 assert the LIBRARY -- that it compiles, that it computes, # and that what it computes on wasm32 is what the same source computes on # x86-64. # # THE NUMERIC COMPARISON IS BYTE-EXACT AND DELIBERATELY NOT A TOLERANCE. Every # reference below is the stdout of the probe's own source compiled with g++ on # x86-64 and run there, at %.9e -- one digit more than the nine that round-trip # a binary32, so two values that print alike ARE the same float. That it can be # byte-exact at all is a measured fact rather than a hope: on x86-64 the same # source produces identical output at -O0, -O2, -Os, under -fno-exceptions and # under -DGLM_FORCE_PURE. Everything compared here is built from +, -, *, / and # sqrt on exactly-representable inputs, and IEEE-754 makes all five correctly # rounded, so a divergence is a real arm finding to attribute rather than # summation order to widen a window around. # # THE THREE EXCEPTIONS TO THAT ARE NAMED, and they are section 4's first three # cases. tan(fovy/2) inside perspective() and sin/cos(angle/2) inside # angleAxis() are libm calls, and libm is not correctly rounded by any standard # -- so those are the values that may legitimately differ between two libms. # They are asserted SEPARATELY from the matrices they feed precisely so that a # red downstream of them is attributable to libm from this transcript alone, # without a rebuild. # # Section 5's residual cases are the separate question a byte comparison cannot # ask -- whether the answer is CORRECT -- and are tolerance-based because that # is what a residual is. Both have to hold. # # Style follows 76-libeigen and 81-rapidjson: the probe is run ONCE and its # output parsed in a single fork-free loop, because the runner's per-suite # bound is a budget in process spawns rather than in cases. inc=/usr/include/glm cm=/usr/lib/cmake/glm pcf=/usr/lib/pkgconfig/glm.pc PT=/usr/libexec/glm-tests # --------------------------------------------------------------------------- # 1. The payload. check glm/inc-dir test -d "$inc" check glm/detail-dir test -d "$inc/detail" check glm/ext-dir test -d "$inc/ext" check glm/gtc-dir test -d "$inc/gtc" check glm/gtx-dir test -d "$inc/gtx" check glm/simd-dir test -d "$inc/simd" # The headers a consumer actually writes #include against. glm.hpp is # the umbrella the probe uses; the rest are asserted because the port installs # upstream's tree whole and a truncated install is what this catches. for h in glm.hpp fwd.hpp ext.hpp common.hpp exponential.hpp geometric.hpp \ integer.hpp matrix.hpp packing.hpp trigonometric.hpp \ vector_relational.hpp vec2.hpp vec3.hpp vec4.hpp \ mat2x2.hpp mat3x3.hpp mat4x4.hpp; do check "glm/header-$h" test -f "$inc/$h" done # The three extension headers the probe reaches for by name, so a run that # passes section 4 cannot be passing against a tree that lost them. check glm/gtc-matrix-transform test -f "$inc/gtc/matrix_transform.hpp" check glm/gtc-matrix-inverse test -f "$inc/gtc/matrix_inverse.hpp" check glm/gtc-quaternion test -f "$inc/gtc/quaternion.hpp" # Counted per directory, because the trees move independently -- ext/ and gtx/ # are upstream's extension sets and gain files far more often than the core # does, so a single total would absorb a loss in one against a gain in another. nt=$(find "$inc" -type f 2>/dev/null | grep -c .) nr=$(find "$inc" -maxdepth 1 -type f 2>/dev/null | grep -c .) nd=$(find "$inc/detail" -maxdepth 1 -type f 2>/dev/null | grep -c .) ne=$(find "$inc/ext" -maxdepth 1 -type f 2>/dev/null | grep -c .) nc=$(find "$inc/gtc" -maxdepth 1 -type f 2>/dev/null | grep -c .) nx=$(find "$inc/gtx" -maxdepth 1 -type f 2>/dev/null | grep -c .) ns=$(find "$inc/simd" -maxdepth 1 -type f 2>/dev/null | grep -c .) check_eq glm/file-count-total "427" "$nt" check_eq glm/file-count-root "23" "$nr" check_eq glm/file-count-detail "61" "$nd" check_eq glm/file-count-ext "170" "$ne" check_eq glm/file-count-gtc "37" "$nc" check_eq glm/file-count-gtx "126" "$nx" check_eq glm/file-count-simd "10" "$ns" # The recipe deletes upstream's glm/CMakeLists.txt from the installed tree; a # build system leaking into ${includedir} is what this pins. check_eq glm/no-cmakelists "0" "$(find "$inc" -name CMakeLists.txt 2>/dev/null | grep -c .)" # --------------------------------------------------------------------------- # 2. The integration surface, and the versions agreeing with each other. # # Three independent statements of the version ship in this package -- the # header macros, the .pc and the cmake package -- and a port that got one of # them wrong would still pass any existence check. # # THEY DO NOT ALL SAY THE SAME THING, and that is upstream's arrangement rather # than a defect of this port. The HEADERS are GLM 0.9.9.9 (setup.hpp's # GLM_VERSION_MESSAGE says so outright); the recipe is glm_0.9.9.8.bb with # `PV .= "+0.9.9.9+git"`, so the .pc and the cmake package carry the RECIPE's # version string, 0.9.9.8+0.9.9.9+git. Both numbers are asserted, each against # the file that states it, so the discrepancy is pinned rather than papered # over: a future recipe bump that moves one and not the other is a red here. # (The port ticket said `glm_0.9.9.8+git`; meta-openembedded at this layer pin # carries `glm_0.9.9.8.bb` whose PV appends `+0.9.9.9+git`, and these cases are # what settle which string is the measured one.) check glm/pc-file test -f "$pcf" check glm/cmake-dir test -d "$cm" check glm/cmake-config test -f "$cm/glmConfig.cmake" check glm/cmake-version test -f "$cm/glmConfigVersion.cmake" check glm/cmake-targets test -f "$cm/glmTargets.cmake" setup=$(cat "$inc/detail/setup.hpp" 2>/dev/null) case "$setup" in *"#define GLM_VERSION_MAJOR 0"*) hm=0 ;; *) hm=? ;; esac case "$setup" in *"#define GLM_VERSION_MINOR 9"*) hn=9 ;; *) hn=? ;; esac case "$setup" in *"#define GLM_VERSION_PATCH 9"*) hp=9 ;; *) hp=? ;; esac case "$setup" in *"#define GLM_VERSION_REVISION 9"*) hr=9 ;; *) hr=? ;; esac check_eq glm/header-version "0.9.9.9" "$hm.$hn.$hp.$hr" case "$setup" in *'"GLM: version 0.9.9.9"'*) hs=ok ;; *) hs=absent ;; esac check_eq glm/header-version-message "ok" "$hs" pc=$(cat "$pcf" 2>/dev/null) case "$pc" in *"Version: 0.9.9.8+0.9.9.9+git"*) pv=ok ;; *) pv=absent ;; esac check_eq glm/pc-version "ok" "$pv" # The include path a consumer compiles against must be the SHIPPED one, not a # build-tree path -- the failure a bare existence check cannot see. case "$pc" in *'Cflags: -I${includedir}'*) pi=ok ;; *) pi=other ;; esac check_eq glm/pc-cflags-template "ok" "$pi" # The .pc read by the image's OWN pkgconf, which is the assertion that matters: # /usr/lib/pkgconfig is on pkgconf's compiled-in default search path, so this # is exactly what a consumer on this image would get. A .pc that exists but # does not parse, or that parses to the wrong include path, is invisible to the # byte checks above and visible here. if command -v pkg-config >/dev/null 2>&1; then bst_ok glm/pkgconf-present check glm/pkgconf-exists pkg-config --exists glm check_eq glm/pkgconf-modversion "0.9.9.8+0.9.9.9+git" "$(pkg-config --modversion glm 2>&1)" # The variable, not --cflags, is the unambiguous assertion that the .pc # EXPANDS correctly: it goes through the same prefix/includedir chain a # consumer would and returns a bare path, where --cflags below is filtered. check_eq glm/pkgconf-includedir "/usr/include" "$(pkg-config --variable=includedir glm 2>&1)" # EMPTY IS THE CORRECT ANSWER HERE and it is asserted rather than skipped. # pkgconf drops -I/usr/include from --cflags because it is on its # compiled-in PKG_CONFIG_SYSTEM_INCLUDE_PATH -- the compiler already # searches it, so emitting it would reorder the search path. A reader who # ran this by hand would otherwise read the empty output as a broken .pc, # which is exactly why it is a named case with the includedir case beside # it. Measured on pkgconf 1.8.1; PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 is the # override that returns "-I/usr/include", not asserted because its exact # spacing is a pkgconf-version detail. check_eq glm/pkgconf-cflags-system-filtered "" "$(pkg-config --cflags glm 2>&1)" # Header-only: there is nothing to link, and a .pc that claimed otherwise # would send a consumer looking for an archive this package does not ship. check_eq glm/pkgconf-libs "" "$(pkg-config --libs glm 2>&1)" else bst_skip glm/pkgconf-present "pkgconf not installed on this image" fi cfg=$(cat "$cm/glmConfig.cmake" 2>/dev/null) case "$cfg" in *'set(GLM_VERSION "0.9.9.8+0.9.9.9+git")'*) cv=ok ;; *) cv=absent ;; esac check_eq glm/cmake-config-version "ok" "$cv" case "$cfg" in *'${PACKAGE_PREFIX_DIR}/include'*) ci=relative ;; *) ci=other ;; esac check_eq glm/cmake-include-dir "relative" "$ci" cfgv=$(cat "$cm/glmConfigVersion.cmake" 2>/dev/null) case "$cfgv" in *'set(PACKAGE_VERSION "0.9.9.8+0.9.9.9+git")'*) pkv=ok ;; *) pkv=absent ;; esac check_eq glm/cmake-pkg-version "ok" "$pkv" # RECORDED RATHER THAN REQUIRED, and this is the one case in the suite that # pins something WRONG on purpose. # # meta-openembedded ships glmConfigVersion.cmake.in with cmake's word-size # guard already EXPANDED -- the literal `8` below is where upstream's template # has @CMAKE_SIZEOF_VOID_P@, baked on whatever 64-bit host generated the file. # On this 32-bit target a cmake consumer therefore takes the mismatch branch # and is told PACKAGE_VERSION is "0.9.9.8+0.9.9.9+git (64bit)". It does not # REJECT the package -- meta-oe's copy sets PACKAGE_VERSION_UNSUITABLE FALSE # where upstream's sets TRUE, and PACKAGE_VERSION_COMPATIBLE/EXACT are computed # above the branch -- so find_package(glm) still resolves. It is also # unreachable on this image, which carries no cmake. # # Left unpatched deliberately: a sed over a third-party packaging file, for a # string with no consumer on the target, would be a divergence to carry with # nothing gained. Pinned instead, so that a meta-oe update which fixes it shows # up here as a red with this comment attached rather than passing unnoticed. # The probe's own P-PTR-BITS (section 3) is what actually establishes that this # arm is 32-bit; this case only records that the shipped file disagrees. case "$cfgv" in *'if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")'*) cb=8 ;; *) cb=other ;; esac check_eq glm/cmake-version-bitness-baked-64 "8" "$cb" # glmTargets.cmake is a PRE-GENERATED file meta-oe carries, not something cmake # emitted during this build, so the prefix it walks back from is a literal. It # must be this distro's libdir or the exported include directory lands wrong. tg=$(cat "$cm/glmTargets.cmake" 2>/dev/null) case "$tg" in *'"/usr/lib/cmake/glm"'*) tp=ok ;; *) tp=other ;; esac check_eq glm/cmake-targets-prefix "ok" "$tp" case "$tg" in *'add_library(glm INTERFACE IMPORTED)'*) ti=interface ;; *) ti=other ;; esac check_eq glm/cmake-targets-interface "interface" "$ti" # --------------------------------------------------------------------------- # 3-5. The probe. Run ONCE; everything below reads the captured output. if [ ! -x "$PT/glm-probe" ]; then bst_skip glm/probe-present "glm-probe not installed on this image" exit 0 fi bst_ok glm/probe-present # The probe's size is a functional property on this target, not trivia: the # guest stages an exec image as a segment list capped at WASM32_IMG_MAX_SEGS, # so a large image is refused -ENOMEM on a fragmented heap before it runs # (nopoll measured exactly that at 41 MB). Read against this arm's floor rather # than on its own -- an empty main() costs ~2.3 MB here, glibc and libc++ # merged whole, so most of this is baseline and not GLM. 8 MB is far above what # the port stages (~2.5 MB, 2573748 bytes at the build this suite was written # against) and far below where the cap bites. sz=$(( $(wc -c < "$PT/glm-probe") )) if [ "$sz" -lt 8388608 ]; then bst_ok glm/probe-size else bst_notok glm/probe-size "probe is $sz bytes, over the 8388608 ceiling"; fi out=$("$PT/glm-probe" 2>&1); rc=$? # Parse every P- line in ONE pass. No forks in the loop. P_CXXSTD=; P_EXC=; P_VER=; P_VERINT=; P_PTR=; P_FLOAT=; P_DOUBLE= P_VEC3B=; P_MAT4B=; P_QUATB=; P_ARCH=; P_PURE=; P_SIMD=; P_ALIGNED= P_CLIPRH=; P_CLIPZO= P_MATMUL=; P_MATVEC=; P_DET=; P_TRANSPOSE=; P_INVCOL0= P_LOOKAT=; P_FOVY=; P_TANHALF=; P_PERSP=; P_CLIPW=; P_NDC= P_QEXACT=; P_QMAT=; P_QRT=; P_QROT=; P_MROT=; P_ANGLE=; P_SINHALF=; P_COSHALF=; P_QAA= P_N3=; P_N4=; P_CROSS=; P_GEOM= P_BITS_MATVEC=; P_BITS_LOOKAT=; P_BITS_PERSP=; P_BITS_N3=; P_BITS_QRT=; P_BITS_QAA=; P_BITS_TAN= P_RESULT=; P_FAILURES=; P_SKIPPED=; P_CHECKS= oIFS=$IFS; IFS=' ' for line in $out; do tag=${line%% *}; val=${line#* } case "$tag" in P-CXXSTD) P_CXXSTD=$val ;; P-EXCEPTIONS) P_EXC=$val ;; P-GLM-VERSION) P_VER=$val ;; P-GLM-VERSION-INT) P_VERINT=$val ;; P-PTR-BITS) P_PTR=$val ;; P-FLOAT-BITS) P_FLOAT=$val ;; P-DOUBLE-BITS) P_DOUBLE=$val ;; P-VEC3-BYTES) P_VEC3B=$val ;; P-MAT4-BYTES) P_MAT4B=$val ;; P-QUAT-BYTES) P_QUATB=$val ;; P-GLM-ARCH) P_ARCH=$val ;; P-GLM-ARCH-PURE) P_PURE=$val ;; P-GLM-SIMD) P_SIMD=$val ;; P-GLM-ALIGNED-GENTYPES) P_ALIGNED=$val ;; P-GLM-CLIP-RH) P_CLIPRH=$val ;; P-GLM-CLIP-ZO) P_CLIPZO=$val ;; P-MATMUL) P_MATMUL=$val ;; P-MATVEC) P_MATVEC=$val ;; P-DET) P_DET=$val ;; P-TRANSPOSE) P_TRANSPOSE=$val ;; P-INV-COL0) P_INVCOL0=$val ;; P-LOOKAT) P_LOOKAT=$val ;; P-FOVY) P_FOVY=$val ;; P-TAN-HALF-FOVY) P_TANHALF=$val ;; P-PERSPECTIVE) P_PERSP=$val ;; P-CLIP-W) P_CLIPW=$val ;; P-NDC) P_NDC=$val ;; P-QUAT-EXACT) P_QEXACT=$val ;; P-QUAT-MAT) P_QMAT=$val ;; P-QUAT-ROUNDTRIP) P_QRT=$val ;; P-QUAT-ROTATE) P_QROT=$val ;; P-MAT-ROTATE) P_MROT=$val ;; P-ANGLE) P_ANGLE=$val ;; P-SIN-HALF-ANGLE) P_SINHALF=$val ;; P-COS-HALF-ANGLE) P_COSHALF=$val ;; P-QUAT-ANGLEAXIS) P_QAA=$val ;; P-NORMALIZE3) P_N3=$val ;; P-NORMALIZE4) P_N4=$val ;; P-CROSS) P_CROSS=$val ;; P-GEOM) P_GEOM=$val ;; P-BITS-MATVEC) P_BITS_MATVEC=$val ;; P-BITS-LOOKAT-COL0) P_BITS_LOOKAT=$val ;; P-BITS-PERSPECTIVE-COL0) P_BITS_PERSP=$val ;; P-BITS-NORMALIZE3) P_BITS_N3=$val ;; P-BITS-QUAT-ROUNDTRIP) P_BITS_QRT=$val ;; P-BITS-QUAT-ANGLEAXIS) P_BITS_QAA=$val ;; P-BITS-TAN-HALF-FOVY) P_BITS_TAN=$val ;; P-CHECKS) P_CHECKS=$val ;; P-SKIPPED) P_SKIPPED=$val ;; P-FAILURES) P_FAILURES=$val ;; P-RESULT) P_RESULT=$val ;; esac done IFS=$oIFS # --------------------------------------------------------------------------- # 3. The compile-time state every number below rests on. THE TRIPWIRE. # # P-EXCEPTIONS 0 says the distro's C++ driver compiled the probe with # exceptions off. GLM does not care either way -- it has no throw to disable -- # but the toolchain state is still an input to the numbers, and a change here # means they were measured under a different compiler than the one that ran. check_eq glm/cxx-exceptions-off "0" "$P_EXC" check_eq glm/probe-cxxstd "201103" "$P_CXXSTD" # The version the COMPILER saw, which is the one that matters for the numbers # -- section 2 asserted the versions the file system reports. GLM's headers are # 0.9.9.9 even though the recipe is named for 0.9.9.8. check_eq glm/probe-version "0.9.9.9" "$P_VER" check_eq glm/probe-version-int "999" "$P_VERINT" # A 32-bit arm, with the IEEE-754 types the byte comparison assumes. check_eq glm/probe-ptr-bits "32" "$P_PTR" check_eq glm/probe-float-bits "32" "$P_FLOAT" check_eq glm/probe-double-bits "64" "$P_DOUBLE" # Layout: vec3 is three tightly packed floats and mat4 is sixteen. A padded # vec3 (16 bytes) would mean the aligned gentypes were on, which is exactly # what the next three cases rule out -- asserted here as well because a size is # what a consumer's own struct layout depends on. check_eq glm/probe-vec3-bytes "12" "$P_VEC3B" check_eq glm/probe-mat4-bytes "64" "$P_MAT4B" check_eq glm/probe-quat-bytes "16" "$P_QUATB" # THE ONE THAT MAKES THE BYTE COMPARISON MEANINGFUL. GLM's SIMD backends # specialise on Aligned=true, i.e. the aligned_highp qualifier, which the # default types do not use unless GLM_FORCE_DEFAULT_ALIGNED_GENTYPES is # defined. That matters because glm_vec4_normalize (simd/geometric.h:80) uses # _mm_rsqrt_ps -- a 12-bit APPROXIMATE reciprocal square root -- where the # packed path uses 1/sqrt(x). Had the aligned types been in play, the x86-64 # reference would sit ~2^-12 away from anything a wasm32 arm without SIMD could # produce, and section 4 would be measuring GLM's dispatch rather than this # target's arithmetic. All three must be 0/1 as below on BOTH arms. check_eq glm/probe-arch-pure "1" "$P_PURE" check_eq glm/probe-simd-off "0" "$P_SIMD" check_eq glm/probe-aligned-off "0" "$P_ALIGNED" # perspective()'s output depends on the clip-control convention, so the # convention is part of the reference: right-handed, depth -1..1 (GLM's # default, and OpenGL's). check_eq glm/probe-clip-right-handed "1" "$P_CLIPRH" check_eq glm/probe-clip-zero-to-one "0" "$P_CLIPZO" # --------------------------------------------------------------------------- # 4. The numbers, against the x86-64 reference. Byte-exact at %.9e. # The three libm values first, so that a red below them is already attributed. # tan, sin and cos are not correctly rounded by any standard, so these are the # only values in this suite whose divergence would be a property of two libms # rather than of GLM or of this arm. check_eq glm/libm-tan-half-fovy "4.142135680e-01" "$P_TANHALF" check_eq glm/libm-sin-half-angle "7.071067691e-01" "$P_SINHALF" check_eq glm/libm-cos-half-angle "7.071067691e-01" "$P_COSHALF" # radians() is a multiplication by a constant, so these are exact and are the # INPUTS the two libm calls were made on -- pinned so that a libm red cannot be # confused with having asked libm a different question. check_eq glm/radians-45 "7.853981853e-01" "$P_FOVY" check_eq glm/radians-90 "1.570796371e+00" "$P_ANGLE" # 4a. The matrix multiply. Integer entries, so exact on any conforming arm. check_eq glm/matmul \ "4.000000000e+00 8.000000000e+00 -6.000000000e+00 2.000000000e+00 7.000000000e+00 0.000000000e+00 1.300000000e+01 1.300000000e+01 9.000000000e+00 -2.000000000e+00 3.000000000e+00 1.700000000e+01 7.000000000e+00 -3.000000000e+00 1.100000000e+01 1.400000000e+01" \ "$P_MATMUL" check_eq glm/matrix-times-vector \ "5.200000000e+01 -1.000000000e+00 4.000000000e+01 9.300000000e+01" "$P_MATVEC" # An integer matrix has an integer determinant, so this one SHOULD be exact on # any arm that computes it correctly -- a divergence here is arithmetic rather # than rounding, which makes it the most diagnostic case in the section. check_eq glm/determinant "-8.000000000e+00" "$P_DET" check_eq glm/transpose \ "4.000000000e+00 7.000000000e+00 9.000000000e+00 7.000000000e+00 8.000000000e+00 0.000000000e+00 -2.000000000e+00 -3.000000000e+00 -6.000000000e+00 1.300000000e+01 3.000000000e+00 1.100000000e+01 2.000000000e+00 1.300000000e+01 1.700000000e+01 1.400000000e+01" \ "$P_TRANSPOSE" check_eq glm/inverse-col0 \ "-6.500000000e+00 2.000000000e+00 -5.000000000e-01 4.000000000e+00" "$P_INVCOL0" # 4b. lookAt / perspective. lookAt reaches no libm function beyond sqrt (the # eye is (1,2,2) and the target the origin, so the view direction has length # exactly 3), which is why it is byte-comparable; perspective carries the tan # above into every element of its first two columns. check_eq glm/lookat \ "8.944271803e-01 -2.981424034e-01 3.333333433e-01 0.000000000e+00 0.000000000e+00 7.453560233e-01 6.666666865e-01 0.000000000e+00 -4.472135901e-01 -5.962848067e-01 6.666666865e-01 0.000000000e+00 -0.000000000e+00 -0.000000000e+00 -3.000000000e+00 1.000000000e+00" \ "$P_LOOKAT" check_eq glm/perspective \ "1.609475613e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 2.414213419e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 -1.020202041e+00 -1.000000000e+00 0.000000000e+00 0.000000000e+00 -2.020201921e+00 0.000000000e+00" \ "$P_PERSP" # The composed transform taken to normalised device coordinates -- the # operation a consumer actually performs, and where a wrong handedness or depth # convention shows up as a SIGN rather than as a rounding difference. The eye # sits 3 units from the origin, so w is exactly 3. check_eq glm/clip-w "3.000000000e+00" "$P_CLIPW" check_eq glm/ndc "0.000000000e+00 0.000000000e+00 3.468013704e-01" "$P_NDC" # 4c. The quaternion round-trip. normalize(quat(1,1,1,1)) has length exactly 2, # so every component is exactly 0.5 and mat4_cast of it is the permutation # matrix of the 120-degree rotation that cycles the axes -- exact 0s and 1s, # both directions, no libm anywhere. A red in this group is a real defect # rather than a rounding artefact. check_eq glm/quat-normalize-exact \ "5.000000000e-01 5.000000000e-01 5.000000000e-01 5.000000000e-01" "$P_QEXACT" check_eq glm/quat-to-mat4 \ "0.000000000e+00 1.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 1.000000000e+00 0.000000000e+00 1.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 0.000000000e+00 1.000000000e+00" \ "$P_QMAT" check_eq glm/quat-roundtrip \ "5.000000000e-01 5.000000000e-01 5.000000000e-01 5.000000000e-01" "$P_QRT" # Rotating (1,2,3) by that quaternion cycles it to (3,1,2), through the # quaternion directly and through the matrix it casts to -- two independent # code paths that must agree with each other and with the reference. check_eq glm/quat-rotate-vector "3.000000000e+00 1.000000000e+00 2.000000000e+00" "$P_QROT" check_eq glm/mat-rotate-vector "3.000000000e+00 1.000000000e+00 2.000000000e+00" "$P_MROT" # The libm-derived quaternion: 90 degrees about +Z, so w and z are both # sqrt(2)/2 and x and y are exactly zero. check_eq glm/quat-angleaxis \ "7.071067691e-01 0.000000000e+00 0.000000000e+00 7.071067691e-01" "$P_QAA" # 4d. The normalise. (3,4,12) has length exactly 13 and (1,2,2,4) exactly 5, so # both are correctly rounded divisions of exact integers. check_eq glm/normalize-vec3 \ "2.307692468e-01 3.076923192e-01 9.230769873e-01" "$P_N3" check_eq glm/normalize-vec4 \ "2.000000030e-01 4.000000060e-01 4.000000060e-01 8.000000119e-01" "$P_N4" check_eq glm/cross "-3.000000000e+00 6.000000000e+00 -3.000000000e+00" "$P_CROSS" # length, dot, distance and reflect in one string: four GLSL primitives whose # results are exact here, so one comparison covers all four. check_eq glm/geometric \ "1.300000000e+01 3.200000000e+01 1.300000000e+01 1.000000000e+00 1.000000000e+00 0.000000000e+00" \ "$P_GEOM" # If -- and only if -- one of the numeric cases above diverged, put the # IEEE-754 bit patterns of the compared values on the console. The probe prints # them on every run, but a suite that swallowed its stdout would leave a reader # with a decimal delta and no way to see how many ULPs it actually was without # rebuilding the image and booting again. Emitting them here costs nothing on a # green run and is the whole difference between "re-measure this" and "read it # off the transcript" on a red one. The arch word goes out with them because a # divergence whose cause is that this arm selected a SIMD backend is the one # explanation the decimal values alone cannot distinguish. if [ "$P_MATVEC $P_LOOKAT $P_N3 $P_TANHALF" != \ "5.200000000e+01 -1.000000000e+00 4.000000000e+01 9.300000000e+01 8.944271803e-01 -2.981424034e-01 3.333333433e-01 0.000000000e+00 0.000000000e+00 7.453560233e-01 6.666666865e-01 0.000000000e+00 -4.472135901e-01 -5.962848067e-01 6.666666865e-01 0.000000000e+00 -0.000000000e+00 -0.000000000e+00 -3.000000000e+00 1.000000000e+00 2.307692468e-01 3.076923192e-01 9.230769873e-01 4.142135680e-01" ]; then echo "glm-divergence arch $P_ARCH (pure=$P_PURE simd=$P_SIMD aligned=$P_ALIGNED)" echo "glm-divergence matvec-bits $P_BITS_MATVEC" echo "glm-divergence lookat-col0 $P_BITS_LOOKAT" echo "glm-divergence perspective-col0 $P_BITS_PERSP" echo "glm-divergence normalize3-bits $P_BITS_N3" echo "glm-divergence quat-rt-bits $P_BITS_QRT" echo "glm-divergence quat-aa-bits $P_BITS_QAA" echo "glm-divergence tan-half-bits $P_BITS_TAN" echo "glm-divergence x86-64-ref-matvec 42500000 bf800000 42200000 42ba0000" echo "glm-divergence x86-64-ref-lookat 3f64f92e be98a61f 3eaaaaab 00000000" echo "glm-divergence x86-64-ref-persp 3fce034c 00000000 00000000 00000000" echo "glm-divergence x86-64-ref-norm3 3e6c4ec6 3e9d89d9 3f6c4ec6" echo "glm-divergence x86-64-ref-quatrt 3f000000 3f000000 3f000000 3f000000" echo "glm-divergence x86-64-ref-quataa 3f3504f3 00000000 00000000 3f3504f3" echo "glm-divergence x86-64-ref-tanhalf 3ed413cd" fi # --------------------------------------------------------------------------- # 5. The probe's own verdict, which covers the residual checks section 4 # cannot express as strings -- unit length, the quaternion round-trip error, # the two rotation paths agreeing, det(lookAt)==1, A*inverse(A)==I, and the # near/far planes landing at -1 and +1, each against 1e-5. # # P-SKIPPED must be 0: the probe reports UNREFERENCED rather than OK if any # reference was left empty, so this is what stops a probe passing by having # forgotten to carry the numbers it compares against. check_eq glm/probe-no-unreferenced "0" "$P_SKIPPED" check_eq glm/probe-checks "27" "$P_CHECKS" check_eq glm/probe-failures "0" "$P_FAILURES" check_eq glm/probe-result "OK" "$P_RESULT" check_eq glm/probe-exit "0" "$rc"