Vector4dremoved hand-written AVX intrinsics fromoperator+,operator-, unaryoperator-,operator*(scalar),operator/(scalar),dot(),min(),max(), andlerp(). The compiler's auto-vectorization of the scalar fallback produces equivalent or better code, matching findings inVector4fandMatrix4f.dot()scalar fallback now delegates toVector4<double>::dot(rhs)instead of repeating the arithmetic inline.Matrix4dremoved hand-written AVX intrinsics fromoperator+,operator-, unaryoperator-,operator*(scalar), andcompose(). ARM NEON paths are unchanged.Matrix4f::composeARM guard tightened — the NEON path now requires__arm64__ || __aarch64__(AArch64 only), matching the pattern already used inMatrix4d. Prevents a potential build failure on 32-bit ARMv7.- Benchmark scripts (
launch_benchmark.sh/launch_benchmark.bat) now pass-DVECTOR_MATH_ENABLE_AVX2=ONto CMake so the compiler can use AVX2 when auto-vectorizing.
Matrix4f::rotate(const Quaternion<float>&)— new static factory that builds a rotation matrix directly asMatrix4f, avoiding a temporaryMatrix4<float>+ cross-type copy. Used internally by the optimizedMatrix4f::composepath.
Matrix4f::composeperformance — now uses the new nativeMatrix4f::rotate()instead ofMatrix4<float>::rotate(), eliminating the cross-type temporary copy that made the SIMD compose path ~8× slower than necessary.Matrix4f * Vector4fremoved expensive transpose — the SSE path used_MM_TRANSPOSE4_PS(8 shuffle instructions) before broadcasting vector components. Replaced with a thin forward toMatrix4<float>::operator*(Vector4), letting the compiler auto-vectorize the four scalar dot-products. This produces faster code than hand-written SSE hadd on baseline x86/x64.Vector4f::dotandVector2d::dotremoved hand-written SSE —_mm_hadd_ps/_mm_hadd_pdhave poor latency on many x86 cores; the compiler's auto-vectorization of the inherited scalardot()outperforms the manual intrinsics by ~15–20%.Vector3f::crossARM NEON bug —vextq_f32rotates all 4 lanes, which broughtw=0into lane 2 (the z-component) becauseVector3fstores[x, y, z, 0]. The z-component of the cross product was therefore always corrupted on AArch64. Fixed by rebuilding the exact[y, z, x]and[z, x, y]permutations withvsetq_lane_f32/vgetq_lane_f32after thevextq_f32rotation. This also fixesQuaternionf::rotated, which internally callsVector3f::cross.
- Benchmark suite now uses per-iteration varying inputs (small offset counters) for all factory and vector-operation benchmarks. This defeats compiler constant-folding that previously made scalar/generic paths appear artificially fast (< 1 ns) while SIMD intrinsics paths executed real instructions.
Matrix4fARMv7 build failure — the ARM NEON paths inmatrix4f.hppused AArch64-only intrinsics (vfmaq_laneq_f32,vpaddq_f32) unconditionally for all ARM targets. Added&& (defined(__arm64__) || defined(__aarch64__))guards to both the matrix–matrix and matrix–vector multiply operators, mirroring the existing pattern inmatrix4d.hpp. ARMv7 (armeabi-v7a) now falls back to the scalar implementation.
- Android CI — new
android-buildjob incmake-multi-platform.ymlcross-compiles the library and test suite for all four Android ABIs (armeabi-v7a,arm64-v8a,x86,x86_64) in bothReleaseandDebugusing the NDK toolchain (android-21). Tests are compiled but not executed (no Android runtime on the host runner).
M_PIstill undeclared on MSVC intest/main.cpp—#define _USE_MATH_DEFINES+#include <cmath>were placed after#include <gtest/gtest.h>, which pulls in<cmath>transitively before the define was seen. Moved the define and<cmath>above the gtest include so MSVC exposesM_PIbefore the include-guard prevents a second parse.
_USE_MATH_DEFINESpropagated to library headers —common.hppandvec.hppnow define_USE_MATH_DEFINESbefore<cmath>under_WIN32, so any consumer compiling on MSVC getsM_PIand other POSIX math constants without needing to set the macro themselves. Previously this guard was only applied intest/main.cpp.
- Windows CI (
M_PIundeclared) — added#define _USE_MATH_DEFINESbefore<cmath>under_WIN32so MSVC exposesM_PIand other POSIX math constants intest/main.cpp
Matrix4fARM NEON matrix–matrix multiply —thisrows andrhsrows were swapped in the accumulation loop, producing wrong results on AArch64 (Apple Silicon /ubuntu-24.04-arm). Fixed to match the x86 SSE logic: loadrhsrows as vectors and broadcast scalars from each row ofthis.
Vector4dSIMD arithmetic — all operators now have AVX (x86) and NEON (AArch64) paths instead of falling back to the scalarVector4<double>base class:operator+/operator-:_mm256_add/sub_pd·vaddq/vsubq_f64- unary
operator-:_mm256_xor_pdwith sign-bit mask ·vnegq_f64 operator*(scalar)/operator/(scalar):_mm256_mul/div_pd·vmulq_n_f64dot()(instance + static): AVX hadd trick (_mm256_hadd_pd+ 128-bit extract) ·vpaddq_f64+vaddvq_f64
Matrix4dSIMD add / subtract / negate / scalar-multiply — new overrides replace the 16-element scalar loop with 4 AVX 256-bit ops (one per row) or 8 NEON 128-bit ops:operator+(Matrix4d)/operator-(Matrix4d):_mm256_add/sub_pd·vaddq/vsubq_f64- unary
operator-():_mm256_xor_pd·vnegq_f64 operator*(double):_mm256_mul_pd+_mm256_set1_pd·vmulq_n_f64
- FMA3 in
Matrix4dmat×mat and mat×vec (x86) —mul + addpairs replaced with_mm256_fmadd_pdwhen compiled with-mfma(__FMA__defined); reduces 7 instructions to 4 per accumulation step Matrix4f * Vector4fARM NEON — previously fell back to scalar; now uses twovpaddq_f32passes to compute all four dot products simultaneously-mfmacompiler flag added to the x86 CMake path (gcc/clang:-mavx -mfma; MSVC:/arch:AVX2)- Benchmarks for all new operations:
BM_Vector4dSIMDAdd,BM_Vector4dSIMDScalarMultiply,BM_Vector4dSIMDDot,BM_Matrix4dSIMDAdd,BM_Matrix4dSIMDScalarMultiply,BM_Matrix4fByVectorGeneric, with scalar and GLM baselines - AArch64 NEON full implementation for
Matrix4dmatrix–matrix and matrix–vector multiply (float64x2_t,vfmaq_f64,vpaddq_f64) Matrix4d::lookAtoptimized inline override — avoids genericVec<>loop overhead and intermediate temporaries- CMake install support:
GNUInstallDirs,CMakePackageConfigHelpers, package config files (vector_mathConfig.cmake,vector_mathConfigVersion.cmake), andINSTALL_INTERFACEinclude paths - Benchmark suite expanded:
Matrix4f,Matrix4d,Quaternion, and GLM comparison benchmarks; removed dummyBM_StringCreation
Matrix4dandMatrix4fimplementations moved from.cpptranslation units to header-only inline methods —src/matrix4d.cppandsrc/matrix4f.cppremovedMatrix4::identity()now returns a cachedstatic constinstance (computed once via IIFE) instead of allocating a local array on every call- CI: added
ubuntu-24.04-armrunner (AArch64 NEON coverage); added Debug build type alongside Release;ctestnow runs with--output-on-failure
Vec<T, N> (base class)
dot(rhs)— instance and static dot productoperator*(const Vec&)— component-wise (Hadamard) multiplicationdistanceTo(other)/distanceToSquared(other)— Euclidean distancesetZero()/setFrom(other)— mutation helpersabsolute()— in-place per-componentabsfloor()/ceil()/round()— in-place per-component roundingclamp(min, max)/clamp(minVec, maxVec)— scalar and per-component clampingstatic min(a, b)/static max(a, b)— component-wise min/maxstatic lerp(a, b, t)— linear interpolation
Vector2<T>
- Named accessors
x()/y()returning references dot()instance + staticcross(other)— 2D scalar cross productangleTo(other)— unsigned angle in radians [0, π]angleToSigned(other)— signed angle in radians (-π, π]reflect(normal)/reflected(normal)— reflection about a normal- Typed
operator+,-,*,/overrides (returnVector2instead ofVec)
Vector3<T>
- Named accessors
x()/y()/z()returning references angleTo(other)— unsigned angle in radiansangleToSigned(other, normal)— signed angle with reference normalreflect(normal)/reflected(normal)— reflection about a normalnormalizeInto(out)— normalized copy into an output parameter- Typed
operator+,-,*,/overrides (returnVector3)
Vector4<T>
- Named accessors
x()/y()/z()/w()returning references dot()instance + staticsetValues(x, y, z, w)— convenience setter- Typed operator overrides (return
Vector4)
Quaternion<T>
static identity()— no-rotation quaternion (0, 0, 0, 1)static axisAngle(axis, angle)— construct from axis-anglestatic slerp(a, b, t)— spherical linear interpolationoperator*— Hamilton product (quaternion composition)operator+/operator-/ unaryoperator-/operator*(scalar)setAxisAngle(axis, angle)conjugate()/conjugated()inverse()/inverted()rotate(v&)/rotated(v)— rotate aVector3by this quaternionaxis()— extract normalized rotation axisangle()— extract rotation angle in radians
Matrix4<T>
operator+/operator-/ unaryoperator-trace()— sum of diagonal elementsdeterminant()— 4×4 determinant via cofactor expansioninvert()— in-place inversion; returnsfalseif singularinverted()— returns an inverted copygetRow(i)/setRow(i, v)— row access by indexgetColumn(i)/setColumn(i, v)— column access by indexgetTranslation()/setTranslation(v)— translation componentgetScale()— extract TRS scale factors (column lengths)transform3(v)— transform a point (w = 1, includes translation)rotate3(v)— transform a direction (w = 0, no translation)static compose(translation, rotation, scale)— build a TRS matrix
Matrix4::rotateZhad identical code torotateY(wrong column indices); corrected to rotate in the X–Y planeMatrix4::rotateX,rotateY,rotateZ: local variablescos/sinshadowed standard library functions; renamed tocosA/sinA
- All public headers now carry Doxygen
///documentation covering every class, constructor, operator, and method
Matrix4dAVX intrinsics: wrong register type__m256(8×float) replaced with__m256d(4×double) throughoutMatrix4dmatrix multiply:_mm256_add_pstypo corrected to_mm256_add_pdMatrix4dmatrix-vector multiply:_mm256_hadd_pdreduction now correctly combines 128-bit lanes using_mm256_extractf128_pd+_mm_add_pd, fixing wrong results on x86/x64Vecoperator*(scalar, vec): friend declaration did not match external template definition, causing a linker error; fixed by defining it inline in the class bodyVecoperator/(vec, scalar): declared as both a member and a free friend function, causing call ambiguity; removed the duplicate free functionMatrix4scaleandperspective:Matrix4<T>(0)was ambiguous between value and pointer constructor; fixed toMatrix4<T>(DATA_TYPE(0))Matrix4ortho: called non-existentCreateOrthoOffCenter; corrected toorthoOffCenterMatrix4ortho: passed width/height arguments toorthoOffCenterin wrong positions, causingleft == right(division by zero); fixed argument order
- ARM NEON implementation for
Matrix4fmultiply (scalar fallback removed for float path) Matrix4dandMatrix4fSIMD-accelerated multiply operatorsalignas(16)onMatrix4fandVector4ffor correct SSE alignmentMatrix4transpose operation- Benchmarks with Release mode build support and GLM comparison
- SIMD architecture detection consolidated in
common.hpp
- Google Test and Google Benchmark integration via CMake FetchContent
Matrix4benchmark suite- GitHub Actions CI for Ubuntu, Windows, and macOS
cmathinclude and SIMD conditional compilation
- Generic
Vec<T, N>andMat<T, R, C>templates Vector2,Vector3,Vector4,Matrix2,Matrix3,Matrix4typed specializationsQuaternion<T>- CMake build system with C++17