geomancy 0.2.4.1 → 0.2.4.2
raw patch · 6 files changed
+122/−17 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +4/−0
- README.md +11/−7
- cbits/mat4.c +100/−2
- geomancy.cabal +3/−3
- src/Geomancy/Mat4.hs +4/−4
- src/Geomancy/Tree.hs +0/−1
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for geomancy +## 0.2.4.2++* Support ARM/aarch64 SIMD.+ ## 0.2.4.1 * Support simple-affine-space-0.2
README.md view
@@ -1,14 +1,18 @@ # Geomancy -Linear is nice, but slow. Those are naughty, but a bit faster.+> Linear is nice, but slow. Those are naughty, but a bit faster. -* All data types are monomorphic, unpacked and specialized to `Float`.-* `Mat4` is a `ByteArray#`.-* `Mat4` multiplication with SSE.-* Matrix construction states their element order.-* Transforms don't require transposition for GLSL.+* All data types are monomorphic, unpacked and specialized.+* `Mat4` and `Vec4` are `ByteArray#`.+* `Mat4`x`Mat4` and `Mat4`x`Vec4` is done with SIMD. -### The Numbers+## Matrix layout++CPU-side matrices compose in MVP order, optimized for `mconcat (local1 : local2 : ... : root)` operation.++GPU-side, in GLSL, it is `PVM * v`.++## The Numbers Storing a list of 1000 transformations (e.g. rendering instance data):
cbits/mat4.c view
@@ -1,6 +1,8 @@+#if defined(__i386__) || defined(__x86_64__)+ #include <xmmintrin.h> -void Mat4xMat4_SSE(float *A, float *B, float *O) {+void Mat4xMat4_SIMD(float *A, float *B, float *O) { __m128 row0 = _mm_load_ps(&B[0]); __m128 row1 = _mm_load_ps(&B[4]); __m128 row2 = _mm_load_ps(&B[8]);@@ -21,7 +23,7 @@ } } -void Mat4xVec4_SSE(float *M, float *V, float *O) {+void Mat4xVec4_SIMD(float *M, float *V, float *O) { __m128 row0 = _mm_load_ps(&M[0]); __m128 row1 = _mm_load_ps(&M[4]); __m128 row2 = _mm_load_ps(&M[8]);@@ -41,3 +43,99 @@ _mm_storeu_ps(O, res); }++#elif defined(__arm__) || defined(__aarch64__)++#include <arm_neon.h>+// Assumes 32bit floats++void Mat4xMat4_SIMD(float *A, float *B, float *O) {++ // Based on the column major ARM example:+ // https://developer.arm.com/documentation/102467/0100/Example---matrix-multiplication+ //+ // NEON intrinsics mapping:+ // https://arm-software.github.io/acle/neon_intrinsics/advsimd.html++ // vfmaq_laneq_f32 -> FMLA+ // Floating-point fused Multiply-Add to accumulator (by element). This+ // instruction multiplies the vector elements in the first source SIMD and+ // FP register by the specified value in the second source SIMD and FP+ // register, and accumulates the results in the vector elements of the+ // destination SIMD and FP register. All the values in this instruction are+ // floating-point values.++ // Contrary to the SSE function above we unroll the loop manually++ // these are the rows of A+ float32x4_t A0 = vld1q_f32(A);+ float32x4_t A1 = vld1q_f32(&A[4]);+ float32x4_t A2 = vld1q_f32(&A[8]);+ float32x4_t A3 = vld1q_f32(&A[12]);++ // these are the rows B+ float32x4_t B0 = vld1q_f32(B);+ float32x4_t B1 = vld1q_f32(&B[4]);+ float32x4_t B2 = vld1q_f32(&B[8]);+ float32x4_t B3 = vld1q_f32(&B[12]);++ // Zero accumulators for output rows+ float32x4_t C0 = vmovq_n_f32(0);+ float32x4_t C1 = vmovq_n_f32(0);+ float32x4_t C2 = vmovq_n_f32(0);+ float32x4_t C3 = vmovq_n_f32(0);++ // Multiply accumulate in 4x1 blocks to output row+ C0 = vfmaq_laneq_f32(C0, B0, A0, 0);+ C0 = vfmaq_laneq_f32(C0, B1, A0, 1);+ C0 = vfmaq_laneq_f32(C0, B2, A0, 2);+ C0 = vfmaq_laneq_f32(C0, B3, A0, 3);+ vst1q_f32(O, C0);++ C1 = vfmaq_laneq_f32(C1, B0, A1, 0);+ C1 = vfmaq_laneq_f32(C1, B1, A1, 1);+ C1 = vfmaq_laneq_f32(C1, B2, A1, 2);+ C1 = vfmaq_laneq_f32(C1, B3, A1, 3);+ vst1q_f32(&O[4], C1);++ C2 = vfmaq_laneq_f32(C2, B0, A2, 0);+ C2 = vfmaq_laneq_f32(C2, B1, A2, 1);+ C2 = vfmaq_laneq_f32(C2, B2, A2, 2);+ C2 = vfmaq_laneq_f32(C2, B3, A2, 3);+ vst1q_f32(&O[8], C2);++ C3 = vfmaq_laneq_f32(C3, B0, A3, 0);+ C3 = vfmaq_laneq_f32(C3, B1, A3, 1);+ C3 = vfmaq_laneq_f32(C3, B2, A3, 2);+ C3 = vfmaq_laneq_f32(C3, B3, A3, 3);+ vst1q_f32(&O[12], C3);++}++void Mat4xVec4_SIMD(float *M, float *V, float *O) {++ // Rows+ float32x4_t M0 = vld1q_f32(M);+ float32x4_t M1 = vld1q_f32(&M[4]);+ float32x4_t M2 = vld1q_f32(&M[8]);+ float32x4_t M3 = vld1q_f32(&M[12]);++ // Col+ float32x4_t VC = vld1q_f32(V);++ // Local output vector+ float32x4_t L0 = vmovq_n_f32(0);++ L0 = vfmaq_laneq_f32(L0, M0, VC, 0);+ L0 = vfmaq_laneq_f32(L0, M1, VC, 1);+ L0 = vfmaq_laneq_f32(L0, M2, VC, 2);+ L0 = vfmaq_laneq_f32(L0, M3, VC, 3);++ vst1q_f32(O, L0);+}++#else++#error "Architecture not supported! Please submit an issue."++#endif
geomancy.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0.+-- This file has been generated from package.yaml by hpack version 0.35.1. -- -- see: https://github.com/sol/hpack ----- hash: c412ee063c335404b0a3d322adb6e19f362c3bfdedf86b7720b8fe6db4a7a741+-- hash: 13d331028f6ae562f3d1e96d1909faf808cab9b792c467ef78c9d3fa8285c72e name: geomancy-version: 0.2.4.1+version: 0.2.4.2 synopsis: Geometry and matrix manipulation description: Sometimes it is unavoidable you have to do stuff on CPU. Let's at least do it faster.
src/Geomancy/Mat4.hs view
@@ -402,13 +402,13 @@ zipWith :: (Float -> Float -> c) -> Mat4 -> Mat4 -> [c] zipWith f a b = List.zipWith f (toList a) (toList b) -foreign import ccall unsafe "Mat4xMat4_SSE" m4m4sse :: Addr# -> Addr# -> Addr# -> IO ()+foreign import ccall unsafe "Mat4xMat4_SIMD" m4m4simd :: Addr# -> Addr# -> Addr# -> IO () {-# INLINE matrixProduct #-} matrixProduct :: Mat4 -> Mat4 -> Mat4 matrixProduct (Mat4 l) (Mat4 r) = unsafePerformIO do result@(Mat4 m) <- unsafeNewMat4- m4m4sse+ m4m4simd (byteArrayContents# l) (byteArrayContents# r) (byteArrayContents# m)@@ -438,13 +438,13 @@ (m02 * x) (m12 * x) (m22 * x) (m32 * x) (m03 * x) (m13 * x) (m23 * x) (m33 * x) -foreign import ccall unsafe "Mat4xVec4_SSE" m4v4sse :: Addr# -> Addr# -> Addr# -> IO ()+foreign import ccall unsafe "Mat4xVec4_SIMD" m4v4simd :: Addr# -> Addr# -> Addr# -> IO () -- | Matrix - column vector multiplication (!*) :: Coercible a Mat4 => a -> Vec4 -> Vec4 (!*) (coerce -> Mat4 m) (Vec4 v) = unsafePerformIO do result@(Vec4 o) <- unsafeNewVec4- m4v4sse+ m4v4simd (byteArrayContents# m) (byteArrayContents# v) (byteArrayContents# o)
src/Geomancy/Tree.hs view
@@ -39,7 +39,6 @@ -> Tree (acc, a) applyWith f = mapAccum next where- -- nextAcc = f ann acc next acc (ann, item) = let acc' = f ann acc