packages feed

gl-block (empty) → 1.0

raw patch · 10 files changed

+1020/−0 lines, 10 filesdep +basedep +gl-blockdep +ptrdiffsetup-changed

Dependencies added: base, gl-block, ptrdiff, tasty, tasty-bench, tasty-hunit, vector

Files

+ ChangeLog.md view
+ LICENSE view
@@ -0,0 +1,229 @@+# License++Licensed under either of+ * Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)+ * BSD 2-Clause license (https://opensource.org/licenses/BSD-2-Clause)+at your option.++## BSD 2-Clause License++- Copyright 2014-2019 Edward Kmett++All rights reserved.++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.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.++## Apache License++_Version 2.0, January 2004_  +_&lt;<http://www.apache.org/licenses/>&gt;_++### Terms and Conditions for use, reproduction, and distribution++#### 1. Definitions++“License” shall mean the terms and conditions for use, reproduction, and+distribution as defined by Sections 1 through 9 of this document.++“Licensor” shall mean the copyright owner or entity authorized by the copyright+owner that is granting the License.++“Legal Entity” shall mean the union of the acting entity and all other entities+that control, are controlled by, or are under common control with that entity.+For the purposes of this definition, “control” means **(i)** the power, direct or+indirect, to cause the direction or management of such entity, whether by+contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the+outstanding shares, or **(iii)** beneficial ownership of such entity.++“You” (or “Your”) shall mean an individual or Legal Entity exercising+permissions granted by this License.++“Source” form shall mean the preferred form for making modifications, including+but not limited to software source code, documentation source, and configuration+files.++“Object” form shall mean any form resulting from mechanical transformation or+translation of a Source form, including but not limited to compiled object code,+generated documentation, and conversions to other media types.++“Work” shall mean the work of authorship, whether in Source or Object form, made+available under the License, as indicated by a copyright notice that is included+in or attached to the work (an example is provided in the Appendix below).++“Derivative Works” shall mean any work, whether in Source or Object form, that+is based on (or derived from) the Work and for which the editorial revisions,+annotations, elaborations, or other modifications represent, as a whole, an+original work of authorship. For the purposes of this License, Derivative Works+shall not include works that remain separable from, or merely link (or bind by+name) to the interfaces of, the Work and Derivative Works thereof.++“Contribution” shall mean any work of authorship, including the original version+of the Work and any modifications or additions to that Work or Derivative Works+thereof, that is intentionally submitted to Licensor for inclusion in the Work+by the copyright owner or by an individual or Legal Entity authorized to submit+on behalf of the copyright owner. For the purposes of this definition,+“submitted” means any form of electronic, verbal, or written communication sent+to the Licensor or its representatives, including but not limited to+communication on electronic mailing lists, source code control systems, and+issue tracking systems that are managed by, or on behalf of, the Licensor for+the purpose of discussing and improving the Work, but excluding communication+that is conspicuously marked or otherwise designated in writing by the copyright+owner as “Not a Contribution.”++“Contributor” shall mean Licensor and any individual or Legal Entity on behalf+of whom a Contribution has been received by Licensor and subsequently+incorporated within the Work.++#### 2. Grant of Copyright License++Subject to the terms and conditions of this License, each Contributor hereby+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,+irrevocable copyright license to reproduce, prepare Derivative Works of,+publicly display, publicly perform, sublicense, and distribute the Work and such+Derivative Works in Source or Object form.++#### 3. Grant of Patent License++Subject to the terms and conditions of this License, each Contributor hereby+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,+irrevocable (except as stated in this section) patent license to make, have+made, use, offer to sell, sell, import, and otherwise transfer the Work, where+such license applies only to those patent claims licensable by such Contributor+that are necessarily infringed by their Contribution(s) alone or by combination+of their Contribution(s) with the Work to which such Contribution(s) was+submitted. If You institute patent litigation against any entity (including a+cross-claim or counterclaim in a lawsuit) alleging that the Work or a+Contribution incorporated within the Work constitutes direct or contributory+patent infringement, then any patent licenses granted to You under this License+for that Work shall terminate as of the date such litigation is filed.++#### 4. Redistribution++You may reproduce and distribute copies of the Work or Derivative Works thereof+in any medium, with or without modifications, and in Source or Object form,+provided that You meet the following conditions:++* **(a)** You must give any other recipients of the Work or Derivative Works a copy of+this License; and+* **(b)** You must cause any modified files to carry prominent notices stating that You+changed the files; and+* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,+all copyright, patent, trademark, and attribution notices from the Source form+of the Work, excluding those notices that do not pertain to any part of the+Derivative Works; and+* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any+Derivative Works that You distribute must include a readable copy of the+attribution notices contained within such NOTICE file, excluding those notices+that do not pertain to any part of the Derivative Works, in at least one of the+following places: within a NOTICE text file distributed as part of the+Derivative Works; within the Source form or documentation, if provided along+with the Derivative Works; or, within a display generated by the Derivative+Works, if and wherever such third-party notices normally appear. The contents of+the NOTICE file are for informational purposes only and do not modify the+License. You may add Your own attribution notices within Derivative Works that+You distribute, alongside or as an addendum to the NOTICE text from the Work,+provided that such additional attribution notices cannot be construed as+modifying the License.++You may add Your own copyright statement to Your modifications and may provide+additional or different license terms and conditions for use, reproduction, or+distribution of Your modifications, or for any such Derivative Works as a whole,+provided Your use, reproduction, and distribution of the Work otherwise complies+with the conditions stated in this License.++#### 5. Submission of Contributions++Unless You explicitly state otherwise, any Contribution intentionally submitted+for inclusion in the Work by You to the Licensor shall be under the terms and+conditions of this License, without any additional terms or conditions.+Notwithstanding the above, nothing herein shall supersede or modify the terms of+any separate license agreement you may have executed with Licensor regarding+such Contributions.++#### 6. Trademarks++This License does not grant permission to use the trade names, trademarks,+service marks, or product names of the Licensor, except as required for+reasonable and customary use in describing the origin of the Work and+reproducing the content of the NOTICE file.++#### 7. Disclaimer of Warranty++Unless required by applicable law or agreed to in writing, Licensor provides the+Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,+including, without limitation, any warranties or conditions of TITLE,+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are+solely responsible for determining the appropriateness of using or+redistributing the Work and assume any risks associated with Your exercise of+permissions under this License.++#### 8. Limitation of Liability++In no event and under no legal theory, whether in tort (including negligence),+contract, or otherwise, unless required by applicable law (such as deliberate+and grossly negligent acts) or agreed to in writing, shall any Contributor be+liable to You for damages, including any direct, indirect, special, incidental,+or consequential damages of any character arising as a result of this License or+out of the use or inability to use the Work (including but not limited to+damages for loss of goodwill, work stoppage, computer failure or malfunction, or+any and all other commercial damages or losses), even if such Contributor has+been advised of the possibility of such damages.++#### 9. Accepting Warranty or Additional Liability++While redistributing the Work or Derivative Works thereof, You may choose to+offer, and charge a fee for, acceptance of support, warranty, indemnity, or+other liability obligations and/or rights consistent with this License. However,+in accepting such obligations, You may act only on Your own behalf and on Your+sole responsibility, not on behalf of any other Contributor, and only if You+agree to indemnify, defend, and hold each Contributor harmless for any liability+incurred by, or claims asserted against, such Contributor by reason of your+accepting any such warranty or additional liability.++_END OF TERMS AND CONDITIONS_++### APPENDIX: How to apply the Apache License to your work++To apply the Apache License to your work, attach the following boilerplate+notice, with the fields enclosed by brackets `[]` replaced with your own+identifying information. (Don't include the brackets!) The text should be+enclosed in the appropriate comment syntax for the file format. We also+recommend that a file or class name and description of purpose be included on+the same “printed page” as the copyright notice for easier identification within+third-party archives.++    Copyright [yyyy] [name of copyright owner]+    +    Licensed under the Apache License, Version 2.0 (the "License");+    you may not use this file except in compliance with the License.+    You may obtain a copy of the License at+    +      http://www.apache.org/licenses/LICENSE-2.0+    +    Unless required by applicable law or agreed to in writing, software+    distributed under the License is distributed on an "AS IS" BASIS,+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+    See the License for the specific language governing permissions and+    limitations under the License.
+ README.md view
@@ -0,0 +1,89 @@+## gl-block++### Using++Primitive types should have the `Block` instance.+With that in place, you can build structures and get Storable instances derived generically, according to intented usage.++```haskell+import GHC.Generics (Generic)+import Graphics.Gl.Block (Block, Packed(..), Std140(..), Std430(..))++-- | Attribute streams can be packed tightly together+data VertexAttrs = VertexAttrs+  { color :: Vec4+  , texCoords :: Vec2+  }+  deriving stock (Eq, Ord, Show, Generic) -- The regular stuff and Generic+  deriving anyclass Block -- The layout class, with Generic defaults+  deriving Storable via (Packed VertexAttrs) -- Free goodies!++-- | Uniform data require jumping through padding and alignment flaming hoop.+-- You can use derive-storable-plugin or hs2c instead, but there are gotchas.+data SceneUniform = SceneUniform+  { projection :: Mat4+  , viewPosition :: Vec3 -- Here comes the jazz+  , viewDirection :: Vec3+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass Block+  deriving Storable via (Std140 VertexAttrs) -- With comfy padding++-- | Shader buffer objects are less vacuous, but the rules are specific to the domain.+data Material = Material+  { baseColor :: Vec4+  , metallicRoughness :: Vec2+  , emission :: Vec4+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass Block+  deriving Storable via (Std430 VertexAttrs) -- Less alignment, less calculations+```++* [Shader Buffer Memory Layout Info](https://gist.github.com/teoxoy/936891c16c2a3d1c3c5e7204ac6cd76c)++++### Benchmarks++The benchmark consists of filling a Storable vector.++* Packed layout is on par with manual instances.+* Std140 is slower, but not catastrophically so.+* Std430 seems to regain some performance due to being a tad simpler.++> There's only one "manual" case standing for all the layouts since it would only be different in pointer offsets.+> And no way in hell I'm going to calculate them by hand!++```python+  struct+    10+      manual: OK (2.26s)+        60.6 ns ± 5.4 ns+      packed: OK (1.21s)+        63.7 ns ± 5.8 ns+      std140: OK (1.19s)+        256  ns ±  26 ns+      std430: OK (0.13s)+        269  ns ±  24 ns+    1000+      manual: OK (1.95s)+        3.34 μs ± 244 ns+      packed: OK (1.96s)+        3.34 μs ± 235 ns+      std140: OK (1.97s)+        6.70 μs ± 425 ns+      std430: OK (1.44s)+        4.89 μs ±  93 ns+    1000000+      manual: OK (2.59s)+        2.22 ms ± 176 μs+      packed: OK (1.50s)+        2.47 ms ±  24 μs+      std140: OK (2.70s)+        4.54 ms ± 431 μs+      std430: OK (1.08s)+        3.28 ms ± 244 μs+```++> Caveat: nested structures have degraded performance.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/Bench.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE ImportQualifiedPost #-}++import Test.Tasty.Bench (bench, bgroup, defaultMain, nf)++import Bench.Struct+import Data.Vector.Storable qualified as Storable++main :: IO ()+main = defaultMain+  [ bgroup "struct"+      [ bgroup "10"+          [ bench "manual" $+              nf (flip Storable.replicate structManual) 10+          , bench "packed" $+              nf (flip Storable.replicate structPacked) 10+          , bench "std140" $+              nf (flip Storable.replicate struct140) 10+          , bench "std430" $+              nf (flip Storable.replicate struct430) 10+          ]+      , bgroup "1000"+          [ bench "manual" $+              nf (flip Storable.replicate structManual) 1000+          , bench "packed" $+              nf (flip Storable.replicate structPacked) 1000+          , bench "std140" $+              nf (flip Storable.replicate struct140) 1000+          , bench "std430" $+              nf (flip Storable.replicate struct430) 1000+          ]+      , bgroup "1000000"+          [ bench "manual" $+              nf (flip Storable.replicate structManual) 1000000+          , bench "packed" $+              nf (flip Storable.replicate structPacked) 1000000+          , bench "std140" $+              nf (flip Storable.replicate struct140) 1000000+          , bench "std430" $+              nf (flip Storable.replicate struct430) 1000000+          ]+      ]+  ]
+ bench/Bench/Struct.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE Strict #-}++{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE RecordWildCards #-}++module Bench.Struct where++import Foreign+import GHC.Generics (Generic)++import Graphics.Gl.Block (Block, Packed(..), Std140(..), Std430(..))++data StructManual = StructManual+  { smWat :: Bool -- 4+  , smThis :: Double -- 8+  , smThat :: Float -- 4+  , smThose :: (Float, Float) -- 4 + 4+  }+  deriving stock (Eq, Ord, Show, Generic)++structManual :: StructManual+structManual = StructManual+  { smWat = True+  , smThis = 2 * pi+  , smThat = pi+  , smThose = (1/60, -1e6)+  }++instance Storable StructManual where+  alignment ~_ = 8+  sizeOf ~_ = 24+  peek ptr =+    StructManual+      <$> peekByteOff ptr 0+      <*> peekByteOff ptr 4+      <*> peekByteOff ptr 12+      <*> (+        (,)+          <$> peekByteOff ptr 16+          <*> peekByteOff ptr 20+      )+  poke ptr StructManual{..} = do+    pokeByteOff ptr 0 smWat+    pokeByteOff ptr 4 smThis+    pokeByteOff ptr 12 smThat+    pokeByteOff ptr 16 (fst smThose)+    pokeByteOff ptr 20 (snd smThose)++data StructPacked = StructPacked+  { spWat :: Bool -- 4+  , spThis :: Double -- 8+  , spThat :: Float -- 4+  , spThose :: (Float, Float) -- 4 + 4+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass Block+  deriving Storable via (Packed StructPacked)++structPacked :: StructPacked+structPacked = StructPacked+  { spWat = True+  , spThis = 2 * pi+  , spThat = pi+  , spThose = (1/60, -1e6)+  }++data Struct140 = Struct140+  { std140Wat :: Bool -- 4+  , std140This :: Double -- 8+  , std140That :: Float -- 4+  , std140Those :: (Float, Float) -- 4 + 4+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass Block+  deriving Storable via (Std140 Struct140)++struct140 :: Struct140+struct140 = Struct140+  { std140Wat = True+  , std140This = 2 * pi+  , std140That = pi+  , std140Those = (1/60, -1e6)+  }++data Struct430 = Struct430+  { std430Wat :: Bool -- 4+  , std430This :: Double -- 8+  , std430That :: Float -- 4+  , std430Those :: (Float, Float) -- 4 + 4+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass Block+  deriving Storable via (Std430 Struct430)++struct430 :: Struct430+struct430 = Struct430+  { std430Wat = True+  , std430This = 2 * pi+  , std430That = pi+  , std430Those = (1/60, -1e6)+  }
+ gl-block.cabal view
@@ -0,0 +1,77 @@+cabal-version: 2.2++-- This file has been generated from package.yaml by hpack version 0.35.2.+--+-- see: https://github.com/sol/hpack++name:           gl-block+version:        1.0+synopsis:       OpenGL standard memory layouts+category:       Graphics+author:         IC Rainbow+maintainer:     ic.rbow@gmail.com+copyright:      2023 IC Rainbow, 2014-2019 Edward Kmett+license:        (BSD-2-Clause OR Apache-2.0)+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://gitlab.com/dpwiz/gl-block++library+  exposed-modules:+      Graphics.Gl.Block+  other-modules:+      Paths_gl_block+  autogen-modules:+      Paths_gl_block+  hs-source-dirs:+      src+  ghc-options: -Wall+  build-depends:+      base >=4.7 && <5+    , ptrdiff+  default-language: Haskell2010++test-suite gl-block-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Spec.Packed+      Paths_gl_block+  autogen-modules:+      Paths_gl_block+  hs-source-dirs:+      test+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-qn1 -with-rtsopts=-A128m+  build-tool-depends:+      tasty-discover:tasty-discover+  build-depends:+      base >=4.7 && <5+    , gl-block+    , tasty+    , tasty-hunit+  default-language: Haskell2010++benchmark gl-block-bench+  type: exitcode-stdio-1.0+  main-is: Bench.hs+  other-modules:+      Bench.Struct+      Paths_gl_block+  autogen-modules:+      Paths_gl_block+  hs-source-dirs:+      bench+  ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N1 -fproc-alignment=64 -with-rtsopts=-A128m+  build-depends:+      base >=4.7 && <5+    , gl-block+    , tasty+    , tasty-bench+    , vector+  default-language: Haskell2010
+ src/Graphics/Gl/Block.hs view
@@ -0,0 +1,429 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Copyright:  (c) 2023 IC Rainbow+            (c) 2014-2019 Edward Kmett+License : BSD-2-Clause OR Apache-2.0+Maintainer: IC Rainbow <aenor.realm@gmail.com>+Stability: experimental+Portability: non-portable++OpenGL std140 and std430 support+-}+module Graphics.Gl.Block+  ( Block(..)+  , GBlock(..)+  , Packed(..)+  , Std140(..)+  , Std430(..)+  , roundUp+  ) where++import Control.Monad.IO.Class (MonadIO(..))+import Data.Data (Data, Typeable)+import Data.Int (Int32)+import Data.Proxy (Proxy(..))+import Data.Word (Word32)+import Foreign.Ptr (Ptr)+import Foreign.Ptr.Diff (Diff(..), peekDiffOff, pokeDiffOff)+import Foreign.Storable (Storable(..))+import GHC.Generics (Generic, Rep, M1(..), K1(..), U1(..), C, D, S, (:*:)(..), from, to)+import GHC.TypeLits (KnownNat, Nat, type (+), natVal)++newtype Packed a = Packed { getPacked :: a }+  deriving (Data, Typeable, Generic, Functor, Foldable, Traversable, Eq, Ord, Show, Read)++instance Block a => Storable (Packed a) where+  alignment _ = 1+  sizeOf _ = sizeOfPacked (Proxy :: Proxy a)+  peekByteOff p o = Packed <$> readPacked p (Diff o)+  pokeByteOff p o = writePacked p (Diff o) . getPacked++newtype Std140 a = Std140 { getStd140 :: a }+  deriving (Data, Typeable, Generic, Functor, Foldable, Traversable, Eq, Ord, Show, Read)++instance Block a => Storable (Std140 a) where+  alignment _ = alignment140 (Proxy :: Proxy a)+  sizeOf _ = sizeOf140 (Proxy :: Proxy a)+  peekByteOff p o = Std140 <$> read140 p (Diff o)+  pokeByteOff p o = write140 p (Diff o) . getStd140++newtype Std430 a = Std430 { getStd430 :: a }+  deriving (Data, Typeable, Generic, Functor, Foldable, Traversable, Eq, Ord, Show, Read)++instance Block a => Storable (Std430 a) where+  alignment _ = alignment430 (Proxy :: Proxy a)+  sizeOf _ = sizeOf430 (Proxy :: Proxy a)+  peekByteOff p o = Std430 <$> read430 p (Diff o)+  pokeByteOff p o = write430 p (Diff o) . getStd430++-- | This describes how to load and store primitives+-- through a uniform/shader storage blocks according to+-- OpenGL Std140 and Std430.+--+-- There are lots of fiddly little constants around, beware.+class Block b where+  -- | As per 'Storable' 'alignment', but matching OpenGL Std140.+  alignment140 :: proxy b -> Int+  default alignment140 :: GBlock (Rep b) => proxy b -> Int+  alignment140 _ = galignment140 (Proxy :: Proxy (Rep b))++  -- | As per 'Storable' 'sizeOf', but matching OpenGL Std140.+  sizeOf140 :: proxy b -> Int+  default sizeOf140 :: GBlock (Rep b) => proxy b -> Int+  sizeOf140 _ = gsizeOf140 (Proxy :: Proxy (Rep b))++  -- | Structures get smashed up to a minimum of a vec4 alignment in 140 mode+  isStruct :: proxy b -> Bool+  isStruct _ = True++  read140 :: MonadIO m => Ptr a -> Diff a b -> m b+  default read140 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> m b+  read140 p (Diff o) = liftIO $ to <$> gread140 p o++  write140 :: MonadIO m => Ptr a -> Diff a b -> b -> m ()+  default write140 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> b -> m ()+  write140 p (Diff o) b = liftIO $ gwrite140 p o (from b)++  -- | As per 'Storable' 'alignment', but matching OpenGL Std430.+  alignment430 :: proxy b -> Int+  default alignment430 :: GBlock (Rep b) => proxy b -> Int+  alignment430 _ = galignment430 (Proxy :: Proxy (Rep b))++  -- | As per 'Storable' 'sizeOf', but matching OpenGL Std430.+  sizeOf430 :: proxy b -> Int+  default sizeOf430 :: GBlock (Rep b) => proxy b -> Int+  sizeOf430 _ = gsizeOf430 (Proxy :: Proxy (Rep b))++  read430 :: MonadIO m => Ptr a -> Diff a b -> m b+  default read430 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> m b+  read430 p (Diff o) = liftIO $ to <$> gread430 p o++  write430 :: MonadIO m => Ptr a -> Diff a b -> b -> m ()+  default write430 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> b -> m ()+  write430 p (Diff o) b = liftIO $ gwrite430 p o (from b)++  type PackedSize b :: Nat+  type PackedSize b = GPackedSize (Rep b)++  -- | As per 'Storable' 'sizeOf', but without padding and no alignment+  sizeOfPacked :: proxy b -> Int+  default sizeOfPacked :: KnownNat (PackedSize b) => proxy b -> Int+  sizeOfPacked _ = fromInteger $! natVal (Proxy :: Proxy (PackedSize b))++  readPacked :: MonadIO m => Ptr a -> Diff a b -> m b+  default readPacked :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> m b+  readPacked p (Diff o) = liftIO $ to <$> greadPacked p o++  writePacked :: MonadIO m => Ptr a -> Diff a b -> b -> m ()+  default writePacked :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> b -> m ()+  writePacked p (Diff o) b = liftIO $ gwritePacked p o (from b)++-- | Automatically derive Std140 and Std430 alignment using GHC Generics+class GBlock f where+  type GPackedSize f :: Nat+  galignment140    :: p f -> Int+  galignment430    :: p f -> Int+  gsizeOf140    :: p f -> Int+  gsizeOf430    :: p f -> Int+  gsizeOfPacked :: p f -> Int+  gread140    :: Ptr a -> Int -> IO (f b)+  gread430    :: Ptr a -> Int -> IO (f b)+  greadPacked :: Ptr a -> Int -> IO (f b)+  gwrite140    :: Ptr a -> Int -> f b -> IO ()+  gwrite430    :: Ptr a -> Int -> f b -> IO ()+  gwritePacked :: Ptr a -> Int -> f b -> IO ()++instance GBlock U1 where+  type GPackedSize U1 = 0+  galignment140 _ = 1+  gsizeOf140    _ = 0+  galignment430 _ = 1+  gsizeOf430    _ = 0+  gsizeOfPacked    _ = 0+  gread140 _ _ = return U1+  gread430 _ _ = return U1+  greadPacked _ _ = return U1+  gwrite140 _ _ U1 = return ()+  gwrite430 _ _ U1 = return ()+  gwritePacked _ _ U1 = return ()++instance (GBlock f, GBlock g) => GBlock (f :*: g) where+  type GPackedSize (f :*: g) = GPackedSize f + GPackedSize g++  gsizeOfPacked _ =+    gsizeOfPacked (Proxy :: Proxy f) + gsizeOfPacked (Proxy :: Proxy g)++  galignment140 _ =+    max+      (galignment140 (Proxy :: Proxy f))+      (galignment140 (Proxy :: Proxy g))++  galignment430 _ =+    max+      (galignment430 (Proxy :: Proxy f))+      (galignment430 (Proxy :: Proxy g))++  gsizeOf140 _ =+    roundUp+      (gsizeOf140 (Proxy :: Proxy f))+      (galignment140 (Proxy :: Proxy g))+    + gsizeOf140 (Proxy :: Proxy g)++  gsizeOf430 _ =+    roundUp+      (gsizeOf430 (Proxy :: Proxy f))+      (galignment430 (Proxy :: Proxy g))+    + gsizeOf430 (Proxy :: Proxy g)++  gread140 p o = (:*:)+    <$> gread140 p o+    <*> gread140 p (o + roundUp (gsizeOf140 (Proxy :: Proxy f)) (galignment140 (Proxy :: Proxy g)))++  gread430 p o = (:*:)+    <$> gread430 p o+    <*> gread430 p (o + roundUp (gsizeOf430 (Proxy :: Proxy f)) (galignment430 (Proxy :: Proxy g)))++  greadPacked p o = (:*:)+    <$> greadPacked p o+    <*> greadPacked p (o + gsizeOfPacked (Proxy :: Proxy f))++  gwrite140 p o (a :*: b) = do+    gwrite140 p o a+    gwrite140 p (o + roundUp (gsizeOf140 (Proxy :: Proxy f)) (galignment140 (Proxy :: Proxy g))) b++  gwrite430 p o (a :*: b) = do+    gwrite430 p o a+    gwrite430 p (o + roundUp (gsizeOf430 (Proxy :: Proxy f)) (galignment430 (Proxy :: Proxy g))) b++  gwritePacked p o (a :*: b) = do+    gwritePacked p o a+    gwritePacked p (o + gsizeOfPacked (Proxy :: Proxy f)) b++instance GBlock f => GBlock (M1 S c f) where+  type GPackedSize (M1 S c f) = GPackedSize f+  galignment140 _ = galignment140 (Proxy :: Proxy f)+  galignment430 _ = galignment430 (Proxy :: Proxy f)+  gsizeOf140    _ = gsizeOf140 (Proxy :: Proxy f)+  gsizeOf430    _ = gsizeOf430 (Proxy :: Proxy f)+  gsizeOfPacked _ = gsizeOfPacked (Proxy :: Proxy f)+  gread140 p o = M1 <$> gread140 p o+  gread430 p o = M1 <$> gread430 p o+  greadPacked p o = M1 <$> greadPacked p o+  gwrite140 p o (M1 a) = gwrite140 p o a+  gwrite430 p o (M1 a) = gwrite430 p o a+  gwritePacked p o (M1 a) = gwritePacked p o a++instance GBlock f => GBlock (M1 C c f) where+  type GPackedSize (M1 C c f) = GPackedSize f+  galignment140 _ = lcm 16 $ galignment140 (Proxy :: Proxy f) -- std140 rule 9+  galignment430 _ = galignment430 (Proxy :: Proxy f) -- std140 rule 9, relaxed by std430+  gsizeOf140    _ = roundUp (gsizeOf140 (Proxy :: Proxy f)) (galignment140 (Proxy :: Proxy f)) -- std140 rule 9+  gsizeOf430    _ = roundUp (gsizeOf430 (Proxy :: Proxy f)) (galignment430 (Proxy :: Proxy f)) -- std140 rule 9, relaxed by std430+  gsizeOfPacked _ = gsizeOfPacked (Proxy :: Proxy f)+  gread140 p o = M1 <$> gread140 p o+  gread430 p o = M1 <$> gread430 p o+  greadPacked p o = M1 <$> greadPacked p o+  gwrite140 p o (M1 a) = gwrite140 p o a+  gwrite430 p o (M1 a) = gwrite430 p o a+  gwritePacked p o (M1 a) = gwritePacked p o a++instance GBlock f => GBlock (M1 D c f) where+  type GPackedSize (M1 D c f) = GPackedSize f+  galignment140 _ = galignment140 (Proxy :: Proxy f)+  galignment430 _ = galignment430 (Proxy :: Proxy f)+  gsizeOf140    _ = gsizeOf140 (Proxy :: Proxy f)+  gsizeOf430    _ = gsizeOf430 (Proxy :: Proxy f)+  gsizeOfPacked _ = gsizeOfPacked (Proxy :: Proxy f)+  gread140 p o = M1 <$> gread140 p o+  gread430 p o = M1 <$> gread430 p o+  greadPacked p o = M1 <$> greadPacked p o+  gwrite140 p o (M1 a) = gwrite140 p o a+  gwrite430 p o (M1 a) = gwrite430 p o a+  gwritePacked p o (M1 a) = gwritePacked p o a++instance Block c => GBlock (K1 i c) where+  type GPackedSize (K1 i c) = PackedSize c+  galignment140 _ = alignment140 (Proxy :: Proxy c)+  galignment430 _ = alignment430 (Proxy :: Proxy c)+  gsizeOf140    _ = sizeOf140 (Proxy :: Proxy c)+  gsizeOf430    _ = sizeOf430 (Proxy :: Proxy c)+  gsizeOfPacked _ = sizeOfPacked (Proxy :: Proxy c)+  gread140 p o = K1 <$> read140 p (Diff o)+  gread430 p o = K1 <$> read430 p (Diff o)+  greadPacked p o = K1 <$> readPacked p (Diff o)+  gwrite140 p o (K1 a) = write140 p (Diff o) a+  gwrite430 p o (K1 a) = write430 p (Diff o) a+  gwritePacked p o (K1 a) = writePacked p (Diff o) a++toBool :: Int32 -> Bool+toBool 0 = False+toBool _ = True++fromBool :: Bool -> Int32+fromBool False = 0+fromBool True = 1++instance Block Bool where+  type PackedSize Bool = 4+  alignment140 _ = 4+  sizeOf140 = sizeOfPacked+  alignment430 = alignment140+  sizeOf430 = sizeOf140+  isStruct _ = False+  read140 p (Diff d) = fmap toBool $ peekDiffOff p (Diff d)+  write140 p (Diff d) = pokeDiffOff p (Diff d) . fromBool+  read430 = read140+  write430 = write140+  readPacked = read140+  writePacked = write140+  {-# INLINE alignment140 #-}+  {-# INLINE sizeOf140 #-}+  {-# INLINE alignment430 #-}+  {-# INLINE sizeOf430 #-}+  {-# INLINE isStruct #-}+  {-# INLINE read140 #-}+  {-# INLINE write140 #-}+  {-# INLINE read430 #-}+  {-# INLINE write430 #-}+  {-# INLINE readPacked #-}+  {-# INLINE writePacked #-}++instance Block Int32 where+  type PackedSize Int32 = 4+  alignment140 _ = 4+  sizeOf140 = sizeOfPacked+  alignment430 = alignment140+  sizeOf430 = sizeOf140+  isStruct _ = False+  read140 = peekDiffOff+  write140 = pokeDiffOff+  read430 = read140+  write430 = write140+  readPacked = read140+  writePacked = write140+  {-# INLINE alignment140 #-}+  {-# INLINE sizeOf140 #-}+  {-# INLINE alignment430 #-}+  {-# INLINE sizeOf430 #-}+  {-# INLINE isStruct #-}+  {-# INLINE read140 #-}+  {-# INLINE write140 #-}+  {-# INLINE read430 #-}+  {-# INLINE write430 #-}+  {-# INLINE readPacked #-}+  {-# INLINE writePacked #-}++instance Block Word32 where+  type PackedSize Word32 = 4+  alignment140 _ = 4+  sizeOf140 = sizeOfPacked+  alignment430 = alignment140+  sizeOf430 = sizeOf140+  isStruct _ = False+  read140 = peekDiffOff+  write140 = pokeDiffOff+  read430 = read140+  write430 = write140+  readPacked = read140+  writePacked = write140+  {-# INLINE alignment140 #-}+  {-# INLINE sizeOf140 #-}+  {-# INLINE alignment430 #-}+  {-# INLINE sizeOf430 #-}+  {-# INLINE isStruct #-}+  {-# INLINE read140 #-}+  {-# INLINE write140 #-}+  {-# INLINE read430 #-}+  {-# INLINE write430 #-}+  {-# INLINE readPacked #-}+  {-# INLINE writePacked #-}++instance Block Float where+  type PackedSize Float = 4+  alignment140 _ = 4+  sizeOf140 = sizeOfPacked+  alignment430 = alignment140+  sizeOf430 = sizeOf140+  isStruct _ = False+  read140 = peekDiffOff+  write140 = pokeDiffOff+  read430 = read140+  write430 = write140+  readPacked = read140+  writePacked = write140+  {-# INLINE alignment140 #-}+  {-# INLINE sizeOf140 #-}+  {-# INLINE alignment430 #-}+  {-# INLINE sizeOf430 #-}+  {-# INLINE isStruct #-}+  {-# INLINE read140 #-}+  {-# INLINE write140 #-}+  {-# INLINE read430 #-}+  {-# INLINE write430 #-}+  {-# INLINE readPacked #-}+  {-# INLINE writePacked #-}++instance Block Double where+  type PackedSize Double = 8+  alignment140 _ = 8+  sizeOf140 = sizeOfPacked+  alignment430 = alignment140+  sizeOf430 = sizeOf140+  isStruct _ = False+  read140 = peekDiffOff+  write140 = pokeDiffOff+  read430 = read140+  write430 = write140+  readPacked  = read140+  writePacked = write140+  {-# INLINE alignment140 #-}+  {-# INLINE sizeOf140 #-}+  {-# INLINE alignment430 #-}+  {-# INLINE sizeOf430 #-}+  {-# INLINE isStruct #-}+  {-# INLINE read140 #-}+  {-# INLINE write140 #-}+  {-# INLINE read430 #-}+  {-# INLINE write430 #-}+  {-# INLINE readPacked #-}+  {-# INLINE writePacked #-}++-- -- | Can be used for fixed-sized arrays+-- instance (Dim n, Block a) => Block (V n a) where+--   isStruct _ = isStruct (Proxy :: Proxy a)+--   alignment140 _+--     | isStruct (Proxy :: Proxy a) = lcm 16 n -- std140 rule 9+--     | otherwise = n+--     where n = alignment140 (Proxy :: Proxy a)+--   alignment430 _ = alignment430 (Proxy :: Proxy a)+--   sizeOf140 _ = roundUp (sizeOf140 (Proxy :: Proxy a)) (alignment140 (Proxy :: Proxy a)) * reflectDim (Proxy :: Proxy n)+--   sizeOf430 _ = roundUp (sizeOf430 (Proxy :: Proxy a)) (alignment430 (Proxy :: Proxy a)) * reflectDim (Proxy :: Proxy n)+--   read140 p (Diff o) = liftIO $ sequence $ tabulate \i -> read140 p $ Diff (o + i*d) where+--     d = roundUp (sizeOf140 (Proxy :: Proxy a)) (alignment140 (Proxy :: Proxy a))+--   write140 p (Diff o) v = liftIO $ iforM_ v \i -> write140 p (Diff (o + i*d)) where+--     d = roundUp (sizeOf140 (Proxy :: Proxy a)) (alignment140 (Proxy :: Proxy a))+--   read430 p (Diff o) = liftIO $ sequence $ tabulate \i -> read430 p $ Diff (o + i*d) where+--     d = roundUp (sizeOf430 (Proxy :: Proxy a)) (alignment430 (Proxy :: Proxy a))+--   write430 p (Diff o) v = liftIO $ iforM_ v \i -> write430 p (Diff (o + i*d)) where+--     d = roundUp (sizeOf430 (Proxy :: Proxy a)) (alignment430 (Proxy :: Proxy a))++-- | @roundUp k n@ rounds up k up to an integral multiple of n+roundUp :: Int -> Int -> Int+roundUp k n = k + mod (n - k) n+{-# INLINEABLE roundUp #-}++instance (Block a, Block b, KnownNat (PackedSize a + PackedSize b)) => Block (a, b)+instance (Block a, Block b, Block c, KnownNat (PackedSize a + (PackedSize b + PackedSize c))) => Block (a, b, c)
+ test/Spec.hs view
@@ -0,0 +1,2 @@+{-# OPTIONS_GHC -F -pgmF tasty-discover #-}+{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}
+ test/Spec/Packed.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE TypeFamilies #-}++module Spec.Packed where++import Graphics.Gl.Block (Block, Packed(..))+import Graphics.Gl.Block qualified as Block++import GHC.Generics (Generic)+import Foreign (Storable)+import Foreign qualified+import Data.Proxy (Proxy(..))++import Test.Tasty.HUnit++data SomeStruct = SomeStruct+  { wat :: Bool -- 4+  , this :: Double -- 8+  , that :: Float -- 4+  , those :: (Float, Float) -- 4 + 4+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass Block+  deriving Storable via (Packed SomeStruct)++someStruct :: SomeStruct+someStruct = SomeStruct+  { wat = True+  , this = 2 * pi+  , that = pi+  , those = (1/60, -1e6)+  }++unit_packedSize :: (Block.PackedSize SomeStruct ~ 24) => Assertion+unit_packedSize = Block.sizeOfPacked (Proxy :: Proxy SomeStruct) @?= 24++unit_peekPoked :: Assertion+unit_peekPoked = do+  tripped <- Foreign.with someStruct Foreign.peek+  tripped @?= someStruct