packages feed

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 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