heph-aligned-storable-0.1.0.0: test/Foreign/GPU/Marshal/AlignedSpec.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Foreign.GPU.Marshal.AlignedSpec where
import Control.Exception.Lifted (bracket)
import Control.Monad.IO.Class
import Data.Coerce
import Data.Foldable
import Data.Traversable
import qualified Data.Vector.Storable as SV
import qualified Data.Vector.Storable.Sized as SSV
import Foreign
import GHC.Generics
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Linear (V4 (..))
import Foreign.GPU.Marshal.Aligned
import Foreign.GPU.Storable.Aligned
import Foreign.GPU.Storable.AlignedSpec
getBytes :: Int -> Ptr a -> IO [Word8]
getBytes n = peekArray @Word8 n . castPtr
genSV :: (Storable a) => Gen a -> Gen (SV.Vector a)
genSV gen = SV.fromList <$> Gen.list (Range.linear 1 1000) gen
hprop_dynamic_vector_copy_byte_identity :: Property
hprop_dynamic_vector_copy_byte_identity = property do
vec <- forAll $ genSV (Strided @Std140 <$> genSimple)
let totalBytes = SV.length vec * sizeOf (SV.head vec)
sutBytes <- liftIO $ SV.unsafeWith vec (getBytes totalBytes . castPtr)
oracleBytes <- liftIO $ bracket (mallocBytes totalBytes) free \ptr -> do
alignedCopyVector (AlignedPtr ptr) vec
getBytes totalBytes ptr
sutBytes === oracleBytes
hprop_dynamic_vector_copy :: Property
hprop_dynamic_vector_copy = property do
values <- forAll $ Gen.list (Range.linear 1 100) genSimple
let n = length values
vec = SV.fromList $ map (Strided @Std140) values
stride = sizeOf (SV.head vec)
totalBytes = n * stride
oracleResult <- liftIO $ bracket (mallocBytes totalBytes) free \ptr -> do
alignedCopyVector (AlignedPtr (castPtr ptr)) vec
for [0 .. n - 1] \i ->
coerce <$> peekByteOff @SimpleStd140 ptr (i * stride)
packedResult <- liftIO $ bracket (mallocBytes totalBytes) free \ptr -> do
alignedCopyVector (AlignedPtr (castPtr ptr)) vec
for [0 .. n - 1] \i ->
coerce <$> peekByteOff @(Packed Std140 Simple) ptr (i * stride)
oracleResult === values
packedResult === values
-- | A struct containing a fixed-size array, designed to be optimized
-- by AlignedArray.
data StructWithArray (layout :: MemoryLayout) = StructWithArray
{ fieldA :: Float
, fieldB :: AlignedArray layout 4 (V4 Float)
}
deriving (Generic)
deriving instance
(MemoryLayoutRules layout, AlignedStorable layout (V4 Float)) => Show (StructWithArray layout)
deriving instance
(MemoryLayoutRules layout, AlignedStorable layout (V4 Float)) => Eq (StructWithArray layout)
instance AlignedStorable Std140 (StructWithArray Std140)
hprop_dynamic_vector_with_aligned_array_copy :: Property
hprop_dynamic_vector_with_aligned_array_copy = property do
values <-
forAll $
Gen.list
(Range.linear 1 100)
(StructWithArray <$> genFloat <*> (mkAlignedArray @Std140 <$> SSV.replicateM (genV genFloat)))
let vec = SV.fromList $ map (Strided @Std140) values
structStride = sizeOf (SV.head vec)
totalBytes = length values * structStride
bracket (liftIO $ mallocBytes totalBytes) (liftIO . free) \ptr -> do
alignedCopyVector (AlignedPtr (castPtr ptr)) vec
for_ (zip [0 ..] values) \(i, StructWithArray expectedFieldA expectedFieldB) -> do
let structBaseOffset = i * structStride
fieldAOffset = structBaseOffset
actualFieldA <- liftIO $ peekByteOff @Float ptr fieldAOffset
actualFieldA === expectedFieldA
let fieldBBaseOffset = structBaseOffset + 16
vec4Stride = 16
expectedVecs = unAlignedArray expectedFieldB
for_ [0 .. 3] \j -> do
let fieldBElementOffset = fieldBBaseOffset + (fromIntegral j * vec4Stride)
actualVec4 <- liftIO $ peekByteOff @(V4 Float) ptr fieldBElementOffset
let expectedVec4 = SSV.index expectedVecs j
actualVec4 === coerce expectedVec4