packages feed

heph-aligned-storable (empty) → 0.1.0.0

raw patch · 11 files changed

+4431/−0 lines, 11 filesdep +basedep +bytestringdep +halfsetup-changed

Dependencies added: base, bytestring, half, hashable, hedgehog, heph-aligned-storable, lifted-base, linear, monad-control, tasty, tasty-discover, tasty-hedgehog, tasty-hunit, tasty-inspection-testing, unliftio, vector, vector-sized

Files

+ CHANGELOG.md view
@@ -0,0 +1,20 @@+# Changelog for `heph-aligned-storable`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0 - 2026-02-02++### Added++- Initial release. Generic derivation of `AlignedStorable` for GPU memory layouts.+- Provided instances for `Std140`, `Std430`, and `Scalar` layout rules.+- Added support for SPIR-V primitives, vectors, and matrices.+- Included `AlignedArray` for opt-in `memcpy` of fixed-size arrays.+- Added marshaling helpers (`withPacked`, `allocaPacked`) with guaranteed zero-initialized padding.+- Tested via property-based tests, unit tests verified against GLSL compiler output, and inspection tests.
+ LICENSE view
@@ -0,0 +1,26 @@+Copyright 2025 Jeremy Nuttall++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1.  Redistributions of source code must retain the above copyright notice, this+    list of conditions and the following disclaimer.++2.  Redistributions in binary form must reproduce the above copyright notice,+    this list of conditions and the following disclaimer in the documentation+    and/or other materials provided with the distribution.++3.  Neither the name of the copyright holder nor the names of its contributors+    may be used to endorse or promote products derived from this software+    without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,103 @@+# heph-aligned-storable++Generically derive `Storable` instances for GPU memory layouts (`std140`, `std430`, `scalar`).++[![CI](https://github.com/jtnuttall/heph/actions/workflows/haskell.yml/badge.svg)](https://github.com/jtnuttall/heph/actions/workflows/haskell.yml)++<!-- [![Hackage](https://img.shields.io/hackage/v/heph-aligned-storable.svg)](https://hackage.haskell.org/package/heph-aligned-storable) -->++## Quick Start++**IMPORTANT**: Be sure to use `layout(row_major)` if you are using `linear` with this library.++GLSL:++```glsl+layout(std140, row_major, binding = 0) uniform myuniforms {+  mat4 modelViewProjection;+  vec3 cameraPosition;+  float time;+};+```++Haskell:++```haskell+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}++import Foreign.GPU.Storable.Aligned+import Foreign.GPU.Marshal.Aligned+import GHC.Generics (Generic)+import Linear (M44, V3, V4(..))++data Uniforms = Uniforms+  { modelViewProjection :: M44 Float+  , cameraPosition      :: V3 Float+  , time                :: Float+  } deriving (Generic, Show, Eq)++instance AlignedStorable Std140 Uniforms++main :: IO ()+main = do+  let uniforms = Uniforms+        { modelViewProjection = V4 (V4 1 0 0 0) (V4 0 1 0 0) (V4 0 0 1 0) (V4 0 0 0 1)+        , cameraPosition = V3 0 0 5+        , time = 0+        }+  withPacked @Std140 uniforms $ \ptr -> do+    -- ptr is ready for vkCmdPushConstants, memcpy to mapped buffer, etc.+    pure ()+```++## Features++- Correct, spec-compliant padding for `Std140`, `Std430`, and `Scalar` layouts+- Single `memcpy` for arrays via `AlignedArray`+- Type-level layout witnesses prevent mismatched layouts at compile time+- Zero runtime overhead—generic machinery fully eliminated by GHC++## The Contract++**`alignedPoke` writes member data only. Padding bytes are untouched.**++Use the helpers in `Foreign.GPU.Marshal.Aligned` (`withPacked`, `allocaPacked`, etc.) for guaranteed zero-initialized padding. If you allocate memory yourself, use `calloc` or zero the buffer before poking.++## Arrays++By default, arrays are poked element-by-element. For a single `memcpy`, wrap in `AlignedArray`:++```haskell+data MyStruct (layout :: MemoryLayout) = MyStruct+  { meta   :: Float+  , pixels :: AlignedArray layout 64 (V4 Float)  -- memcpy'd as a block+  } deriving Generic++instance AlignedStorable Std140 (MyStruct Std140)+```++## Gotchas++### Matrix naming conventions++`linear` uses `Mnm` for n rows × m columns. GLSL uses `matNxM` for N columns of M-vectors.++- `M32 Float` (3 rows, 2 cols) → `mat2x3`+- `M24 Double` (2 rows, 4 cols) → `dmat4x2`++### `row_major`++GLSL's `layout(row_major)` affects memory layout, not matrix semantics. Matrices are still column-major for arithmetic. This library implements the memory layout correctly. You don't need to transpose before upload.++### `vec3` and `mat3` are cursed++Driver handling of the round-up rules for these types has historically been inconsistent. Consider padding to `vec4`/`mat4` and pretending the 3-element variants don't exist.++## Why not `derive-storable`?++`derive-storable` produces FFI-compatible layouts (C struct ABI), not GPU layouts. GPU alignment rules differ:++- `std140` rounds struct alignment to 16 bytes+- `scalar` layout requires 4-byte booleans, not 1-byte
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple++main = defaultMain
+ heph-aligned-storable.cabal view
@@ -0,0 +1,82 @@+cabal-version: 2.2++-- This file has been generated from package.yaml by hpack version 0.39.1.+--+-- see: https://github.com/sol/hpack++name:           heph-aligned-storable+version:        0.1.0.0+synopsis:       Generically derive Storable instances suitable for CPU-GPU transfer+description:    Please see the README on GitHub at <https://github.com/jtnuttall/heph/tree/main/heph-aligned-storable#readme>+category:       Graphics+homepage:       https://github.com/jtnuttall/heph/tree/main/heph-aligned-storable#readme+bug-reports:    https://github.com/jtnuttall/heph/issues+author:         Jeremy Nuttall+maintainer:     jeremy@jeremy-nuttall.com+copyright:      2025 Jeremy Nuttall+license:        BSD-3-Clause+license-file:   LICENSE+build-type:     Simple+tested-with:+    GHC == 9.8.4 || == 9.6.5 || == 8.10.7 || == 8.10.4+extra-doc-files:+    README.md+    CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/jtnuttall/heph++library+  exposed-modules:+      Foreign.GPU.Marshal.Aligned+      Foreign.GPU.Storable.Aligned+  other-modules:+      Paths_heph_aligned_storable+  autogen-modules:+      Paths_heph_aligned_storable+  hs-source-dirs:+      src+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+  build-depends:+      base >=4.7 && <5+    , half >=0.3 && <0.5+    , hashable >=1.3 && <1.6+    , linear >=1.20 && <1.24+    , unliftio ==0.2.*+    , vector >=0.12.3.0 && <0.14+    , vector-sized >=1.4 && <1.7+  default-language: Haskell2010++test-suite heph-aligned-storable-test+  type: exitcode-stdio-1.0+  main-is: Driver.hs+  other-modules:+      Foreign.GPU.Marshal.AlignedSpec+      Foreign.GPU.Storable.AlignedInspectionSpec+      Foreign.GPU.Storable.AlignedSpec+      Paths_heph_aligned_storable+  autogen-modules:+      Paths_heph_aligned_storable+  hs-source-dirs:+      test+  ghc-options: -Wall -Wcompat -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , bytestring >=0.10 && <0.13+    , half >=0.3 && <0.5+    , hashable >=1.3 && <1.6+    , hedgehog >=1.0.4 && <1.6+    , heph-aligned-storable+    , lifted-base ==0.2.*+    , linear >=1.20 && <1.24+    , monad-control ==1.0.*+    , tasty >=1.4.1 && <1.6+    , tasty-discover >=4.2.1 && <6+    , tasty-hedgehog >=1.1 && <1.5+    , tasty-hunit ==0.10.*+    , tasty-inspection-testing >=0.1 && <0.3+    , unliftio ==0.2.*+    , vector >=0.12.3.0 && <0.14+    , vector-sized >=1.4 && <1.7+  default-language: Haskell2010
+ src/Foreign/GPU/Marshal/Aligned.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++-- |+-- Description : Zero-initialized allocation for GPU-aligned types+-- Copyright   : (c) Jeremy Nuttall, 2025+-- License     : BSD-3-Clause+-- Maintainer  : jeremy@jeremy-nuttall.com+-- Stability   : experimental+--+-- Allocation helpers with guaranteed zero-initialized padding.+-- Use these instead of 'alloca' or 'malloc' to avoid garbage in padding bytes.+module Foreign.GPU.Marshal.Aligned (+  -- * Utilities for 'Packed' values+  PackedPtr,+  withPacked,+  allocaPacked,++  -- * Utilities for 'Strided' values+  StridedPtr,+  withStrided,+  allocaStrided,++  -- * Utilities for runtime length arrays+  alignedCopyVector,+) where++import qualified Data.Vector.Storable as SV+import UnliftIO (MonadIO, MonadUnliftIO, liftIO)+import UnliftIO.Foreign++import Foreign.GPU.Storable.Aligned++-- | Convenience type for 'Packed' 'AlignedPtr'+type PackedPtr layout a = AlignedPtr layout (Packed layout a)++-- | Temporarily allocates a zero-initialized block of memory and pokes a+-- 'Packed' value into it, providing a pointer to the result.+-- The storage is freed automatically. The pointer is only valid within the continuation.+withPacked+  :: forall layout a m b+   . (MonadUnliftIO m, AlignedStorable layout a)+  => a+  -> (PackedPtr layout a -> m b)+  -> m b+withPacked a f = withZeroed (Packed a) (f . AlignedPtr)+{-# INLINEABLE withPacked #-}++-- | Allocates temporary, zero-initialized storage for a 'Packed' value on the stack.+-- The pointer is only valid within the continuation.+allocaPacked+  :: forall layout a b m+   . (MonadUnliftIO m, AlignedStorable layout a)+  => (PackedPtr layout a -> m b)+  -> m b+allocaPacked f = allocaZeroed (f . AlignedPtr)+{-# INLINEABLE allocaPacked #-}++-- | Convenience type for 'Strided' 'AlignedPtr'+type StridedPtr layout a = AlignedPtr layout (Strided layout a)++-- | Temporarily allocates a zero-initialized block of memory and pokes a+-- 'Strided' value into it, providing a pointer to the result.+-- The storage is freed automatically. The pointer is only valid within the continuation.+withStrided+  :: (MonadUnliftIO m, AlignedStorable layout a) => a -> (StridedPtr layout a -> m b) -> m b+withStrided a f = withZeroed (Strided a) (f . AlignedPtr)+{-# INLINEABLE withStrided #-}++-- | Allocates temporary, zero-initialized storage for a 'Strided' value on the stack.+-- The pointer is only valid within the continuation.+allocaStrided :: (MonadUnliftIO m, AlignedStorable layout a) => (StridedPtr layout a -> m b) -> m b+allocaStrided f = allocaZeroed (f . AlignedPtr)+{-# INLINEABLE allocaStrided #-}++-- | Performs a straight 'copyBytes' on the underlying pointer of 'SV.Vector'+alignedCopyVector+  :: forall layout a m+   . (MonadIO m, AlignedStorable layout a)+  => AlignedPtr layout a+  -> SV.Vector (Strided layout a)+  -> m ()+alignedCopyVector (AlignedPtr dest) v = liftIO $ SV.unsafeWith v \src ->+  copyBytes+    dest+    (castPtr src)+    (sizeOf @(Strided layout a) undefined * SV.length v)+{-# INLINE alignedCopyVector #-}++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++zeroPtr :: forall a m. (MonadIO m, Storable a) => Ptr a -> m ()+zeroPtr ptr = fillBytes ptr 0 (sizeOf @a undefined)+{-# INLINE zeroPtr #-}++allocaZeroed :: (MonadUnliftIO m, Storable a) => (Ptr a -> m b) -> m b+allocaZeroed f = alloca \ptr -> zeroPtr ptr >> f ptr+{-# INLINE allocaZeroed #-}++withZeroed :: (MonadUnliftIO m, Storable a) => a -> (Ptr a -> m b) -> m b+withZeroed a f = allocaZeroed \ptr -> do+  liftIO $ poke ptr a+  f ptr+{-# INLINE withZeroed #-}
+ src/Foreign/GPU/Storable/Aligned.hs view
@@ -0,0 +1,1216 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Description : Generically derive Storable instances for GPU memory layouts+-- Copyright   : (c) Jeremy Nuttall, 2025+-- License     : BSD-3-Clause+-- Maintainer  : jeremy@jeremy-nuttall.com+-- Stability   : experimental+--+-- Derive @Storable@ instances that respect GPU memory layout rules (@std140@,+-- @std430@, @scalar@). Works with any product type that has a 'Generic' instance.+--+-- @+-- data Uniforms = Uniforms+--   { viewProj  :: M44 Float+--   , cameraPos :: V3 Float+--   } deriving Generic+--+-- instance AlignedStorable Std140 Uniforms+-- @+--+-- Then use "Foreign.GPU.Marshal.Aligned" to get properly-padded pointers:+--+-- @+-- withPacked \@Std140 uniforms $ \\ptr -> uploadToGPU ptr+-- @+--+-- __NB:__ @vec3@ and @mat3@ types have historically had driver bugs+-- around alignment. Prefer @vec4@/@mat4@ when possible.+module Foreign.GPU.Storable.Aligned (+  -- * The AlignedStorable class+  AlignedStorable (..),++  -- * Memory layout+  MemoryLayout (..),+  SMemoryLayout (..),+  KnownMemoryLayout (..),+  memoryLayoutVal,++  -- * Storable Wrappers+  Strided (..),+  StridedVector,+  Packed (..),+  AlignedArray (..),+  mkAlignedArray,+  withAlignedArray,+  AlignedPtr (..),++  -- * Memory layout rules+  MemoryLayoutRules (..),++  -- * Generics+  GAlignedStorable (..),+)+where++import Control.Monad+import Data.Bifunctor (first)+import Data.Bits+import Data.Coerce+import Data.Foldable (traverse_)+import Data.Hashable (Hashable)+import Data.Int+import Data.Kind+import Data.Maybe (fromJust)+import Data.Proxy+import Data.Typeable (Typeable)+import qualified Data.Vector.Generic as GV+import qualified Data.Vector.Generic.Sized as SGV+import qualified Data.Vector.Storable as SV+import qualified Data.Vector.Storable.Mutable as SMV+import qualified Data.Vector.Storable.Sized as SSV+import Data.Word+import Foreign (Ptr, castPtr, copyBytes, plusPtr)+import Foreign.Storable+import GHC.Generics+import GHC.TypeLits+import Linear (M22, M23, M24, M32, M33, M34, M42, M43, M44, V2 (..), V3 (..), V4 (..))+import Numeric.Half (Half)++--------------------------------------------------------------------------------+-- Layout Rules+--------------------------------------------------------------------------------++-- | A type-level tag representing a memory layout standard.+data MemoryLayout+  = -- | `std140` is a standardized memory layout for SPIR-V interface blocks.+    -- It has strict padding and alignment rules, ensuring layout consistency across platforms.+    --+    -- For details, refer to 7.6.2.2 "Standard Uniform Block Layout" of the+    -- [OpenGL 4.6 Specification](https://registry.khronos.org/OpenGL/specs/gl/glspec46.core.pdf).+    Std140+  | -- | `std430` is a standardized memory layout, typically used for Shader Storage Buffer+    -- Objects (SSBOs). It has more relaxed alignment rules for arrays and structs+    -- than `std140`, which can result in more compact memory usage.+    --+    -- For details, please refer to the section 7.6.2.2 "Standard Uniform Block Layout" of the+    -- [OpenGL 4.6 Specification](https://registry.khronos.org/OpenGL/specs/gl/glspec46.core.pdf).+    Std430+  | -- | `scalar` block layout is a memory layout with the most relaxed alignment rules,+    -- closely matching the alignment of scalar and vector types in C.+    --+    -- For details, please refer to the+    -- [extension specification](https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GL_EXT_scalar_block_layout.txt)+    Scalar+  deriving (Show, Generic, Typeable, Enum, Bounded, Eq, Ord)++instance Hashable MemoryLayout++-- | Singled 'MemoryLayout', primarily useful for authors of higher-level libraries+-- (e.g., shader eDSLs, descriptor set builders).+data SMemoryLayout (layout :: MemoryLayout) where+  SStd140 :: SMemoryLayout Std140+  SStd430 :: SMemoryLayout Std430+  SScalar :: SMemoryLayout Scalar++-- | This class gives the 'SMemoryLayout' associated with a 'MemoryLayout'.+--+-- A very rough sketch of this use case:+--+-- > emitLayoutQualifier :: forall layout. KnownMemoryLayout layout => String+-- > emitLayoutQualifier = case memoryLayoutVal (Proxy @layout) of+-- >   Std140 -> "layout(std140)"+-- >   Std430 -> "layout(std430)"+-- >   Scalar -> "layout(scalar)"+class KnownMemoryLayout (layout :: MemoryLayout) where+  memoryLayoutSing :: SMemoryLayout layout++instance KnownMemoryLayout Std140 where+  memoryLayoutSing = SStd140+  {-# INLINE memoryLayoutSing #-}++instance KnownMemoryLayout Std430 where+  memoryLayoutSing = SStd430+  {-# INLINE memoryLayoutSing #-}++instance KnownMemoryLayout Scalar where+  memoryLayoutSing = SScalar+  {-# INLINE memoryLayoutSing #-}++memoryLayoutVal :: forall layout. (KnownMemoryLayout layout) => Proxy layout -> MemoryLayout+memoryLayoutVal _ = case memoryLayoutSing @layout of+  SStd140 -> Std140+  SStd430 -> Std430+  SScalar -> Scalar+{-# INLINE memoryLayoutVal #-}++-- | Defines the precise calculation rules for a given 'MemoryLayout'.+--+-- These rules correspond to the memory layout requirements found in the OpenGL+-- and Vulkan specifications.+class MemoryLayoutRules (layout :: MemoryLayout) where+  -- | The alignment rule for top-level structs, matrix rows, and array members.+  alignBlock :: Proxy layout -> Int -> Int++  -- | The final size rule for a top-level struct.+  roundStructSize :: Proxy layout -> Int -> Int -> Int++  -- | The stride rule for an element within an array.+  layoutStride :: Proxy layout -> Int -> Int -> Int++std140Align :: Int+std140Align = 16++instance MemoryLayoutRules Std140 where+  alignBlock _ = roundUpTo std140Align+  {-# INLINE alignBlock #-}+  roundStructSize _ = flip roundUpTo+  {-# INLINE roundStructSize #-}+  layoutStride _ size align = roundUpTo std140Align (roundUpTo align size)+  {-# INLINE layoutStride #-}++instance MemoryLayoutRules Std430 where+  alignBlock _ = id+  {-# INLINE alignBlock #-}+  roundStructSize _ = flip roundUpTo+  {-# INLINE roundStructSize #-}+  layoutStride _ = flip roundUpTo+  {-# INLINE layoutStride #-}++instance MemoryLayoutRules Scalar where+  alignBlock _ = id+  {-# INLINE alignBlock #-}+  roundStructSize _ size _ = size+  {-# INLINE roundStructSize #-}+  layoutStride _ = flip roundUpTo+  {-# INLINE layoutStride #-}++--------------------------------------------------------------------------------+-- Layout-aware Storable+--------------------------------------------------------------------------------++-- | Wrapper whose 'sizeOf' includes stride padding, for use in arrays.+-- @SV.Vector (Strided Std140 MyType)@ can be 'copyBytes'd to the GPU in one shot.+newtype Strided (layout :: MemoryLayout) a = Strided {unStrided :: a}+  deriving (Generic, Typeable, Show, Eq, Ord)++-- | Wrapper without stride padding. Use for single values (push constants, lone UBOs).+newtype Packed (layout :: MemoryLayout) a = Packed {unPacked :: a}+  deriving (Generic, Typeable, Show, Eq, Ord)++-- | A convenience type for Data.Vector.Storable.Vector (Strided layout a)+type StridedVector (layout :: MemoryLayout) a = SV.Vector (Strided layout a)++-- | Opt-in 'copyBytes' for fixed-size arrays. Parameterize your struct by layout:+--+-- @+-- data MyStruct layout = MyStruct { pixels :: AlignedArray layout 64 (V4 Float) }+-- @+newtype AlignedArray (layout :: MemoryLayout) (n :: Nat) a = AlignedArray+  {unAlignedArray :: SSV.Vector n (Strided layout a)}+  deriving (Generic, Typeable, Show, Eq, Ord)++-- | Construct an 'AlignedArray' from a sized vector.+mkAlignedArray+  :: (AlignedStorable layout a, Storable a) => SSV.Vector n a -> AlignedArray layout n a+mkAlignedArray = AlignedArray . SSV.map Strided+{-# INLINE CONLIKE mkAlignedArray #-}++-- | Helper function to unwrap an 'AlignedArray' and manipulate its contents.+-- Don't do unsafe pointer tricks in the closure.+withAlignedArray+  :: AlignedArray layout n a+  -> (SSV.Vector n (Strided layout a) -> SSV.Vector n (Strided layout a))+  -> AlignedArray layout n a+withAlignedArray (AlignedArray v) f = AlignedArray (f v)+{-# INLINE withAlignedArray #-}++-- | A 'Ptr' that is tagged with its 'MemoryLayout'.+newtype AlignedPtr (layout :: MemoryLayout) a = AlignedPtr {unAlignedPtr :: Ptr a}+  deriving (Generic, Typeable, Show)++type role AlignedPtr nominal nominal++-- | A class for types that have calculable layouts according to GPU requirements.+--+-- __NB__: `alignedPoke` only writes member data. Only poke into zeroed buffers+-- unless you really enjoy parsing through garbage in RenderDoc.+class (MemoryLayoutRules layout) => AlignedStorable (layout :: MemoryLayout) a where+  -- | The size of the type 'a' after its contents are laid out, but *before*+  -- any final padding is applied to the container struct itself. This is+  -- the offset after the last member.+  packedAlignedSizeOf :: Proxy layout -> Proxy a -> Int+  default packedAlignedSizeOf+    :: (GAlignedStorable layout (Rep a))+    => Proxy layout+    -> Proxy a+    -> Int+  packedAlignedSizeOf l _ = fst (galignedSize l (Proxy @(Rep a)) 0)+  {-# INLINE packedAlignedSizeOf #-}++  -- | The size of the type 'a' including final padding/rounding according to the+  -- layout rules for a struct. For `Std140`, this means the size is rounded+  -- up to a multiple of 16.+  alignedSizeOf :: Proxy layout -> Proxy a -> Int+  default alignedSizeOf+    :: (GAlignedStorable layout (Rep a))+    => Proxy layout+    -> Proxy a+    -> Int+  alignedSizeOf l _ =+    let (!s, !a) = galignedSize l (Proxy @(Rep a)) 0+        !structAlign = alignBlock l a+     in roundStructSize l s structAlign+  {-# INLINE alignedSizeOf #-}++  -- | The base alignment requirement for the type 'a'.+  alignedAlignment :: Proxy layout -> Proxy a -> Int+  default alignedAlignment+    :: (GAlignedStorable layout (Rep a))+    => Proxy layout+    -> Proxy a+    -> Int+  alignedAlignment l _ =+    let (_, !a) = galignedSize l (Proxy @(Rep a)) 0+     in alignBlock l a+  {-# INLINE alignedAlignment #-}++  -- | Read a value from the given pointer, respecting the layout rules.+  alignedPeek :: AlignedPtr layout a -> IO a+  default alignedPeek+    :: (Generic a, GAlignedStorable layout (Rep a))+    => AlignedPtr layout a+    -> IO a+  alignedPeek (AlignedPtr ptr) = to . fst <$> galignedPeek @layout (AlignedPtr (castPtr ptr)) 0+  {-# INLINE alignedPeek #-}++  -- | Write a value to the given pointer, respecting the layout rules.+  alignedPoke :: AlignedPtr layout a -> a -> IO ()+  default alignedPoke+    :: (Generic a, GAlignedStorable layout (Rep a))+    => AlignedPtr layout a+    -> a+    -> IO ()+  alignedPoke (AlignedPtr ptr) val = void $ galignedPoke @layout (AlignedPtr (castPtr ptr)) (from val) 0+  {-# INLINE alignedPoke #-}++-- | @sizeOf @(Strided layout a)@ calculates the full stride of the type,+-- including final padding, making it suitable for array allocations.+--+-- Use zeroed buffers when using this newtype.+instance (MemoryLayoutRules layout, AlignedStorable layout a) => Storable (Strided layout a) where+  sizeOf _ =+    layoutStride+      (Proxy @layout)+      (alignedSizeOf (Proxy @layout) (Proxy @a))+      (alignedAlignment (Proxy @layout) (Proxy @a))+  {-# INLINE sizeOf #-}+  alignment _ = alignedAlignment (Proxy @layout) (Proxy @a)+  {-# INLINE alignment #-}+  peek ptr = Strided <$> alignedPeek @layout (AlignedPtr (castPtr ptr))+  {-# INLINE peek #-}+  poke ptr a = alignedPoke @layout (AlignedPtr (castPtr ptr)) (coerce a :: a)+  {-# INLINE poke #-}++instance (AlignedStorable layout a) => Storable (Packed layout a) where+  sizeOf _ = alignedSizeOf (Proxy @layout) (Proxy @a)+  {-# INLINE sizeOf #-}+  alignment _ = alignedAlignment (Proxy @layout) (Proxy @a)+  {-# INLINE alignment #-}+  peek ptr = Packed <$> alignedPeek @layout (AlignedPtr (castPtr ptr))+  {-# INLINE peek #-}+  poke ptr a = alignedPoke @layout (AlignedPtr (castPtr ptr)) (coerce a :: a)+  {-# INLINE poke #-}++--------------------------------------------------------------------------------+-- Generic deriving+--------------------------------------------------------------------------------++-- | Generically derive 'AlignedStorable'.+--+-- > data MyType = MyType { field1 :: Float, field2 :: V3 Float }+-- >   deriving (Generic)+-- >+-- > instance AlignedStorable Std140 MyType+class GAlignedStorable (layout :: MemoryLayout) (rep :: Type -> Type) where+  galignedSize :: Proxy layout -> Proxy rep -> Int -> (Int, Int)+  galignedPoke :: AlignedPtr layout a -> rep a -> Int -> IO Int+  galignedPeek :: AlignedPtr layout a -> Int -> IO (rep a, Int)++instance+  ( TypeError+      (Text "Cannot derive AlignedStorable for empty data types as there is no shader equivalent.")+  )+  => GAlignedStorable layout V1+  where+  galignedSize = error "unreachable: empty data type"+  galignedPoke = error "unreachable: empty data type"+  galignedPeek = error "unreachable: empty data type"++instance+  ( TypeError+      (Text "Cannot derive AlignedStorable for nullary constructors as there is no shader equivalent.")+  )+  => GAlignedStorable layout U1+  where+  galignedSize = error "unreachable: nullary constructor"+  galignedPoke = error "unreachable: nullary constructor"+  galignedPeek = error "unreachable: nullary constructor"++instance+  ( TypeError+      ( Text+          "Cannot derive AlignedStorable for sum types as there is no unambiguous shader equivalent."+      )+  )+  => GAlignedStorable layout (a :+: b)+  where+  galignedSize = error "unreachable: sum type"+  galignedPoke = error "unreachable: sum type"+  galignedPeek = error "unreachable: sum type"++instance (AlignedStorable layout c) => GAlignedStorable layout (K1 i c) where+  galignedSize l _ off =+    let !a = alignedAlignment l (Proxy @c)+        !s = alignedSizeOf l (Proxy @c)+        !off' = roundUpTo a off+     in (off' + s, a)+  {-# INLINE galignedSize #-}+  galignedPoke (AlignedPtr ptr) (K1 !v) !off = do+    let !a = alignedAlignment (Proxy @layout) (Proxy @c)+        !s = alignedSizeOf (Proxy @layout) (Proxy @c)+        !off' = roundUpTo a off+    alignedPoke @layout (AlignedPtr (ptr `plusPtr` off')) v+    pure (off' + s)+  {-# INLINE galignedPoke #-}+  galignedPeek (AlignedPtr ptr) !off = do+    let !a = alignedAlignment (Proxy @layout) (Proxy @c)+        !s = alignedSizeOf (Proxy @layout) (Proxy @c)+        !off' = roundUpTo a off+    !v <- alignedPeek @layout (AlignedPtr (ptr `plusPtr` off'))+    pure (K1 v, off' + s)+  {-# INLINE galignedPeek #-}++instance (GAlignedStorable layout a, GAlignedStorable layout b) => GAlignedStorable layout (a :*: b) where+  galignedSize l _ off =+    let (!nextOffA, !alignA) = galignedSize l (Proxy @a) off+        (!nextOffB, !alignB) = galignedSize l (Proxy @b) nextOffA+     in (nextOffB, max alignA alignB)+  {-# INLINE galignedSize #-}+  galignedPoke ptr (valA :*: valB) !off = do+    !nextOffA <- galignedPoke ptr valA off+    galignedPoke ptr valB nextOffA+  {-# INLINE galignedPoke #-}+  galignedPeek ptr off = do+    (!valA, !nextOffA) <- galignedPeek ptr off+    (!valB, !nextOffB) <- galignedPeek ptr nextOffA+    pure (valA :*: valB, nextOffB)+  {-# INLINE galignedPeek #-}++instance (GAlignedStorable layout f) => GAlignedStorable layout (M1 i c f) where+  galignedSize l _ = galignedSize l (Proxy @f)+  {-# INLINE galignedSize #-}+  galignedPoke ptr = galignedPoke ptr . unM1+  {-# INLINE galignedPoke #-}+  galignedPeek ptr off = first M1 <$> galignedPeek ptr off+  {-# INLINE galignedPeek #-}++--------------------------------------------------------------------------------+-- Sized arrays+--------------------------------------------------------------------------------++instance+  {-# OVERLAPPABLE #-}+  (KnownNat n, AlignedStorable layout a, GV.Vector v a)+  => AlignedStorable layout (SGV.Vector v n a)+  where+  packedAlignedSizeOf _ _ = packedAlignedSizeOfArray (Proxy @layout) (Proxy @n) (Proxy @a)+  {-# INLINE packedAlignedSizeOf #-}+  alignedSizeOf = packedAlignedSizeOf+  {-# INLINE alignedSizeOf #-}+  alignedAlignment l _ = arrayAlignedAlignment l (Proxy @a)+  {-# INLINE alignedAlignment #-}+  alignedPeek = garrayAlignedPeek+  {-# INLINE alignedPeek #-}+  alignedPoke = garrayAlignedPoke+  {-# INLINE alignedPoke #-}++instance (KnownNat n, AlignedStorable Scalar a, GV.Vector v a) => AlignedStorable Scalar (SGV.Vector v n a) where+  packedAlignedSizeOf _ _ = scalarPackedAlignedSizeOfArray (Proxy @n) (Proxy @a)+  {-# INLINE packedAlignedSizeOf #-}+  alignedSizeOf = packedAlignedSizeOf+  {-# INLINE alignedSizeOf #-}+  alignedAlignment l _ = arrayAlignedAlignment l (Proxy @a)+  {-# INLINE alignedAlignment #-}+  alignedPeek = garrayAlignedPeek+  {-# INLINE alignedPeek #-}+  alignedPoke = garrayAlignedPoke+  {-# INLINE alignedPoke #-}++instance+  {-# OVERLAPPABLE #-}+  (KnownNat n, AlignedStorable layout a)+  => AlignedStorable layout (AlignedArray layout n a)+  where+  packedAlignedSizeOf _ _ = packedAlignedSizeOfArray (Proxy @layout) (Proxy @n) (Proxy @a)+  {-# INLINE packedAlignedSizeOf #-}+  alignedSizeOf = packedAlignedSizeOf+  {-# INLINE alignedSizeOf #-}+  alignedAlignment l _ = arrayAlignedAlignment l (Proxy @a)+  {-# INLINE alignedAlignment #-}+  alignedPeek (AlignedPtr src) = do+    let s = fromIntegral $ natVal (Proxy @n)+    v <- SMV.new s+    SMV.unsafeWith v \dest ->+      copyBytes (castPtr dest) src (alignedSizeOf (Proxy @layout) (Proxy @(AlignedArray layout n a)))+    AlignedArray . fromJust . SSV.toSized @n <$> SV.unsafeFreeze v+  {-# INLINE alignedPeek #-}+  alignedPoke (AlignedPtr dest) (AlignedArray v) = SV.unsafeWith (SSV.fromSized v) \src ->+    copyBytes dest (castPtr src) (alignedSizeOf (Proxy @layout) (Proxy @(AlignedArray layout n a)))+  {-# INLINE alignedPoke #-}++instance+  (KnownNat n, AlignedStorable Scalar a)+  => AlignedStorable Scalar (AlignedArray Scalar n a)+  where+  packedAlignedSizeOf _ _ = scalarPackedAlignedSizeOfArray (Proxy @n) (Proxy @a)+  {-# INLINE packedAlignedSizeOf #-}+  alignedSizeOf = packedAlignedSizeOf+  {-# INLINE alignedSizeOf #-}+  alignedAlignment l _ = arrayAlignedAlignment l (Proxy @a)+  {-# INLINE alignedAlignment #-}+  alignedPeek (AlignedPtr src) = do+    let s = fromIntegral $ natVal (Proxy @n)+    v <- SMV.new s+    SMV.unsafeWith v \dest ->+      copyBytes (castPtr dest) src (alignedSizeOf (Proxy @Scalar) (Proxy @(AlignedArray Scalar n a)))+    AlignedArray . fromJust . SSV.toSized @n <$> SV.unsafeFreeze v+  {-# INLINE alignedPeek #-}+  alignedPoke (AlignedPtr dest) (AlignedArray v) = SV.unsafeWith (SSV.fromSized v) \src ->+    copyBytes dest (castPtr src) (alignedSizeOf (Proxy @Scalar) (Proxy @(AlignedArray Scalar n a)))+  {-# INLINE alignedPoke #-}++packedAlignedSizeOfArray+  :: forall layout n a+   . (KnownNat n, AlignedStorable layout a)+  => Proxy layout+  -> Proxy n+  -> Proxy a+  -> Int+packedAlignedSizeOfArray _ _ _ = fromIntegral (natVal (Proxy @n)) * sizeOf @(Strided layout a) undefined+{-# INLINE packedAlignedSizeOfArray #-}++scalarPackedAlignedSizeOfArray+  :: forall n a+   . (KnownNat n, AlignedStorable Scalar a)+  => Proxy n+  -> Proxy a+  -> Int+scalarPackedAlignedSizeOfArray _ _ =+  let n = fromIntegral $ natVal (Proxy @n)+      stride = sizeOf @(Strided Scalar a) undefined+      lastElementSize = packedAlignedSizeOf (Proxy @Scalar) (Proxy @a)+   in if n > 0+        then ((n - 1) * stride) + lastElementSize+        else 0+{-# INLINE scalarPackedAlignedSizeOfArray #-}++arrayAlignedAlignment+  :: forall layout a. (AlignedStorable layout a) => Proxy layout -> Proxy a -> Int+arrayAlignedAlignment l a = alignBlock l $ alignedAlignment l a+{-# INLINE arrayAlignedAlignment #-}++garrayAlignedPeek+  :: forall layout n a v+   . (KnownNat n, GV.Vector v a, AlignedStorable layout a)+  => AlignedPtr layout (SGV.Vector v n a)+  -> IO (SGV.Vector v n a)+garrayAlignedPeek (AlignedPtr ptr) =+  let !stride = sizeOf @(Strided layout a) undefined+   in SGV.generateM \i ->+        alignedPeek @layout (AlignedPtr (ptr `plusPtr` (fromIntegral i * stride)))+{-# INLINE garrayAlignedPeek #-}++garrayAlignedPoke+  :: forall layout n a v+   . (KnownNat n, GV.Vector v a, AlignedStorable layout a)+  => AlignedPtr layout (SGV.Vector v n a)+  -> SGV.Vector v n a+  -> IO ()+garrayAlignedPoke (AlignedPtr ptr) =+  let !stride = sizeOf @(Strided layout a) undefined+   in fusedimapMSized_ \i -> alignedPoke @layout (AlignedPtr (ptr `plusPtr` (i * stride)))+{-# INLINE garrayAlignedPoke #-}++--------------------------------------------------------------------------------+-- Utils+--------------------------------------------------------------------------------++-- | Bit-twiddling hack. Assumes multiple is a power of 2.+-- The assumption holds for all standard GPU layouts.+--+-- __NB__: As of writing, GHC's optimizer fails when optimizing GAlignedStorable+-- if this function branches.+roundUpTo :: (Bits a, Num a) => a -> a -> a+roundUpTo multiple val = (val + multiple - 1) .&. complement (multiple - 1)+{-# INLINE roundUpTo #-}++-- | Exploits the knowledge of the array length to create a straight, bounded loop over the+-- underlying vector.+fusedimapMSized_+  :: forall n v a f+   . (Monad f, KnownNat n, GV.Vector v a)+  => (Int -> a -> f ())+  -> SGV.Vector v n a+  -> f ()+fusedimapMSized_ f v =+  traverse_+    (\i -> f i (SGV.unsafeIndex v i))+    [0 .. fromIntegral (natVal (Proxy @n)) - 1]+{-# INLINE fusedimapMSized_ #-}++--------------------------------------------------------------------------------+-- Internal helpers used across macros+--------------------------------------------------------------------------------++defaultAlignedPeek :: (Storable a) => AlignedPtr layout a -> IO a+defaultAlignedPeek (AlignedPtr ptr) = peek ptr+{-# INLINE defaultAlignedPeek #-}++defaultAlignedPoke :: (Storable a) => AlignedPtr layout a -> a -> IO ()+defaultAlignedPoke (AlignedPtr ptr) = poke ptr+{-# INLINE defaultAlignedPoke #-}++packedAlignedSizeOfMat+  :: forall layout a f g+   . (AlignedStorable layout (g a))+  => Proxy layout+  -> Proxy (f (g a))+  -> Int+packedAlignedSizeOfMat l _ =+  let rowSize = packedAlignedSizeOf l (Proxy @(g a))+      rowAlign = alignedAlignment l (Proxy @(g a))+   in layoutStride (Proxy @layout) rowSize rowAlign+{-# INLINE packedAlignedSizeOfMat #-}++-- | Unsafe, for internal use only+castAlignedPtr :: forall layout a b. AlignedPtr layout a -> AlignedPtr layout b+castAlignedPtr (AlignedPtr ptr) = AlignedPtr (castPtr ptr)++-- | Unsafe, for internal use only+alignedPeekByteOff+  :: forall layout a. (AlignedStorable layout a) => AlignedPtr layout a -> Int -> IO a+alignedPeekByteOff (AlignedPtr ptr) off = alignedPeek (AlignedPtr @layout (ptr `plusPtr` off))+{-# INLINE alignedPeekByteOff #-}++-- | Unsafe, for internal use only+alignedPokeByteOff+  :: forall layout a. (AlignedStorable layout a) => AlignedPtr layout a -> Int -> a -> IO ()+alignedPokeByteOff (AlignedPtr ptr) off = alignedPoke (AlignedPtr @layout (ptr `plusPtr` off))+{-# INLINE alignedPokeByteOff #-}++alignedPeekV2+  :: forall layout a+   . (AlignedStorable layout a, Storable a)+  => AlignedPtr layout (V2 a)+  -> IO (V2 a)+alignedPeekV2 (AlignedPtr ptr) =+  let !stride = alignedAlignment (Proxy @layout) (Proxy @a)+   in V2 <$> peekByteOff ptr 0 <*> peekByteOff ptr stride+{-# INLINE alignedPeekV2 #-}++alignedPokeV2+  :: forall layout a+   . (AlignedStorable layout a, Storable a)+  => AlignedPtr layout (V2 a)+  -> V2 a+  -> IO ()+alignedPokeV2 (AlignedPtr ptr) (V2 x y) = do+  let !stride = alignedAlignment (Proxy @layout) (Proxy @a)+  pokeByteOff ptr 0 x+  pokeByteOff ptr stride y+{-# INLINE alignedPokeV2 #-}++alignedPeekM2+  :: forall layout a v+   . (AlignedStorable layout (v a))+  => AlignedPtr layout (V2 (v a))+  -> IO (V2 (v a))+alignedPeekM2 aptr =+  let ptr = castAlignedPtr aptr+      !rowSize = packedAlignedSizeOf (Proxy @layout) (Proxy @(v a))+      !rowAlign = alignedAlignment (Proxy @layout) (Proxy @(v a))+      !stride = layoutStride (Proxy @layout) rowSize rowAlign+   in V2+        <$> alignedPeekByteOff ptr 0+        <*> alignedPeekByteOff ptr stride+{-# INLINE alignedPeekM2 #-}++alignedPokeM2+  :: forall layout a v+   . (AlignedStorable layout (v a))+  => AlignedPtr layout (V2 (v a))+  -> V2 (v a)+  -> IO ()+alignedPokeM2 aptr (V2 x y) = do+  let ptr = castAlignedPtr aptr+      !rowSize = packedAlignedSizeOf (Proxy @layout) (Proxy @(v a))+      !rowAlign = alignedAlignment (Proxy @layout) (Proxy @(v a))+      !stride = layoutStride (Proxy @layout) rowSize rowAlign+  alignedPokeByteOff ptr 0 x+  alignedPokeByteOff ptr stride y+{-# INLINE alignedPokeM2 #-}++alignedPeekV3+  :: forall layout a. (AlignedStorable layout a, Storable a) => AlignedPtr layout (V3 a) -> IO (V3 a)+alignedPeekV3 (AlignedPtr ptr) =+  let a = alignedAlignment (Proxy @layout) (Proxy @a)+   in V3 <$> peekByteOff ptr 0 <*> peekByteOff ptr a <*> peekByteOff ptr (a * 2)+{-# INLINE alignedPeekV3 #-}++alignedPokeV3+  :: forall layout a. (AlignedStorable layout a, Storable a) => AlignedPtr layout (V3 a) -> V3 a -> IO ()+alignedPokeV3 (AlignedPtr ptr) (V3 x y z) = do+  let a = alignedAlignment (Proxy @layout) (Proxy @a)+  pokeByteOff ptr 0 x+  pokeByteOff ptr a y+  pokeByteOff ptr (a * 2) z+{-# INLINE alignedPokeV3 #-}++alignedPeekM3+  :: forall layout a v+   . (AlignedStorable layout (v a))+  => AlignedPtr layout (V3 (v a))+  -> IO (V3 (v a))+alignedPeekM3 aptr =+  let ptr = castAlignedPtr aptr+      !rowSize = packedAlignedSizeOf (Proxy @layout) (Proxy @(v a))+      !rowAlign = alignedAlignment (Proxy @layout) (Proxy @(v a))+      !stride = layoutStride (Proxy @layout) rowSize rowAlign+   in V3+        <$> alignedPeekByteOff ptr 0+        <*> alignedPeekByteOff ptr stride+        <*> alignedPeekByteOff ptr (stride * 2)+{-# INLINE alignedPeekM3 #-}++alignedPokeM3+  :: forall layout a v+   . (AlignedStorable layout (v a))+  => AlignedPtr layout (V3 (v a))+  -> V3 (v a)+  -> IO ()+alignedPokeM3 aptr (V3 x y z) = do+  let ptr = castAlignedPtr aptr+      !rowSize = packedAlignedSizeOf (Proxy @layout) (Proxy @(v a))+      !rowAlign = alignedAlignment (Proxy @layout) (Proxy @(v a))+      !stride = layoutStride (Proxy @layout) rowSize rowAlign+  alignedPokeByteOff ptr 0 x+  alignedPokeByteOff ptr stride y+  alignedPokeByteOff ptr (stride * 2) z+{-# INLINE alignedPokeM3 #-}++alignedPokeV4+  :: forall layout a. (AlignedStorable layout a, Storable a) => AlignedPtr layout (V4 a) -> V4 a -> IO ()+alignedPokeV4 (AlignedPtr ptr) (V4 x y z w) = do+  let a = alignedAlignment (Proxy @layout) (Proxy @a)+  pokeByteOff ptr 0 x+  pokeByteOff ptr a y+  pokeByteOff ptr (a * 2) z+  pokeByteOff ptr (a * 3) w+{-# INLINE alignedPokeV4 #-}++alignedPeekV4+  :: forall layout a. (AlignedStorable layout a, Storable a) => AlignedPtr layout (V4 a) -> IO (V4 a)+alignedPeekV4 (AlignedPtr ptr) =+  let a = alignedAlignment (Proxy @layout) (Proxy @a)+   in V4+        <$> peekByteOff ptr 0+        <*> peekByteOff ptr a+        <*> peekByteOff ptr (a * 2)+        <*> peekByteOff ptr (a * 3)+{-# INLINE alignedPeekV4 #-}++alignedPeekM4+  :: forall layout a v+   . (AlignedStorable layout (v a))+  => AlignedPtr layout (V4 (v a))+  -> IO (V4 (v a))+alignedPeekM4 aptr =+  let ptr = castAlignedPtr aptr+      !rowSize = packedAlignedSizeOf (Proxy @layout) (Proxy @(v a))+      !rowAlign = alignedAlignment (Proxy @layout) (Proxy @(v a))+      !stride = layoutStride (Proxy @layout) rowSize rowAlign+   in V4+        <$> alignedPeekByteOff ptr 0+        <*> alignedPeekByteOff ptr stride+        <*> alignedPeekByteOff ptr (stride * 2)+        <*> alignedPeekByteOff ptr (stride * 3)+{-# INLINE alignedPeekM4 #-}++alignedPokeM4+  :: forall layout a v+   . (AlignedStorable layout (v a))+  => AlignedPtr layout (V4 (v a))+  -> V4 (v a)+  -> IO ()+alignedPokeM4 aptr (V4 x y z w) = do+  let ptr = castAlignedPtr aptr+      !rowSize = packedAlignedSizeOf (Proxy @layout) (Proxy @(v a))+      !rowAlign = alignedAlignment (Proxy @layout) (Proxy @(v a))+      !stride = layoutStride (Proxy @layout) rowSize rowAlign+  alignedPokeByteOff ptr 0 x+  alignedPokeByteOff ptr stride y+  alignedPokeByteOff ptr (stride * 2) z+  alignedPokeByteOff ptr (stride * 3) w+{-# INLINE alignedPokeM4 #-}++--------------------------------------------------------------------------------+-- Base instances for primitive shader types+--+-- CPP is the simple way to stamp out the instances. I'll probably switch to TH+-- at some point.+--------------------------------------------------------------------------------++-- schema: (MemoryLayout, Type, size, alignment)+#define FOR_EACH_STD140_PRIMITIVE(X) \+  X (Std140, Bool, 4, 4) \+  X (Std140, Half, 2, 2) \+  X (Std140, Float, 4, 4) \+  X (Std140, Double, 8, 8) \+  X (Std140, Int8, 4, 4) \+  X (Std140, Int16, 4, 4) \+  X (Std140, Int32, 4, 4) \+  X (Std140, Int64, 8, 8) \+  X (Std140, Word8, 4, 4) \+  X (Std140, Word16, 4, 4) \+  X (Std140, Word32, 4, 4) \+  X (Std140, Word64, 8, 8)++-- schema: (MemoryLayout, Type, size, alignment)+#define FOR_EACH_STD430_PRIMITIVE(X) \+  X (Std430, Bool, 4, 4) \+  X (Std430, Half, 2, 2) \+  X (Std430, Float, 4, 4) \+  X (Std430, Double, 8, 8) \+  X (Std430, Int8, 1, 1) \+  X (Std430, Int16, 2, 2) \+  X (Std430, Int32, 4, 4) \+  X (Std430, Int64, 8, 8) \+  X (Std430, Word8, 1, 1) \+  X (Std430, Word16, 2, 2) \+  X (Std430, Word32, 4, 4) \+  X (Std430, Word64, 8, 8)++-- schema: (MemoryLayout, Type, size, alignment)+#define FOR_EACH_SCALAR_PRIMITIVE(X) \+  X (Scalar, Bool, 4, 4) \+  X (Scalar, Half, 2, 2) \+  X (Scalar, Float, 4, 4) \+  X (Scalar, Double, 8, 8) \+  X (Scalar, Int8, 1, 1) \+  X (Scalar, Int16, 2, 2) \+  X (Scalar, Int32, 4, 4) \+  X (Scalar, Int64, 8, 8) \+  X (Scalar, Word8, 1, 1) \+  X (Scalar, Word16, 2, 2) \+  X (Scalar, Word32, 4, 4) \+  X (Scalar, Word64, 8, 8)++-- schema: (Type)+#define FOR_EACH_MATRIX_PRIMITIVE(X) \+  X (Half) \+  X (Float) \+  X (Double)++#define PRIMITIVE_INSTANCE(LAYOUT, T, S, A) \+instance AlignedStorable LAYOUT T where { \+  packedAlignedSizeOf _ _ = S; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf _ _ = S; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment _ _ = A; \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = defaultAlignedPeek; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = defaultAlignedPoke; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(LAYOUT, T, S, A) PRIMITIVE_INSTANCE(LAYOUT, T, S, A)+FOR_EACH_STD140_PRIMITIVE (X)+FOR_EACH_STD430_PRIMITIVE (X)+FOR_EACH_SCALAR_PRIMITIVE (X)+#undef X++--------------------------------------------------------------------------------+-- Base instances for vectors and matrices+--------------------------------------------------------------------------------++#define VEC2_STD_INSTANCE(LAYOUT, T) \+instance AlignedStorable LAYOUT (V2 T) where { \+  packedAlignedSizeOf _ _ = 2 * alignedSizeOf (Proxy :: Proxy LAYOUT) (Proxy :: Proxy T); \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf _ _ = 2 * alignedSizeOf (Proxy :: Proxy LAYOUT) (Proxy :: Proxy T); \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment _ _ = 2 * alignedAlignment (Proxy :: Proxy LAYOUT) (Proxy :: Proxy T); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekV2; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeV2; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(LAYOUT, T, S, A) VEC2_STD_INSTANCE(LAYOUT, T)+FOR_EACH_STD140_PRIMITIVE (X)+FOR_EACH_STD430_PRIMITIVE (X)+#undef X++#define VEC2_SCALAR_INSTANCE(T) \+instance AlignedStorable Scalar (V2 T) where { \+  packedAlignedSizeOf _ _ = 2 * alignedSizeOf (Proxy :: Proxy Scalar) (Proxy :: Proxy T); \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf _ _ = 2 * alignedSizeOf (Proxy :: Proxy Scalar) (Proxy :: Proxy T); \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment _ _ = alignedAlignment (Proxy :: Proxy Scalar) (Proxy :: Proxy T); \+  alignedPeek = defaultAlignedPeek; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = defaultAlignedPoke; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(LAYOUT, T, S, A) VEC2_SCALAR_INSTANCE(T)+FOR_EACH_SCALAR_PRIMITIVE (X)+#undef X++#define MAT2_STD_INSTANCE(LAYOUT, T) \+instance AlignedStorable LAYOUT (M22 T) where { \+  packedAlignedSizeOf l a = 2 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V2 T))); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekM2; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM2; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT2_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT2_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC2_SCALAR_INSTANCE((V2 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define MAT23_STD_INSTANCE(LAYOUT, T) \+instance AlignedStorable LAYOUT (M23 T) where { \+  packedAlignedSizeOf l a = 2 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V3 T))); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekM2; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM2; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT23_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT23_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC2_SCALAR_INSTANCE((V3 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define MAT24_STD_INSTANCE(LAYOUT, T) \+instance AlignedStorable LAYOUT (M24 T) where { \+  packedAlignedSizeOf l a = 2 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf ; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V4 T))); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekM2; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM2; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT24_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT24_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC2_SCALAR_INSTANCE((V4 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define VEC3_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (V3 A) where { \+  packedAlignedSizeOf l _ = 3 * alignedSizeOf l (Proxy :: Proxy A); \+  {-# INLINE packedAlignedSizeOf #-} ; \+  alignedSizeOf l _ = 3 * alignedSizeOf l (Proxy :: Proxy A); \+  alignedAlignment l _ = 4 * alignedAlignment l (Proxy :: Proxy A); \+  {-# INLINE alignedAlignment #-} ; \+  alignedPeek = alignedPeekV3; \+  {-# INLINE alignedPeek #-} ; \+  alignedPoke = alignedPokeV3; \+    {-# INLINE alignedPoke #-} ; \+  }; \++#define X(LAYOUT, T, S, A) VEC3_STD_INSTANCE(LAYOUT, T)+FOR_EACH_STD140_PRIMITIVE (X)+FOR_EACH_STD430_PRIMITIVE (X)+#undef X++#define VEC3_SCALAR_INSTANCE(A) \+instance AlignedStorable Scalar (V3 A) where { \+  packedAlignedSizeOf _ _ = 3 * alignedSizeOf (Proxy :: Proxy Scalar) (Proxy :: Proxy A); \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf _ _ = 3 * alignedSizeOf (Proxy :: Proxy Scalar) (Proxy :: Proxy A); \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment _ _ = alignedAlignment (Proxy :: Proxy Scalar) (Proxy :: Proxy A); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = defaultAlignedPeek; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = defaultAlignedPoke; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(LAYOUT, T, S, A) VEC3_SCALAR_INSTANCE(T)+FOR_EACH_SCALAR_PRIMITIVE (X)+#undef X++#define MAT3_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (M33 A) where { \+  packedAlignedSizeOf l a = 3 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V3 A))); \+  {-# INLINE alignedAlignment #-} ; \+  alignedPeek = alignedPeekM3; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM3; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT3_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT3_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC3_SCALAR_INSTANCE((V3 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define MAT32_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (M32 A) where { \+  packedAlignedSizeOf l a = 3 * packedAlignedSizeOfMat l a ; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V2 A))); \+  {-# INLINE alignedAlignment #-} ; \+  alignedPeek = alignedPeekM3; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM3; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT32_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT32_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC3_SCALAR_INSTANCE((V2 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define MAT34_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (M34 A) where { \+  packedAlignedSizeOf l a = 3 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf ; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V4 A))); \+  {-# INLINE alignedAlignment #-} ; \+  alignedPeek = alignedPeekM3; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM3; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT34_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT34_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC3_SCALAR_INSTANCE((V4 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define VEC4_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (V4 A) where { \+  packedAlignedSizeOf l _ = 4 * alignedSizeOf l (Proxy :: Proxy A); \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf l _ = 4 * alignedSizeOf l (Proxy :: Proxy A); \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = 4 * alignedAlignment l (Proxy :: Proxy A); \+  {-# INLINE alignedAlignment #-} ; \+  alignedPeek = alignedPeekV4; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeV4; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(LAYOUT, T, S, A) VEC4_STD_INSTANCE(LAYOUT, T)+FOR_EACH_STD140_PRIMITIVE (X)+FOR_EACH_STD430_PRIMITIVE (X)+#undef X++#define VEC4_SCALAR_INSTANCE(A) \+instance AlignedStorable Scalar (V4 A) where { \+  packedAlignedSizeOf _ _ = 4 * alignedSizeOf (Proxy :: Proxy Scalar) (Proxy :: Proxy A); \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf _ _ = 4 * alignedSizeOf (Proxy :: Proxy Scalar) (Proxy :: Proxy A); \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment _ _ = alignedAlignment (Proxy :: Proxy Scalar) (Proxy :: Proxy A); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = defaultAlignedPeek; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = defaultAlignedPoke; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(LAYOUT, T, S, A) VEC4_SCALAR_INSTANCE(T)+FOR_EACH_SCALAR_PRIMITIVE (X)+#undef X++#define MAT4_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (M44 A) where { \+  packedAlignedSizeOf l a = 4 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V4 A))); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekM4; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM4; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT4_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT4_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC4_SCALAR_INSTANCE((V4 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define MAT42_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (M42 A) where { \+  packedAlignedSizeOf l a = 4 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V2 A))); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekM4; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM4; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT42_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT42_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC4_SCALAR_INSTANCE((V2 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define MAT43_STD_INSTANCE(LAYOUT, A) \+instance AlignedStorable LAYOUT (M43 A) where { \+  packedAlignedSizeOf l a = 4 * packedAlignedSizeOfMat l a; \+  {-# INLINE packedAlignedSizeOf #-}; \+  alignedSizeOf = packedAlignedSizeOf; \+  {-# INLINE alignedSizeOf #-}; \+  alignedAlignment l _ = alignBlock l (alignedAlignment l (Proxy :: Proxy (V3 A))); \+  {-# INLINE alignedAlignment #-}; \+  alignedPeek = alignedPeekM4; \+  {-# INLINE alignedPeek #-}; \+  alignedPoke = alignedPokeM4; \+  {-# INLINE alignedPoke #-}; \+  };++#define X(T) MAT43_STD_INSTANCE(Std140, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++#define X(T) MAT43_STD_INSTANCE(Std430, T)+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X++-- For Scalar layout, matrices are trivially the same as nested vectors+#define X(T) VEC4_SCALAR_INSTANCE((V3 T))+FOR_EACH_MATRIX_PRIMITIVE (X)+#undef X
+ test/Driver.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF tasty-discover -optF --tree-display #-}
+ test/Foreign/GPU/Marshal/AlignedSpec.hs view
@@ -0,0 +1,113 @@+{-# 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
+ test/Foreign/GPU/Storable/AlignedInspectionSpec.hs view
@@ -0,0 +1,309 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -O -dsuppress-all -dno-suppress-type-signatures -fplugin=Test.Tasty.Inspection.Plugin #-}++module Foreign.GPU.Storable.AlignedInspectionSpec where++import Data.Proxy+import GHC.Generics (Generic)+import Test.Tasty+import Test.Tasty.Inspection++import Foreign.GPU.Storable.Aligned+import Foreign.GPU.Storable.AlignedSpec++-- | Any type that pokes an array incurs allocations, even if it uses `copyBytes` internally.+-- I suspect llvm will unroll the loop if it is used, but I can't prove this by inspecting core.+data Stress+  = Stress+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+      Insanity+  deriving (Generic)++instance AlignedStorable Std140 Stress+instance AlignedStorable Std430 Stress+instance AlignedStorable Scalar Stress++packedAlignedSizeOf140Stress :: Proxy Std140 -> Proxy Stress -> Int+packedAlignedSizeOf140Stress = packedAlignedSizeOf+packedAlignedSizeOf430Stress :: Proxy Std430 -> Proxy Stress -> Int+packedAlignedSizeOf430Stress = packedAlignedSizeOf+packedAlignedSizeOfScalarStress :: Proxy Scalar -> Proxy Stress -> Int+packedAlignedSizeOfScalarStress = packedAlignedSizeOf++alignedAlignment140Stress :: Proxy Std140 -> Proxy Stress -> Int+alignedAlignment140Stress = alignedAlignment+alignedAlignment430Stress :: Proxy Std430 -> Proxy Stress -> Int+alignedAlignment430Stress = alignedAlignment+alignedAlignmentScalarStress :: Proxy Scalar -> Proxy Stress -> Int+alignedAlignmentScalarStress = alignedAlignment++alignedSizeOf140Stress :: Proxy Std140 -> Proxy Stress -> Int+alignedSizeOf140Stress = alignedSizeOf+alignedSizeOf430Stress :: Proxy Std430 -> Proxy Stress -> Int+alignedSizeOf430Stress = alignedSizeOf+alignedSizeOfScalarStress :: Proxy Scalar -> Proxy Stress -> Int+alignedSizeOfScalarStress = alignedSizeOf++alignedPoke140Stress :: AlignedPtr Std140 Stress -> Stress -> IO ()+alignedPoke140Stress = alignedPoke+alignedPoke430Stress :: AlignedPtr Std430 Stress -> Stress -> IO ()+alignedPoke430Stress = alignedPoke+alignedPokeScalarStress :: AlignedPtr Scalar Stress -> Stress -> IO ()+alignedPokeScalarStress = alignedPoke++alignedPeek140Stress :: AlignedPtr Std140 Stress -> IO Stress+alignedPeek140Stress = alignedPeek+alignedPeek430Stress :: AlignedPtr Std430 Stress -> IO Stress+alignedPeek430Stress = alignedPeek+alignedPeekScalarStress :: AlignedPtr Scalar Stress -> IO Stress+alignedPeekScalarStress = alignedPeek++packedAlignedSizeOf140Nested :: Proxy Std140 -> Proxy Nested -> Int+packedAlignedSizeOf140Nested = packedAlignedSizeOf+packedAlignedSizeOf430Nested :: Proxy Std430 -> Proxy Nested -> Int+packedAlignedSizeOf430Nested = packedAlignedSizeOf+packedAlignedSizeOfScalarNested :: Proxy Scalar -> Proxy Nested -> Int+packedAlignedSizeOfScalarNested = packedAlignedSizeOf++alignedSizeOf140Nested :: Proxy Std140 -> Proxy Nested -> Int+alignedSizeOf140Nested = alignedSizeOf+alignedSizeOf430Nested :: Proxy Std430 -> Proxy Nested -> Int+alignedSizeOf430Nested = alignedSizeOf+alignedSizeOfScalarNested :: Proxy Scalar -> Proxy Nested -> Int+alignedSizeOfScalarNested = alignedSizeOf++alignedAlignment140Nested :: Proxy Std140 -> Proxy Nested -> Int+alignedAlignment140Nested = alignedAlignment+alignedAlignment430Nested :: Proxy Std430 -> Proxy Nested -> Int+alignedAlignment430Nested = alignedAlignment+alignedAlignmentScalarNested :: Proxy Scalar -> Proxy Nested -> Int+alignedAlignmentScalarNested = alignedAlignment++alignedPoke140Nested :: AlignedPtr Std140 Nested -> Nested -> IO ()+alignedPoke140Nested = alignedPoke+alignedPoke430Nested :: AlignedPtr Std430 Nested -> Nested -> IO ()+alignedPoke430Nested = alignedPoke+alignedPokeScalarNested :: AlignedPtr Scalar Nested -> Nested -> IO ()+alignedPokeScalarNested = alignedPoke++alignedPeek140Nested :: AlignedPtr Std140 Nested -> IO Nested+alignedPeek140Nested = alignedPeek+alignedPeek430Nested :: AlignedPtr Std430 Nested -> IO Nested+alignedPeek430Nested = alignedPeek+alignedPeekScalarNested :: AlignedPtr Scalar Nested -> IO Nested+alignedPeekScalarNested = alignedPeek++test_inspection :: [TestTree]+test_inspection =+  [ testGroup+      "Stress"+      [ testGroup+          "std140"+          [ $(inspectTest $ hasNoGenerics 'packedAlignedSizeOf140Stress)+          , $(inspectTest $ hasNoTypeClasses 'packedAlignedSizeOf140Stress)+          , $( inspectTest $+                'packedAlignedSizeOf140Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedSizeOf140Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedSizeOf140Stress)+          , $( inspectTest $+                'alignedSizeOf140Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedAlignment140Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedAlignment140Stress)+          , $( inspectTest $+                'alignedAlignment140Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPoke140Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedPoke140Stress)+          , $( inspectTest $+                'alignedPoke140Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPeek140Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedPeek140Stress)+          , $( inspectTest $+                'alignedPeek140Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          ]+      , testGroup+          "std430"+          [ $(inspectTest $ hasNoGenerics 'packedAlignedSizeOf430Stress)+          , $(inspectTest $ hasNoTypeClasses 'packedAlignedSizeOf430Stress)+          , $( inspectTest $+                'packedAlignedSizeOf140Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedSizeOf430Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedSizeOf430Stress)+          , $( inspectTest $+                'alignedSizeOf430Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedAlignment430Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedAlignment430Stress)+          , $( inspectTest $+                'alignedAlignment430Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPoke430Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedPoke430Stress)+          , $( inspectTest $+                'alignedPoke430Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPeek430Stress)+          , $(inspectTest $ hasNoTypeClasses 'alignedPeek430Stress)+          , $( inspectTest $+                'alignedPeek430Stress+                  `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          ]+      , testGroup+          "scalar"+          [ $(inspectTest $ hasNoGenerics 'packedAlignedSizeOfScalarStress)+          , $(inspectTest $ hasNoTypeClasses 'packedAlignedSizeOfScalarStress)+          , $( inspectTest $+                'packedAlignedSizeOfScalarStress `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedSizeOfScalarStress)+          , $(inspectTest $ hasNoTypeClasses 'alignedSizeOfScalarStress)+          , $( inspectTest $+                'alignedSizeOfScalarStress `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedAlignmentScalarStress)+          , $(inspectTest $ hasNoTypeClasses 'alignedAlignmentScalarStress)+          , $( inspectTest $+                'alignedAlignmentScalarStress `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPokeScalarStress)+          , $(inspectTest $ hasNoTypeClasses 'alignedPokeScalarStress)+          , $( inspectTest $+                'alignedPokeScalarStress `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPeekScalarStress)+          , $(inspectTest $ hasNoTypeClasses 'alignedPeekScalarStress)+          , $( inspectTest $+                'alignedPeekScalarStress `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          ]+      ]+  , testGroup+      "Nested"+      [ testGroup+          "std140"+          [ $(inspectTest $ hasNoGenerics 'packedAlignedSizeOf140Nested)+          , $(inspectTest $ mkObligation 'packedAlignedSizeOf140Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'packedAlignedSizeOf140Nested)+          , $( inspectTest $+                'packedAlignedSizeOf140Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedSizeOf140Nested)+          , $(inspectTest $ mkObligation 'alignedSizeOf140Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedSizeOf140Nested)+          , $( inspectTest $+                'alignedSizeOf140Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedAlignment140Nested)+          , $(inspectTest $ mkObligation 'alignedAlignment140Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedAlignment140Nested)+          , $( inspectTest $+                'alignedAlignment140Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPoke140Nested)+          , $(inspectTest $ mkObligation 'alignedPoke140Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedPoke140Nested)+          , $( inspectTest $ 'alignedPoke140Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPeek140Nested)+          , $(inspectTest $ mkObligation 'alignedPeek140Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedPeek140Nested)+          , $( inspectTest $ 'alignedPeek140Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          ]+      , testGroup+          "std430"+          [ $(inspectTest $ hasNoGenerics 'packedAlignedSizeOf430Nested)+          , $(inspectTest $ mkObligation 'packedAlignedSizeOf430Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'packedAlignedSizeOf430Nested)+          , $( inspectTest $+                'packedAlignedSizeOf430Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedSizeOf430Nested)+          , $(inspectTest $ mkObligation 'alignedSizeOf430Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedSizeOf430Nested)+          , $( inspectTest $+                'alignedSizeOf430Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedAlignment430Nested)+          , $(inspectTest $ mkObligation 'alignedAlignment430Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedAlignment430Nested)+          , $( inspectTest $+                'alignedAlignment430Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPoke430Nested)+          , $(inspectTest $ mkObligation 'alignedPoke430Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedPoke430Nested)+          , $( inspectTest $ 'alignedPoke430Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPeek430Nested)+          , $(inspectTest $ mkObligation 'alignedPeek430Nested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedPeek430Nested)+          , $( inspectTest $ 'alignedPeek430Nested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          ]+      , testGroup+          "scalar"+          [ $(inspectTest $ hasNoGenerics 'packedAlignedSizeOfScalarNested)+          , $(inspectTest $ mkObligation 'packedAlignedSizeOfScalarNested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'packedAlignedSizeOfScalarNested)+          , $( inspectTest $+                'packedAlignedSizeOfScalarNested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedSizeOfScalarNested)+          , $(inspectTest $ mkObligation 'alignedSizeOfScalarNested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedSizeOfScalarNested)+          , $( inspectTest $+                'alignedSizeOfScalarNested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedAlignmentScalarNested)+          , $(inspectTest $ mkObligation 'alignedAlignmentScalarNested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedAlignmentScalarNested)+          , $( inspectTest $+                'alignedAlignmentScalarNested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPokeScalarNested)+          , $(inspectTest $ mkObligation 'alignedPokeScalarNested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedPokeScalarNested)+          , $( inspectTest $+                'alignedPokeScalarNested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          , $(inspectTest $ hasNoGenerics 'alignedPeekScalarNested)+          , $(inspectTest $ mkObligation 'alignedPeekScalarNested NoAllocation)+          , $(inspectTest $ hasNoTypeClasses 'alignedPeekScalarNested)+          , $( inspectTest $+                'alignedPeekScalarNested `doesNotUseAnyOf` ['galignedSize, 'galignedPoke, 'galignedPeek]+             )+          ]+      ]+  ]
+ test/Foreign/GPU/Storable/AlignedSpec.hs view
@@ -0,0 +1,2448 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++-- | Alignments verified by running glslang against AlignedSpec.glsl and manually+-- inspecting the SPIR-V.+--+-- You can use https://godbolt.org/z/njnvM8q4o to check for yourself.+module Foreign.GPU.Storable.AlignedSpec where++import Control.Exception.Lifted (bracket)+import Control.Monad (replicateM)+import Control.Monad.IO.Class+import Control.Monad.Trans.Control (MonadBaseControl)+import Data.Coerce+import Data.Foldable (traverse_)+import Data.Int+import Data.Proxy+import Data.Typeable (Typeable, showsTypeRep, typeRep)+import qualified Data.Vector.Generic as GV+import qualified Data.Vector.Generic.Sized as SGV+import qualified Data.Vector.Sized as SV+import qualified Data.Vector.Storable.Sized as SSV+import Data.Word+import Foreign+import GHC.Generics+import GHC.TypeLits+import Hedgehog+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+import Linear hiding (outer)+import Numeric (showHex)+import Numeric.Half+import Test.Tasty.HUnit++import Foreign.GPU.Storable.Aligned++--------------------------------------------------------------------------------+-- Test harness+--------------------------------------------------------------------------------++newtype Canary = Canary {unCanary :: Word64}+  deriving newtype (Storable, Num, Eq)++instance Show Canary where+  showsPrec _ (Canary n) = showHex n++genCanary :: Gen Canary+genCanary =+  Gen.element+    [ 0xDEADBEEFDEADBEEF+    , 0xCAFEBABECAFEBABE+    , 0xABADBABEABADBABE+    , 0xCDCDCDCDCDCDCDCD+    , 0x5555555555555555+    , 0xAAAAAAAAAAAAAAAA+    , 0x0000000000000000+    , 0xFFFFFFFFFFFFFFFF+    , 0x0123456789ABCDEF+    , 0xFEDCBA9876543210+    ]++genCanaries :: Int -> Gen [Canary]+genCanaries sz = replicateM sz genCanary++-- |+-- Given a SUT and an oracle, checks that they round trip with oneself and one another.+--+-- We validate four ways: SUT vs. SUT, Oracle vs. Oracle, SUT vs. Oracle, Oracle vs. SUT.+-- At the top of the test, the SUT size and alignment is verified to match the Oracle's.+--+-- This verifies:+-- 1. The SUT is coherent.+-- 2. The Oracle is coherent.+-- 3. The SUT's 'peek' is the inverse of the Oracle's 'poke'.+-- 4. The Oracle's 'peek' is the inverse of the SUT's 'poke'.+--+-- This makes it highly probable that we catch subtle unidirectional bugs.+--+-- For each combination (a, b):+-- 1. Allocate a buffer approximately 3*sizeOf(SUT) - rounded up to the next multiple of sizeof(Canary)+-- 2. Poison the buffer with a repeating bit pattern (0xCD)+-- 3. Fill the edges of the buffer with a canary value.+-- 4. Check that the canaries got written to the right place.+-- 5. Poke `a` into the middle of the buffer+-- 6. Check the canaries -- if we overrun or undrrun the buffer, it's very likely to show here.+-- 7. Peek `b` out of the middle of the buffer+-- 8. Out of an overabundance of caution, check the canaries again+-- 9. Check that the peeked value of `b` is equal to the poked value of `a`+-- 10. Check that the peeked value of `b` is equal to the poked value of `b`+--+-- This makes it incredibly unlikely (but not impossible) that this module corrupts memory in a way+-- that isn't caught in test.+ptrTripping+  :: forall s o m+   . ( HasCallStack+     , Coercible s o+     , MonadBaseControl IO m+     , MonadIO m+     , Typeable s+     , Typeable o+     , Eq s+     , Eq o+     , Show s+     , Show o+     , Storable s+     , Storable o+     )+  => s+  -> o+  -> PropertyT m ()+ptrTripping sut oracle = do+  let sz = sizeOf sut+      cSz = sizeOf @Canary undefined+      pSz = roundUpTo cSz sz+      tSz = sz + 2 * pSz+      lPadOffsets = [0, cSz .. pSz - cSz]+      rPadOffsets = [pSz + sz, pSz + sz + cSz .. tSz - cSz]+      poison = 0xCD+  sz === sizeOf oracle+  alignment sut === alignment oracle++  lCanaries <- zip lPadOffsets <$> forAll (genCanaries (length lPadOffsets))+  rCanaries <- zip rPadOffsets <$> forAll (genCanaries (length rPadOffsets))++  let populateCanaries ptr = liftIO do+        traverse_ (uncurry (pokeByteOff ptr)) lCanaries+        traverse_ (uncurry (pokeByteOff ptr)) rCanaries++      checkCanary ptr offset canary = do+        result <- liftIO $ peekByteOff ptr offset+        Canary result === canary++      checkCanaries ptr = do+        annotate $ "Left pad: 0-" <> show pSz+        traverse_ (uncurry (checkCanary ptr)) lCanaries+        annotate $ "Right pad: " <> show (pSz + sz) <> "-" <> show tSz+        traverse_ (uncurry (checkCanary ptr)) rCanaries++      checkRoundTrip+        :: forall a b+         . ( HasCallStack+           , Coercible a b+           , MonadBaseControl IO m+           , MonadIO m+           , Typeable a+           , Typeable b+           , Eq a+           , Eq b+           , Show a+           , Show b+           , Storable a+           , Storable b+           )+        => a+        -> b+        -> PropertyT m ()+      checkRoundTrip a b = bracket (liftIO $ mallocBytes @a tSz) (liftIO . free) \ptr -> do+        annotate+          $ showsTypeRep (typeRep (Proxy @a))+            . showString " x "+            . showsTypeRep (typeRep (Proxy @b))+          $ ""+        liftIO $ fillBytes ptr poison tSz+        populateCanaries ptr+        checkCanaries ptr++        liftIO $ pokeByteOff ptr pSz a+        checkCanaries ptr++        (result :: b) <- liftIO $ peekByteOff (castPtr @a @b ptr) pSz+        checkCanaries ptr++        result === coerce a+        result === b++  checkRoundTrip sut sut+  checkRoundTrip oracle oracle+  checkRoundTrip sut oracle+  checkRoundTrip oracle sut++hprop_base_float_is_storable :: Property+hprop_base_float_is_storable = property do+  let x = Strided @Std140 (1.0 :: Float)+  let _ = sizeOf x+  success++hprop_base_int16_round_trips :: Property+hprop_base_int16_round_trips = property do+  ptrTripping @Int16 @Int16 12 12++--------------------------------------------------------------------------------+-- Simple+--------------------------------------------------------------------------------++data Simple = Simple+  { simpleA :: Float+  , simpleB :: Int32+  }+  deriving (Generic, Typeable, Show, Eq)++genSimple :: Gen Simple+genSimple = Simple <$> genFloat <*> genInt++instance AlignedStorable Std140 Simple+instance AlignedStorable Std430 Simple+instance AlignedStorable Scalar Simple++newtype SimpleStd140 = SimpleStd140 Simple+  deriving (Generic, Typeable, Show, Eq)++instance Storable SimpleStd140 where+  sizeOf _ = 16+  alignment _ = 16+  peek ptr = SimpleStd140 <$> (Simple <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (SimpleStd140 Simple{..}) = do+    pokeByteOff ptr 0 simpleA+    pokeByteOff ptr 4 simpleB++unit_simple_std140_packed_size :: Assertion+unit_simple_std140_packed_size =+  packedAlignedSizeOf @Std140 @Simple Proxy Proxy @?= 8++hprop_simple_std140_round_trip :: Property+hprop_simple_std140_round_trip = property do+  simple <- forAll genSimple+  let sut = Strided @Std140 simple+      oracle = SimpleStd140 simple+  ptrTripping sut oracle++newtype SimpleStd430 = SimpleStd430 Simple+  deriving (Generic, Typeable, Show, Eq)++instance Storable SimpleStd430 where+  sizeOf _ = 8+  alignment _ = 4+  peek ptr = SimpleStd430 <$> (Simple <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (SimpleStd430 Simple{..}) = do+    pokeByteOff ptr 0 simpleA+    pokeByteOff ptr 4 simpleB++unit_simple_std430_packed_size :: Assertion+unit_simple_std430_packed_size =+  packedAlignedSizeOf @Std430 @Simple Proxy Proxy @?= 8++hprop_simple_std430_round_trip :: Property+hprop_simple_std430_round_trip = property do+  val <- forAll genSimple+  let sut = Strided @Std430 val+  let oracle = SimpleStd430 val+  ptrTripping sut oracle++newtype SimpleScalar = SimpleScalar Simple+  deriving (Generic, Typeable, Show, Eq)++instance Storable SimpleScalar where+  sizeOf _ = 8+  alignment _ = 4+  peek ptr = SimpleScalar <$> (Simple <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (SimpleScalar Simple{..}) = do+    pokeByteOff ptr 0 simpleA+    pokeByteOff ptr 4 simpleB++unit_simple_scalar_packed_size :: Assertion+unit_simple_scalar_packed_size =+  packedAlignedSizeOf @Scalar @Simple Proxy Proxy @?= 8++hprop_simple_scalar_round_trip :: Property+hprop_simple_scalar_round_trip = property do+  val <- forAll genSimple+  let sut = Strided @Scalar val+  let oracle = SimpleScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Nonsquare+--------------------------------------------------------------------------------++-- | Needs to be documented, but Mnm -> matnxm+data TestNonSquare = TestNonSquare+  { matA :: M32 Half+  , matB :: M43 Double+  }+  deriving (Generic, Typeable, Show, Eq)++genTestNonSquare :: Gen TestNonSquare+genTestNonSquare = TestNonSquare <$> genMat genFloat <*> genMat genFloat++instance AlignedStorable Std140 TestNonSquare+instance AlignedStorable Std430 TestNonSquare+instance AlignedStorable Scalar TestNonSquare++newtype TestNonSquareStd140 = TestNonSquareStd140 TestNonSquare+  deriving (Generic, Typeable, Show, Eq)++instance Storable TestNonSquareStd140 where+  sizeOf _ = 192+  alignment _ = 32+  peek ptr =+    TestNonSquareStd140+      <$> (TestNonSquare <$> peekMat3Oracle Std140 ptr 0 <*> peekMat4Oracle Std140 ptr 64)+  poke ptr (TestNonSquareStd140 TestNonSquare{..}) = do+    pokeMat3Oracle Std140 ptr 0 matA+    pokeMat4Oracle Std140 ptr 64 matB++hprop_testnonsquare_std140_round_trip :: Property+hprop_testnonsquare_std140_round_trip = property do+  value <- forAll genTestNonSquare+  let sut = Strided @Std140 value+      oracle = TestNonSquareStd140 value+  ptrTripping sut oracle++newtype TestNonSquareStd430 = TestNonSquareStd430 TestNonSquare+  deriving (Generic, Typeable, Show, Eq)++instance Storable TestNonSquareStd430 where+  sizeOf _ = 160+  alignment _ = 32+  peek ptr =+    TestNonSquareStd430+      <$> (TestNonSquare <$> peekMat3Oracle Std430 ptr 0 <*> peekMat4Oracle Std430 ptr 32)+  poke ptr (TestNonSquareStd430 TestNonSquare{..}) = do+    pokeMat3Oracle Std430 ptr 0 matA+    pokeMat4Oracle Std430 ptr 32 matB++hprop_testnonsquare_std430_round_trip :: Property+hprop_testnonsquare_std430_round_trip = property do+  value <- forAll genTestNonSquare+  let sut = Strided @Std430 value+      oracle = TestNonSquareStd430 value+  ptrTripping sut oracle++newtype TestNonSquareScalar = TestNonSquareScalar TestNonSquare+  deriving (Generic, Typeable, Show, Eq)++instance Storable TestNonSquareScalar where+  sizeOf _ = 112+  alignment _ = 8+  peek ptr = TestNonSquareScalar <$> (TestNonSquare <$> peekByteOff ptr 0 <*> peekByteOff ptr 16)+  poke ptr (TestNonSquareScalar TestNonSquare{..}) = do+    pokeByteOff ptr 0 matA+    pokeByteOff ptr 16 matB++hprop_testnonsquare_scalar_round_trip :: Property+hprop_testnonsquare_scalar_round_trip = property do+  value <- forAll genTestNonSquare+  let sut = Strided @Scalar value+      oracle = TestNonSquareScalar value+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- D2+--------------------------------------------------------------------------------++data D2 = D2+  { d2A :: Half+  , d2B :: V2 Float+  , d2C :: Half+  , d2D :: M22 Float+  , d2E :: Half+  }+  deriving (Generic, Typeable, Show, Eq)++genD2 :: Gen D2+genD2 = D2 <$> genFloat <*> genV genFloat <*> genFloat <*> genMat genFloat <*> genFloat++instance AlignedStorable Std140 D2+instance AlignedStorable Std430 D2+instance AlignedStorable Scalar D2++newtype D2Std140 = D2Std140 D2+  deriving (Generic, Typeable, Show, Eq)++instance Storable D2Std140 where+  sizeOf _ = 80+  alignment _ = 16+  peek ptr =+    D2Std140+      <$> ( D2+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 8+              <*> peekByteOff ptr 16+              <*> peekMat2Oracle Std140 ptr 32+              <*> peekByteOff ptr 64+          )+  poke ptr (D2Std140 D2{..}) = do+    pokeByteOff ptr 0 d2A+    pokeByteOff ptr 8 d2B+    pokeByteOff ptr 16 d2C+    pokeMat2Oracle Std140 ptr 32 d2D+    pokeByteOff ptr 64 d2E++unit_d2_std140_packed_size :: Assertion+unit_d2_std140_packed_size =+  packedAlignedSizeOf @Std140 @D2 Proxy Proxy @?= 66++hprop_d2_std140_round_trip :: Property+hprop_d2_std140_round_trip = property do+  d2 <- forAll genD2+  let sut = Strided @Std140 d2+      oracle = D2Std140 d2+  ptrTripping sut oracle++newtype D2Std430 = D2Std430 D2+  deriving (Generic, Typeable, Show, Eq)++instance Storable D2Std430 where+  sizeOf _ = 48+  alignment _ = 8+  peek ptr =+    D2Std430+      <$> ( D2+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 8+              <*> peekByteOff ptr 16+              <*> peekMat2Oracle Std430 ptr 24+              <*> peekByteOff ptr 40+          )+  poke ptr (D2Std430 D2{..}) = do+    pokeByteOff ptr 0 d2A+    pokeByteOff ptr 8 d2B+    pokeByteOff ptr 16 d2C+    pokeMat2Oracle Std430 ptr 24 d2D+    pokeByteOff ptr 40 d2E++unit_d2_std430_packed_size :: Assertion+unit_d2_std430_packed_size =+  packedAlignedSizeOf @Std430 @D2 Proxy Proxy @?= 42++hprop_d2_std430_round_trip :: Property+hprop_d2_std430_round_trip = property do+  val <- forAll genD2+  let sut = Strided @Std430 val+      oracle = D2Std430 val+  ptrTripping sut oracle++newtype D2Scalar = D2Scalar D2+  deriving (Generic, Typeable, Show, Eq)++instance Storable D2Scalar where+  sizeOf _ = 36+  alignment _ = 4+  peek ptr =+    D2Scalar+      <$> ( D2+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 4+              <*> peekByteOff ptr 12+              <*> peekByteOff ptr 16+              <*> peekByteOff ptr 32+          )+  poke ptr (D2Scalar D2{..}) = do+    pokeByteOff ptr 0 d2A+    pokeByteOff ptr 4 d2B+    pokeByteOff ptr 12 d2C+    pokeByteOff ptr 16 d2D+    pokeByteOff ptr 32 d2E++unit_d2_scalar_packed_size :: Assertion+unit_d2_scalar_packed_size =+  packedAlignedSizeOf @Scalar @D2 Proxy Proxy @?= 34++hprop_d2_scalar_round_trip :: Property+hprop_d2_scalar_round_trip = property do+  val <- forAll genD2+  let sut = Strided @Scalar val+      oracle = D2Scalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Kitchen sink+--------------------------------------------------------------------------------++-- | Every shader primitive, ordered to be pretty close to worst case for padding+-- rules+data KitchenSink = KitchenSink+  { ksA :: Half+  , ksB :: V3 Double+  , ksC :: Float+  , ksD :: M33 Float+  , ksE :: Int32+  , ksF :: M33 Float+  , ksG :: Word8+  , ksH :: M32 Half+  , ksI :: Word16+  , ksJ :: M23 Half+  , ksK :: Word16+  , ksL :: M43 Float+  , ksM :: Int16+  , ksN :: M34 Double+  , ksO :: Bool+  , ksP :: Int8+  , ksQ :: M44 Half+  , ksR :: Int64+  , ksS :: M42 Double+  , ksT :: Word64+  , ksU :: M24 Half+  , ksV :: Half+  }+  deriving (Generic, Typeable, Show, Eq)++genKitchenSink :: Gen KitchenSink+genKitchenSink =+  KitchenSink+    <$> genFloat+    <*> genV genFloat+    <*> genFloat+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> Gen.bool+    <*> genInt+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> genInt+    <*> genMat genFloat+    <*> genFloat++instance AlignedStorable Std140 KitchenSink+instance AlignedStorable Std430 KitchenSink+instance AlignedStorable Scalar KitchenSink++newtype KitchenSinkStd140 = KitchenSinkStd140 KitchenSink+  deriving (Generic, Typeable, Show, Eq)++instance Storable KitchenSinkStd140 where+  sizeOf _ = 704+  alignment _ = 32+  peek ptr =+    KitchenSinkStd140+      <$> ( KitchenSink+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 32+              <*> peekByteOff ptr 56+              <*> peekMat3Oracle Std140 ptr 64+              <*> peekByteOff ptr 112+              <*> peekMat3Oracle Std140 ptr 128+              <*> peekByteOff ptr 176+              <*> peekMat3Oracle Std140 ptr 192+              <*> peekByteOff ptr 240+              <*> peekMat2Oracle Std140 ptr 256+              <*> peekByteOff ptr 288+              <*> peekMat4Oracle Std140 ptr 304+              <*> peekByteOff ptr 368+              <*> peekMat3Oracle Std140 ptr 384+              <*> peekByteOff ptr 480+              <*> peekByteOff ptr 484+              <*> peekMat4Oracle Std140 ptr 496+              <*> peekByteOff ptr 560+              <*> peekMat4Oracle Std140 ptr 576+              <*> peekByteOff ptr 640+              <*> peekMat2Oracle Std140 ptr 656+              <*> peekByteOff ptr 688+          )+  poke ptr (KitchenSinkStd140 KitchenSink{..}) = do+    pokeByteOff ptr 0 ksA+    pokeByteOff ptr 32 ksB+    pokeByteOff ptr 56 ksC+    pokeMat3Oracle Std140 ptr 64 ksD+    pokeByteOff ptr 112 ksE+    pokeMat3Oracle Std140 ptr 128 ksF+    pokeByteOff ptr 176 ksG+    pokeMat3Oracle Std140 ptr 192 ksH+    pokeByteOff ptr 240 ksI+    pokeMat2Oracle Std140 ptr 256 ksJ+    pokeByteOff ptr 288 ksK+    pokeMat4Oracle Std140 ptr 304 ksL+    pokeByteOff ptr 368 ksM+    pokeMat3Oracle Std140 ptr 384 ksN+    pokeByteOff ptr 480 ksO+    pokeByteOff ptr 484 ksP+    pokeMat4Oracle Std140 ptr 496 ksQ+    pokeByteOff ptr 560 ksR+    pokeMat4Oracle Std140 ptr 576 ksS+    pokeByteOff ptr 640 ksT+    pokeMat2Oracle Std140 ptr 656 ksU+    pokeByteOff ptr 688 ksV++hprop_kitchensink_std140_round_trip :: Property+hprop_kitchensink_std140_round_trip = property do+  val <- forAll genKitchenSink+  let sut = Strided @Std140 val+      oracle = KitchenSinkStd140 val+  ptrTripping sut oracle++newtype KitchenSinkStd430 = KitchenSinkStd430 KitchenSink+  deriving (Generic, Typeable, Show, Eq)++instance Storable KitchenSinkStd430 where+  sizeOf _ = 576+  alignment _ = 32+  peek ptr =+    KitchenSinkStd430+      <$> ( KitchenSink+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 32+              <*> peekByteOff ptr 56+              <*> peekMat3Oracle Std430 ptr 64+              <*> peekByteOff ptr 112+              <*> peekMat3Oracle Std430 ptr 128+              <*> peekByteOff ptr 176+              <*> peekMat3Oracle Std430 ptr 180+              <*> peekByteOff ptr 192+              <*> peekMat2Oracle Std430 ptr 200+              <*> peekByteOff ptr 216+              <*> peekMat4Oracle Std430 ptr 224+              <*> peekByteOff ptr 288+              <*> peekMat3Oracle Std430 ptr 320+              <*> peekByteOff ptr 416+              <*> peekByteOff ptr 420+              <*> peekMat4Oracle Std430 ptr 424+              <*> peekByteOff ptr 456+              <*> peekMat4Oracle Std430 ptr 464+              <*> peekByteOff ptr 528+              <*> peekMat2Oracle Std430 ptr 536+              <*> peekByteOff ptr 552+          )+  poke ptr (KitchenSinkStd430 KitchenSink{..}) = do+    pokeByteOff ptr 0 ksA+    pokeByteOff ptr 32 ksB+    pokeByteOff ptr 56 ksC+    pokeMat3Oracle Std430 ptr 64 ksD+    pokeByteOff ptr 112 ksE+    pokeMat3Oracle Std430 ptr 128 ksF+    pokeByteOff ptr 176 ksG+    pokeMat3Oracle Std430 ptr 180 ksH+    pokeByteOff ptr 192 ksI+    pokeMat2Oracle Std430 ptr 200 ksJ+    pokeByteOff ptr 216 ksK+    pokeMat4Oracle Std430 ptr 224 ksL+    pokeByteOff ptr 288 ksM+    pokeMat3Oracle Std430 ptr 320 ksN+    pokeByteOff ptr 416 ksO+    pokeByteOff ptr 420 ksP+    pokeMat4Oracle Std430 ptr 424 ksQ+    pokeByteOff ptr 456 ksR+    pokeMat4Oracle Std430 ptr 464 ksS+    pokeByteOff ptr 528 ksT+    pokeMat2Oracle Std430 ptr 536 ksU+    pokeByteOff ptr 552 ksV++hprop_kitchensink_std430_round_trip :: Property+hprop_kitchensink_std430_round_trip = property do+  val <- forAll genKitchenSink+  let sut = Strided @Std430 val+      oracle = KitchenSinkStd430 val+  ptrTripping sut oracle++-- Oracle for scalar layout+newtype KitchenSinkScalar = KitchenSinkScalar KitchenSink+  deriving (Generic, Typeable, Show, Eq)++instance Storable KitchenSinkScalar where+  sizeOf _ = 440+  alignment _ = 8 -- Based on alignment of dvec3+  peek ptr =+    KitchenSinkScalar+      <$> ( KitchenSink+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 8+              <*> peekByteOff ptr 32+              <*> peekByteOff ptr 36+              <*> peekByteOff ptr 72+              <*> peekByteOff ptr 76+              <*> peekByteOff ptr 112+              <*> peekByteOff ptr 114+              <*> peekByteOff ptr 126+              <*> peekByteOff ptr 128+              <*> peekByteOff ptr 140+              <*> peekByteOff ptr 144+              <*> peekByteOff ptr 192+              <*> peekByteOff ptr 200+              <*> peekByteOff ptr 296+              <*> peekByteOff ptr 300+              <*> peekByteOff ptr 302+              <*> peekByteOff ptr 336+              <*> peekByteOff ptr 344+              <*> peekByteOff ptr 408+              <*> peekByteOff ptr 416+              <*> peekByteOff ptr 432+          )+  poke ptr (KitchenSinkScalar KitchenSink{..}) = do+    pokeByteOff ptr 0 ksA+    pokeByteOff ptr 8 ksB+    pokeByteOff ptr 32 ksC+    pokeByteOff ptr 36 ksD+    pokeByteOff ptr 72 ksE+    pokeByteOff ptr 76 ksF+    pokeByteOff ptr 112 ksG+    pokeByteOff ptr 114 ksH+    pokeByteOff ptr 126 ksI+    pokeByteOff ptr 128 ksJ+    pokeByteOff ptr 140 ksK+    pokeByteOff ptr 144 ksL+    pokeByteOff ptr 192 ksM+    pokeByteOff ptr 200 ksN+    pokeByteOff ptr 296 ksO+    pokeByteOff ptr 300 ksP+    pokeByteOff ptr 302 ksQ+    pokeByteOff ptr 336 ksR+    pokeByteOff ptr 344 ksS+    pokeByteOff ptr 408 ksT+    pokeByteOff ptr 416 ksU+    pokeByteOff ptr 432 ksV++hprop_kitchensink_scalar_round_trip :: Property+hprop_kitchensink_scalar_round_trip = property do+  val <- forAll genKitchenSink+  let sut = Strided @Scalar val+      oracle = KitchenSinkScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- LayoutBug+--------------------------------------------------------------------------------++data LayoutBug = LayoutBug+  { fieldA :: Float+  , fieldB :: Float+  , fieldC :: Double+  }+  deriving (Generic, Typeable, Show, Eq)++genLayoutBug :: Gen LayoutBug+genLayoutBug = LayoutBug <$> genFloat <*> genFloat <*> genFloat++instance AlignedStorable Std140 LayoutBug+instance AlignedStorable Std430 LayoutBug+instance AlignedStorable Scalar LayoutBug++newtype LayoutBugStd140 = LayoutBugStd140 LayoutBug+  deriving (Generic, Typeable, Show, Eq)++instance Storable LayoutBugStd140 where+  sizeOf _ = 16+  alignment _ = 16+  peek ptr = LayoutBugStd140 <$> (LayoutBug <$> peekByteOff ptr 0 <*> peekByteOff ptr 4 <*> peekByteOff ptr 8)+  poke ptr (LayoutBugStd140 LayoutBug{..}) = do+    pokeByteOff ptr 0 fieldA+    pokeByteOff ptr 4 fieldB+    pokeByteOff ptr 8 fieldC++unit_layoutbug_std140_packed_size :: Assertion+unit_layoutbug_std140_packed_size =+  packedAlignedSizeOf @Std140 @LayoutBug Proxy Proxy @?= 16++hprop_layoutbug_std140_round_trip :: Property+hprop_layoutbug_std140_round_trip = property do+  val <- forAll genLayoutBug+  let sut = Strided @Std140 val+      oracle = LayoutBugStd140 val+  ptrTripping sut oracle++newtype LayoutBugStd430 = LayoutBugStd430 LayoutBug+  deriving (Generic, Typeable, Show, Eq)++instance Storable LayoutBugStd430 where+  sizeOf _ = 16+  alignment _ = 8+  peek ptr = LayoutBugStd430 <$> (LayoutBug <$> peekByteOff ptr 0 <*> peekByteOff ptr 4 <*> peekByteOff ptr 8)+  poke ptr (LayoutBugStd430 LayoutBug{..}) = do+    pokeByteOff ptr 0 fieldA+    pokeByteOff ptr 4 fieldB+    pokeByteOff ptr 8 fieldC++unit_layoutbug_std430_packed_size :: Assertion+unit_layoutbug_std430_packed_size =+  packedAlignedSizeOf @Std430 @LayoutBug Proxy Proxy @?= 16++hprop_layoutbug_std430_round_trip :: Property+hprop_layoutbug_std430_round_trip = property do+  val <- forAll genLayoutBug+  let sut = Strided @Std430 val+  let oracle = LayoutBugStd430 val+  ptrTripping sut oracle++newtype LayoutBugScalar = LayoutBugScalar LayoutBug+  deriving (Generic, Typeable, Show, Eq)++instance Storable LayoutBugScalar where+  sizeOf _ = 16+  alignment _ = 8+  peek ptr = LayoutBugScalar <$> (LayoutBug <$> peekByteOff ptr 0 <*> peekByteOff ptr 4 <*> peekByteOff ptr 8)+  poke ptr (LayoutBugScalar LayoutBug{..}) = do+    pokeByteOff ptr 0 fieldA+    pokeByteOff ptr 4 fieldB+    pokeByteOff ptr 8 fieldC++unit_layoutbug_scalar_packed_size :: Assertion+unit_layoutbug_scalar_packed_size =+  packedAlignedSizeOf @Scalar @LayoutBug Proxy Proxy @?= 16++hprop_layoutbug_scalar_round_trip :: Property+hprop_layoutbug_scalar_round_trip = property do+  val <- forAll genLayoutBug+  let sut = Strided @Scalar val+  let oracle = LayoutBugScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- PaddingTest+--------------------------------------------------------------------------------++data PaddingTest = PaddingTest+  { f :: Float+  , v :: V4 Float+  }+  deriving (Generic, Typeable, Show, Eq)++genPaddingTest :: Gen PaddingTest+genPaddingTest = PaddingTest <$> genFloat <*> genV genFloat++instance AlignedStorable Std140 PaddingTest+instance AlignedStorable Std430 PaddingTest+instance AlignedStorable Scalar PaddingTest++newtype PaddingTestStd140 = PaddingTestStd140 PaddingTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable PaddingTestStd140 where+  sizeOf _ = 32+  alignment _ = 16+  peek ptr = PaddingTestStd140 <$> (PaddingTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 16)+  poke ptr (PaddingTestStd140 PaddingTest{..}) = do+    pokeByteOff ptr 0 f+    pokeByteOff ptr 16 v++unit_paddingtest_std140_packed_size :: Assertion+unit_paddingtest_std140_packed_size =+  packedAlignedSizeOf @Std140 @PaddingTest Proxy Proxy @?= 32++hprop_paddingtest_std140_round_trip :: Property+hprop_paddingtest_std140_round_trip = property do+  val <- forAll genPaddingTest+  let sut = Strided @Std140 val+  let oracle = PaddingTestStd140 val+  ptrTripping sut oracle++-- Oracle for Std430 layout.+-- Layout is identical to Std140 for this struct, but we define a separate+-- oracle to maintain a 1-to-1 mapping between tests and layouts.+newtype PaddingTestStd430 = PaddingTestStd430 PaddingTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable PaddingTestStd430 where+  sizeOf _ = 32+  alignment _ = 16+  peek ptr = PaddingTestStd430 <$> (PaddingTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 16)+  poke ptr (PaddingTestStd430 PaddingTest{..}) = do+    pokeByteOff ptr 0 f+    pokeByteOff ptr 16 v++unit_paddingtest_std430_packed_size :: Assertion+unit_paddingtest_std430_packed_size =+  packedAlignedSizeOf @Std430 @PaddingTest Proxy Proxy @?= 32++hprop_paddingtest_std430_round_trip :: Property+hprop_paddingtest_std430_round_trip = property do+  val <- forAll genPaddingTest+  let sut = Strided @Std430 val+  let oracle = PaddingTestStd430 val+  ptrTripping sut oracle++newtype PaddingTestScalar = PaddingTestScalar PaddingTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable PaddingTestScalar where+  sizeOf _ = 20+  alignment _ = 4+  peek ptr = PaddingTestScalar <$> (PaddingTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (PaddingTestScalar PaddingTest{..}) = do+    pokeByteOff ptr 0 f+    pokeByteOff ptr 4 v++unit_paddingtest_scalar_packed_size :: Assertion+unit_paddingtest_scalar_packed_size =+  packedAlignedSizeOf @Scalar @PaddingTest Proxy Proxy @?= 20++hprop_paddingtest_scalar_round_trip :: Property+hprop_paddingtest_scalar_round_trip = property do+  val <- forAll genPaddingTest+  let sut = Strided @Scalar val+  let oracle = PaddingTestScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Std140Test+--------------------------------------------------------------------------------++data Std140Test = Std140Test+  { v3 :: V3 Float+  , f1 :: Float+  }+  deriving (Generic, Typeable, Show, Eq)++genStd140Test :: Gen Std140Test+genStd140Test =+  Std140Test+    <$> (V3 <$> genFloat <*> genFloat <*> genFloat)+    <*> genFloat++instance AlignedStorable Std140 Std140Test+instance AlignedStorable Std430 Std140Test+instance AlignedStorable Scalar Std140Test++newtype Std140TestStd140 = Std140TestStd140 Std140Test+  deriving (Generic, Typeable, Show, Eq)++instance Storable Std140TestStd140 where+  sizeOf _ = 16+  alignment _ = 16+  peek ptr = Std140TestStd140 <$> (Std140Test <$> peekByteOff ptr 0 <*> peekByteOff ptr 12)+  poke ptr (Std140TestStd140 Std140Test{..}) = do+    pokeByteOff ptr 0 v3+    pokeByteOff ptr 12 f1++unit_std140test_std140_packed_size :: Assertion+unit_std140test_std140_packed_size =+  packedAlignedSizeOf @Std140 @Std140Test Proxy Proxy @?= 16++hprop_std140test_std140_round_trip :: Property+hprop_std140test_std140_round_trip = property do+  val <- forAll genStd140Test+  let sut = Strided @Std140 val+      oracle = Std140TestStd140 val+  ptrTripping sut oracle++-- Oracle for Std430 layout. For this type, it is identical to std140.+newtype Std140TestStd430 = Std140TestStd430 Std140Test+  deriving (Generic, Typeable, Show, Eq)++instance Storable Std140TestStd430 where+  sizeOf _ = 16+  alignment _ = 16+  peek ptr = Std140TestStd430 <$> (Std140Test <$> peekByteOff ptr 0 <*> peekByteOff ptr 12)+  poke ptr (Std140TestStd430 Std140Test{..}) = do+    pokeByteOff ptr 0 v3+    pokeByteOff ptr 12 f1++unit_std140test_std430_packed_size :: Assertion+unit_std140test_std430_packed_size =+  packedAlignedSizeOf @Std430 @Std140Test Proxy Proxy @?= 16++hprop_std140test_std430_round_trip :: Property+hprop_std140test_std430_round_trip = property do+  val <- forAll genStd140Test+  let sut = Strided @Std430 val+  let oracle = Std140TestStd430 val+  ptrTripping sut oracle++newtype Std140TestScalar = Std140TestScalar Std140Test+  deriving (Generic, Typeable, Show, Eq)++instance Storable Std140TestScalar where+  sizeOf _ = 16+  alignment _ = 4+  peek ptr = Std140TestScalar <$> (Std140Test <$> peekByteOff ptr 0 <*> peekByteOff ptr 12)+  poke ptr (Std140TestScalar Std140Test{..}) = do+    pokeByteOff ptr 0 v3+    pokeByteOff ptr 12 f1++unit_std140test_scalar_packed_size :: Assertion+unit_std140test_scalar_packed_size =+  packedAlignedSizeOf @Scalar @Std140Test Proxy Proxy @?= 16++hprop_std140test_scalar_round_trip :: Property+hprop_std140test_scalar_round_trip = property do+  val <- forAll genStd140Test+  let sut = Strided @Scalar val+  let oracle = Std140TestScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- ComplexPadding+--------------------------------------------------------------------------------++-- |+--   complexpaddingA : float (size=4, align=4); tSize=4+--   complexpaddingB : vec3 (size=12, align=16); tSize=16+12=28; align to 16, start at 16+--   complexpaddingC : mat3 (size=3*16=48, align=16); tSize=32+48=80; align to 16, start at 32+--   complexpaddingD : float (size=4, align=4); tSize=80+4=4; align to 4, start at 80+--   complexpaddingE : mat4 (size=4*4=64, align=16); tSize=96+64=160; align to 16, start at 96+--   complexpaddingF : float (size=4, align=4); tSize=160+4=164; align to 4, start at 160+--+--   overall size: alignTo 16 164+--               = 176+data ComplexPadding = ComplexPadding+  { complexpaddingA :: Float+  , complexpaddingB :: V3 Float+  , complexpaddingC :: M33 Float+  , complexpaddingD :: Float+  , complexpaddingE :: M44 Float+  , complexpaddingF :: Float+  }+  deriving (Generic, Typeable, Show, Eq)++-- >>> alignedSizeOf (Data.Proxy.Proxy @Std430) (Data.Proxy.Proxy @ComplexPadding)+-- 176++genComplexPadding :: Gen ComplexPadding+genComplexPadding =+  ComplexPadding+    <$> genFloat+    <*> genV genFloat+    <*> genMat genFloat+    <*> genFloat+    <*> genMat genFloat+    <*> genFloat++instance AlignedStorable Std140 ComplexPadding+instance AlignedStorable Std430 ComplexPadding+instance AlignedStorable Scalar ComplexPadding++newtype ComplexPaddingStd140 = ComplexPaddingStd140 ComplexPadding+  deriving (Generic, Typeable, Show, Eq)++instance Storable ComplexPaddingStd140 where+  sizeOf _ = 176+  alignment _ = 16+  peek ptr =+    ComplexPaddingStd140+      <$> ( ComplexPadding+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 16+              <*> peekMat3Oracle Std140 ptr 32+              <*> peekByteOff ptr 80+              <*> peekByteOff ptr 96+              <*> peekByteOff ptr 160+          )+  poke ptr (ComplexPaddingStd140 ComplexPadding{..}) = do+    pokeByteOff ptr 0 complexpaddingA+    pokeByteOff ptr 16 complexpaddingB+    pokeMat3Oracle Std140 ptr 32 complexpaddingC+    pokeByteOff ptr 80 complexpaddingD+    pokeByteOff ptr 96 complexpaddingE+    pokeByteOff ptr 160 complexpaddingF++unit_complexpadding_std140_packed_size :: Assertion+unit_complexpadding_std140_packed_size =+  packedAlignedSizeOf @Std140 @ComplexPadding Proxy Proxy @?= 164++hprop_complexpadding_std140_round_trip :: Property+hprop_complexpadding_std140_round_trip = property do+  complexpadding <- forAll genComplexPadding+  let sut = Strided @Std140 complexpadding+      oracle = ComplexPaddingStd140 complexpadding+  ptrTripping sut oracle++newtype ComplexPaddingStd430 = ComplexPaddingStd430 ComplexPadding+  deriving (Generic, Typeable, Show, Eq)++instance Storable ComplexPaddingStd430 where+  sizeOf _ = 176+  alignment _ = 16+  peek ptr =+    ComplexPaddingStd430+      <$> ( ComplexPadding+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 16+              <*> peekMat3Oracle Std430 ptr 32+              <*> peekByteOff ptr 80+              <*> peekByteOff ptr 96+              <*> peekByteOff ptr 160+          )+  poke ptr (ComplexPaddingStd430 ComplexPadding{..}) = do+    pokeByteOff ptr 0 complexpaddingA+    pokeByteOff ptr 16 complexpaddingB+    pokeMat3Oracle Std430 ptr 32 complexpaddingC+    pokeByteOff ptr 80 complexpaddingD+    pokeByteOff ptr 96 complexpaddingE+    pokeByteOff ptr 160 complexpaddingF++unit_complexpadding_std430_packed_size :: Assertion+unit_complexpadding_std430_packed_size =+  packedAlignedSizeOf @Std430 @ComplexPadding Proxy Proxy @?= 164++hprop_complexpadding_std430_round_trip :: Property+hprop_complexpadding_std430_round_trip = property do+  val <- forAll genComplexPadding+  let sut = Strided @Std430 val+  let oracle = ComplexPaddingStd430 val+  ptrTripping sut oracle++newtype ComplexPaddingScalar = ComplexPaddingScalar ComplexPadding+  deriving (Generic, Typeable, Show, Eq)++instance Storable ComplexPaddingScalar where+  sizeOf _ = 124+  alignment _ = 4+  peek ptr =+    ComplexPaddingScalar+      <$> ( ComplexPadding+              <$> peekByteOff ptr 0+              <*> peekByteOff ptr 4+              <*> peekByteOff ptr 16+              <*> peekByteOff ptr 52+              <*> peekByteOff ptr 56+              <*> peekByteOff ptr 120+          )+  poke ptr (ComplexPaddingScalar ComplexPadding{..}) = do+    pokeByteOff ptr 0 complexpaddingA+    pokeByteOff ptr 4 complexpaddingB+    pokeByteOff ptr 16 complexpaddingC+    pokeByteOff ptr 52 complexpaddingD+    pokeByteOff ptr 56 complexpaddingE+    pokeByteOff ptr 120 complexpaddingF++unit_complexpadding_scalar_packed_size :: Assertion+unit_complexpadding_scalar_packed_size =+  packedAlignedSizeOf @Scalar @ComplexPadding Proxy Proxy @?= 124++hprop_complexpadding_scalar_round_trip :: Property+hprop_complexpadding_scalar_round_trip = property do+  val <- forAll genComplexPadding+  let sut = Strided @Scalar val+  let oracle = ComplexPaddingScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Vertex+--------------------------------------------------------------------------------++data Vertex = Vertex+  { position :: V3 Float+  , normal :: V3 Float+  , uv :: V2 Float+  }+  deriving (Generic, Typeable, Show, Eq)++genVertex :: Gen Vertex+genVertex = Vertex <$> genV genFloat <*> genV genFloat <*> genV genFloat++instance AlignedStorable Std140 Vertex+instance AlignedStorable Std430 Vertex+instance AlignedStorable Scalar Vertex++newtype VertexStd140 = VertexStd140 Vertex+  deriving (Generic, Typeable, Show, Eq)++instance Storable VertexStd140 where+  sizeOf _ = 48+  alignment _ = 16+  peek ptr = VertexStd140 <$> (Vertex <$> peekByteOff ptr 0 <*> peekByteOff ptr 16 <*> peekByteOff ptr 32)+  poke ptr (VertexStd140 Vertex{..}) = do+    pokeByteOff ptr 0 position+    pokeByteOff ptr 16 normal+    pokeByteOff ptr 32 uv++unit_vertex_std140_packed_size :: Assertion+unit_vertex_std140_packed_size =+  packedAlignedSizeOf @Std140 @Vertex Proxy Proxy @?= 40++hprop_vertex_std140_round_trip :: Property+hprop_vertex_std140_round_trip = property do+  val <- forAll genVertex+  let sut = Strided @Std140 val+      oracle = VertexStd140 val+  ptrTripping sut oracle++newtype VertexStd430 = VertexStd430 Vertex+  deriving (Generic, Typeable, Show, Eq)++instance Storable VertexStd430 where+  sizeOf _ = 48+  alignment _ = 16+  peek ptr = VertexStd430 <$> (Vertex <$> peekByteOff ptr 0 <*> peekByteOff ptr 16 <*> peekByteOff ptr 32)+  poke ptr (VertexStd430 Vertex{..}) = do+    pokeByteOff ptr 0 position+    pokeByteOff ptr 16 normal+    pokeByteOff ptr 32 uv++unit_vertex_std430_packed_size :: Assertion+unit_vertex_std430_packed_size =+  packedAlignedSizeOf @Std430 @Vertex Proxy Proxy @?= 40++hprop_vertex_std430_round_trip :: Property+hprop_vertex_std430_round_trip = property do+  val <- forAll genVertex+  let sut = Strided @Std430 val+      oracle = VertexStd430 val+  ptrTripping sut oracle++newtype VertexScalar = VertexScalar Vertex+  deriving (Generic, Typeable, Show, Eq)++instance Storable VertexScalar where+  sizeOf _ = 32+  alignment _ = 4+  peek ptr = VertexScalar <$> (Vertex <$> peekByteOff ptr 0 <*> peekByteOff ptr 12 <*> peekByteOff ptr 24)+  poke ptr (VertexScalar Vertex{..}) = do+    pokeByteOff ptr 0 position+    pokeByteOff ptr 12 normal+    pokeByteOff ptr 24 uv++unit_vertex_scalar_packed_size :: Assertion+unit_vertex_scalar_packed_size =+  packedAlignedSizeOf @Scalar @Vertex Proxy Proxy @?= 32++hprop_vertex_scalar_round_trip :: Property+hprop_vertex_scalar_round_trip = property do+  val <- forAll genVertex+  let sut = Strided @Scalar val+      oracle = VertexScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- LargePrimitives+--------------------------------------------------------------------------------++data LargePrimitives = LargePrimitives+  { d :: Double+  , l :: Int64+  }+  deriving (Generic, Typeable, Show, Eq)++genLargePrimitives :: Gen LargePrimitives+genLargePrimitives = LargePrimitives <$> genFloat <*> genInt++instance AlignedStorable Std140 LargePrimitives+instance AlignedStorable Std430 LargePrimitives+instance AlignedStorable Scalar LargePrimitives++newtype LargePrimitivesStd140 = LargePrimitivesStd140 LargePrimitives+  deriving (Generic, Typeable, Show, Eq)++instance Storable LargePrimitivesStd140 where+  sizeOf _ = 16+  alignment _ = 16+  peek ptr = LargePrimitivesStd140 <$> (LargePrimitives <$> peekByteOff ptr 0 <*> peekByteOff ptr 8)+  poke ptr (LargePrimitivesStd140 LargePrimitives{..}) = do+    pokeByteOff ptr 0 d+    pokeByteOff ptr 8 l++unit_largeprimitives_std140_packed_size :: Assertion+unit_largeprimitives_std140_packed_size =+  packedAlignedSizeOf @Std140 @LargePrimitives Proxy Proxy @?= 16++hprop_largeprimitives_std140_round_trip :: Property+hprop_largeprimitives_std140_round_trip = property do+  val <- forAll genLargePrimitives+  let sut = Strided @Std140 val+  let oracle = LargePrimitivesStd140 val+  ptrTripping sut oracle++newtype LargePrimitivesStd430 = LargePrimitivesStd430 LargePrimitives+  deriving (Generic, Typeable, Show, Eq)++instance Storable LargePrimitivesStd430 where+  sizeOf _ = 16+  alignment _ = 8+  peek ptr = LargePrimitivesStd430 <$> (LargePrimitives <$> peekByteOff ptr 0 <*> peekByteOff ptr 8)+  poke ptr (LargePrimitivesStd430 LargePrimitives{..}) = do+    pokeByteOff ptr 0 d+    pokeByteOff ptr 8 l++unit_largeprimitives_std430_packed_size :: Assertion+unit_largeprimitives_std430_packed_size =+  packedAlignedSizeOf @Std430 @LargePrimitives Proxy Proxy @?= 16++hprop_largeprimitives_std430_round_trip :: Property+hprop_largeprimitives_std430_round_trip = property do+  val <- forAll genLargePrimitives+  let sut = Strided @Std430 val+  let oracle = LargePrimitivesStd430 val+  ptrTripping sut oracle++newtype LargePrimitivesScalar = LargePrimitivesScalar LargePrimitives+  deriving (Generic, Typeable, Show, Eq)++instance Storable LargePrimitivesScalar where+  sizeOf _ = 16+  alignment _ = 8+  peek ptr = LargePrimitivesScalar <$> (LargePrimitives <$> peekByteOff ptr 0 <*> peekByteOff ptr 8)+  poke ptr (LargePrimitivesScalar LargePrimitives{..}) = do+    pokeByteOff ptr 0 d+    pokeByteOff ptr 8 l++hprop_largeprimitives_scalar_round_trip :: Property+hprop_largeprimitives_scalar_round_trip = property do+  val <- forAll genLargePrimitives+  let sut = Strided @Scalar val+  let oracle = LargePrimitivesScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Uniforms+--------------------------------------------------------------------------------++data Uniforms = Uniforms+  { modelView :: V4 (V4 Float)+  , cameraPos :: V4 Float+  }+  deriving (Generic, Typeable, Show, Eq)++genUniforms :: Gen Uniforms+genUniforms = Uniforms <$> genMat genFloat <*> genV genFloat++instance AlignedStorable Std140 Uniforms+instance AlignedStorable Std430 Uniforms+instance AlignedStorable Scalar Uniforms++newtype UniformsStd140 = UniformsStd140 Uniforms+  deriving (Generic, Typeable, Show, Eq)++instance Storable UniformsStd140 where+  sizeOf _ = 80+  alignment _ = 16+  peek ptr = UniformsStd140 <$> (Uniforms <$> peekByteOff ptr 0 <*> peekByteOff ptr 64)+  poke ptr (UniformsStd140 Uniforms{..}) = do+    pokeByteOff ptr 0 modelView+    pokeByteOff ptr 64 cameraPos++hprop_uniforms_std140_round_trip :: Property+hprop_uniforms_std140_round_trip = property do+  val <- forAll genUniforms+  let sut = Strided @Std140 val+  let oracle = UniformsStd140 val+  ptrTripping sut oracle++newtype UniformsStd430 = UniformsStd430 Uniforms+  deriving (Generic, Typeable, Show, Eq)++instance Storable UniformsStd430 where+  sizeOf _ = 80+  alignment _ = 16+  peek ptr = UniformsStd430 <$> (Uniforms <$> peekByteOff ptr 0 <*> peekByteOff ptr 64)+  poke ptr (UniformsStd430 Uniforms{..}) = do+    pokeByteOff ptr 0 modelView+    pokeByteOff ptr 64 cameraPos++hprop_uniforms_std430_round_trip :: Property+hprop_uniforms_std430_round_trip = property do+  val <- forAll genUniforms+  let sut = Strided @Std430 val+  let oracle = UniformsStd430 val+  ptrTripping sut oracle++newtype UniformsScalar = UniformsScalar Uniforms+  deriving (Generic, Typeable, Show, Eq)++instance Storable UniformsScalar where+  sizeOf _ = 80+  alignment _ = 4+  peek ptr = UniformsScalar <$> (Uniforms <$> peekByteOff ptr 0 <*> peekByteOff ptr 64)+  poke ptr (UniformsScalar Uniforms{..}) = do+    pokeByteOff ptr 0 modelView+    pokeByteOff ptr 64 cameraPos++hprop_uniforms_scalar_round_trip :: Property+hprop_uniforms_scalar_round_trip = property do+  val <- forAll genUniforms+  let sut = Strided @Scalar val+  let oracle = UniformsScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- MixedAlign (explicitly tests the difference between std140 and std430)+--------------------------------------------------------------------------------++data MixedAlign = MixedAlign+  { a :: Int16+  , b :: Double+  , c :: Int32+  }+  deriving (Generic, Typeable, Show, Eq)++genMixedAlign :: Gen MixedAlign+genMixedAlign = MixedAlign <$> genInt <*> genFloat <*> genInt++instance AlignedStorable Std140 MixedAlign+instance AlignedStorable Std430 MixedAlign+instance AlignedStorable Scalar MixedAlign++newtype MixedAlignStd140 = MixedAlignStd140 MixedAlign+  deriving (Generic, Typeable, Show, Eq)++instance Storable MixedAlignStd140 where+  sizeOf _ = 32+  alignment _ = 16+  peek ptr =+    MixedAlignStd140 <$> (MixedAlign <$> peekByteOff ptr 0 <*> peekByteOff ptr 8 <*> peekByteOff ptr 16)+  poke ptr (MixedAlignStd140 MixedAlign{..}) = do+    pokeByteOff ptr 0 a+    pokeByteOff ptr 8 b+    pokeByteOff ptr 16 c++hprop_mixedalign_std140_round_trip :: Property+hprop_mixedalign_std140_round_trip = property do+  val <- forAll genMixedAlign+  let sut = Strided @Std140 val+  let oracle = MixedAlignStd140 val+  ptrTripping sut oracle++newtype MixedAlignStd430 = MixedAlignStd430 MixedAlign+  deriving (Generic, Typeable, Show, Eq)++instance Storable MixedAlignStd430 where+  sizeOf _ = 24+  alignment _ = 8+  peek ptr =+    MixedAlignStd430 <$> (MixedAlign <$> peekByteOff ptr 0 <*> peekByteOff ptr 8 <*> peekByteOff ptr 16)+  poke ptr (MixedAlignStd430 MixedAlign{..}) = do+    pokeByteOff ptr 0 a+    pokeByteOff ptr 8 b+    pokeByteOff ptr 16 c++hprop_mixedalign_std430_round_trip :: Property+hprop_mixedalign_std430_round_trip = property do+  val <- forAll genMixedAlign+  let sut = Strided @Std430 val+  let oracle = MixedAlignStd430 val+  ptrTripping sut oracle++newtype MixedAlignScalar = MixedAlignScalar MixedAlign+  deriving (Generic, Typeable, Show, Eq)++instance Storable MixedAlignScalar where+  sizeOf _ = 24+  alignment _ = 8+  peek ptr =+    MixedAlignScalar <$> (MixedAlign <$> peekByteOff ptr 0 <*> peekByteOff ptr 8 <*> peekByteOff ptr 16)+  poke ptr (MixedAlignScalar MixedAlign{..}) = do+    pokeByteOff ptr 0 a+    pokeByteOff ptr 8 b+    pokeByteOff ptr 16 c++hprop_mixedalign_scalar_round_trip :: Property+hprop_mixedalign_scalar_round_trip = property do+  val <- forAll genMixedAlign+  let sut = Strided @Scalar val+  let oracle = MixedAlignScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- FinalPaddingTest+--------------------------------------------------------------------------------++data FinalPaddingTest = FinalPaddingTest+  { fptA :: V4 Float+  , fptB :: Int32+  }+  deriving (Generic, Typeable, Show, Eq)++genFinalPaddingTest :: Gen FinalPaddingTest+genFinalPaddingTest = FinalPaddingTest <$> genV genFloat <*> genInt++instance AlignedStorable Std140 FinalPaddingTest+instance AlignedStorable Std430 FinalPaddingTest+instance AlignedStorable Scalar FinalPaddingTest++newtype FinalPaddingTestStd140 = FinalPaddingTestStd140 FinalPaddingTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable FinalPaddingTestStd140 where+  sizeOf _ = 32+  alignment _ = 16+  peek ptr = FinalPaddingTestStd140 <$> (FinalPaddingTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 16)+  poke ptr (FinalPaddingTestStd140 FinalPaddingTest{..}) = do+    pokeByteOff ptr 0 fptA+    pokeByteOff ptr 16 fptB++hprop_finalpaddingtest_std140_round_trip :: Property+hprop_finalpaddingtest_std140_round_trip = property do+  val <- forAll genFinalPaddingTest+  let sut = Strided @Std140 val+  let oracle = FinalPaddingTestStd140 val+  ptrTripping sut oracle++newtype FinalPaddingTestStd430 = FinalPaddingTestStd430 FinalPaddingTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable FinalPaddingTestStd430 where+  sizeOf _ = 32+  alignment _ = 16+  peek ptr = FinalPaddingTestStd430 <$> (FinalPaddingTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 16)+  poke ptr (FinalPaddingTestStd430 FinalPaddingTest{..}) = do+    pokeByteOff ptr 0 fptA+    pokeByteOff ptr 16 fptB++hprop_finalpaddingtest_std430_round_trip :: Property+hprop_finalpaddingtest_std430_round_trip = property do+  val <- forAll genFinalPaddingTest+  let sut = Strided @Std430 val+  let oracle = FinalPaddingTestStd430 val+  ptrTripping sut oracle++newtype FinalPaddingTestScalar = FinalPaddingTestScalar FinalPaddingTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable FinalPaddingTestScalar where+  sizeOf _ = 20+  alignment _ = 4+  peek ptr = FinalPaddingTestScalar <$> (FinalPaddingTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 16)+  poke ptr (FinalPaddingTestScalar FinalPaddingTest{..}) = do+    pokeByteOff ptr 0 fptA+    pokeByteOff ptr 16 fptB++hprop_finalpaddingtest_scalar_round_trip :: Property+hprop_finalpaddingtest_scalar_round_trip = property do+  val <- forAll genFinalPaddingTest+  let sut = Strided @Scalar val+  let oracle = FinalPaddingTestScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- BoolTest+--------------------------------------------------------------------------------++data BoolTest = BoolTest+  { isEnabled :: Bool+  , value :: Float+  }+  deriving (Generic, Typeable, Show, Eq)++genBoolTest :: Gen BoolTest+genBoolTest = BoolTest <$> Gen.bool <*> genFloat++instance AlignedStorable Std140 BoolTest+instance AlignedStorable Std430 BoolTest+instance AlignedStorable Scalar BoolTest++newtype BoolTestStd140 = BoolTestStd140 BoolTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable BoolTestStd140 where+  sizeOf _ = 16+  alignment _ = 16+  peek ptr = BoolTestStd140 <$> (BoolTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (BoolTestStd140 BoolTest{..}) = do+    pokeByteOff ptr 0 isEnabled+    pokeByteOff ptr 4 value++hprop_booltest_std140_round_trip :: Property+hprop_booltest_std140_round_trip = property do+  val <- forAll genBoolTest+  let sut = Strided @Std140 val+      oracle = BoolTestStd140 val+  ptrTripping sut oracle++newtype BoolTestStd430 = BoolTestStd430 BoolTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable BoolTestStd430 where+  sizeOf _ = 8+  alignment _ = 4+  peek ptr = BoolTestStd430 <$> (BoolTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (BoolTestStd430 BoolTest{..}) = do+    pokeByteOff ptr 0 isEnabled+    pokeByteOff ptr 4 value++hprop_booltest_std430_round_trip :: Property+hprop_booltest_std430_round_trip = property do+  val <- forAll genBoolTest+  let sut = Strided @Std430 val+      oracle = BoolTestStd430 val+  ptrTripping sut oracle++newtype BoolTestScalar = BoolTestScalar BoolTest+  deriving (Generic, Typeable, Show, Eq)++instance Storable BoolTestScalar where+  sizeOf _ = 8+  alignment _ = 4+  peek ptr = BoolTestScalar <$> (BoolTest <$> peekByteOff ptr 0 <*> peekByteOff ptr 4)+  poke ptr (BoolTestScalar BoolTest{..}) = do+    pokeByteOff ptr 0 isEnabled+    pokeByteOff ptr 4 value++hprop_booltest_scalar_round_trip :: Property+hprop_booltest_scalar_round_trip = property do+  val <- forAll genBoolTest+  let sut = Strided @Scalar val+      oracle = BoolTestScalar val+  ptrTripping sut oracle++data Nested = Nested+  { nestedV3 :: V3 Float+  , nestedSimple :: Simple+  , nestedMixed :: MixedAlign+  , nestedBoolTest :: BoolTest+  }+  deriving (Generic, Typeable, Show, Eq)++genNested :: Gen Nested+genNested =+  Nested+    <$> genV genFloat+    <*> genSimple+    <*> genMixedAlign+    <*> genBoolTest++instance AlignedStorable Std140 Nested+instance AlignedStorable Std430 Nested+instance AlignedStorable Scalar Nested++newtype NestedStd140 = NestedStd140 Nested+  deriving (Generic, Typeable, Show, Eq)++instance Storable NestedStd140 where+  sizeOf _ = 80+  alignment _ = 16+  peek ptr =+    NestedStd140+      <$> ( Nested+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Strided Std140 Simple) ptr 16)+              <*> (coerce <$> peekByteOff @(Strided Std140 MixedAlign) ptr 32)+              <*> (coerce <$> peekByteOff @(Strided Std140 BoolTest) ptr 64)+          )+  poke ptr (NestedStd140 Nested{..}) = do+    pokeByteOff ptr 0 nestedV3+    pokeByteOff ptr 16 (Strided @Std140 nestedSimple)+    pokeByteOff ptr 32 (Strided @Std140 nestedMixed)+    pokeByteOff ptr 64 (Strided @Std140 nestedBoolTest)++hprop_nested_std140_round_trip :: Property+hprop_nested_std140_round_trip = property do+  val <- forAll genNested+  let sut = Strided @Std140 val+      oracle = NestedStd140 val+  ptrTripping sut oracle++newtype NestedStd430 = NestedStd430 Nested+  deriving (Generic, Typeable, Show, Eq)++instance Storable NestedStd430 where+  sizeOf _ = 64+  alignment _ = 16+  peek ptr =+    NestedStd430+      <$> ( Nested+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Strided Std430 Simple) ptr 12)+              <*> (coerce <$> peekByteOff @(Strided Std430 MixedAlign) ptr 24)+              <*> (coerce <$> peekByteOff @(Strided Std430 BoolTest) ptr 48)+          )+  poke ptr (NestedStd430 Nested{..}) = do+    pokeByteOff ptr 0 nestedV3+    pokeByteOff ptr 12 (Strided @Std430 nestedSimple)+    pokeByteOff ptr 24 (Strided @Std430 nestedMixed)+    pokeByteOff ptr 48 (Strided @Std430 nestedBoolTest)++hprop_nested_std430_round_trip :: Property+hprop_nested_std430_round_trip = property do+  val <- forAll genNested+  let sut = Strided @Std430 val+      oracle = NestedStd430 val+  ptrTripping sut oracle++newtype NestedScalar = NestedScalar Nested+  deriving (Generic, Typeable, Show, Eq)++instance Storable NestedScalar where+  sizeOf _ = 56+  alignment _ = 8+  peek ptr =+    NestedScalar+      <$> ( Nested+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Strided Scalar Simple) ptr 12)+              <*> (coerce <$> peekByteOff @(Strided Scalar MixedAlign) ptr 24)+              <*> (coerce <$> peekByteOff @(Strided Scalar BoolTest) ptr 44)+          )+  poke ptr (NestedScalar Nested{..}) = do+    pokeByteOff ptr 0 nestedV3+    pokeByteOff ptr 12 (Strided @Scalar nestedSimple)+    pokeByteOff ptr 24 (Strided @Scalar nestedMixed)+    pokeByteOff ptr 44 (Strided @Scalar nestedBoolTest)++hprop_nested_scalar_round_trip :: Property+hprop_nested_scalar_round_trip = property do+  val <- forAll genNested+  let sut = Strided @Scalar val+      oracle = NestedScalar val+  ptrTripping sut oracle++newtype NestedNewtype = NestedNewtype Nested+  deriving (Generic, Typeable, Show, Eq)++genNestedNewtype :: Gen NestedNewtype+genNestedNewtype = NestedNewtype <$> genNested++instance AlignedStorable Std140 NestedNewtype+instance AlignedStorable Std430 NestedNewtype+instance AlignedStorable Scalar NestedNewtype++newtype NestedNewtypeStd140 = NestedNewtypeStd140 NestedNewtype+  deriving (Generic, Typeable, Show, Eq)++instance Storable NestedNewtypeStd140 where+  sizeOf _ = sizeOf @(Strided Std140 Nested) undefined+  alignment _ = alignment @(Strided Std140 Nested) undefined+  peek ptr = NestedNewtypeStd140 . coerce <$> peek @(Strided Std140 Nested) (castPtr ptr)+  poke ptr (NestedNewtypeStd140 nested) = poke @(Strided Std140 Nested) (castPtr ptr) (coerce nested)++hprop_nested_newtype_std140_round_trip :: Property+hprop_nested_newtype_std140_round_trip = property do+  val <- forAll genNestedNewtype+  let sut = Strided @Std140 val+      oracle = NestedNewtypeStd140 val+  ptrTripping sut oracle++newtype NestedNewtypeStd430 = NestedNewtypeStd430 NestedNewtype+  deriving (Generic, Typeable, Show, Eq)++instance Storable NestedNewtypeStd430 where+  sizeOf _ = sizeOf @(Strided Std430 Nested) undefined+  alignment _ = alignment @(Strided Std430 Nested) undefined+  peek ptr = NestedNewtypeStd430 . coerce <$> peek @(Strided Std430 Nested) (castPtr ptr)+  poke ptr (NestedNewtypeStd430 nested) = poke @(Strided Std430 Nested) (castPtr ptr) (coerce nested)++hprop_nested_newtype_std430_round_trip :: Property+hprop_nested_newtype_std430_round_trip = property do+  val <- forAll genNestedNewtype+  let sut = Strided @Std430 val+      oracle = NestedNewtypeStd430 val+  ptrTripping sut oracle++newtype NestedNewtypeScalar = NestedNewtypeScalar NestedNewtype+  deriving (Generic, Typeable, Show, Eq)++instance Storable NestedNewtypeScalar where+  sizeOf _ = sizeOf @(Strided Scalar Nested) undefined+  alignment _ = alignment @(Strided Scalar Nested) undefined+  peek ptr =+    NestedNewtypeScalar . coerce <$> peek @(Strided Scalar Nested) (castPtr ptr)+  poke ptr (NestedNewtypeScalar nested) = poke @(Strided Scalar Nested) (castPtr ptr) (coerce nested)++hprop_nested_newtype_scalar_round_trip :: Property+hprop_nested_newtype_scalar_round_trip = property do+  val <- forAll genNestedNewtype+  let sut = Strided @Scalar val+      oracle = NestedNewtypeScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- DoubleTrouble+--------------------------------------------------------------------------------++data DoubleTrouble = DoubleTrouble+  { dtD0 :: Double+  , dtM0 :: M33 Double+  , dtF0 :: Float+  , dtV0 :: V3 Double+  , dtF1 :: Float+  , dtV1 :: V4 Double+  , dtF2 :: Float+  , dtM1 :: M44 Double+  }+  deriving (Generic, Typeable, Show, Eq)++genDoubleTrouble :: Gen DoubleTrouble+genDoubleTrouble =+  DoubleTrouble+    <$> genFloat+    <*> genMat genFloat+    <*> genFloat+    <*> genV genFloat+    <*> genFloat+    <*> genV genFloat+    <*> genFloat+    <*> genMat genFloat++instance AlignedStorable Std140 DoubleTrouble+instance AlignedStorable Std430 DoubleTrouble+instance AlignedStorable Scalar DoubleTrouble++newtype DoubleTroubleStd140 = DoubleTroubleStd140 DoubleTrouble+  deriving (Generic, Typeable, Show, Eq)++instance Storable DoubleTroubleStd140 where+  sizeOf _ = 384+  alignment _ = 32+  peek ptr =+    DoubleTroubleStd140+      <$> ( DoubleTrouble+              <$> peekByteOff ptr 0+              <*> peekMat3Oracle Std140 ptr 32+              <*> peekByteOff ptr 128+              <*> peekByteOff ptr 160+              <*> peekByteOff ptr 184+              <*> peekByteOff ptr 192+              <*> peekByteOff ptr 224+              <*> peekByteOff ptr 256+          )+  poke ptr (DoubleTroubleStd140 DoubleTrouble{..}) = do+    pokeByteOff ptr 0 dtD0+    pokeMat3Oracle Std140 ptr 32 dtM0+    pokeByteOff ptr 128 dtF0+    pokeByteOff ptr 160 dtV0+    pokeByteOff ptr 184 dtF1+    pokeByteOff ptr 192 dtV1+    pokeByteOff ptr 224 dtF2+    pokeByteOff ptr 256 dtM1++hprop_doubletrouble_std140_round_trip :: Property+hprop_doubletrouble_std140_round_trip = property do+  val <- forAll genDoubleTrouble+  let sut = Strided @Std140 val+      oracle = DoubleTroubleStd140 val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- VectorStruct+--------------------------------------------------------------------------------++data VectorStruct = VectorStruct+  { vsA :: Float+  , vsB :: SSV.Vector 2 (V3 Float)+  , vsC :: Int32+  }+  deriving (Generic, Typeable, Show, Eq)++genVectorStruct :: Gen VectorStruct+genVectorStruct =+  VectorStruct+    <$> genFloat+    <*> SSV.replicateM (genV genFloat)+    <*> genInt++instance AlignedStorable Std140 VectorStruct+instance AlignedStorable Std430 VectorStruct+instance AlignedStorable Scalar VectorStruct++newtype VectorStructStd140 = VectorStructStd140 VectorStruct+  deriving (Generic, Typeable, Show, Eq)++instance Storable VectorStructStd140 where+  sizeOf _ = 64+  alignment _ = 16+  peek ptr =+    VectorStructStd140+      <$> ( VectorStruct+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Packed Std140 (SSV.Vector 2 (V3 Float))) ptr 16)+              <*> peekByteOff ptr 48+          )+  poke ptr (VectorStructStd140 VectorStruct{..}) = do+    pokeByteOff ptr 0 vsA+    pokeByteOff ptr 16 (Packed @Std140 vsB)+    pokeByteOff ptr 48 vsC++hprop_vectorstruct_std140_round_trip :: Property+hprop_vectorstruct_std140_round_trip = property do+  val <- forAll genVectorStruct+  let sut = Strided @Std140 val+      oracle = VectorStructStd140 val+  ptrTripping sut oracle++newtype VectorStructStd430 = VectorStructStd430 VectorStruct+  deriving (Generic, Typeable, Show, Eq)++instance Storable VectorStructStd430 where+  sizeOf _ = 64+  alignment _ = 16+  peek ptr =+    VectorStructStd430+      <$> ( VectorStruct+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Packed Std430 (SSV.Vector 2 (V3 Float))) ptr 16)+              <*> peekByteOff ptr 48+          )+  poke ptr (VectorStructStd430 VectorStruct{..}) = do+    pokeByteOff ptr 0 vsA+    pokeByteOff ptr 16 (Packed @Std430 vsB)+    pokeByteOff ptr 48 vsC++hprop_vectorstruct_std430_round_trip :: Property+hprop_vectorstruct_std430_round_trip = property do+  val <- forAll genVectorStruct+  let sut = Strided @Std430 val+      oracle = VectorStructStd430 val+  ptrTripping sut oracle++newtype VectorStructScalar = VectorStructScalar VectorStruct+  deriving (Generic, Typeable, Show, Eq)++instance Storable VectorStructScalar where+  sizeOf _ = 32+  alignment _ = 4+  peek ptr =+    VectorStructScalar+      <$> ( VectorStruct+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Packed Scalar (SSV.Vector 2 (V3 Float))) ptr 4)+              <*> peekByteOff ptr 28+          )+  poke ptr (VectorStructScalar VectorStruct{..}) = do+    pokeByteOff ptr 0 vsA+    pokeByteOff ptr 4 (Packed @Scalar vsB)+    pokeByteOff ptr 28 vsC++hprop_vectorstruct_scalar_round_trip :: Property+hprop_vectorstruct_scalar_round_trip = property do+  val <- forAll genVectorStruct+  let sut = Strided @Scalar val+      oracle = VectorStructScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- ComplexVectorStruct+--------------------------------------------------------------------------------++data ComplexVectorStruct = ComplexVectorStruct+  { cvsA :: Float+  , cvsB :: SSV.Vector 3 (V3 Word16)+  , cvsC :: SSV.Vector 2 (V2 Double)+  , cvsD :: Float+  , cvsE :: Double+  }+  deriving (Generic, Typeable, Show, Eq)++genComplexVectorStruct :: Gen ComplexVectorStruct+genComplexVectorStruct =+  ComplexVectorStruct+    <$> genFloat+    <*> SSV.replicateM (genV genInt)+    <*> SSV.replicateM (genV genFloat)+    <*> genFloat+    <*> genFloat++instance AlignedStorable Std140 ComplexVectorStruct+instance AlignedStorable Std430 ComplexVectorStruct+instance AlignedStorable Scalar ComplexVectorStruct++newtype ComplexVectorStructStd140 = ComplexVectorStructStd140 ComplexVectorStruct+  deriving (Generic, Typeable, Show, Eq)++instance Storable ComplexVectorStructStd140 where+  sizeOf _ = 112+  alignment _ = 16+  peek ptr =+    ComplexVectorStructStd140+      <$> ( ComplexVectorStruct+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Packed Std140 (SSV.Vector 3 (V3 Word16))) ptr 16)+              <*> (coerce <$> peekByteOff @(Packed Std140 (SSV.Vector 2 (V2 Double))) ptr 64)+              <*> peekByteOff ptr 96+              <*> peekByteOff ptr 104+          )+  poke ptr (ComplexVectorStructStd140 ComplexVectorStruct{..}) = do+    pokeByteOff ptr 0 cvsA+    pokeByteOff ptr 16 (Packed @Std140 cvsB)+    pokeByteOff ptr 64 (Packed @Std140 cvsC)+    pokeByteOff ptr 96 cvsD+    pokeByteOff ptr 104 cvsE++hprop_complexvectorstruct_std140_round_trip :: Property+hprop_complexvectorstruct_std140_round_trip = property do+  val <- forAll genComplexVectorStruct+  let sut = Strided @Std140 val+      oracle = ComplexVectorStructStd140 val+  ptrTripping sut oracle++newtype ComplexVectorStructStd430 = ComplexVectorStructStd430 ComplexVectorStruct+  deriving (Generic, Typeable, Show, Eq)++instance Storable ComplexVectorStructStd430 where+  sizeOf _ = 80+  alignment _ = 16+  peek ptr =+    ComplexVectorStructStd430+      <$> ( ComplexVectorStruct+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Packed Std430 (SSV.Vector 3 (V3 Word16))) ptr 8)+              <*> (coerce <$> peekByteOff @(Packed Std430 (SSV.Vector 2 (V2 Double))) ptr 32)+              <*> peekByteOff ptr 64+              <*> peekByteOff ptr 72+          )+  poke ptr (ComplexVectorStructStd430 ComplexVectorStruct{..}) = do+    pokeByteOff ptr 0 cvsA+    pokeByteOff ptr 8 (Packed @Std430 cvsB)+    pokeByteOff ptr 32 (Packed @Std430 cvsC)+    pokeByteOff ptr 64 cvsD+    pokeByteOff ptr 72 cvsE++hprop_complexvectorstruct_std430_round_trip :: Property+hprop_complexvectorstruct_std430_round_trip = property do+  val <- forAll genComplexVectorStruct+  let sut = Strided @Std430 val+      oracle = ComplexVectorStructStd430 val+  ptrTripping sut oracle++newtype ComplexVectorStructScalar = ComplexVectorStructScalar ComplexVectorStruct+  deriving (Generic, Typeable, Show, Eq)++instance Storable ComplexVectorStructScalar where+  sizeOf _ = 72+  alignment _ = 8+  peek ptr =+    ComplexVectorStructScalar+      <$> ( ComplexVectorStruct+              <$> peekByteOff ptr 0+              <*> (coerce <$> peekByteOff @(Packed Scalar (SSV.Vector 3 (V3 Word16))) ptr 4)+              <*> (coerce <$> peekByteOff @(Packed Scalar (SSV.Vector 2 (V2 Double))) ptr 24)+              <*> peekByteOff ptr 56+              <*> peekByteOff ptr 64+          )+  poke ptr (ComplexVectorStructScalar ComplexVectorStruct{..}) = do+    pokeByteOff ptr 0 cvsA+    pokeByteOff ptr 4 (Packed @Scalar cvsB)+    pokeByteOff ptr 24 (Packed @Scalar cvsC)+    pokeByteOff ptr 56 (Packed @Scalar cvsD)+    pokeByteOff ptr 64 (Packed @Scalar cvsE)++hprop_complexvectorstruct_scalar_round_trip :: Property+hprop_complexvectorstruct_scalar_round_trip = property do+  val <- forAll genComplexVectorStruct+  let sut = Strided @Scalar val+      oracle = ComplexVectorStructScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Insanity+--------------------------------------------------------------------------------++data Insanity = Insanity+  { ina :: Float+  , inb :: SV.Vector 7 ComplexVectorStruct+  , inc :: Word8+  , ind :: Word64+  , ine :: Word16+  , inf :: V3 Bool+  , ing :: SV.Vector 5 Nested+  , inh :: V3 Word16+  , ini :: SV.Vector 5 (SV.Vector 2 (SV.Vector 4 Nested))+  , inj :: Bool+  , ink :: V3 Double+  }+  deriving (Generic, Typeable, Show, Eq)++instance AlignedStorable Std140 Insanity+instance AlignedStorable Std430 Insanity+instance AlignedStorable Scalar Insanity++genInsanity :: Gen Insanity+genInsanity =+  Insanity+    <$> genFloat+    <*> SV.replicateM genComplexVectorStruct+    <*> genInt+    <*> genInt+    <*> genInt+    <*> genV Gen.bool+    <*> SV.replicateM genNested+    <*> genV genInt+    <*> SV.replicateM (SV.replicateM (SV.replicateM genNested))+    <*> Gen.bool+    <*> genV genFloat++newtype InsanityStd140 = InsanityStd140 Insanity+  deriving (Generic, Typeable, Show, Eq)++instance Storable InsanityStd140 where+  sizeOf _ = 4512+  alignment _ = 32++  peek ptr =+    InsanityStd140+      <$> ( Insanity+              <$> peekByteOff ptr 0+              <*> peekSizedVector (Proxy @ComplexVectorStructStd140) ptr 16+              <*> peekByteOff ptr 800+              <*> peekByteOff ptr 808+              <*> peekByteOff ptr 816+              <*> peekByteOff ptr 832+              <*> peekSizedVector (Proxy @NestedStd140) ptr 848+              <*> peekStd140U16Vec3OracleByteOff ptr 1248+              <*> peekSized3DVector (Proxy @NestedStd140) ptr 1264+              <*> peekByteOff ptr 4464+              <*> peekByteOff ptr 4480+          )++  poke ptr (InsanityStd140 Insanity{..}) = do+    pokeByteOff ptr 0 ina+    pokeSizedVector ptr 16 (SV.map ComplexVectorStructStd140 inb)+    pokeByteOff ptr 800 inc+    pokeByteOff ptr 808 ind+    pokeByteOff ptr 816 ine+    pokeByteOff ptr 832 inf+    pokeSizedVector ptr 848 (SV.map NestedStd140 ing)+    pokeStd140U16Vec3OracleByteOff ptr 1248 inh+    pokeSized3DVector (Proxy @NestedStd140) ptr 1264 ini+    pokeByteOff ptr 4464 inj+    pokeByteOff ptr 4480 ink++hprop_insanity_std140_round_trips :: Property+hprop_insanity_std140_round_trips = withShrinks 10 $ property do+  val <- forAll genInsanity+  let sut = Strided @Std140 val+      oracle = InsanityStd140 val+  ptrTripping sut oracle++newtype InsanityStd430 = InsanityStd430 Insanity+  deriving (Generic, Typeable, Show, Eq)++instance Storable InsanityStd430 where+  sizeOf _ = 3584+  alignment _ = 32++  peek ptr =+    InsanityStd430+      <$> ( Insanity+              <$> peekByteOff ptr 0+              <*> peekSizedVector (Proxy @ComplexVectorStructStd430) ptr 16+              <*> peekByteOff ptr 576+              <*> peekByteOff ptr 584+              <*> peekByteOff ptr 592+              <*> peekByteOff ptr 608+              <*> peekSizedVector (Proxy @NestedStd430) ptr 624+              <*> peekStd430U16Vec3OracleByteOff ptr 944+              <*> peekSized3DVector (Proxy @NestedStd430) ptr 960+              <*> peekByteOff ptr 3520+              <*> peekByteOff ptr 3552+          )++  poke ptr (InsanityStd430 Insanity{..}) = do+    pokeByteOff ptr 0 ina+    pokeSizedVector ptr 16 (SV.map ComplexVectorStructStd430 inb)+    pokeByteOff ptr 576 inc+    pokeByteOff ptr 584 ind+    pokeByteOff ptr 592 ine+    pokeByteOff ptr 608 inf+    pokeSizedVector ptr 624 (SV.map NestedStd430 ing)+    pokeStd430U16Vec3OracleByteOff ptr 944 inh+    pokeSized3DVector (Proxy @NestedStd430) ptr 960 ini+    pokeByteOff ptr 3520 inj+    pokeByteOff ptr 3552 ink++hprop_insanity_std430_round_trips :: Property+hprop_insanity_std430_round_trips = withShrinks 10 $ property do+  val <- forAll genInsanity+  let sut = Strided @Std430 val+      oracle = InsanityStd430 val+  ptrTripping sut oracle++newtype InsanityScalar = InsanityScalar Insanity+  deriving (Generic, Typeable, Show, Eq)++instance Storable InsanityScalar where+  sizeOf _ = 3096+  alignment _ = 8++  peek ptr =+    InsanityScalar+      <$> ( Insanity+              <$> peekByteOff ptr 0+              <*> peekSizedVector (Proxy @ComplexVectorStructScalar) ptr 8+              <*> peekByteOff ptr 512+              <*> peekByteOff ptr 520+              <*> peekByteOff ptr 528+              <*> peekByteOff ptr 532+              <*> peekSizedVector (Proxy @NestedScalar) ptr 544+              <*> peekStd430U16Vec3OracleByteOff ptr 820+              <*> peekSized3DVector (Proxy @NestedScalar) ptr 832+              <*> peekByteOff ptr 3068+              <*> peekByteOff ptr 3072+          )++  poke ptr (InsanityScalar Insanity{..}) = do+    pokeByteOff ptr 0 ina+    pokeSizedVector ptr 8 (SV.map ComplexVectorStructScalar inb)+    pokeByteOff ptr 512 inc+    pokeByteOff ptr 520 ind+    pokeByteOff ptr 528 ine+    pokeByteOff ptr 532 inf+    pokeSizedVector ptr 544 (SV.map NestedScalar ing)+    pokeStd430U16Vec3OracleByteOff ptr 820 inh+    pokeSized3DVector (Proxy @NestedScalar) ptr 832 ini+    pokeByteOff ptr 3068 inj+    pokeByteOff ptr 3072 ink++hprop_insanity_scalar_round_trips :: Property+hprop_insanity_scalar_round_trips = withShrinks 10 $ property do+  val <- forAll genInsanity+  let sut = Strided @Scalar val+      oracle = InsanityScalar val+  ptrTripping sut oracle++--------------------------------------------------------------------------------+-- Utils+--------------------------------------------------------------------------------+genFloat :: (RealFloat a) => Gen a+genFloat = Gen.realFloat (Range.linearFrac (-infinity) infinity)++genInt :: (Bounded a, Integral a) => Gen a+genInt = Gen.integral (Range.linear minBound maxBound)++genV :: (Traversable t, Applicative t) => Gen a -> Gen (t a)+genV = sequence . pure++genMat :: (Traversable t, Applicative t, Traversable f, Applicative f) => Gen a -> Gen (t (f a))+genMat = sequence . pure . genV++infinity :: (Fractional a) => a+infinity = 1 / 0++peekSizedVector+  :: forall n a b c v+   . (KnownNat n, Storable a, Coercible a b, GV.Vector v a, GV.Vector v b)+  => Proxy a+  -> Ptr c+  -> Int+  -> IO (SGV.Vector v n b)+peekSizedVector _ p baseOffset = SGV.generateM \i ->+  coerce <$> peekByteOff @a p (baseOffset + fromIntegral i * sizeOf @a undefined)++pokeSizedVector+  :: forall n a b v+   . (KnownNat n, Storable a, GV.Vector v a)+  => Ptr b+  -> Int+  -> SGV.Vector v n a+  -> IO ()+pokeSizedVector p baseOffset = SGV.imapM_ \i a -> pokeByteOff p (baseOffset + sizeOf @a undefined * fromIntegral i) a++peekSized3DVector+  :: forall n m o a b c v+   . ( KnownNat n+     , KnownNat m+     , KnownNat o+     , Storable a+     , Coercible a b+     , GV.Vector v a+     , GV.Vector v b+     , GV.Vector v (SGV.Vector v o b)+     , GV.Vector v (SGV.Vector v m (SGV.Vector v o b))+     )+  => Proxy a+  -> Ptr c+  -> Int+  -> IO (SGV.Vector v n (SGV.Vector v m (SGV.Vector v o b)))+peekSized3DVector _ p baseOffset =+  let strideO = sizeOf @a undefined+      strideM = strideO * fromIntegral (natVal @o Proxy)+      strideN = strideM * fromIntegral (natVal @m Proxy)+   in SGV.generateM \i ->+        SGV.generateM \j ->+          SGV.generateM \k ->+            let offset = baseOffset + fromIntegral i * strideN + fromIntegral j * strideM + fromIntegral k * strideO+             in coerce <$> peekByteOff @a p offset++pokeSized3DVector+  :: forall n m o a b c v+   . ( KnownNat n+     , KnownNat m+     , KnownNat o+     , Storable a+     , Coercible a b+     , GV.Vector v a+     , GV.Vector v b+     , GV.Vector v c+     , GV.Vector v (SGV.Vector v o b)+     , GV.Vector v (SGV.Vector v m (SGV.Vector v o b))+     )+  => Proxy a+  -> Ptr c+  -> Int+  -> SGV.Vector v n (SGV.Vector v m (SGV.Vector v o b))+  -> IO ()+pokeSized3DVector _ p baseOffset =+  let strideO = sizeOf @a undefined+      strideM = strideO * fromIntegral (natVal @o Proxy)+      strideN = strideM * fromIntegral (natVal @m Proxy)+   in SGV.imapM_ \i ->+        SGV.imapM_ \j ->+          SGV.imapM_ \k a ->+            let offset = baseOffset + fromIntegral i * strideN + fromIntegral j * strideM + fromIntegral k * strideO+             in pokeByteOff @a p offset (coerce a)++class MatrixStride v a where+  matStride :: Proxy (v a) -> MemoryLayout -> Int++instance MatrixStride V2 Half where+  matStride _ = \case+    Std140 -> 16+    Std430 -> 4+    Scalar -> 4++instance MatrixStride V2 Float where+  matStride _ = \case+    Std140 -> 16+    Std430 -> 8+    Scalar -> 8++instance MatrixStride V2 Double where+  matStride _ = \case+    Std140 -> 16+    Std430 -> 16+    Scalar -> 8++instance MatrixStride V3 Half where+  matStride _ = \case+    Std140 -> 16+    Std430 -> 8+    Scalar -> 6++instance MatrixStride V3 Float where+  matStride _ = \case+    Std140 -> 16+    Std430 -> 16+    Scalar -> 12++instance MatrixStride V3 Double where+  matStride _ = \case+    Std140 -> 32+    Std430 -> 32+    Scalar -> 24++instance MatrixStride V4 Half where+  matStride _ = \case+    Std140 -> 16+    Std430 -> 8+    Scalar -> 8++instance MatrixStride V4 Float where+  matStride _ _ = 16++instance MatrixStride V4 Double where+  matStride _ _ = 32++peekMat2Oracle+  :: forall a b v. (MatrixStride v a, Storable (v a)) => MemoryLayout -> Ptr b -> Int -> IO (V2 (v a))+peekMat2Oracle layout ptr off =+  let stride = matStride (Proxy @(v a)) layout+   in V2 <$> peekByteOff ptr off <*> peekByteOff ptr (off + stride)++pokeMat2Oracle+  :: forall a b v+   . (MatrixStride v a, Storable (v a))+  => MemoryLayout+  -> Ptr b+  -> Int+  -> V2 (v a)+  -> IO ()+pokeMat2Oracle layout ptr off (V2 r1 r2) = do+  let stride = matStride (Proxy @(v a)) layout+  pokeByteOff ptr off r1+  pokeByteOff ptr (off + stride) r2++peekMat3Oracle+  :: forall a b v. (MatrixStride v a, Storable (v a)) => MemoryLayout -> Ptr b -> Int -> IO (V3 (v a))+peekMat3Oracle layout ptr off =+  let stride = matStride (Proxy @(v a)) layout+   in V3+        <$> peekByteOff ptr off+        <*> peekByteOff ptr (off + stride)+        <*> peekByteOff ptr (off + stride * 2)++pokeMat3Oracle+  :: forall a b v+   . (MatrixStride v a, Storable (v a))+  => MemoryLayout+  -> Ptr b+  -> Int+  -> V3 (v a)+  -> IO ()+pokeMat3Oracle layout ptr off (V3 r1 r2 r3) = do+  let stride = matStride (Proxy @(v a)) layout+  pokeByteOff ptr off r1+  pokeByteOff ptr (off + stride) r2+  pokeByteOff ptr (off + stride * 2) r3++peekMat4Oracle+  :: forall a b v. (MatrixStride v a, Storable (v a)) => MemoryLayout -> Ptr b -> Int -> IO (V4 (v a))+peekMat4Oracle layout ptr off =+  let stride = matStride (Proxy @(v a)) layout+   in V4+        <$> peekByteOff ptr off+        <*> peekByteOff ptr (off + stride)+        <*> peekByteOff ptr (off + stride * 2)+        <*> peekByteOff ptr (off + stride * 3)++pokeMat4Oracle+  :: forall a b v+   . (MatrixStride v a, Storable (v a))+  => MemoryLayout+  -> Ptr b+  -> Int+  -> V4 (v a)+  -> IO ()+pokeMat4Oracle layout ptr off (V4 r1 r2 r3 r4) = do+  let stride = matStride (Proxy @(v a)) layout+  pokeByteOff ptr off r1+  pokeByteOff ptr (off + stride) r2+  pokeByteOff ptr (off + stride * 2) r3+  pokeByteOff ptr (off + stride * 3) r4++peekStd140U16Vec3OracleByteOff :: (Storable a) => Ptr b -> Int -> IO (V3 a)+peekStd140U16Vec3OracleByteOff ptr off =+  V3+    <$> peekByteOff ptr off+    <*> peekByteOff ptr (off + 4)+    <*> peekByteOff ptr (off + 8)++peekStd140U16Mat3OracleByteOff :: Ptr b -> Int -> IO (M33 Word16)+peekStd140U16Mat3OracleByteOff ptr off =+  V3+    <$> peekStd140U16Vec3OracleByteOff ptr off+    <*> peekStd140U16Vec3OracleByteOff ptr (off + 16)+    <*> peekStd140U16Vec3OracleByteOff ptr (off + 32)++peekStd430U16Vec3OracleByteOff :: (Storable a) => Ptr b -> Int -> IO (V3 a)+peekStd430U16Vec3OracleByteOff ptr off =+  V3+    <$> peekByteOff ptr off+    <*> peekByteOff ptr (off + 2)+    <*> peekByteOff ptr (off + 4)++pokeStd140U16Vec3OracleByteOff :: Ptr b -> Int -> V3 Word16 -> IO ()+pokeStd140U16Vec3OracleByteOff ptr off (V3 x y z) = do+  pokeByteOff ptr off x+  pokeByteOff ptr (off + 4) y+  pokeByteOff ptr (off + 8) z++pokeStd140U16Mat3OracleByteOff :: Ptr b -> Int -> M33 Word16 -> IO ()+pokeStd140U16Mat3OracleByteOff ptr off (V3 r1 r2 r3) = do+  pokeStd140U16Vec3OracleByteOff ptr off r1+  pokeStd140U16Vec3OracleByteOff ptr (off + 16) r2+  pokeStd140U16Vec3OracleByteOff ptr (off + 32) r3++pokeStd430U16Vec3OracleByteOff :: Ptr b -> Int -> V3 Word16 -> IO ()+pokeStd430U16Vec3OracleByteOff ptr off (V3 x y z) = do+  pokeByteOff ptr off x+  pokeByteOff ptr (off + 2) y+  pokeByteOff ptr (off + 4) z++-- Duplicating this from the main module so it doesn't need to be exported.+roundUpTo :: (Bits a, Num a) => a -> a -> a+roundUpTo multiple val = (val + multiple - 1) .&. complement (multiple - 1)