Skip to content

Commit 450b59f

Browse files
authored
Merge pull request #21 from alt-romes/wip/romes/storable-vecs
Update createMesh functions to take Storable.Vector
2 parents d35b766 + 1477363 commit 450b59f

12 files changed

Lines changed: 79 additions & 90 deletions

File tree

examples/planets-core/Planet.hs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,15 @@ import Ghengin.Core.Render.Pipeline
1313
import Ghengin.Core.Render.Property
1414
import Ghengin.Core.Material
1515
import Ghengin.Core.Type.Compatible
16-
import Data.List (foldl')
1716
import Ghengin.Core.Log
1817

19-
import qualified Data.List.NonEmpty as NE
2018
import qualified Data.Vector as V
19+
import qualified Data.Vector.Storable as SV
2120

2221
import Geomancy.Transform
2322

2423
import Ghengin.Core
2524
import Ghengin.Core.Mesh
26-
import Geomancy (VectorSpace(..))
2725
import Geomancy.Vec3
2826

2927
import Ghengin.Geometry.Sphere
@@ -83,13 +81,13 @@ newPlanetMesh rp Planet{..} = enterD "newPlanetMesh" $ Linear.do
8381
let UnitSphere us is = newUnitSphere resolution
8482

8583
(planetPs, elevations)
86-
= P.unzip $ P.map (\(p :&: _) -> pointOnPlanet planetShape p) us
87-
planetNs = V.toList $ computeNormals (V.fromList (P.map fromIntegral is)) (V.fromList planetPs)
88-
planetVs = P.zipWith (:&:) planetPs planetNs
84+
= V.unzip $ V.map (\(p :&: _) -> pointOnPlanet planetShape p) (V.convert us)
85+
planetNs = computeNormals (SV.map fromIntegral is) (V.convert planetPs)
86+
planetVs = SV.zipWith (:&:) (V.convert planetPs) planetNs
8987

9088
minmax = MinMax (P.minimum elevations) (P.maximum elevations)
9189

92-
in (, Ur minmax) <$> (createMeshWithIxs rp (DynamicBinding (Ur mempty) :## GHNil) planetVs is )
90+
in (, Ur minmax) <$> (createMeshWithIxsSV rp (DynamicBinding (Ur mempty) :## GHNil) planetVs is )
9391

9492
--------------------------------------------------------------------------------
9593
-- * Material

ghengin-core/ghengin-core-indep/Ghengin/Core/Mesh/Vertex.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ infixr 6 :&
5050
pattern (:&:) :: v -> v' -> Vertex '[v, v']
5151
pattern (:&:) a b = a :& Sin b
5252
infixr 6 :&:
53+
{-# COMPLETE (:&:) #-}
5354

5455
-- It is kind of precarious to copy over the generic implementation here.
5556
-- Would be better to implement Generic for Vertex, and derive generically Block.

ghengin-core/ghengin-core-indep/Ghengin/Core/Prelude.hs

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ module Ghengin.Core.Prelude
2727

2828
-- vector
2929
, V.Vector
30+
, toSV
31+
, SVector
3032

3133
-- reference-counting
3234
, Forgettable, Shareable
@@ -80,6 +82,7 @@ import qualified Data.Set as S
8082
import qualified Data.Map as M
8183
import qualified Data.List.NonEmpty as NE
8284
import qualified Data.Vector as V
85+
import qualified Data.Vector.Storable as SV
8386
import qualified Data.V.Linear.Internal as VL
8487
import qualified Data.V.Linear.Internal.Instances ()
8588

@@ -95,6 +98,18 @@ import Data.Linear.Alias as Alias
9598

9699
import qualified Unsafe.Linear as Unsafe
97100

101+
--------------------------------------------------------------------------------
102+
-- * Storable vector utils
103+
--------------------------------------------------------------------------------
104+
105+
type SVector = SV.Vector
106+
107+
-- | Convert a list in a storable vector. Useful when creating meshes from lists
108+
toSV :: SV.Storable a => [a] -> SV.Vector a
109+
toSV = SV.fromList
110+
111+
--------------------------------------------------------------------------------
112+
98113
-- Worry about performance of doing things safely later.
99114
-- For now, simply strive for correctness.
100115

@@ -104,21 +119,17 @@ import qualified Unsafe.Linear as Unsafe
104119
{-# INLINE (<$$>) #-}
105120
(<$$>) = (Prelude.<$>)
106121

122+
--------------------------------------------------------------------------------
123+
-- * Generic HList (aka "Product"), but this one is linear!
124+
--------------------------------------------------------------------------------
125+
107126
-- | Generic HList
108127
-- Perhaps move to its own module?
109128
data GHList c xs where
110129
GHNil :: GHList c '[]
111130
(:##) :: c a GHList c as GHList c (a ': as)
112131
infixr 6 :##
113132

114-
{-
115-
Note [Coerce HList to List]
116-
~~~~~~~~~~~~~~~~~~~~~~~~~~~
117-
They have the same representation ^_^, so unsafeCoerce is safe ^_^
118-
-}
119-
120-
-- GHList instances
121-
122133
instance Consumable (GHList c '[]) where
123134
consume GHNil = ()
124135

@@ -144,12 +155,7 @@ instance (∀ a. Shareable m (c a)) => Shareable m (GHList c as) where
144155
(as1, as2) <- Alias.share as
145156
pure (a1:##as1, a2:##as2)
146157

147-
-- TODO: Some special linear lenses to use propertyAt ... import Control.Lens ((^.), Lens', lens)
148-
-- ROMES:TODO: For the lens to be used as a getter, I think we will need this definition of functor rather than the control one.
149-
-- Otherwise, I think we can assume the lens is always over something which is
150-
-- Consumable, (we only ever deal with properties which are consumable, btut I
151-
-- suppose we could have properties which aren't, and are always updated).
152-
-- Perhaps just the setter lens could be over the thing if it is Consumable
158+
--------------------------------------------------------------------------------
153159

154160
(=<<) :: Monad m => (a m b) m a m b
155161
f =<< x = x >>= f
@@ -180,19 +186,6 @@ vzipWith f (VL.V va) (VL.V vb) = VL.V (Unsafe.toLinear3 V.zipWith (Unsafe.toLine
180186
v2vec :: VL.V n a V.Vector a
181187
v2vec (VL.V v) = v
182188

183-
184-
-- | Shame, traversable should really be polimorphic over %p.
185-
-- I'm afraid it could break some instances, so I'll just specialize this to V
186-
-- threadThrough :: Data.Linear.Traversable t => (a %p -> c ⊸ (b,c)) -> c ⊸ t a %p -> (t b, c)
187-
--
188-
-- Wait, isn't this (linear) mapAccumL? It already exists! Only it's linear on
189-
-- the first argument too. We can work around it with dup2 @Int in our case
190-
--
191-
-- We comment it out. Delete in the next commit.
192-
-- threadThrough :: KnownNat n => (a %p -> c ⊸ (b,c)) -> VL.V n a %p -> c ⊸ (VL.V n b, c)
193-
-- threadThrough f va c = runState (vtraverse (\a -> StateT (pure . f a)) va) c
194-
195-
196189
l2vec :: [a] V.Vector a
197190
l2vec = Unsafe.toLinear V.fromList
198191

ghengin-core/ghengin-core/Ghengin/Core/Mesh.hs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
module Ghengin.Core.Mesh
44
( Mesh(..) -- Export these from an Internals module, not from here
55
, Some(..)
6+
67
, createMesh
78
, createMeshWithIxs
8-
-- , calculateFlatNormals
9-
-- , calculateSmoothNormals
9+
10+
, createMeshSV
11+
, createMeshWithIxsSV
12+
1013
, freeMesh
1114

1215
, meshId
@@ -15,7 +18,6 @@ module Ghengin.Core.Mesh
1518
, module Ghengin.Core.Mesh.Vertex
1619
) where
1720

18-
import GHC.Stack
1921
import Ghengin.Core.Prelude as Linear
2022
import Data.Unique
2123

@@ -40,17 +42,6 @@ import qualified Data.IntMap.Strict as IM
4042

4143
import qualified Data.Linear.Alias as Alias
4244

43-
{-
44-
Note [Meshes]
45-
~~~~~~~~~~~~~
46-
47-
OUTDATED!
48-
49-
All meshes are freed when the window is closed. However, if you change/discard a
50-
mesh during the game you must free it explicitly. TODO: Enforce it somehow: linear types+reference counting
51-
52-
-}
53-
5445
type Mesh :: [Type] -- ^ Vertex attributes
5546
-> [Type] -- ^ Mesh properties
5647
-> Type
@@ -126,11 +117,18 @@ createMesh :: (CompatibleMesh props π, CompatibleVertex ts π, Storable (Vertex
126117
-- ^ The render pipeline
127118
PropertyBindings props
128119
-- ^ The 'PropertyBindings' for the properties of this mesh (the second type argument to 'Mesh')
129-
[Vertex ts] -- TODO: Use Vector
120+
[Vertex ts]
130121
-- ^ Vertices
131122
-> Renderer (Mesh ts props, RenderPipeline π bs)
132-
createMesh (RenderProperty pr rps) props0 vs = createMesh rps props0 vs >>= \case (m, rp) -> pure (m, RenderProperty pr rp)
133-
createMesh (RenderPipeline gpip rpass (rdset, rres, (Ur bmap), dpool0) shaders uq) props0 (SV.fromList -> vs) = enterD "createMesh" Linear.do
123+
createMesh a b c = createMeshSV a b (SV.fromList c)
124+
125+
-- | Like 'createMesh', but takes a storable vector directly rather than a list.
126+
createMeshSV
127+
:: (CompatibleMesh props π, CompatibleVertex ts π, Storable (Vertex ts))
128+
=> RenderPipeline π bs PropertyBindings props SV.Vector (Vertex ts)
129+
-> Renderer (Mesh ts props, RenderPipeline π bs)
130+
createMeshSV (RenderProperty pr rps) props0 vs = createMeshSV rps props0 vs >>= \case (m, rp) -> pure (m, RenderProperty pr rp)
131+
createMeshSV (RenderPipeline gpip rpass (rdset, rres, (Ur bmap), dpool0) shaders uq) props0 vs = enterD "createMesh" Linear.do
134132
Ur uniq <- liftSystemIOU newUnique
135133
vertexBuffer <- createVertexBuffer vs
136134

@@ -142,16 +140,23 @@ createMesh (RenderPipeline gpip rpass (rdset, rres, (Ur bmap), dpool0) shaders u
142140

143141
-- | Like 'createMesh', but create the mesh using a vertex buffer created from
144142
-- the vertices and an indexbuffer created from the indices
145-
createMeshWithIxs :: HasCallStack => (CompatibleMesh props π, CompatibleVertex ts π, Storable (Vertex ts))
143+
createMeshWithIxs :: (CompatibleMesh props π, CompatibleVertex ts π, Storable (Vertex ts))
146144
=> RenderPipeline π bs
147145
PropertyBindings props
148-
[Vertex ts] -- TODO: Use Vector
146+
[Vertex ts]
149147
-- ^ Vertices
150-
-> [Int32] -- TODO: Use Vector
148+
-> [Int32]
151149
-- ^ Indices
152150
-> Renderer (Mesh ts props, RenderPipeline π bs)
153-
createMeshWithIxs (RenderProperty pr rps) props0 vs ixs = createMeshWithIxs rps props0 vs ixs >>= \case (m, rp) -> pure (m, RenderProperty pr rp)
154-
createMeshWithIxs (RenderPipeline gpip rpass (rdset, rres, (Ur bmap), dpool0) shaders uq) props0 (SV.fromList -> vertices) (SV.fromList -> ixs) = enterD "createMeshWithIxs" Linear.do
151+
createMeshWithIxs a b c d = createMeshWithIxsSV a b (SV.fromList c) (SV.fromList d)
152+
153+
createMeshWithIxsSV
154+
:: (CompatibleMesh props π, CompatibleVertex ts π, Storable (Vertex ts))
155+
=> RenderPipeline π bs PropertyBindings props
156+
SV.Vector (Vertex ts) -> SV.Vector Int32
157+
-> Renderer (Mesh ts props, RenderPipeline π bs)
158+
createMeshWithIxsSV (RenderProperty pr rps) props0 vs ixs = createMeshWithIxsSV rps props0 vs ixs >>= \case (m, rp) -> pure (m, RenderProperty pr rp)
159+
createMeshWithIxsSV (RenderPipeline gpip rpass (rdset, rres, (Ur bmap), dpool0) shaders uq) props0 vertices ixs = enterD "createMeshWithIxs" Linear.do
155160
Ur uniq <- liftSystemIOU newUnique
156161
vertexBuffer <- createVertexBuffer vertices
157162
indexBuffer <- createIndex32Buffer ixs
@@ -199,5 +204,3 @@ freeMesh mesh = Linear.do
199204
Alias.forget ds >> destroyDeviceLocalBuffer vb >> destroyDeviceLocalBuffer ib
200205
MeshProperty prop xs ->
201206
Alias.forget prop >> freeMesh xs
202-
203-
-- TODO: Nub vertices (make indexes pointing at different vertices which are equal to point at the same vertice and remove the other)?

ghengin-core/ghengin-core/Ghengin/Core/Render/Property.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import Ghengin.Core.Render
2424
import Ghengin.Core.Type.Utils
2525
import qualified Data.Linear.Alias as Alias
2626
import qualified Data.IntMap.Linear as IM
27-
import Ghengin.Core.Renderer.DescriptorSet (BindingsMap)
2827
import Vulkan.Linear ()
2928

3029
import qualified Vulkan as Vk -- TODO: Core shouldn't depend on any specific renderer implementation external to Core

ghengin-core/ghengin-core/Ghengin/Core/Renderer/Buffer.hsig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Data.Word (Word, Word32)
44
import Data.Int (Int32)
55
import qualified Data.Vector.Storable as SV
66
-- TODO:Exchange Vk specific types to renderer agnostic enumerations
7-
import qualified Vulkan as Vk (DescriptorType, BufferUsageFlags)
7+
import qualified Vulkan as Vk (BufferUsageFlags)
88

99
import Ghengin.Core.Mesh.Vertex
1010
import Ghengin.Core.Renderer.Kernel

ghengin-core/ghengin-core/Ghengin/Core/Renderer/Command.hsig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Ghengin.Core.Log
55
import qualified Data.Functor.Linear as Data
66
import qualified Data.Linear.Alias as Alias
77

8-
import Ghengin.Core.Renderer.Kernel
98
import Ghengin.Core.Renderer.Pipeline
109
import Ghengin.Core.Renderer.Buffer
1110
import Ghengin.Core.Renderer.DescriptorSet

ghengin-core/ghengin-vulkan/Ghengin/Vulkan/Renderer/Buffer.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import qualified Prelude
1111

1212
import Foreign.Storable
1313

14-
import Data.Int
1514
import qualified Data.Linear.Alias as Alias
1615

1716
import qualified Data.Vector.Storable as SV

ghengin-core/ghengin-vulkan/Ghengin/Vulkan/Renderer/DescriptorSet.hs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ import Ghengin.Core.Shader.Pipeline
6060

6161
import qualified Data.Linear.Alias as Alias
6262

63-
import qualified FIR.Layout
64-
6563
-- The descriptor set number 0 will be used for engine-global resources, and bound
6664
-- once per frame. The descriptor set number 1 will be used for per-pass
6765
-- resources, and bound once per pass. The descriptor set number 2 will be used

ghengin/ghengin-geometry/Ghengin/Geometry/Normals.hs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import Geomancy
1717
import qualified Geomancy.Vec3 as Vec3
1818

1919
import Prelude.Linear
20-
import Data.Vector (Vector)
2120
import qualified Data.Vector as V
21+
import qualified Data.Vector.Storable as SV
2222
import qualified Data.Array.Mutable.Linear as Array
2323

2424
{- | Compute the smooth normal vectors of a mesh surface.
@@ -58,28 +58,32 @@ have all allocated buffers automatically initialized to zero. In that case, if
5858
this is the first and only normalization for a given mesh, you may skip the
5959
first loop on the function too of course.
6060
-}
61-
computeNormals :: Vector Int -- ^ Every three indices into the vertices array forms a face
62-
-> Vector Vec3 -- ^ The position of every vertex
63-
-> Vector Vec3 -- ^ The normal vector for each vertex
61+
computeNormals :: SV.Vector Int -- ^ Every three indices into the vertices array forms a face
62+
-> SV.Vector Vec3 -- ^ The position of every vertex
63+
-> SV.Vector Vec3 -- ^ The normal vector for each vertex
6464
computeNormals ixs vs =
65-
unur $ Array.alloc (V.length vs) (vec3 0 0 0) $ \arr0 ->
65+
V.convert $ unur $ -- todo: freeze variant which constructs SV.Vector?
66+
Array.alloc (SV.length vs) (vec3 0 0 0) $ \arr0 ->
6667
Array.freeze $
6768
Array.map Vec3.normalize $
6869
foldl' (\arr (Ur i) -> let
6970
ia = ixs ! (i)
7071
ib = ixs ! (i+1)
7172
ic = ixs ! (i+2)
72-
e1 = vs ! ia ^-^ vs ! ib
73-
e2 = vs ! ic ^-^ vs ! ib
73+
e1 = ((vs ! ia) :: Vec3) ^-^ (vs ! ib)
74+
e2 = (vs ! ic) ^-^ (vs ! ib)
7475
no = Vec3.cross e1 e2
7576
in arr & ia += no
7677
& ib += no
7778
& ic += no
78-
) arr0 [Ur i | i <- [0,3..V.length ixs - 1]]
79+
) arr0 [Ur i | i <- [0,3..SV.length ixs - 1]]
7980
where
80-
(!) = V.unsafeIndex
81+
(!) :: SV.Storable x => SV.Vector x -> Int -> x
82+
(!) = SV.unsafeIndex
8183

8284
(+=) :: Int -> Vec3 -> Array.Array Vec3 %1 -> Array.Array Vec3
8385
(+=) i new arr0 = case Array.unsafeGet i arr0 of
8486
(Ur exists, arr1) -> Array.unsafeSet i (new ^+^ exists) arr1
8587

88+
-- TODO: Try backpermute
89+

0 commit comments

Comments
 (0)