keid-core 0.1.7.2 → 0.1.8.0
raw patch · 51 files changed
+1969/−804 lines, 51 filesdep ~ktx-codecPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: ktx-codec
API changes (from Hackage documentation)
- Engine.Vulkan.Pipeline: destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Pipeline dsl vertices instances -> io ()
- Engine.Vulkan.Pipeline.Compute: destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Pipeline dsl vertices instances -> io ()
- Engine.Vulkan.Pipeline.Graphics: destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Pipeline dsl vertices instances -> io ()
- Engine.Vulkan.Types: type DsBindings = [(DescriptorSetLayoutBinding, DescriptorBindingFlags)]
- Engine.Worker: class HasWorker a
- Engine.Worker: getWorker :: HasWorker a => a -> ThreadId
- Engine.Worker: instance Engine.Worker.HasWorker (Engine.Worker.Cell i o)
- Engine.Worker: instance Engine.Worker.HasWorker (Engine.Worker.Merge o)
- Engine.Worker: instance Engine.Worker.HasWorker (Engine.Worker.Timed c o)
- Engine.Worker: register :: (MonadResource m, HasWorker process) => process -> m ReleaseKey
- Engine.Worker: registerCollection :: (MonadResource m, HasWorker process, Foldable t) => t process -> m ReleaseKey
- Engine.Worker: registered :: (MonadResource m, HasWorker a) => m a -> m (ReleaseKey, a)
- Engine.Worker: registeredCollection :: (MonadResource m, HasWorker process, Traversable t) => (input -> m process) -> t input -> m (ReleaseKey, t process)
- Render.Samplers: create :: (MonadIO io, HasVulkan context) => context -> ("max anisotropy" ::: Float) -> Params -> io Sampler
- Render.Samplers: destroy :: (MonadIO io, HasVulkan context) => context -> Sampler -> io ()
- Resource.Buffer: destroyAll :: (MonadUnliftIO io, HasVulkan context, Foldable t) => context -> t (Allocated s a) -> io ()
- Resource.DescriptorSet: allocatePool :: (MonadResource m, MonadReader env m, HasVulkan env) => Word32 -> TypeMap Word32 -> m (ReleaseKey, DescriptorPool)
- Resource.DescriptorSet: mkPoolCI :: Word32 -> TypeMap Word32 -> DescriptorPoolCreateInfo '[]
- Resource.DescriptorSet: type TypeMap a = [(DescriptorType, a)]
- Resource.Image: create :: (MonadIO io, HasVulkan ctx) => ctx -> Maybe Text -> ImageAspectFlags -> ("image dimensions" ::: Extent2D) -> ("mip levels" ::: Word32) -> ("stored layers" ::: Word32) -> SampleCountFlagBits -> Format -> ImageUsageFlags -> io AllocatedImage
- Resource.Image: destroy :: (MonadIO io, HasVulkan context) => context -> AllocatedImage -> io ()
- Resource.Texture: allocateCollectionWith :: (MonadResource m, MonadVulkan env m, Traversable t) => TextureLoaderAction src m layers -> t src -> m (ReleaseKey, t (Texture layers))
- Resource.Texture: allocateTextureWith :: (MonadResource m, MonadVulkan env m) => TextureLoaderAction src m layers -> src -> m (ReleaseKey, Texture layers)
- Resource.Texture: destroy :: (MonadIO io, HasVulkan context) => context -> Texture a -> io ()
- Resource.Texture: instance GHC.Show.Show (Resource.Texture.Texture a)
+ Engine.Events.Sink: type MonadSink rs m = (MonadReader (App GlobalHandles rs) m, MonadState rs m, MonadResource m, MonadUnliftIO m)
+ Engine.Frame: [$sel:fMSAA:Frame] :: Frame renderpass pipelines resources -> SampleCountFlagBits
+ Engine.Frame: [$sel:fPresent:Frame] :: Frame renderpass pipelines resources -> Maybe PresentModeKHR
+ Engine.Types: [$sel:fMSAA:Frame] :: Frame renderpass pipelines resources -> SampleCountFlagBits
+ Engine.Types: [$sel:fPresent:Frame] :: Frame renderpass pipelines resources -> Maybe PresentModeKHR
+ Engine.Types: instance Engine.Vulkan.Types.HasSwapchain (Engine.Types.Frame renderpass pipelines resources)
+ Engine.Types: type family HKD f a
+ Engine.Types.Options: [$sel:optionsMsaa:Options] :: Options -> SampleCountFlagBits
+ Engine.Types.Options: [$sel:optionsPresent:Options] :: Options -> Maybe PresentModeKHR
+ Engine.Types.RefCounted: wrapped :: MonadResource m => m (ReleaseKey, a) -> m (RefCounted, a)
+ Engine.Vulkan.Format: class HasVkFormat a
+ Engine.Vulkan.Format: formatSize :: Integral a => Format -> a
+ Engine.Vulkan.Format: genericVkFormat :: forall a. GVkFormat (Rep a) => [Format]
+ Engine.Vulkan.Format: getVkFormat :: (HasVkFormat a, GVkFormat (Rep a)) => [Format]
+ Engine.Vulkan.Format: instance (Engine.Vulkan.Format.GVkFormat l, Engine.Vulkan.Format.GVkFormat r) => Engine.Vulkan.Format.GVkFormat (l GHC.Generics.:*: r)
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.GVkFormat f => Engine.Vulkan.Format.GVkFormat (GHC.Generics.M1 c cb f)
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat ()
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat GHC.Int.Int32
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat GHC.Types.Float
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat GHC.Word.Word32
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.IVec2.IVec2
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.IVec3.IVec3
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.IVec3.Packed
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.IVec4.IVec4
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Mat4.Mat4
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Quaternion.Quaternion
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Transform.Transform
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.UVec2.UVec2
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.UVec3.Packed
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.UVec3.UVec3
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.UVec4.UVec4
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Vec2.Vec2
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Vec3.Packed
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Vec3.Vec3
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat Geomancy.Vec4.Vec4
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat a => Engine.Vulkan.Format.GVkFormat (GHC.Generics.K1 r a)
+ Engine.Vulkan.Format: instance Engine.Vulkan.Format.HasVkFormat v => Engine.Vulkan.Format.HasVkFormat (Geomancy.Point.Point v)
+ Engine.Vulkan.Pipeline: allocateWith :: (MonadVulkan env m, MonadResource m) => m (Pipeline dsl vertices instances) -> m (ReleaseKey, Pipeline dsl vertices instances)
+ Engine.Vulkan.Pipeline.Graphics: class HasVertexInputBindings a
+ Engine.Vulkan.Pipeline.Graphics: instance Engine.Vulkan.Pipeline.Graphics.HasVertexInputBindings ()
+ Engine.Vulkan.Pipeline.Graphics: instance Engine.Vulkan.Pipeline.Graphics.HasVertexInputBindings Geomancy.Transform.Transform
+ Engine.Vulkan.Pipeline.Graphics: instanceFormat :: forall a. HasVkFormat a => VertexInputBinding
+ Engine.Vulkan.Pipeline.Graphics: vertexFormat :: forall a. HasVkFormat a => VertexInputBinding
+ Engine.Vulkan.Pipeline.Graphics: vertexInputBindings :: HasVertexInputBindings a => [VertexInputBinding]
+ Engine.Vulkan.Types: instance Control.Monad.Reader.Class.MonadReader r m => Control.Monad.Reader.Class.MonadReader r (Engine.Vulkan.Types.Bound dsl vertices instances m)
+ Engine.Vulkan.Types: instance Control.Monad.State.Class.MonadState s m => Control.Monad.State.Class.MonadState s (Engine.Vulkan.Types.Bound dsl vertices instances m)
+ Engine.Vulkan.Types: type DsLayoutBindings = [(DescriptorSetLayoutBinding, DescriptorBindingFlags)]
+ Engine.Worker: [$sel:mKey:Merge] :: Merge o -> ReleaseKey
+ Engine.Worker: [$sel:tKey:Timed] :: Timed config output -> ReleaseKey
+ Render.Code: targetEnv :: IsString a => a
+ Render.Pass.Offscreen: [$sel:sColorLayout:Settings] :: Settings -> Maybe ImageLayout
+ Render.Pass.Offscreen: [$sel:sDepthLayout:Settings] :: Settings -> Maybe ImageLayout
+ Render.Samplers: allocateFrom :: (PokeChain e, Extendss SamplerCreateInfo e, MonadVulkan env io) => ("max anisotropy" ::: Float) -> Params -> (SamplerCreateInfo '[] -> SamplerCreateInfo e) -> ResourceT io Sampler
+ Render.Samplers: createInfo :: ("max anisotropy" ::: Float) -> Filter -> ("max LoD" ::: Float) -> SamplerAddressMode -> SamplerCreateInfo '[]
+ Resource.Buffer: [$sel:aLabel:Allocated] :: Allocated (s :: Store) a -> Maybe Text
+ Resource.Buffer: instance Vulkan.Core10.APIConstants.HasObjectType (Resource.Buffer.Allocated s a)
+ Resource.Buffer: observeCoherentSingle :: (MonadVulkan env m, HasOutput source, GetOutput source ~ output, Storable output) => source -> ObserverCoherent output -> m ()
+ Resource.Buffer: pokeCoherent :: (MonadVulkan env m, Storable a) => Allocated 'Coherent a -> Word32 -> a -> m ()
+ Resource.Buffer: register :: (MonadVulkan env m, MonadResource m) => Allocated stage a -> m ReleaseKey
+ Resource.Image: allocate :: (MonadVulkan env io, MonadResource io) => Maybe Text -> ImageAspectFlags -> ("image dimensions" ::: Extent3D) -> ("mip levels" ::: Word32) -> ("stored layers" ::: Word32) -> SampleCountFlagBits -> Format -> ImageUsageFlags -> io AllocatedImage
+ Resource.Image: allocateDst :: (MonadVulkan env m, MonadResource m) => Queues CommandPool -> Maybe Text -> ("image dimensions" ::: Extent3D) -> ("mip levels" ::: Word32) -> ("stored layers" ::: Word32) -> Format -> m DstImage
+ Resource.Image: allocateView :: (MonadVulkan env m, MonadResource m) => Image -> Format -> ImageSubresourceRange -> m ImageView
+ Resource.Image: copyBufferToDst :: (MonadVulkan env m, Integral deviceSize, Foldable t) => Queues CommandPool -> Buffer -> DstImage -> ("mip offsets" ::: t deviceSize) -> m AllocatedImage
+ Resource.Image: data DstImage
+ Resource.Image: inflateExtent :: Extent2D -> Word32 -> Extent3D
+ Resource.Image: updateFromStorable :: (Storable a, MonadVulkan env m, MonadResource m) => Queues CommandPool -> AllocatedImage -> Vector a -> m AllocatedImage
+ Resource.Model: [$sel:iLabel:Indexed] :: Indexed storage pos attrs -> Maybe Text
+ Resource.Model: class GHasVertexBuffers f
+ Resource.Model: gInstanceCount :: forall a. GHasVertexBuffers f => f a -> Word32
+ Resource.Model: gVertexBuffers :: forall a. GHasVertexBuffers f => f a -> [Buffer]
+ Resource.Model: genericGetInstanceCount :: (Generic a, GHasVertexBuffers (Rep a)) => a -> Word32
+ Resource.Model: genericGetVertexBuffers :: (Generic a, GHasVertexBuffers (Rep a)) => a -> [Buffer]
+ Resource.Model: instance (Engine.Vulkan.Format.HasVkFormat pos, Engine.Vulkan.Format.HasVkFormat attrs) => Engine.Vulkan.Pipeline.Graphics.HasVertexInputBindings (Resource.Model.Vertex pos attrs)
+ Resource.Model: instance (Resource.Model.GHasVertexBuffers l, Resource.Model.GHasVertexBuffers r) => Resource.Model.GHasVertexBuffers (l GHC.Generics.:*: r)
+ Resource.Model: instance Resource.Model.GHasVertexBuffers f => Resource.Model.GHasVertexBuffers (GHC.Generics.M1 c cb f)
+ Resource.Model: instance Resource.Model.HasVertexBuffers a => Resource.Model.GHasVertexBuffers (GHC.Generics.K1 r a)
+ Resource.Model: registerIndexed_ :: (MonadVulkan env m, MonadResource m) => Indexed storage pos attrs -> m ()
+ Resource.Model: type Vertex2d attrs = Vertex Vec2 attrs
+ Resource.Model: type Vertex3d attrs = Vertex Packed attrs
+ Resource.Model.Observer: class UpdateCoherent bufs stores
+ Resource.Model.Observer: class VertexBuffers a
+ Resource.Model.Observer: createInitial :: forall env m. (VertexBuffers a, Generic a, GVertexBuffers (Rep a), MonadVulkan env m) => Int -> Text -> ResourceT m a
+ Resource.Model.Observer: destroyCurrent :: forall env. (VertexBuffers a, Generic a, GVertexBuffers (Rep a), HasVulkan env) => env -> a -> IO ()
+ Resource.Model.Observer: genericCreateInitial :: (Generic a, GVertexBuffers (Rep a), MonadVulkan env m) => Int -> Text -> ResourceT m a
+ Resource.Model.Observer: genericDestroyCurrent :: (Generic a, GVertexBuffers (Rep a), HasVulkan env) => env -> a -> IO ()
+ Resource.Model.Observer: genericUpdateCoherent :: (Generic bufs, Generic stores, GUpdateCoherent (Rep bufs) (Rep stores), MonadVulkan env m) => bufs -> stores -> m bufs
+ Resource.Model.Observer: instance (Resource.Model.Observer.GUpdateCoherent fbl fsl, Resource.Model.Observer.GUpdateCoherent fbr fsr) => Resource.Model.Observer.GUpdateCoherent (fbl GHC.Generics.:*: fbr) (fsl GHC.Generics.:*: fsr)
+ Resource.Model.Observer: instance (Resource.Model.Observer.GVertexBuffers f, GHC.Generics.Selector c) => Resource.Model.Observer.GVertexBuffers (GHC.Generics.M1 GHC.Generics.S c f)
+ Resource.Model.Observer: instance (Resource.Model.Observer.GVertexBuffers l, Resource.Model.Observer.GVertexBuffers r) => Resource.Model.Observer.GVertexBuffers (l GHC.Generics.:*: r)
+ Resource.Model.Observer: instance Foreign.Storable.Storable a => Resource.Model.Observer.UpdateCoherent (Resource.Buffer.Allocated 'Resource.Buffer.Coherent a) (Data.Vector.Storable.Vector a)
+ Resource.Model.Observer: instance Foreign.Storable.Storable a => Resource.Model.Observer.VertexBuffers (Resource.Buffer.Allocated 'Resource.Buffer.Coherent a)
+ Resource.Model.Observer: instance Resource.Model.Observer.GUpdateCoherent fb fs => Resource.Model.Observer.GUpdateCoherent (GHC.Generics.M1 c cb fb) (GHC.Generics.M1 c cs fs)
+ Resource.Model.Observer: instance Resource.Model.Observer.GVertexBuffers f => Resource.Model.Observer.GVertexBuffers (GHC.Generics.C1 c f)
+ Resource.Model.Observer: instance Resource.Model.Observer.GVertexBuffers f => Resource.Model.Observer.GVertexBuffers (GHC.Generics.D1 c f)
+ Resource.Model.Observer: instance Resource.Model.Observer.UpdateCoherent ba sa => Resource.Model.Observer.GUpdateCoherent (GHC.Generics.K1 br ba) (GHC.Generics.K1 sr sa)
+ Resource.Model.Observer: instance Resource.Model.Observer.VertexBuffers a => Resource.Model.Observer.GVertexBuffers (GHC.Generics.K1 r a)
+ Resource.Model.Observer: newCoherent :: (VertexBuffers res, MonadVulkan env m) => Int -> Text -> ResourceT m (ObserverIO res)
+ Resource.Model.Observer: observeCoherent :: (MonadVulkan env m, HasOutput output, UpdateCoherent bufs (GetOutput output)) => output -> ObserverIO bufs -> m ()
+ Resource.Model.Observer: updateCoherent :: forall env m. (UpdateCoherent bufs stores, Generic bufs, Generic stores, GUpdateCoherent (Rep bufs) (Rep stores), MonadVulkan env m) => bufs -> stores -> m bufs
+ Resource.Model.Observer.Example: Example :: HKD f Float -> HKD f Float -> ExampleF f
+ Resource.Model.Observer.Example: [$sel:x:Example] :: ExampleF f -> HKD f Float
+ Resource.Model.Observer.Example: [$sel:y:Example] :: ExampleF f -> HKD f Float
+ Resource.Model.Observer.Example: data ExampleF f
+ Resource.Model.Observer.Example: instance GHC.Generics.Generic (Resource.Model.Observer.Example.ExampleF f)
+ Resource.Model.Observer.Example: instance GHC.Show.Show Resource.Model.Observer.Example.Example
+ Resource.Model.Observer.Example: instance GHC.Show.Show Resource.Model.Observer.Example.ExampleBuffers
+ Resource.Model.Observer.Example: instance GHC.Show.Show Resource.Model.Observer.Example.ExampleStores
+ Resource.Model.Observer.Example: instance Resource.Model.Observer.UpdateCoherent Resource.Model.Observer.Example.ExampleBuffers Resource.Model.Observer.Example.ExampleStores
+ Resource.Model.Observer.Example: instance Resource.Model.Observer.VertexBuffers Resource.Model.Observer.Example.ExampleBuffers
+ Resource.Model.Observer.Example: new :: ResourceT (StageRIO st) (ObserverIO ExampleBuffers)
+ Resource.Model.Observer.Example: type Example = ExampleF Identity
+ Resource.Model.Observer.Example: type ExampleBuffers = ExampleF (Allocated 'Coherent)
+ Resource.Model.Observer.Example: type ExampleStores = ExampleF Vector
+ Resource.Model.Observer.Example: update :: MonadVulkan env m => ExampleBuffers -> ExampleStores -> m ExampleBuffers
+ Resource.Source: instance GHC.Records.HasField "label" Resource.Source.Source (GHC.Maybe.Maybe Data.Text.Internal.Text)
+ Resource.Texture: instance GHC.Records.HasField "size" (Resource.Texture.Texture tag) Geomancy.Vec2.Vec2
+ Resource.Texture: instance GHC.Show.Show (Resource.Texture.Texture tag)
+ Resource.Texture: withSize2d :: Num i => (i -> i -> a) -> Texture tag -> a
+ Resource.Texture: withSize3d :: Num i => (i -> i -> i -> a) -> Texture tag -> a
+ Resource.Texture.Ktx2: load :: (TextureLayers a, MonadVulkan env m, MonadResource m, MonadThrow m, HasLogFunc env, Typeable a, HasCallStack) => Queues CommandPool -> Source -> m (Texture a)
+ Resource.Texture.Ktx2: loadBytes :: (TextureLayers a, MonadVulkan env m, MonadResource m, MonadThrow m, HasLogFunc env) => Maybe Text -> Queues CommandPool -> ByteString -> m (Texture a)
+ Resource.Texture.Ktx2: loadKtx2 :: forall a m env src. (TextureLayers a, MonadVulkan env m, MonadResource m, MonadThrow m, HasLogFunc env, ReadChunk src, ReadLevel src) => Maybe Text -> Queues CommandPool -> Context src -> m (Texture a)
+ Resource.Vulkan.DescriptorLayout: create :: MonadVulkan env m => Vector [(DescriptorSetLayoutBinding, DescriptorBindingFlags)] -> m (Vector DescriptorSetLayout)
+ Resource.Vulkan.DescriptorLayout: forPipeline :: MonadVulkan env m => Vector DescriptorSetLayout -> Vector PushConstantRange -> m PipelineLayout
+ Resource.Vulkan.DescriptorPool: allocate :: (MonadVulkan env m, MonadResource m) => Maybe Text -> Word32 -> [(DescriptorType, Word32)] -> m (ReleaseKey, DescriptorPool)
+ Resource.Vulkan.DescriptorPool: allocateSetsFrom :: MonadVulkan env m => DescriptorPool -> Maybe Text -> Vector DescriptorSetLayout -> m (Vector DescriptorSet)
+ Resource.Vulkan.Named: object :: (MonadVulkan env m, HasObjectType a) => a -> Text -> m ()
+ Resource.Vulkan.Named: objectOrigin :: (MonadVulkan env m, HasObjectType a, HasCallStack) => a -> m ()
- Engine.Camera: spawnOrthoPixelsCentered :: StageRIO env (ProjectionProcess 'Orthographic)
+ Engine.Camera: spawnOrthoPixelsCentered :: (MonadReader (App GlobalHandles st) m, MonadResource m, MonadUnliftIO m) => m (ProjectionProcess 'Orthographic)
- Engine.Camera: spawnPerspective :: StageRIO env (ProjectionProcess 'Perspective)
+ Engine.Camera: spawnPerspective :: (MonadReader (App GlobalHandles st) m, MonadResource m, MonadUnliftIO m) => m (ProjectionProcess 'Perspective)
- Engine.Camera: spawnProjection :: (Extent2D -> ProjectionInput pk -> Transform) -> ProjectionParams pk -> StageRIO env (ProjectionProcess pk)
+ Engine.Camera: spawnProjection :: (MonadReader (App GlobalHandles st) m, MonadResource m, MonadUnliftIO m) => (Extent2D -> ProjectionInput pk -> Transform) -> ProjectionParams pk -> m (ProjectionProcess pk)
- Engine.Camera: spawnProjectionWith :: (Extent2D -> ProjectionInput pk -> Transform) -> ProjectionInput pk -> StageRIO env (ProjectionProcess pk)
+ Engine.Camera: spawnProjectionWith :: (MonadReader (App GlobalHandles st) m, MonadResource m, MonadUnliftIO m) => (Extent2D -> ProjectionInput pk -> Transform) -> ProjectionInput pk -> m (ProjectionProcess pk)
- Engine.Camera.Controls: spawnControls :: ViewProcess -> RIO env ControlsProcess
+ Engine.Camera.Controls: spawnControls :: (MonadResource m, MonadUnliftIO m) => ViewProcess -> m ControlsProcess
- Engine.Camera.Controls: spawnViewOrbital :: ViewOrbitalInput -> RIO env ViewProcess
+ Engine.Camera.Controls: spawnViewOrbital :: (MonadResource m, MonadUnliftIO m) => ViewOrbitalInput -> m ViewProcess
- Engine.Events: Sink :: (event -> StageRIO st ()) -> Sink event st
+ Engine.Events: Sink :: (forall m. MonadSink rs m => event -> m ()) -> Sink event rs
- Engine.Events: [$sel:signal:Sink] :: Sink event st -> event -> StageRIO st ()
+ Engine.Events: [$sel:signal:Sink] :: Sink event rs -> forall m. MonadSink rs m => event -> m ()
- Engine.Events: newtype Sink event st
+ Engine.Events: newtype Sink event rs
- Engine.Events: registerMany :: [StageRIO st ReleaseKey] -> StageRIO st ReleaseKey
+ Engine.Events: registerMany :: MonadResource m => [m ReleaseKey] -> m ReleaseKey
- Engine.Events: spawn :: (event -> StageRIO st ()) -> [Sink event st -> StageRIO st ReleaseKey] -> StageRIO st (ReleaseKey, Sink event st)
+ Engine.Events: spawn :: MonadSink st m => (event -> m ()) -> [Sink event st -> m ReleaseKey] -> m (ReleaseKey, Sink event st)
- Engine.Events.CursorPos: callback :: (HasInput cursor, GetInput cursor ~ Vec2) => cursor -> Sink e rs -> StageRIO rs ReleaseKey
+ Engine.Events.CursorPos: callback :: (MonadSink rs m, HasInput cursor, GetInput cursor ~ Vec2) => cursor -> Sink e st -> m ReleaseKey
- Engine.Events.CursorPos: handler :: (HasInput cursor, GetInput cursor ~ Vec2) => cursor -> Sink e rs -> Callback rs
+ Engine.Events.CursorPos: handler :: (MonadResource m, HasInput cursor, GetInput cursor ~ Vec2) => cursor -> Sink e st -> Callback m
- Engine.Events.CursorPos: spawn :: StageRIO env Process
+ Engine.Events.CursorPos: spawn :: MonadSink rs m => m Process
- Engine.Events.MouseButton: callback :: (HasOutput cursor, GetOutput cursor ~ Vec2) => cursor -> ClickHandler e st -> Sink e st -> StageRIO st ReleaseKey
+ Engine.Events.MouseButton: callback :: (MonadSink rs m, HasOutput cursor, GetOutput cursor ~ Vec2) => cursor -> ClickHandler e st m -> Sink e st -> m ReleaseKey
- Engine.Events.MouseButton: handler :: (HasOutput cursor, GetOutput cursor ~ Vec2) => cursor -> ClickHandler e st -> Sink e st -> (ModifierKeys, MouseButtonState, MouseButton) -> StageRIO st ()
+ Engine.Events.MouseButton: handler :: (MonadSink rs m, HasOutput cursor, GetOutput cursor ~ Vec2) => cursor -> ClickHandler e st m -> Sink e st -> (ModifierKeys, MouseButtonState, MouseButton) -> m ()
- Engine.Events.MouseButton: type ClickHandler e st = Sink e st -> Vec2 -> (ModifierKeys, MouseButtonState, MouseButton) -> StageRIO st ()
+ Engine.Events.MouseButton: type ClickHandler e st m = Sink e st -> Vec2 -> (ModifierKeys, MouseButtonState, MouseButton) -> m ()
- Engine.Events.Sink: Sink :: (event -> StageRIO st ()) -> Sink event st
+ Engine.Events.Sink: Sink :: (forall m. MonadSink rs m => event -> m ()) -> Sink event rs
- Engine.Events.Sink: [$sel:signal:Sink] :: Sink event st -> event -> StageRIO st ()
+ Engine.Events.Sink: [$sel:signal:Sink] :: Sink event rs -> forall m. MonadSink rs m => event -> m ()
- Engine.Events.Sink: newtype Sink event st
+ Engine.Events.Sink: newtype Sink event rs
- Engine.Events.Sink: spawn :: (event -> StageRIO rs ()) -> StageRIO rs (ReleaseKey, Sink event rs)
+ Engine.Events.Sink: spawn :: MonadSink rs m => (event -> m ()) -> m (ReleaseKey, Sink event rs)
- Engine.Frame: Frame :: Word64 -> Window -> SurfaceKHR -> SwapchainResources -> renderpass -> pipelines -> Semaphore -> (RefCounted, InternalState) -> IORef [GPUWork] -> (ReleaseKey, InternalState) -> RecycledResources resources -> Frame renderpass pipelines resources
+ Engine.Frame: Frame :: Word64 -> Window -> SurfaceKHR -> Maybe PresentModeKHR -> SampleCountFlagBits -> SwapchainResources -> renderpass -> pipelines -> Semaphore -> (RefCounted, InternalState) -> IORef [GPUWork] -> (ReleaseKey, InternalState) -> RecycledResources resources -> Frame renderpass pipelines resources
- Engine.SpirV.Reflect: stagesInterfaceMap :: (MonadIO m, Traversable stages) => stages (Maybe Module) -> m (StageInterface stages)
+ Engine.SpirV.Reflect: stagesInterfaceMap :: Traversable stages => stages (Maybe Module) -> StageInterface stages
- Engine.Types: Frame :: Word64 -> Window -> SurfaceKHR -> SwapchainResources -> renderpass -> pipelines -> Semaphore -> (RefCounted, InternalState) -> IORef [GPUWork] -> (ReleaseKey, InternalState) -> RecycledResources resources -> Frame renderpass pipelines resources
+ Engine.Types: Frame :: Word64 -> Window -> SurfaceKHR -> Maybe PresentModeKHR -> SampleCountFlagBits -> SwapchainResources -> renderpass -> pipelines -> Semaphore -> (RefCounted, InternalState) -> IORef [GPUWork] -> (ReleaseKey, InternalState) -> RecycledResources resources -> Frame renderpass pipelines resources
- Engine.Types: askScreenVar :: StageRIO env (Var Extent2D)
+ Engine.Types: askScreenVar :: MonadReader (App GlobalHandles st) m => m (Var Extent2D)
- Engine.Types.Options: Options :: Bool -> Maybe Int -> Bool -> Natural -> Maybe (Int, Int) -> Maybe Int -> Options
+ Engine.Types.Options: Options :: Bool -> Maybe Int -> Maybe PresentModeKHR -> SampleCountFlagBits -> Bool -> Natural -> Maybe (Int, Int) -> Maybe Int -> Options
- Engine.UI.Layout: fitPlaceAbs :: (MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box) => Alignment -> ("dimensions" ::: Vec2) -> parent -> m BoxProcess
+ Engine.UI.Layout: fitPlaceAbs :: (MonadResource m, MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box) => Alignment -> ("dimensions" ::: Vec2) -> parent -> m BoxProcess
- Engine.UI.Layout: hSplitRel :: (MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, HasOutput proportion, GetOutput proportion ~ Float) => parent -> proportion -> m (BoxProcess, BoxProcess)
+ Engine.UI.Layout: hSplitRel :: (MonadResource m, MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, HasOutput proportion, GetOutput proportion ~ Float) => parent -> proportion -> m (BoxProcess, BoxProcess)
- Engine.UI.Layout: padAbs :: (MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, HasOutput padding, GetOutput padding ~ Vec4) => parent -> padding -> m BoxProcess
+ Engine.UI.Layout: padAbs :: (MonadResource m, MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, HasOutput padding, GetOutput padding ~ Vec4) => parent -> padding -> m BoxProcess
- Engine.UI.Layout: splitsRelStatic :: (MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, Traversable t) => ((Float, Float) -> Vec4) -> parent -> t Float -> m (t BoxProcess)
+ Engine.UI.Layout: splitsRelStatic :: (MonadResource m, MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, Traversable t) => ((Float, Float) -> Vec4) -> parent -> t Float -> m (t BoxProcess)
- Engine.UI.Layout: trackScreen :: StageRIO st BoxProcess
+ Engine.UI.Layout: trackScreen :: (MonadReader (App GlobalHandles st) m, MonadResource m, MonadUnliftIO m) => m BoxProcess
- Engine.UI.Layout: vSplitRel :: (MonadUnliftIO m, HasOutput parent, GetOutput parent ~ Box, HasOutput proportion, GetOutput proportion ~ Float) => parent -> proportion -> m (BoxProcess, BoxProcess)
+ Engine.UI.Layout: vSplitRel :: (MonadUnliftIO m, MonadResource m, HasOutput parent, GetOutput parent ~ Box, HasOutput proportion, GetOutput proportion ~ Float) => parent -> proportion -> m (BoxProcess, BoxProcess)
- Engine.Vulkan.Pipeline.Compute: Config :: ByteString -> Tagged dsl [DsBindings] -> Vector PushConstantRange -> spec -> Config (dsl :: [Type]) spec
+ Engine.Vulkan.Pipeline.Compute: Config :: ByteString -> Tagged dsl [DsLayoutBindings] -> Vector PushConstantRange -> spec -> Config (dsl :: [Type]) spec
- Engine.Vulkan.Pipeline.Compute: [$sel:cDescLayouts:Config] :: Config (dsl :: [Type]) spec -> Tagged dsl [DsBindings]
+ Engine.Vulkan.Pipeline.Compute: [$sel:cDescLayouts:Config] :: Config (dsl :: [Type]) spec -> Tagged dsl [DsLayoutBindings]
- Engine.Vulkan.Pipeline.Compute: create :: (HasVulkan ctx, MonadUnliftIO m, Specialization spec) => ctx -> Config dsl spec -> m (Pipeline dsl Compute Compute)
+ Engine.Vulkan.Pipeline.Compute: create :: (MonadVulkan env io, Specialization spec, HasCallStack) => Config dsl spec -> io (Pipeline dsl Compute Compute)
- Engine.Vulkan.Pipeline.External: observeCompute :: (HasOutput output, GetOutput output ~ config, Specialization spec, config ~ Configure pipeline spec, pipeline ~ Pipeline dsl Compute Compute) => Tagged dsl [DsBindings] -> output -> ObserverIO (ReleaseKey, pipeline) -> StageFrameRIO rp p fr rs ()
+ Engine.Vulkan.Pipeline.External: observeCompute :: (HasOutput output, GetOutput output ~ config, Specialization spec, config ~ Configure pipeline spec, pipeline ~ Pipeline dsl Compute Compute) => Tagged dsl [DsLayoutBindings] -> output -> ObserverIO (ReleaseKey, pipeline) -> StageFrameRIO rp p fr rs ()
- Engine.Vulkan.Pipeline.External: observeField :: forall pf p renderpass dsl s vs is rps ps fr rs. (p ~ Pipeline s vs is, Specialization (Specialization p), HasRenderPass renderpass) => renderpass -> SampleCountFlagBits -> Tagged dsl DsBindings -> pf ConfigureGraphics -> pf Observers -> (forall a. pf a -> a ^ p) -> StageFrameRIO rps ps fr rs ()
+ Engine.Vulkan.Pipeline.External: observeField :: forall pf p renderpass dsl s vs is rps ps fr rs. (p ~ Pipeline s vs is, Specialization (Specialization p), HasRenderPass renderpass) => renderpass -> SampleCountFlagBits -> Tagged dsl DsLayoutBindings -> pf ConfigureGraphics -> pf Observers -> (forall a. pf a -> a ^ p) -> StageFrameRIO rps ps fr rs ()
- Engine.Vulkan.Pipeline.External: observeGraphics :: (HasRenderPass renderpass, HasOutput output, GetOutput output ~ Configure pipeline, pipeline ~ Pipeline dsl vertices instances, spec ~ Specialization pipeline, Specialization spec) => renderpass -> SampleCountFlagBits -> Tagged dsl [DsBindings] -> output -> ObserverIO (ReleaseKey, pipeline) -> StageFrameRIO rp p fr rs ()
+ Engine.Vulkan.Pipeline.External: observeGraphics :: (HasRenderPass renderpass, HasOutput output, GetOutput output ~ Configure pipeline, pipeline ~ Pipeline dsl vertices instances, spec ~ Specialization pipeline, Specialization spec) => renderpass -> SampleCountFlagBits -> Tagged dsl [DsLayoutBindings] -> output -> ObserverIO (ReleaseKey, pipeline) -> StageFrameRIO rp p fr rs ()
- Engine.Vulkan.Pipeline.External: spawn :: (Foldable stages, MonadUnliftIO m, MonadReader env m, HasLogFunc env) => (stages (Maybe FilePath) -> m stuff) -> Text -> stages (Maybe FilePath) -> (stuff -> config) -> m (Process config)
+ Engine.Vulkan.Pipeline.External: spawn :: (Foldable stages, MonadReader env m, HasLogFunc env, MonadResource m, MonadUnliftIO m) => (stages (Maybe FilePath) -> m stuff) -> Text -> stages (Maybe FilePath) -> (stuff -> config) -> m (Process config)
- Engine.Vulkan.Pipeline.External: spawnReflect :: (MonadUnliftIO m, MonadReader env m, HasLogFunc env, StageInfo stages) => Text -> stages (Maybe FilePath) -> ((stages (Maybe ByteString), Reflect stages) -> config) -> m (Process config)
+ Engine.Vulkan.Pipeline.External: spawnReflect :: (MonadResource m, MonadUnliftIO m, MonadReader env m, HasLogFunc env, StageInfo stages) => Text -> stages (Maybe FilePath) -> ((stages (Maybe ByteString), Reflect stages) -> config) -> m (Process config)
- Engine.Vulkan.Pipeline.Graphics: Config :: StageSpirv -> Maybe StageReflect -> SomeStruct PipelineVertexInputStateCreateInfo -> Tagged dsl [DsBindings] -> Vector PushConstantRange -> Bool -> Bool -> Bool -> CompareOp -> PrimitiveTopology -> CullModeFlagBits -> Maybe ("constant" ::: Float, "slope" ::: Float) -> spec -> Config (dsl :: [Type]) vertices instances spec
+ Engine.Vulkan.Pipeline.Graphics: Config :: StageSpirv -> Maybe StageReflect -> SomeStruct PipelineVertexInputStateCreateInfo -> Tagged dsl [DsLayoutBindings] -> Vector PushConstantRange -> Bool -> Bool -> Bool -> CompareOp -> PrimitiveTopology -> CullModeFlagBits -> Maybe ("constant" ::: Float, "slope" ::: Float) -> spec -> Config (dsl :: [Type]) vertices instances spec
- Engine.Vulkan.Pipeline.Graphics: [$sel:cDescLayouts:Config] :: Config (dsl :: [Type]) vertices instances spec -> Tagged dsl [DsBindings]
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cDescLayouts:Config] :: Config (dsl :: [Type]) vertices instances spec -> Tagged dsl [DsLayoutBindings]
- Engine.Vulkan.Pipeline.Graphics: create :: (MonadUnliftIO io, HasVulkan ctx, HasRenderPass renderpass, Specialization spec, HasCallStack) => ctx -> Maybe Extent2D -> SampleCountFlagBits -> renderpass -> Config dsl vertices instances spec -> io (Pipeline dsl vertices instances)
+ Engine.Vulkan.Pipeline.Graphics: create :: (MonadVulkan env io, HasRenderPass renderpass, Specialization spec, HasCallStack) => Maybe Extent2D -> SampleCountFlagBits -> renderpass -> Config dsl vertices instances spec -> io (Pipeline dsl vertices instances)
- Engine.Vulkan.Pipeline.Graphics: vertexInput :: [(VertexInputRate, [Format])] -> SomeStruct PipelineVertexInputStateCreateInfo
+ Engine.Vulkan.Pipeline.Graphics: vertexInput :: forall a pipeLayout vertices instances. (a ~ Pipeline pipeLayout vertices instances, HasVertexInputBindings vertices, HasVertexInputBindings instances) => SomeStruct PipelineVertexInputStateCreateInfo
- Engine.Vulkan.Shader: create :: (MonadIO io, HasVulkan ctx, StageInfo t) => ctx -> t (Maybe ByteString) -> Maybe SpecializationInfo -> io Shader
+ Engine.Vulkan.Shader: create :: (MonadVulkan env io, StageInfo t, HasCallStack) => t (Maybe ByteString) -> Maybe SpecializationInfo -> io Shader
- Engine.Vulkan.Shader: destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Shader -> io ()
+ Engine.Vulkan.Shader: destroy :: MonadVulkan env io => Shader -> io ()
- Engine.Vulkan.Swapchain: allocSwapchainResources :: (MonadResource (RIO env), HasVulkan env, HasLogFunc env) => SwapchainKHR -> Extent2D -> SurfaceKHR -> Var Extent2D -> RIO env SwapchainResources
+ Engine.Vulkan.Swapchain: allocSwapchainResources :: (MonadResource (RIO env), HasVulkan env, HasLogFunc env) => Maybe PresentModeKHR -> SampleCountFlagBits -> SwapchainKHR -> Extent2D -> SurfaceKHR -> Var Extent2D -> RIO env SwapchainResources
- Engine.Vulkan.Swapchain: createSwapchain :: (MonadResource m, MonadVulkan env m, HasLogFunc env) => SwapchainKHR -> Extent2D -> SurfaceKHR -> m SwapchainInfo
+ Engine.Vulkan.Swapchain: createSwapchain :: (MonadResource m, MonadVulkan env m, HasLogFunc env) => Maybe PresentModeKHR -> SampleCountFlagBits -> SwapchainKHR -> Extent2D -> SurfaceKHR -> m SwapchainInfo
- Engine.Vulkan.Swapchain: recreateSwapchainResources :: (MonadResource (RIO env), HasVulkan env, HasLogFunc env) => Extent2D -> SwapchainResources -> RIO env SwapchainResources
+ Engine.Vulkan.Swapchain: recreateSwapchainResources :: (MonadResource (RIO env), HasVulkan env, HasLogFunc env) => Maybe PresentModeKHR -> SampleCountFlagBits -> Extent2D -> SwapchainResources -> RIO env SwapchainResources
- Engine.Vulkan.Swapchain: setDynamicFullscreen :: MonadIO io => CommandBuffer -> SwapchainResources -> io ()
+ Engine.Vulkan.Swapchain: setDynamicFullscreen :: (HasSwapchain swapchain, MonadIO io) => CommandBuffer -> swapchain -> io ()
- Engine.Window.CursorPos: callback :: Callback st -> StageRIO st ReleaseKey
+ Engine.Window.CursorPos: callback :: MonadSink rs m => Callback m -> m ReleaseKey
- Engine.Window.CursorPos: mkCallback :: UnliftIO (StageRIO st) -> Callback st -> CursorPosCallback
+ Engine.Window.CursorPos: mkCallback :: UnliftIO m -> Callback m -> CursorPosCallback
- Engine.Window.CursorPos: type Callback st = Double -> Double -> StageRIO st ()
+ Engine.Window.CursorPos: type Callback m = Double -> Double -> m ()
- Engine.Window.Drop: callback :: Callback st -> StageRIO st ReleaseKey
+ Engine.Window.Drop: callback :: MonadSink rs m => Callback m -> m ReleaseKey
- Engine.Window.Drop: mkCallback :: UnliftIO (StageRIO st) -> Callback st -> DropCallback
+ Engine.Window.Drop: mkCallback :: UnliftIO m -> Callback m -> DropCallback
- Engine.Window.Drop: type Callback st = [FilePath] -> StageRIO st ()
+ Engine.Window.Drop: type Callback m = [FilePath] -> m ()
- Engine.Window.Key: callback :: Callback st -> StageRIO st ReleaseKey
+ Engine.Window.Key: callback :: MonadSink rs m => Callback m -> m ReleaseKey
- Engine.Window.Key: mkCallback :: UnliftIO (StageRIO st) -> Callback st -> KeyCallback
+ Engine.Window.Key: mkCallback :: UnliftIO m -> Callback m -> KeyCallback
- Engine.Window.Key: type Callback st = Int -> (ModifierKeys, KeyState, Key) -> StageRIO st ()
+ Engine.Window.Key: type Callback m = Int -> (ModifierKeys, KeyState, Key) -> m ()
- Engine.Window.MouseButton: callback :: Callback st -> StageRIO st ReleaseKey
+ Engine.Window.MouseButton: callback :: MonadSink rs m => Callback m -> m ReleaseKey
- Engine.Window.MouseButton: mkCallback :: UnliftIO (StageRIO st) -> Callback st -> MouseButtonCallback
+ Engine.Window.MouseButton: mkCallback :: UnliftIO m -> Callback m -> MouseButtonCallback
- Engine.Window.MouseButton: type Callback st = (ModifierKeys, MouseButtonState, MouseButton) -> StageRIO st ()
+ Engine.Window.MouseButton: type Callback m = (ModifierKeys, MouseButtonState, MouseButton) -> m ()
- Engine.Window.Scroll: callback :: Callback st -> StageRIO st ReleaseKey
+ Engine.Window.Scroll: callback :: MonadSink rs m => Callback m -> m ReleaseKey
- Engine.Window.Scroll: mkCallback :: UnliftIO (StageRIO st) -> Callback st -> ScrollCallback
+ Engine.Window.Scroll: mkCallback :: UnliftIO m -> Callback m -> ScrollCallback
- Engine.Window.Scroll: type Callback st = Double -> Double -> StageRIO st ()
+ Engine.Window.Scroll: type Callback m = Double -> Double -> m ()
- Engine.Worker: Merge :: ThreadId -> TVar (Versioned o) -> Merge o
+ Engine.Worker: Merge :: ThreadId -> ReleaseKey -> TVar (Versioned o) -> Merge o
- Engine.Worker: Timed :: ThreadId -> TVar Bool -> TVar config -> Var output -> Timed config output
+ Engine.Worker: Timed :: ThreadId -> ReleaseKey -> TVar Bool -> TVar config -> Var output -> Timed config output
- Engine.Worker: spawnCell :: MonadUnliftIO m => (input -> output) -> input -> m (Cell input output)
+ Engine.Worker: spawnCell :: (MonadUnliftIO m, MonadResource m) => (input -> output) -> input -> m (Cell input output)
- Engine.Worker: spawnMerge1 :: (MonadUnliftIO m, HasOutput i) => (GetOutput i -> o) -> i -> m (Merge o)
+ Engine.Worker: spawnMerge1 :: (MonadUnliftIO m, MonadResource m, HasOutput i) => (GetOutput i -> o) -> i -> m (Merge o)
- Engine.Worker: spawnMerge2 :: (MonadUnliftIO m, HasOutput i1, HasOutput i2) => (GetOutput i1 -> GetOutput i2 -> o) -> i1 -> i2 -> m (Merge o)
+ Engine.Worker: spawnMerge2 :: (MonadUnliftIO m, MonadResource m, HasOutput i1, HasOutput i2) => (GetOutput i1 -> GetOutput i2 -> o) -> i1 -> i2 -> m (Merge o)
- Engine.Worker: spawnMerge3 :: (MonadUnliftIO m, HasOutput i1, HasOutput i2, HasOutput i3) => (GetOutput i1 -> GetOutput i2 -> GetOutput i3 -> o) -> i1 -> i2 -> i3 -> m (Merge o)
+ Engine.Worker: spawnMerge3 :: (MonadUnliftIO m, MonadResource m, HasOutput i1, HasOutput i2, HasOutput i3) => (GetOutput i1 -> GetOutput i2 -> GetOutput i3 -> o) -> i1 -> i2 -> i3 -> m (Merge o)
- Engine.Worker: spawnMerge4 :: (MonadUnliftIO m, HasOutput i1, HasOutput i2, HasOutput i3, HasOutput i4) => (GetOutput i1 -> GetOutput i2 -> GetOutput i3 -> GetOutput i4 -> o) -> i1 -> i2 -> i3 -> i4 -> m (Merge o)
+ Engine.Worker: spawnMerge4 :: (MonadUnliftIO m, MonadResource m, HasOutput i1, HasOutput i2, HasOutput i3, HasOutput i4) => (GetOutput i1 -> GetOutput i2 -> GetOutput i3 -> GetOutput i4 -> o) -> i1 -> i2 -> i3 -> i4 -> m (Merge o)
- Engine.Worker: spawnMergeT :: (Traversable t, HasOutput input, MonadUnliftIO m) => (t (GetOutput input) -> output) -> t input -> m (Merge output)
+ Engine.Worker: spawnMergeT :: (Traversable t, HasOutput input, MonadUnliftIO m, MonadResource m) => (t (GetOutput input) -> output) -> t input -> m (Merge output)
- Engine.Worker: spawnTimed :: MonadUnliftIO m => Bool -> Either Int (config -> Int) -> (config -> m (output, state)) -> (state -> config -> m (Maybe output, state)) -> config -> m (Timed config output)
+ Engine.Worker: spawnTimed :: (MonadUnliftIO m, MonadResource m) => Bool -> Either Int (config -> Int) -> (config -> m (output, state)) -> (state -> config -> m (Maybe output, state)) -> config -> m (Timed config output)
- Engine.Worker: spawnTimed_ :: MonadUnliftIO m => Bool -> Int -> output -> m output -> m (Timed () output)
+ Engine.Worker: spawnTimed_ :: (MonadUnliftIO m, MonadResource m) => Bool -> Int -> output -> m output -> m (Timed () output)
- Render.Draw: indexed :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos attrs -> instances -> Bound dsl attrs (VertexBuffersOf instances) m ()
+ Render.Draw: indexed :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos attrs -> instances -> Bound dsl (Vertex pos attrs) (VertexBuffersOf instances) m ()
- Render.Draw: indexedParts :: (MonadUnliftIO m, HasVertexBuffers instances, Foldable t) => Bool -> CommandBuffer -> Indexed storage pos attrs -> instances -> Int -> t IndexRange -> Bound dsl attrs (VertexBuffersOf instances) m ()
+ Render.Draw: indexedParts :: (MonadUnliftIO m, HasVertexBuffers instances, Foldable t) => Bool -> CommandBuffer -> Indexed storage pos attrs -> instances -> Int -> t IndexRange -> Bound dsl (Vertex pos attrs) (VertexBuffersOf instances) m ()
- Render.Draw: indexedPos :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos unusedAttrs -> instances -> Bound dsl ignoreAttrs (VertexBuffersOf instances) m ()
+ Render.Draw: indexedPos :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos unusedAttrs -> instances -> Bound dsl (Vertex pos ignoreAttrs) (VertexBuffersOf instances) m ()
- Render.Draw: indexedPosRanges :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos unusedAttrs -> instances -> [IndexRange] -> Bound dsl ignoreAttrs (VertexBuffersOf instances) m ()
+ Render.Draw: indexedPosRanges :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos unusedAttrs -> instances -> [IndexRange] -> Bound dsl (Vertex pos ignoreAttrs) (VertexBuffersOf instances) m ()
- Render.Draw: indexedRanges :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos attrs -> instances -> [IndexRange] -> Bound dsl attrs (VertexBuffersOf instances) m ()
+ Render.Draw: indexedRanges :: (MonadUnliftIO m, HasVertexBuffers instances) => CommandBuffer -> Indexed storage pos attrs -> instances -> [IndexRange] -> Bound dsl (Vertex pos attrs) (VertexBuffersOf instances) m ()
- Render.Pass.Offscreen: Settings :: Text -> Extent2D -> Format -> Format -> Word32 -> Bool -> SampleCountFlagBits -> Bool -> Settings
+ Render.Pass.Offscreen: Settings :: Text -> Extent2D -> Format -> Maybe ImageLayout -> Format -> Maybe ImageLayout -> Word32 -> Bool -> SampleCountFlagBits -> Bool -> Settings
- Render.Samplers: allocate :: (MonadVulkan env m, MonadResource m) => ("max anisotropy" ::: Float) -> m (ReleaseKey, Collection Sampler)
+ Render.Samplers: allocate :: MonadVulkan env io => ("max anisotropy" ::: Float) -> ResourceT io (Collection Sampler)
- Resource.Buffer: Allocated :: Buffer -> Allocation -> AllocationInfo -> Int -> Word32 -> BufferUsageFlagBits -> Allocated (s :: Store) a
+ Resource.Buffer: Allocated :: Buffer -> Allocation -> AllocationInfo -> Int -> Word32 -> BufferUsageFlagBits -> Maybe Text -> Allocated (s :: Store) a
- Resource.Buffer: allocateCoherent :: (MonadResource m, Storable a, HasVulkan context) => context -> BufferUsageFlagBits -> ("initial size" ::: Int) -> Vector a -> m (ReleaseKey, Allocated 'Coherent a)
+ Resource.Buffer: allocateCoherent :: (MonadVulkan env m, MonadResource m, Storable a) => Maybe Text -> BufferUsageFlagBits -> ("initial size" ::: Int) -> Vector a -> m (ReleaseKey, Allocated 'Coherent a)
- Resource.Buffer: createCoherent :: forall a context io. (Storable a, HasVulkan context, MonadUnliftIO io) => context -> BufferUsageFlagBits -> ("initial size" ::: Int) -> Vector a -> io (Allocated 'Coherent a)
+ Resource.Buffer: createCoherent :: forall a env m. (MonadVulkan env m, Storable a) => Maybe Text -> BufferUsageFlagBits -> ("initial size" ::: Int) -> Vector a -> m (Allocated 'Coherent a)
- Resource.Buffer: createStaged :: forall a context io. (Storable a, HasVulkan context, MonadUnliftIO io) => context -> Queues CommandPool -> BufferUsageFlagBits -> Int -> Vector a -> io (Allocated 'Staged a)
+ Resource.Buffer: createStaged :: forall a env m. (Storable a, MonadVulkan env m) => Maybe Text -> Queues CommandPool -> BufferUsageFlagBits -> Int -> Vector a -> m (Allocated 'Staged a)
- Resource.Buffer: newObserverCoherent :: Storable a => BufferUsageFlagBits -> Int -> ResourceT (StageRIO st) (ObserverCoherent a)
+ Resource.Buffer: newObserverCoherent :: (MonadVulkan env m, Storable a) => ("label" ::: Text) -> BufferUsageFlagBits -> Int -> Vector a -> ResourceT m (ObserverCoherent a)
- Resource.Buffer: observeCoherentResize_ :: (HasVulkan env, HasOutput source, GetOutput source ~ Vector output, Storable output) => source -> ObserverCoherent output -> RIO env ()
+ Resource.Buffer: observeCoherentResize_ :: (MonadVulkan env m, HasOutput source, GetOutput source ~ Vector output, Storable output) => source -> ObserverCoherent output -> m ()
- Resource.Buffer: updateCoherent :: (Storable a, MonadUnliftIO io) => Vector a -> Allocated 'Coherent a -> io (Allocated 'Coherent a)
+ Resource.Buffer: updateCoherent :: (MonadUnliftIO io, Storable a) => Vector a -> Allocated 'Coherent a -> io (Allocated 'Coherent a)
- Resource.Buffer: updateCoherentResize_ :: (Storable a, HasVulkan context, MonadUnliftIO io) => context -> Allocated 'Coherent a -> Vector a -> io (Allocated 'Coherent a)
+ Resource.Buffer: updateCoherentResize_ :: (MonadVulkan env m, Storable a) => Allocated 'Coherent a -> Vector a -> m (Allocated 'Coherent a)
- Resource.Image: AllocatedImage :: Allocation -> Extent2D -> Format -> Image -> ImageView -> ImageSubresourceRange -> AllocatedImage
+ Resource.Image: AllocatedImage :: Allocation -> Extent3D -> Format -> Image -> ImageView -> ImageSubresourceRange -> AllocatedImage
- Resource.Image: [$sel:aiExtent:AllocatedImage] :: AllocatedImage -> Extent2D
+ Resource.Image: [$sel:aiExtent:AllocatedImage] :: AllocatedImage -> Extent3D
- Resource.Image: copyBufferToImage :: (HasVulkan context, Foldable t, Integral deviceSize, MonadUnliftIO m) => context -> Queues CommandPool -> Buffer -> Image -> ("base extent" ::: Extent3D) -> ("mip offsets" ::: t deviceSize) -> ("layer count" ::: Word32) -> m ()
+ Resource.Image: copyBufferToImage :: (Foldable t, Integral deviceSize, MonadVulkan env m) => Queues CommandPool -> Buffer -> Image -> ("base extent" ::: Extent3D) -> ("mip offsets" ::: t deviceSize) -> ("layer count" ::: Word32) -> m ()
- Resource.Image: transitionLayout :: (HasVulkan context, MonadUnliftIO m) => context -> Queues CommandPool -> Image -> ("mip levels" ::: Word32) -> ("layer count" ::: Word32) -> Format -> ("old" ::: ImageLayout) -> ("new" ::: ImageLayout) -> m ()
+ Resource.Image: transitionLayout :: MonadVulkan env m => Queues CommandPool -> Image -> ("mip levels" ::: Word32) -> ("layer count" ::: Word32) -> Format -> ("old" ::: ImageLayout) -> ("new" ::: ImageLayout) -> m ()
- Resource.Model: Indexed :: Allocated storage pos -> Allocated storage attrs -> Allocated storage Word32 -> Indexed storage pos attrs
+ Resource.Model: Indexed :: Maybe Text -> Allocated storage pos -> Allocated storage attrs -> Allocated storage Word32 -> Indexed storage pos attrs
- Resource.Model: createCoherentEmpty :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io) => context -> Int -> io (Indexed 'Coherent pos attrs)
+ Resource.Model: createCoherentEmpty :: (MonadVulkan env m, Storable pos, Storable attrs) => Maybe Text -> Int -> m (Indexed 'Coherent pos attrs)
- Resource.Model: createStaged :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io) => context -> Queues CommandPool -> Vector pos -> Vector attrs -> Vector Word32 -> io (Indexed 'Staged pos attrs)
+ Resource.Model: createStaged :: (MonadVulkan env m, Storable pos, Storable attrs) => Maybe Text -> Queues CommandPool -> Vector pos -> Vector attrs -> Vector Word32 -> m (Indexed 'Staged pos attrs)
- Resource.Model: createStagedL :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io) => context -> Queues CommandPool -> [Vertex pos attrs] -> Maybe [Word32] -> io (Indexed 'Staged pos attrs)
+ Resource.Model: createStagedL :: (MonadVulkan env m, Storable pos, Storable attrs) => Maybe Text -> Queues CommandPool -> [Vertex pos attrs] -> Maybe [Word32] -> m (Indexed 'Staged pos attrs)
- Resource.Model: destroyIndexed :: (HasVulkan context, MonadUnliftIO io) => context -> Indexed storage pos attrs -> io ()
+ Resource.Model: destroyIndexed :: MonadVulkan env m => Indexed storage pos attrs -> m ()
- Resource.Model: getInstanceCount :: HasVertexBuffers a => a -> Word32
+ Resource.Model: getInstanceCount :: (HasVertexBuffers a, Generic a, GHasVertexBuffers (Rep a)) => a -> Word32
- Resource.Model: getVertexBuffers :: HasVertexBuffers a => a -> [Buffer]
+ Resource.Model: getVertexBuffers :: (HasVertexBuffers a, Generic a, GHasVertexBuffers (Rep a)) => a -> [Buffer]
- Resource.Model: updateCoherent :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io) => context -> [Vertex pos attrs] -> Indexed 'Coherent pos attrs -> io (Indexed 'Coherent pos attrs)
+ Resource.Model: updateCoherent :: (MonadVulkan env m, Storable pos, Storable attrs) => [Vertex pos attrs] -> Indexed 'Coherent pos attrs -> m (Indexed 'Coherent pos attrs)
- Resource.Texture: Texture :: Format -> Word32 -> Word32 -> AllocatedImage -> Texture a
+ Resource.Texture: Texture :: Format -> Word32 -> Word32 -> AllocatedImage -> Texture tag
- Resource.Texture: [$sel:tAllocatedImage:Texture] :: Texture a -> AllocatedImage
+ Resource.Texture: [$sel:tAllocatedImage:Texture] :: Texture tag -> AllocatedImage
- Resource.Texture: [$sel:tFormat:Texture] :: Texture a -> Format
+ Resource.Texture: [$sel:tFormat:Texture] :: Texture tag -> Format
- Resource.Texture: [$sel:tLayers:Texture] :: Texture a -> Word32
+ Resource.Texture: [$sel:tLayers:Texture] :: Texture tag -> Word32
- Resource.Texture: [$sel:tMipLevels:Texture] :: Texture a -> Word32
+ Resource.Texture: [$sel:tMipLevels:Texture] :: Texture tag -> Word32
- Resource.Texture: data Texture a
+ Resource.Texture: data Texture tag
- Resource.Texture.Ktx1: load :: (TextureLayers a, MonadVulkan env m, MonadThrow m, HasLogFunc env, Typeable a, HasCallStack) => Queues CommandPool -> Source -> m (Texture a)
+ Resource.Texture.Ktx1: load :: (TextureLayers a, MonadVulkan env m, MonadResource m, MonadThrow m, HasLogFunc env, Typeable a, HasCallStack) => Queues CommandPool -> Source -> m (Texture a)
- Resource.Texture.Ktx1: loadBytes :: (TextureLayers a, MonadVulkan env m, MonadThrow m, HasLogFunc env) => Queues CommandPool -> ByteString -> m (Texture a)
+ Resource.Texture.Ktx1: loadBytes :: (TextureLayers a, MonadVulkan env m, MonadResource m, MonadThrow m, HasLogFunc env) => Queues CommandPool -> ByteString -> m (Texture a)
- Resource.Texture.Ktx1: loadKtx1 :: forall a m env. (TextureLayers a, MonadVulkan env m, MonadThrow m, HasLogFunc env) => Queues CommandPool -> Ktx -> m (Texture a)
+ Resource.Texture.Ktx1: loadKtx1 :: forall a m env. (TextureLayers a, MonadVulkan env m, MonadResource m, MonadThrow m, HasLogFunc env) => Queues CommandPool -> Ktx -> m (Texture a)
Files
- ChangeLog.md +23/−0
- keid-core.cabal +10/−4
- src/Engine/Camera.hs +26/−6
- src/Engine/Camera/Controls.hs +13/−2
- src/Engine/Events.hs +10/−7
- src/Engine/Events/CursorPos.hs +14/−12
- src/Engine/Events/MouseButton.hs +11/−10
- src/Engine/Events/Sink.hs +20/−7
- src/Engine/Frame.hs +10/−1
- src/Engine/Render.hs +3/−2
- src/Engine/Setup.hs +10/−1
- src/Engine/Setup/Device.hs +3/−5
- src/Engine/SpirV/Compile.hs +2/−2
- src/Engine/SpirV/Reflect.hs +6/−4
- src/Engine/Types.hs +36/−6
- src/Engine/Types/Options.hs +73/−3
- src/Engine/Types/RefCounted.hs +8/−1
- src/Engine/UI/Layout.hs +19/−6
- src/Engine/Vulkan/Format.hs +130/−0
- src/Engine/Vulkan/Pipeline.hs +21/−9
- src/Engine/Vulkan/Pipeline/Compute.hs +32/−61
- src/Engine/Vulkan/Pipeline/External.hs +15/−11
- src/Engine/Vulkan/Pipeline/Graphics.hs +62/−74
- src/Engine/Vulkan/Shader.hs +15/−10
- src/Engine/Vulkan/Swapchain.hs +55/−31
- src/Engine/Vulkan/Types.hs +6/−3
- src/Engine/Window/CursorPos.hs +8/−4
- src/Engine/Window/Drop.hs +5/−4
- src/Engine/Window/Key.hs +5/−4
- src/Engine/Window/MouseButton.hs +9/−5
- src/Engine/Window/Scroll.hs +8/−4
- src/Engine/Worker.hs +34/−64
- src/Render/Code.hs +7/−3
- src/Render/Draw.hs +9/−9
- src/Render/Pass/Offscreen.hs +57/−58
- src/Render/Samplers.hs +51/−42
- src/Resource/Buffer.hs +128/−56
- src/Resource/CommandBuffer.hs +4/−2
- src/Resource/DescriptorSet.hs +0/−37
- src/Resource/Image.hs +223/−95
- src/Resource/Mesh/Codec.hs +10/−6
- src/Resource/Model.hs +133/−37
- src/Resource/Model/Observer.hs +211/−0
- src/Resource/Model/Observer/Example.hs +44/−0
- src/Resource/Source.hs +8/−1
- src/Resource/Texture.hs +33/−32
- src/Resource/Texture/Ktx1.hs +17/−63
- src/Resource/Texture/Ktx2.hs +183/−0
- src/Resource/Vulkan/DescriptorLayout.hs +47/−0
- src/Resource/Vulkan/DescriptorPool.hs +64/−0
- src/Resource/Vulkan/Named.hs +38/−0
ChangeLog.md view
@@ -1,5 +1,28 @@ # Changelog for keid-core +## 0.1.8.0++Massive ~~breakage~~ fixing around allocate/create/destroy patterns and naming things.++- Deleted `Resource.DescriptorSets`.+- Added `Resource.Vulkan.DescriptorPool`.+- Renamed `Engine.Vulkan.Types.DsBindings` to `DsLayoutBindings`.+- Added `allocate_` for `Engine.Vulkan.Pipeline.Graphics` and `Compute`.+- Added `size`, `withSize` and experimental `HasField` instance for `Resource.Texture`.+- Fixed SPIR-V signatures compatibility by hiding builtin interface vars.+- `Engine.Worker.spawn*` and `Engine.Events.*` handlers and now self-registereing.+- Due to `RunState`/`Sink RunState` loop callbacks now use a `MonadSink rs m` instead of `StageRIO rs`.+- Added `Resource.Texture.Ktx2`.+ * It lacks the BC7 hack to force sRGB for compressed textures as it is possible to set the correct format directly.+- Moved Base textures to KTX2 containers. KTX1 is soft-deprecated.+- Graphics pipelines now able to fill the config automatically with `Graphics.vertexInput` helper. Using:+ * `HasVertexInputBindings` class that provides attribute bindings.+ * `HasVkFormat` class to generate bindings parameters automatically (with generics) or explicitly.+ * `vertexFormat`, `instanceFormat` helpers for using `HasVkFormat` to provide `HasVertexInputBindings` instances.+- Added `Resource.Model.Observer` that wraps coherent buffers and is able to observe multi-buffered resources.+- Changed `Swapchain.setDynamicFullscreen` to accept a frame directly.+- Added `MonadReader` and `MonadState` instances for `Bound` to allow fetching models at draw call site.+ ## 0.1.7.2 - Added `--size` option to set the initial window size.
keid-core.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0.+-- This file has been generated from package.yaml by hpack version 0.35.1. -- -- see: https://github.com/sol/hpack name: keid-core-version: 0.1.7.2+version: 0.1.8.0 synopsis: Core parts of Keid engine. category: Game Engine homepage: https://keid.haskell-game.dev@@ -52,6 +52,7 @@ Engine.UI.Layout Engine.UI.Layout.Linear Engine.Vulkan.DescSets+ Engine.Vulkan.Format Engine.Vulkan.Pipeline Engine.Vulkan.Pipeline.Compute Engine.Vulkan.Pipeline.External@@ -79,18 +80,23 @@ Resource.Combined.Textures Resource.CommandBuffer Resource.Compressed.Zstd- Resource.DescriptorSet Resource.Image Resource.Image.Atlas Resource.Mesh.Codec Resource.Mesh.Types Resource.Mesh.Utils Resource.Model+ Resource.Model.Observer+ Resource.Model.Observer.Example Resource.Region Resource.Source Resource.Static Resource.Texture Resource.Texture.Ktx1+ Resource.Texture.Ktx2+ Resource.Vulkan.DescriptorLayout+ Resource.Vulkan.DescriptorPool+ Resource.Vulkan.Named other-modules: Paths_keid_core hs-source-dirs:@@ -167,7 +173,7 @@ , file-embed , foldl , geomancy- , ktx-codec+ , ktx-codec >=0.0.2.0 , neat-interpolation , optparse-applicative , optparse-simple
src/Engine/Camera.hs view
@@ -32,6 +32,8 @@ import Geomancy.Vec3 qualified as Vec3 import Geomancy.Vulkan.Projection qualified as Projection import Geomancy.Vulkan.View qualified as View+import RIO.App (App)+import UnliftIO.Resource (MonadResource) import Vulkan.Core10 qualified as Vk import Vulkan.NamedType ((:::)) @@ -67,9 +69,13 @@ ProjectionParams 'Orthographic = () spawnProjection- :: (Vk.Extent2D -> ProjectionInput pk -> Transform)+ :: ( MonadReader (App Engine.GlobalHandles st) m+ , MonadResource m+ , MonadUnliftIO m+ )+ => (Vk.Extent2D -> ProjectionInput pk -> Transform) -> ProjectionParams pk- -> Engine.StageRIO env (ProjectionProcess pk)+ -> m (ProjectionProcess pk) spawnProjection mkTransform params = spawnProjectionWith mkTransform ProjectionInput { projectionNear = PROJECTION_NEAR@@ -84,9 +90,13 @@ pattern PROJECTION_FAR = 16384 spawnProjectionWith- :: (Vk.Extent2D -> ProjectionInput pk -> Transform)+ :: ( MonadReader (App Engine.GlobalHandles st) m+ , MonadResource m+ , MonadUnliftIO m+ )+ => (Vk.Extent2D -> ProjectionInput pk -> Transform) -> ProjectionInput pk- -> Engine.StageRIO env (ProjectionProcess pk)+ -> m (ProjectionProcess pk) spawnProjectionWith mkTransform projectionInput = do screen <- Engine.askScreenVar input <- Worker.newVar projectionInput@@ -104,7 +114,12 @@ screen input -spawnPerspective :: Engine.StageRIO env (ProjectionProcess 'Perspective)+spawnPerspective+ :: ( MonadReader (App Engine.GlobalHandles st) m+ , MonadResource m+ , MonadUnliftIO m+ )+ => m (ProjectionProcess 'Perspective) spawnPerspective = spawnProjection mkTransformPerspective (τ / 4) mkTransformPerspective :: Vk.Extent2D -> ProjectionInput 'Perspective -> Transform@@ -116,7 +131,12 @@ width height -spawnOrthoPixelsCentered :: Engine.StageRIO env (ProjectionProcess 'Orthographic)+spawnOrthoPixelsCentered+ :: ( MonadReader (App Engine.GlobalHandles st) m+ , MonadResource m+ , MonadUnliftIO m+ )+ => m (ProjectionProcess 'Orthographic) spawnOrthoPixelsCentered = spawnProjectionWith mkTransformOrthoPixelsCentered ProjectionInput { projectionNear = 0 , projectionFar = 1
src/Engine/Camera/Controls.hs view
@@ -16,8 +16,14 @@ import Engine.Worker qualified as Worker import Geomancy (Vec3, vec3) import Geomancy.Quaternion qualified as Quaternion+import UnliftIO.Resource (MonadResource) -spawnViewOrbital :: Camera.ViewOrbitalInput -> RIO env Camera.ViewProcess+spawnViewOrbital+ :: ( MonadResource m+ , MonadUnliftIO m+ )+ => Camera.ViewOrbitalInput+ -> m Camera.ViewProcess spawnViewOrbital = Worker.spawnCell Camera.mkViewOrbital_ data Controls a = Controls@@ -30,7 +36,12 @@ type ControlsProcess = Controls (Worker.Timed Float ()) -spawnControls :: Camera.ViewProcess -> RIO env ControlsProcess+spawnControls+ :: ( MonadResource m+ , MonadUnliftIO m+ )+ => Camera.ViewProcess+ -> m ControlsProcess spawnControls vp = traverse mkUpdater Controls { panHorizontal = panTargetHorizontal
src/Engine/Events.hs view
@@ -6,17 +6,17 @@ import RIO -import UnliftIO.Resource (ReleaseKey)+import UnliftIO.Resource (MonadResource, ReleaseKey) import UnliftIO.Resource qualified as Resource -import Engine.Events.Sink (Sink)+import Engine.Events.Sink (MonadSink, Sink) import Engine.Events.Sink qualified as Sink-import Engine.Types (StageRIO) spawn- :: (event -> StageRIO st ())- -> [Sink event st -> StageRIO st ReleaseKey]- -> StageRIO st (ReleaseKey, Sink event st)+ :: MonadSink st m+ => (event -> m ())+ -> [Sink event st -> m ReleaseKey]+ -> m (ReleaseKey, Sink event st) spawn handleEvent sources = do (sinkKey, sink) <- Sink.spawn handleEvent @@ -26,7 +26,10 @@ pure (key, sink) -registerMany :: [StageRIO st ReleaseKey] -> StageRIO st ReleaseKey+registerMany+ :: MonadResource m+ => [m ReleaseKey]+ -> m ReleaseKey registerMany actions = sequenceA actions >>= Resource.register . traverse_ Resource.release
src/Engine/Events/CursorPos.hs view
@@ -4,33 +4,33 @@ import Geomancy (Vec2, vec2, pattern WithVec2) import GHC.Float (double2Float)-import UnliftIO.Resource (ReleaseKey)+import UnliftIO.Resource (MonadResource, ReleaseKey) import Vulkan.Core10 qualified as Vk import Vulkan.NamedType ((:::)) -import Engine.Types (StageRIO, askScreenVar)+import Engine.Events.Sink (MonadSink, Sink)+import Engine.Types (askScreenVar) import Engine.Window.CursorPos qualified as CursorPos import Engine.Worker qualified as Worker -import Engine.Events (Sink)- callback- :: ( Worker.HasInput cursor+ :: ( MonadSink rs m+ , Worker.HasInput cursor , Worker.GetInput cursor ~ Vec2 ) => cursor- -------------------------- -> Sink e rs- -> StageRIO rs ReleaseKey+ -> Sink e st+ -> m ReleaseKey callback cursorVar = CursorPos.callback . handler cursorVar handler- :: ( Worker.HasInput cursor+ :: ( MonadResource m+ , Worker.HasInput cursor , Worker.GetInput cursor ~ Vec2 ) => cursor- -> Sink e rs- -> CursorPos.Callback rs+ -> Sink e st+ -> CursorPos.Callback m handler cursorVar _sink windowX windowY = do -- logDebug $ "CursorPos event: " <> displayShow (windowX, windowY) Worker.pushInput cursorVar \_old ->@@ -38,7 +38,9 @@ type Process = Worker.Cell ("window" ::: Vec2) ("centered" ::: Vec2) -spawn :: StageRIO env Process+spawn+ :: MonadSink rs m+ => m Process spawn = do screen <- askScreenVar cursorWindow <- Worker.newVar 0
src/Engine/Events/MouseButton.hs view
@@ -9,38 +9,39 @@ import Geomancy (Vec2) import UnliftIO.Resource (ReleaseKey) -import Engine.Events.Sink (Sink)-import Engine.Types (StageRIO)+import Engine.Events.Sink (MonadSink, Sink) import Engine.Window.MouseButton (ModifierKeys, MouseButton, MouseButtonState) import Engine.Window.MouseButton qualified as MouseButton import Engine.Worker qualified as Worker -type ClickHandler e st =+type ClickHandler e st m = Sink e st -> Vec2 -> (ModifierKeys, MouseButtonState, MouseButton) ->- StageRIO st ()+ m () callback- :: ( Worker.HasOutput cursor+ :: ( MonadSink rs m+ , Worker.HasOutput cursor , Worker.GetOutput cursor ~ Vec2 ) => cursor- -> ClickHandler e st+ -> ClickHandler e st m ------------------------- -> Sink e st- -> StageRIO st ReleaseKey+ -> m ReleaseKey callback cursorP eventHandler = MouseButton.callback . handler cursorP eventHandler handler- :: ( Worker.HasOutput cursor+ :: ( MonadSink rs m+ , Worker.HasOutput cursor , Worker.GetOutput cursor ~ Vec2 ) => cursor- -> ClickHandler e st+ -> ClickHandler e st m -> Sink e st -> (ModifierKeys, MouseButtonState, MouseButton)- -> StageRIO st ()+ -> m () handler cursorP eventHandler sink buttonEvent = do cursorPos <- Worker.getOutputData cursorP logDebug $ "MouseButton event: " <> displayShow (cursorPos, buttonEvent)
src/Engine/Events/Sink.hs view
@@ -1,28 +1,41 @@ module Engine.Events.Sink ( Sink(..) , spawn++ , MonadSink ) where import RIO import Control.Concurrent.Chan.Unagi qualified as Unagi import Control.Exception (AsyncException(ThreadKilled))+import Engine.Types (GlobalHandles)+import RIO.App (App)+import RIO.State (MonadState) import UnliftIO.Concurrent (forkFinally, killThread)-import UnliftIO.Resource (ReleaseKey)+import UnliftIO.Resource (ReleaseKey, MonadResource) import UnliftIO.Resource qualified as Resource -import Engine.Types (StageRIO)+-- | A collection of properties that are available while handling events.+-- Has access to a stage @RunState@, but not @Frame@ data.+type MonadSink rs m =+ ( MonadReader (App GlobalHandles rs) m+ , MonadState rs m+ , MonadResource m+ , MonadUnliftIO m+ ) -newtype Sink event st = Sink- { signal :: event -> StageRIO st ()+newtype Sink event rs = Sink+ { signal :: forall m . MonadSink rs m => event -> m () } spawn- :: (event -> StageRIO rs ())- -> StageRIO rs (ReleaseKey, Sink event rs)+ :: MonadSink rs m+ => (event -> m ())+ -> m (ReleaseKey, Sink event rs) spawn handleEvent = do (eventsIn, eventsOut) <- liftIO Unagi.newChan- let sink = Sink \event -> liftIO (Unagi.writeChan eventsIn event)+ let sink = Sink $ liftIO . Unagi.writeChan eventsIn let handler = forever $
src/Engine/Frame.hs view
@@ -29,6 +29,7 @@ import Engine.DataRecycler (DumpResource, WaitResource) import Engine.Setup.Window qualified as Window import Engine.Types (GlobalHandles(..), StageRIO, Stage(..), Frame(..), GPUWork, RecycledResources(..))+import Engine.Types.Options (optionsPresent, optionsMsaa) import Engine.Types.RefCounted (newRefCounted) import Engine.Vulkan.Swapchain (SwapchainResources(..), SwapchainInfo(..), allocSwapchainResources, recreateSwapchainResources) import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, RenderPass(..), Queues)@@ -44,11 +45,17 @@ GlobalHandles{..} <- asks appEnv let device = ghDevice + let+ fPresent = optionsPresent ghOptions+ fMSAA = optionsMsaa ghOptions+ sfSwapchainResources <- case oldSR of Nothing -> do windowSize <- liftIO $ Window.getExtent2D ghWindow let oldSwapchain = Vk.NULL_HANDLE allocSwapchainResources+ fPresent+ fMSAA oldSwapchain windowSize ghSurface@@ -168,6 +175,8 @@ , fPipelines = fPipelines f , fRenderFinishedHostSemaphore = fRenderFinishedHostSemaphore f , fStageResources = fStageResources f+ , fPresent = fPresent f+ , fMSAA = fMSAA f , fSwapchainResources , fRenderpass , fGPUWork@@ -178,7 +187,7 @@ getNext Frame{..} = do if needsNewSwapchain then do windowSize <- liftIO $ Window.getExtent2D fWindow- newResources <- recreateSwapchainResources windowSize fSwapchainResources+ newResources <- recreateSwapchainResources fPresent fMSAA windowSize fSwapchainResources let formatMatch =
src/Engine/Render.hs view
@@ -7,6 +7,7 @@ import Control.Monad.Trans.Resource qualified as Resource import Vulkan.Core10 qualified as Vk+import Vulkan.Core10.CommandBuffer qualified as CommandBuffer import Vulkan.Core12.Promoted_From_VK_KHR_timeline_semaphore qualified as Vk12 import Vulkan.CStruct.Extends (SomeStruct(..), pattern (:&), pattern (::&)) import Vulkan.Extensions.VK_KHR_swapchain qualified as Khr@@ -65,8 +66,8 @@ let commandBufferBI = zero- { Vk.flags = Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT- } :: Vk.CommandBufferBeginInfo '[]+ { CommandBuffer.flags = Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT+ } Vk.useCommandBuffer commandBuffer commandBufferBI $ recordCommandBuffer commandBuffer stageRecycled imageIndex
src/Engine/Setup.hs view
@@ -15,6 +15,7 @@ import Vulkan.Utils.QueueAssignment (QueueFamilyIndex(..)) import Vulkan.Zero (zero) import VulkanMemoryAllocator qualified as VMA+import Vulkan.Core12 (pattern API_VERSION_1_2) #if MIN_VERSION_vulkan(3,15,0) import Foreign.Ptr (castFunPtr)@@ -47,10 +48,18 @@ "Keid Engine" logDebug "Creating instance"+ let+ appInfo = Vk.ApplicationInfo+ { apiVersion = API_VERSION_1_2+ , applicationName = Nothing+ , applicationVersion = 0+ , engineName = Nothing+ , engineVersion = 0+ } ghInstance <- createInstanceFromRequirements (deviceProps : debugUtils : windowReqs) mempty- zero+ (zero { Vk.applicationInfo = Just appInfo }) logDebug "Creating surface" (_surfaceKey, ghSurface) <- Window.allocateSurface ghWindow ghInstance
src/Engine/Setup/Device.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedRecordDot #-}+ -- | Physical device tools module Engine.Setup.Device where@@ -122,9 +124,7 @@ pdiTotalMemory <- do props <- Vk.getPhysicalDeviceMemoryProperties phys pure . sum $- fmap- (Vk.size :: Vk.MemoryHeap -> Vk.DeviceSize)- (Vk.memoryHeaps props)+ fmap (.size) (Vk.memoryHeaps props) pdiProperties <- Vk.getPhysicalDeviceProperties phys @@ -185,8 +185,6 @@ hasFeat <- getPhysicalDeviceFeatures2KHR phys >>= \case _ ::& (PhysicalDeviceTimelineSemaphoreFeatures hasTimelineSemaphores :& ()) -> pure hasTimelineSemaphores- _ ->- pure False pure $ hasExt && hasFeat
src/Engine/SpirV/Compile.hs view
@@ -13,7 +13,7 @@ import RIO.Process (HasProcessContext, proc, readProcess_) import RIO.Text qualified as Text -import Render.Code (Code(..))+import Render.Code (Code(..), targetEnv) import Engine.Vulkan.Pipeline.Stages qualified as Stages glsl@@ -44,7 +44,7 @@ ByteString.writeFile shaderFile outBytes (_out, _err) <- proc "glslangValidator"- [ "--target-env", "vulkan1.2"+ [ "--target-env", targetEnv , "-S", Text.unpack stage , "-V", shaderFile , "-o", outFile
src/Engine/SpirV/Reflect.hs view
@@ -216,12 +216,11 @@ deriving (Eq, Ord, Show) stagesInterfaceMap- :: ( MonadIO m- , Traversable stages+ :: ( Traversable stages ) => stages (Maybe Module)- -> m (StageInterface stages)-stagesInterfaceMap = traverse $ traverse (pure . moduleInterfaceBinds)+ -> StageInterface stages+stagesInterfaceMap = fmap (fmap moduleInterfaceBinds) moduleInterfaceBinds :: Module -> (InterfaceBinds, InterfaceBinds) moduleInterfaceBinds refl =@@ -233,6 +232,9 @@ interfaceBinds cls vars = IntMap.fromList do var@InterfaceVariable.InterfaceVariable{location} <- toList vars guard $ InterfaceVariable.storage_class var == cls++ -- XXX: Remove vars like @gl_FragCoord@/@SV_Position@ from potential signatures.+ guard $ InterfaceVariable.built_in var == maxBound let td = InterfaceVariable.type_description var
src/Engine/Types.hs view
@@ -4,6 +4,7 @@ import Control.Monad.Trans.Resource (ResourceT) import Control.Monad.Trans.Resource qualified as ResourceT+import Data.Kind (Type) import Graphics.UI.GLFW qualified as GLFW import RIO.App (App, appEnv, appState) import RIO.Lens (_1)@@ -15,12 +16,12 @@ import VulkanMemoryAllocator qualified as VMA import Engine.Setup.Window (Window)+import Engine.Types.Options (Options) import Engine.Types.RefCounted (RefCounted) import Engine.Vulkan.Swapchain (SwapchainResources(..))-import Engine.Vulkan.Types (HasVulkan(..))+import Engine.Vulkan.Types (HasVulkan(..), HasSwapchain(..)) import Engine.Vulkan.Types qualified as Vulkan import Engine.Worker qualified as Worker-import Engine.Types.Options (Options) -- * App globals @@ -39,7 +40,10 @@ , ghStageSwitch :: StageSwitchVar } -askScreenVar :: StageRIO env (Worker.Var Vk.Extent2D)+{-# INLINE askScreenVar #-}+askScreenVar+ :: MonadReader (App GlobalHandles st) m+ => m (Worker.Var Vk.Extent2D) askScreenVar = asks $ ghScreenVar . appEnv instance HasVulkan GlobalHandles where@@ -118,10 +122,13 @@ -- | All the information required to render a single frame data Frame renderpass pipelines resources = Frame- { fIndex :: Word64 -- ^ Which number frame is this- , fWindow :: Window- , fSurface :: Khr.SurfaceKHR+ { fIndex :: Word64 -- ^ Which number frame is this+ , fWindow :: Window+ , fSurface :: Khr.SurfaceKHR + , fPresent :: Maybe Khr.PresentModeKHR+ , fMSAA :: Vk.SampleCountFlagBits+ , fSwapchainResources :: SwapchainResources , fRenderpass :: renderpass , fPipelines :: pipelines@@ -154,6 +161,24 @@ -} } +instance HasSwapchain (Frame renderpass pipelines resources) where+ getSurfaceExtent = getSurfaceExtent . fSwapchainResources+ getSurfaceFormat = getSurfaceFormat . fSwapchainResources+ getDepthFormat = getDepthFormat . fSwapchainResources+ getMultisample = getMultisample . fSwapchainResources+ getAnisotropy = getAnisotropy . fSwapchainResources+ getSwapchainViews = getSwapchainViews . fSwapchainResources+ getMinImageCount = getMinImageCount . fSwapchainResources+ getImageCount = getImageCount . fSwapchainResources+ {-# INLINE getSurfaceExtent #-}+ {-# INLINE getSurfaceFormat #-}+ {-# INLINE getDepthFormat #-}+ {-# INLINE getMultisample #-}+ {-# INLINE getAnisotropy #-}+ {-# INLINE getSwapchainViews #-}+ {-# INLINE getMinImageCount #-}+ {-# INLINE getImageCount #-}+ type GPUWork = ( "host semaphore" ::: Vk.Semaphore , "frame index" ::: Word64@@ -184,3 +209,8 @@ liftResourceT rt = asks (snd . fResources . snd) >>= liftIO . ResourceT.runInternalState rt++type HKD :: (Type -> Type) -> Type -> Type+type family HKD f a where+ HKD Identity a = a+ HKD f a = f a
src/Engine/Types/Options.hs view
@@ -7,6 +7,8 @@ import RIO import Options.Applicative.Simple qualified as Opt+import Vulkan.Core10 qualified as Vk+import Vulkan.Extensions.VK_KHR_surface qualified as Khr import Paths_keid_core qualified @@ -14,6 +16,8 @@ data Options = Options { optionsVerbose :: Bool , optionsMaxFPS :: Maybe Int+ , optionsPresent :: Maybe Khr.PresentModeKHR+ , optionsMsaa :: Vk.SampleCountFlagBits , optionsFullscreen :: Bool , optionsDisplay :: Natural , optionsSize :: Maybe (Int, Int)@@ -51,6 +55,17 @@ , Opt.metavar "FPS" ] + optionsPresent <- Opt.optional . Opt.option readPresent $ mconcat+ [ Opt.long "present"+ , Opt.help "Presentation mode"+ ]++ optionsMsaa <- Opt.option readMsaa $ mconcat+ [ Opt.long "msaa"+ , Opt.help "MSAA level"+ , Opt.value Vk.SAMPLE_COUNT_4_BIT+ ]+ optionsFullscreen <- Opt.switch $ mconcat [ Opt.long "fullscreen" , Opt.short 'f'@@ -76,10 +91,65 @@ pure Options{..} +readPresent :: Opt.ReadM Khr.PresentModeKHR+readPresent = named <|> integral <|> Opt.readerError oops+ where+ named = do+ o <- Opt.str+ case lookup o presentModes of+ Just found ->+ pure found+ Nothing ->+ Opt.readerError $ "unexpected mode name: " <> show o++ integral =+ fmap Khr.PresentModeKHR Opt.auto++ oops = unlines+ [ "Available present modes (or use a number): "+ , show $ map fst presentModes+ ]++presentModes :: [(Text, Khr.PresentModeKHR)]+presentModes =+ [ ("immediate", Khr.PRESENT_MODE_IMMEDIATE_KHR)+ , ("mailbox", Khr.PRESENT_MODE_MAILBOX_KHR)+ , ("fifo", Khr.PRESENT_MODE_FIFO_KHR)+ , ("fifo_relaxed", Khr.PRESENT_MODE_FIFO_RELAXED_KHR)+ , ("shared_demand_refresh", Khr.PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR)+ , ("shared_continuous_refresh", Khr.PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR)+ ]++readMsaa :: Opt.ReadM Vk.SampleCountFlagBits+readMsaa = do+ samples <- Opt.auto+ case lookup samples msaaSamples of+ Just found ->+ pure found+ Nothing ->+ Opt.readerError $ mconcat+ [ "Unexpected MSAA sample count: "+ , show samples+ , " (must be a power of 2 in [1..64] range)"+ ]++msaaSamples :: [(Int, Vk.SampleCountFlagBits)]+msaaSamples =+ [ ( 1, Vk.SAMPLE_COUNT_1_BIT)+ , ( 2, Vk.SAMPLE_COUNT_2_BIT)+ , ( 4, Vk.SAMPLE_COUNT_4_BIT)+ , ( 8, Vk.SAMPLE_COUNT_8_BIT)+ , ( 16, Vk.SAMPLE_COUNT_16_BIT)+ , ( 32, Vk.SAMPLE_COUNT_32_BIT)+ , ( 64, Vk.SAMPLE_COUNT_64_BIT)+ ]+ readSize :: Opt.ReadM (Int, Int) readSize = do o <- Opt.str- let (w,h) = break (== 'x') o+ let (w, h) = break (== 'x') o case (readMaybe w, readMaybe (drop 1 h)) of- (Just w', Just h') -> pure (w', h')- _ -> fail "Can't read window size"+ (Just w', Just h') ->+ pure (w', h')+ _ ->+ fail "Can't read window size"
src/Engine/Types/RefCounted.hs view
@@ -4,7 +4,8 @@ import Control.Monad.Trans.Resource (allocate_) import GHC.IO.Exception (IOErrorType(UserError), IOException(IOError))-import UnliftIO.Resource (MonadResource)+import UnliftIO.Resource (MonadResource, ReleaseKey)+import UnliftIO.Resource qualified as Resource -- | A 'RefCounted' will perform the specified action when the count reaches 0 data RefCounted = RefCounted@@ -48,3 +49,9 @@ resourceTRefCount :: MonadResource f => RefCounted -> f () resourceTRefCount r = void $ allocate_ (takeRefCounted r) (releaseRefCounted r)++wrapped :: MonadResource m => m (ReleaseKey, a) -> m (RefCounted, a)+wrapped action = do+ (key, res) <- action+ rc <- newRefCounted $ Resource.release key+ pure (rc, res)
src/Engine/UI/Layout.hs view
@@ -42,6 +42,8 @@ import Geomancy (Transform, Vec2, Vec4, vec2, vec4, withVec2, pattern WithVec2, pattern WithVec4) import Geomancy.Transform qualified as Transform import Geomancy.Vec4 qualified as Vec4+import RIO.App (App)+import UnliftIO.Resource (MonadResource) import Vulkan.Core10 qualified as Vk import Vulkan.NamedType ((:::)) @@ -94,7 +96,12 @@ type BoxProcess = Worker.Merge Box -trackScreen :: Engine.StageRIO st BoxProcess+trackScreen+ :: ( MonadReader (App Engine.GlobalHandles st) m+ , MonadResource m+ , MonadUnliftIO m+ )+ => m BoxProcess trackScreen = do screen <- Engine.askScreenVar Worker.spawnMerge1 mkBox screen@@ -105,7 +112,8 @@ } padAbs- :: ( MonadUnliftIO m+ :: ( MonadResource m+ , MonadUnliftIO m , Worker.HasOutput parent , Worker.GetOutput parent ~ Box , Worker.HasOutput padding@@ -130,7 +138,8 @@ dh = min h (top + bottom) padRel- :: ( MonadUnliftIO m+ :: ( MonadResource m+ , MonadUnliftIO m , Worker.HasOutput parent , Worker.GetOutput parent ~ Box , Worker.HasOutput padding@@ -147,7 +156,8 @@ boxPadAbs box (pad * vec4 h w h w) fitPlaceAbs- :: ( MonadUnliftIO m+ :: ( MonadResource m+ , MonadUnliftIO m , Worker.HasOutput parent , Worker.GetOutput parent ~ Box )@@ -210,7 +220,8 @@ aspect = w / h splitsRelStatic- :: ( MonadUnliftIO m+ :: ( MonadResource m+ , MonadUnliftIO m , Worker.HasOutput parent , Worker.GetOutput parent ~ Box , Traversable t -- XXX: nested traversables may behave suprisingly@@ -243,7 +254,8 @@ ) hSplitRel- :: ( MonadUnliftIO m+ :: ( MonadResource m+ , MonadUnliftIO m , Worker.HasOutput parent , Worker.GetOutput parent ~ Box , Worker.HasOutput proportion@@ -274,6 +286,7 @@ vSplitRel :: ( MonadUnliftIO m+ , MonadResource m , Worker.HasOutput parent , Worker.GetOutput parent ~ Box , Worker.HasOutput proportion
+ src/Engine/Vulkan/Format.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE AllowAmbiguousTypes #-}++module Engine.Vulkan.Format+ ( HasVkFormat(..)+ , genericVkFormat++ , formatSize+ ) where++import RIO+import GHC.Generics+import Geomancy++import Data.Kind (Constraint, Type)+import Geomancy.IVec3 qualified as IVec3+import Geomancy.UVec3 qualified as UVec3+import Geomancy.Vec3 qualified as Vec3+import Vulkan.Core10 qualified as Vk++class HasVkFormat a where+ getVkFormat :: [Vk.Format]++ default getVkFormat+ :: GVkFormat (Rep a)+ => [Vk.Format]+ getVkFormat = genericVkFormat @a++instance HasVkFormat () where+ getVkFormat = []++instance HasVkFormat Float where+ getVkFormat = [Vk.FORMAT_R32_SFLOAT]++instance HasVkFormat Vec2 where+ getVkFormat = [Vk.FORMAT_R32G32_SFLOAT]++instance HasVkFormat Vec3 where+ getVkFormat = [Vk.FORMAT_R32G32B32A32_SFLOAT]++instance HasVkFormat Vec3.Packed where+ getVkFormat = [Vk.FORMAT_R32G32B32_SFLOAT]++instance HasVkFormat Vec4 where+ getVkFormat = [Vk.FORMAT_R32G32B32A32_SFLOAT]++instance HasVkFormat Quaternion where+ getVkFormat = [Vk.FORMAT_R32G32B32A32_SFLOAT]++instance HasVkFormat Mat4 where+ getVkFormat =+ [ Vk.FORMAT_R32G32B32A32_SFLOAT+ , Vk.FORMAT_R32G32B32A32_SFLOAT+ , Vk.FORMAT_R32G32B32A32_SFLOAT+ , Vk.FORMAT_R32G32B32A32_SFLOAT+ ]++instance HasVkFormat Transform where+ getVkFormat = getVkFormat @Mat4++instance HasVkFormat Int32 where+ getVkFormat = [Vk.FORMAT_R32_SINT]++instance HasVkFormat IVec2 where+ getVkFormat = [Vk.FORMAT_R32G32_SINT]++instance HasVkFormat IVec3 where+ getVkFormat = [Vk.FORMAT_R32G32B32_SINT]++instance HasVkFormat IVec3.Packed where+ getVkFormat = [Vk.FORMAT_R32G32B32_SINT]++instance HasVkFormat IVec4 where+ getVkFormat = [Vk.FORMAT_R32G32B32A32_SINT]++instance HasVkFormat Word32 where+ getVkFormat = [Vk.FORMAT_R32_UINT]++instance HasVkFormat UVec2 where+ getVkFormat = [Vk.FORMAT_R32G32_UINT]++instance HasVkFormat UVec3 where+ getVkFormat = [Vk.FORMAT_R32G32B32A32_UINT]++instance HasVkFormat UVec3.Packed where+ getVkFormat = [Vk.FORMAT_R32G32B32_UINT]++instance HasVkFormat UVec4 where+ getVkFormat = [Vk.FORMAT_R32G32B32A32_UINT]++instance HasVkFormat v => HasVkFormat (Point v) where+ getVkFormat = getVkFormat @v++genericVkFormat+ :: forall a+ . GVkFormat (Rep a)+ => [Vk.Format]+genericVkFormat = gVkFormat (Proxy @(Rep a))++type GVkFormat :: (Type -> Type) -> Constraint+class GVkFormat f where+ gVkFormat :: proxy f -> [Vk.Format]++instance GVkFormat f => GVkFormat (M1 c cb f) where+ gVkFormat _m1 = gVkFormat (Proxy @f)++instance (GVkFormat l, GVkFormat r) => GVkFormat (l :*: r) where+ gVkFormat _lr = gVkFormat (Proxy @l) <> gVkFormat (Proxy @r)++instance HasVkFormat a => GVkFormat (K1 r a) where+ gVkFormat _k1 = getVkFormat @a++formatSize :: Integral a => Vk.Format -> a+formatSize = \case+ Vk.FORMAT_R32G32B32A32_SFLOAT -> 16+ Vk.FORMAT_R32G32B32_SFLOAT -> 12+ Vk.FORMAT_R32G32_SFLOAT -> 8+ Vk.FORMAT_R32_SFLOAT -> 4++ Vk.FORMAT_R32G32B32A32_UINT -> 16+ Vk.FORMAT_R32G32B32_UINT -> 12+ Vk.FORMAT_R32G32_UINT -> 8+ Vk.FORMAT_R32_UINT -> 4++ Vk.FORMAT_R32G32B32A32_SINT -> 16+ Vk.FORMAT_R32G32B32_SINT -> 12+ Vk.FORMAT_R32G32_SINT -> 8+ Vk.FORMAT_R32_SINT -> 4++ format ->+ error $ "Format size unknown: " <> show format
src/Engine/Vulkan/Pipeline.hs view
@@ -1,6 +1,6 @@ module Engine.Vulkan.Pipeline ( Pipeline(..)- , destroy+ , allocateWith , Specialization ) where @@ -9,9 +9,11 @@ import Data.Kind (Type) import Data.Tagged (Tagged(..)) import Data.Vector qualified as Vector+import GHC.Stack (withFrozenCallStack)+import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk -import Engine.Vulkan.Types (HasVulkan(..), DsLayouts)+import Engine.Vulkan.Types (DsLayouts, MonadVulkan, getDevice) data Pipeline (dsl :: [Type]) vertices instances = Pipeline { pipeline :: Vk.Pipeline@@ -19,19 +21,29 @@ , pDescLayouts :: Tagged dsl DsLayouts } -destroy- :: ( MonadIO io- , HasVulkan ctx+allocateWith+ :: ( MonadVulkan env m+ , Resource.MonadResource m )- => ctx+ => m (Pipeline dsl vertices instances)+ -> m (Resource.ReleaseKey, Pipeline dsl vertices instances)+allocateWith action = withFrozenCallStack do+ res <- action+ device <- asks getDevice+ key <- Resource.register $+ destroy device res+ pure (key, res)++destroy+ :: MonadIO io+ => Vk.Device -> Pipeline dsl vertices instances -> io ()-destroy context Pipeline{..} = do+destroy device Pipeline{..} = do+ -- FIXME: leave layout alone Vector.forM_ (unTagged pDescLayouts) \dsLayout -> Vk.destroyDescriptorSetLayout device dsLayout Nothing Vk.destroyPipeline device pipeline Nothing Vk.destroyPipelineLayout device (unTagged pLayout) Nothing- where- device = getDevice context type family Specialization pipeline
src/Engine/Vulkan/Pipeline/Compute.hs view
@@ -17,7 +17,6 @@ , Pipeline(..) , allocate , create- , destroy , bind , Compute@@ -26,31 +25,30 @@ import RIO import Data.Kind (Type)-import Data.List qualified as List import Data.Tagged (Tagged(..)) import Data.Vector qualified as Vector import GHC.Generics (Generic1)-import GHC.Stack (callStack, getCallStack, srcLocModule, withFrozenCallStack)+import GHC.Stack (withFrozenCallStack) import UnliftIO.Resource (MonadResource, ReleaseKey)-import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk-import Vulkan.Core12.Promoted_From_VK_EXT_descriptor_indexing qualified as Vk12-import Vulkan.CStruct.Extends (SomeStruct(..), pattern (:&), pattern (::&))-import Vulkan.Utils.Debug qualified as Debug+import Vulkan.CStruct.Extends (SomeStruct(..)) import Vulkan.Zero (Zero(..)) import Engine.SpirV.Reflect (Reflect) import Engine.Vulkan.DescSets (Bound(..), Compatible)-import Engine.Vulkan.Pipeline (Pipeline(..), destroy)+import Engine.Vulkan.Pipeline (Pipeline(..))+import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Pipeline.Stages (StageInfo(..)) import Engine.Vulkan.Shader qualified as Shader-import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, DsBindings, getPipelineCache)+import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, DsLayoutBindings, getPipelineCache) import Render.Code (Code) import Resource.Collection (Generically1(..))+import Resource.Vulkan.DescriptorLayout qualified as Layout+import Resource.Vulkan.Named qualified as Named data Config (dsl :: [Type]) spec = Config { cComputeCode :: ByteString- , cDescLayouts :: Tagged dsl [DsBindings]+ , cDescLayouts :: Tagged dsl [DsLayoutBindings] , cPushConstantRanges :: Vector Vk.PushConstantRange , cSpecialization :: spec }@@ -87,66 +85,46 @@ ) => Config dsl spec -> m (ReleaseKey, Pipeline dsl Compute Compute)-allocate config = withFrozenCallStack do- ctx <- ask- Resource.allocate- (create ctx config)- (destroy ctx)+allocate config =+ withFrozenCallStack $+ Pipeline.allocateWith $ create config create- :: ( HasVulkan ctx- , MonadUnliftIO m+ :: ( MonadVulkan env io , Shader.Specialization spec+ , HasCallStack )- => ctx- -> Config dsl spec- -> m (Pipeline dsl Compute Compute)-create context Config{..} = do- -- XXX: copypasta from Pipeline.create- let- originModule =- fromString . List.intercalate "|" $- map (srcLocModule . snd) (getCallStack callStack)-- dsLayouts <- Vector.forM (Vector.fromList $ unTagged cDescLayouts) \bindsFlags -> do- let- (binds, flags) = List.unzip bindsFlags-- setCI =- zero- { Vk.bindings = Vector.fromList binds- }- ::& zero- { Vk12.bindingFlags = Vector.fromList flags- }- :& ()-- Vk.createDescriptorSetLayout device setCI Nothing-- -- TODO: get from outside- layout <- Vk.createPipelineLayout device (layoutCI dsLayouts) Nothing- Debug.nameObject device layout originModule+ => Config dsl spec+ -> io (Pipeline dsl Compute Compute)+create Config{..} = withFrozenCallStack do+ -- TODO: get from outside ?+ dsLayouts <- Layout.create $ Vector.fromList (unTagged cDescLayouts) - -- Compute stuff begins...+ -- TODO: get from outside ??+ pipelineLayout <- Layout.forPipeline+ dsLayouts+ cPushConstantRanges+ Named.objectOrigin pipelineLayout shader <- Shader.withSpecialization cSpecialization $- Shader.create context Stages+ Shader.create Stages { comp = Just cComputeCode } let cis = Vector.singleton . SomeStruct $- pipelineCI (Shader.sPipelineStages shader) layout+ pipelineCI (Shader.sPipelineStages shader) pipelineLayout + device <- asks getDevice Vk.createComputePipelines device cache cis Nothing >>= \case (Vk.SUCCESS, pipelines) -> case pipelines of- [one] -> do- Shader.destroy context shader- Debug.nameObject device one originModule+ [pipeline] -> do+ Shader.destroy shader+ Named.objectOrigin pipeline pure Pipeline- { pipeline = one- , pLayout = Tagged layout+ { pipeline = pipeline+ , pLayout = Tagged pipelineLayout , pDescLayouts = Tagged dsLayouts } _ ->@@ -155,14 +133,7 @@ throwString $ "createComputePipelines: " <> show err where- device = getDevice context- cache = getPipelineCache context-- layoutCI dsLayouts = Vk.PipelineLayoutCreateInfo- { flags = zero- , setLayouts = dsLayouts- , pushConstantRanges = cPushConstantRanges- }+ cache = getPipelineCache undefined pipelineCI stages layout = zero { Vk.layout = layout
src/Engine/Vulkan/Pipeline/External.hs view
@@ -28,15 +28,17 @@ import RIO -import Control.Monad.Trans.Resource (ReleaseKey, ResourceT, release)+import Control.Monad.Trans.Resource (ResourceT) import Data.List (maximum) import Data.Tagged (Tagged(..)) import RIO.ByteString qualified as ByteString import RIO.Directory (createDirectoryIfMissing, getModificationTime, doesFileExist)+import RIO.FilePath ((</>), (<.>)) import RIO.Map qualified as Map import RIO.Text qualified as Text import RIO.Time (UTCTime, getCurrentTime)-import RIO.FilePath ((</>), (<.>))+import UnliftIO.Resource (MonadResource, ReleaseKey)+import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk import Render.Code (Code(..))@@ -47,16 +49,17 @@ import Engine.Vulkan.Pipeline.Graphics qualified as Graphics import Engine.Vulkan.Pipeline.Stages (StageInfo(..)) import Engine.Vulkan.Shader qualified as Shader-import Engine.Vulkan.Types (DsBindings, HasRenderPass)+import Engine.Vulkan.Types (DsLayoutBindings, HasRenderPass) import Engine.Worker qualified as Worker type Process config = Worker.Timed () config spawn :: ( Foldable stages- , MonadUnliftIO m , MonadReader env m , HasLogFunc env+ , MonadResource m+ , MonadUnliftIO m ) => (stages (Maybe FilePath) -> m stuff) -> Text@@ -95,7 +98,8 @@ ) spawnReflect- :: ( MonadUnliftIO m+ :: ( MonadResource m+ , MonadUnliftIO m , MonadReader env m , HasLogFunc env , StageInfo stages@@ -149,7 +153,7 @@ reflDS <- Reflect.stagesBindMap stageRefl - reflIS <- Reflect.stagesInterfaceMap stageRefl+ let reflIS = Reflect.stagesInterfaceMap stageRefl case Reflect.interfaceCompatible reflIS of Right ok -> logDebug $ displayShow ok@@ -221,14 +225,14 @@ ) => renderpass -> Vk.SampleCountFlagBits- -> Tagged dsl [DsBindings]+ -> Tagged dsl [DsLayoutBindings] -> output -> Worker.ObserverIO (ReleaseKey, pipeline) -> StageFrameRIO rp p fr rs () observeGraphics rp msaa sceneBinds configP output = void $! Worker.observeIO configP output \(oldKey, _old) config -> do logDebug "Rebuilding pipeline"- release oldKey+ Resource.release oldKey mapRIO fst $ Graphics.allocate Nothing msaa@@ -258,14 +262,14 @@ , config ~ Compute.Configure pipeline spec , pipeline ~ Compute.Pipeline dsl Compute Compute )- => Tagged dsl [DsBindings]+ => Tagged dsl [DsLayoutBindings] -> output -> Worker.ObserverIO (ReleaseKey, pipeline) -> StageFrameRIO rp p fr rs () observeCompute binds configP output = void $! Worker.observeIO configP output \(oldKey, _old) config -> do logDebug "Rebuilding pipeline"- release oldKey+ Resource.release oldKey mapRIO fst $ Compute.allocate config { Compute.cDescLayouts = binds }@@ -297,7 +301,7 @@ ) => renderpass -> Vk.SampleCountFlagBits- -> Tagged dsl DsBindings+ -> Tagged dsl DsLayoutBindings -> pf ConfigureGraphics -> pf Observers -> (forall a . pf a -> a ^ p)
src/Engine/Vulkan/Pipeline/Graphics.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE UndecidableInstances #-} @@ -27,38 +28,42 @@ , Pipeline(..) , allocate , create- , Pipeline.destroy , bind++ , HasVertexInputBindings(..)+ , vertexFormat+ , instanceFormat ) where import RIO+import GHC.Generics import Data.Bits ((.|.)) import Data.Kind (Type) import Data.List qualified as List import Data.Tagged (Tagged(..)) import Data.Vector qualified as Vector-import GHC.Generics (Generic1)-import GHC.Stack (callStack, getCallStack, srcLocModule, withFrozenCallStack)+import Geomancy (Transform)+import GHC.Stack (withFrozenCallStack) import UnliftIO.Resource (MonadResource, ReleaseKey)-import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk-import Vulkan.Core12.Promoted_From_VK_EXT_descriptor_indexing qualified as Vk12-import Vulkan.CStruct.Extends (SomeStruct(..), pattern (:&), pattern (::&))+import Vulkan.CStruct.Extends (SomeStruct(..)) import Vulkan.NamedType ((:::))-import Vulkan.Utils.Debug qualified as Debug import Vulkan.Zero (Zero(..)) import Engine.SpirV.Reflect (Reflect) import Engine.Vulkan.DescSets (Bound(..), Compatible)+import Engine.Vulkan.Format (HasVkFormat(..), formatSize) import Engine.Vulkan.Pipeline (Pipeline(..)) import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Pipeline.Stages (StageInfo(..)) import Engine.Vulkan.Shader qualified as Shader-import Engine.Vulkan.Types (HasVulkan(..), HasRenderPass(..), MonadVulkan, DsBindings, getPipelineCache)+import Engine.Vulkan.Types (HasVulkan(..), HasRenderPass(..), MonadVulkan, DsLayoutBindings, getPipelineCache) import Render.Code (Code) import Resource.Collection (Generically1(..))+import Resource.Vulkan.DescriptorLayout qualified as Layout+import Resource.Vulkan.Named qualified as Named data Stages a = Stages { vert :: a -- ^ vertex@@ -119,7 +124,7 @@ { cStages :: StageSpirv , cReflect :: Maybe StageReflect , cVertexInput :: SomeStruct Vk.PipelineVertexInputStateCreateInfo- , cDescLayouts :: Tagged dsl [DsBindings]+ , cDescLayouts :: Tagged dsl [DsLayoutBindings] , cPushConstantRanges :: Vector Vk.PushConstantRange , cBlend :: Bool , cDepthWrite :: Bool@@ -175,66 +180,48 @@ -> Config dsl vertices instances spec -> renderpass -> m (ReleaseKey, pipeline)-allocate extent msaa config renderpass = withFrozenCallStack do- ctx <- ask- Resource.allocate- (create ctx extent msaa renderpass config)- (Pipeline.destroy ctx)+allocate extent msaa config renderpass =+ withFrozenCallStack $+ Pipeline.allocateWith $ create extent msaa renderpass config create- :: ( MonadUnliftIO io- , HasVulkan ctx+ :: ( MonadVulkan env io , HasRenderPass renderpass , Shader.Specialization spec , HasCallStack )- => ctx- -> Maybe Vk.Extent2D+ => Maybe Vk.Extent2D -> Vk.SampleCountFlagBits -> renderpass -> Config dsl vertices instances spec -> io (Pipeline dsl vertices instances)-create context mextent msaa renderpass Config{..} = do- let- originModule =- fromString . List.intercalate "|" $- map (srcLocModule . snd) (getCallStack callStack)-- dsLayouts <- Vector.forM (Vector.fromList $ unTagged cDescLayouts) \bindsFlags -> do- let- (binds, flags) = List.unzip bindsFlags-- setCI =- zero- { Vk.bindings = Vector.fromList binds- }- ::& zero- { Vk12.bindingFlags = Vector.fromList flags- }- :& ()-- Vk.createDescriptorSetLayout device setCI Nothing-+create mextent msaa renderpass Config{..} = withFrozenCallStack do -- TODO: get from outside- layout <- Vk.createPipelineLayout device (layoutCI dsLayouts) Nothing- Debug.nameObject device layout originModule+ dsLayouts <- Layout.create $ Vector.fromList (unTagged cDescLayouts) + -- TODO: get from outside ?+ pipelineLayout <- Layout.forPipeline+ dsLayouts+ cPushConstantRanges+ Named.objectOrigin pipelineLayout+ shader <- Shader.withSpecialization cSpecialization $- Shader.create context cStages+ Shader.create cStages let cis = Vector.singleton . SomeStruct $- pipelineCI (Shader.sPipelineStages shader) layout+ pipelineCI (Shader.sPipelineStages shader) pipelineLayout + device <- asks getDevice Vk.createGraphicsPipelines device cache cis Nothing >>= \case (Vk.SUCCESS, pipelines) -> case Vector.toList pipelines of- [one] -> do- Shader.destroy context shader- Debug.nameObject device one originModule+ [pipeline] -> do+ Shader.destroy shader+ Named.objectOrigin pipeline pure Pipeline- { pipeline = one- , pLayout = Tagged layout+ { pipeline = pipeline+ , pLayout = Tagged pipelineLayout , pDescLayouts = Tagged dsLayouts } _ ->@@ -242,14 +229,7 @@ (err, _) -> error $ "createGraphicsPipelines: " <> show err where- device = getDevice context- cache = getPipelineCache context-- layoutCI dsLayouts = Vk.PipelineLayoutCreateInfo- { flags = zero- , setLayouts = dsLayouts- , pushConstantRanges = cPushConstantRanges- }+ cache = getPipelineCache undefined pipelineCI stages layout = zero { Vk.stages = stages@@ -385,8 +365,14 @@ Bound $ Vk.cmdBindPipeline cb Vk.PIPELINE_BIND_POINT_GRAPHICS pipeline Bound attrAction -vertexInput :: [(Vk.VertexInputRate, [Vk.Format])] -> SomeStruct Vk.PipelineVertexInputStateCreateInfo-vertexInput bindings = SomeStruct zero+vertexInput+ :: forall a pipeLayout vertices instances+ . ( a ~ Pipeline pipeLayout vertices instances+ , HasVertexInputBindings vertices -- XXX: 0-2 of {positions, attrs} (e.g. position + uv)+ , HasVertexInputBindings instances -- XXX: 0+ of instance attrs (e.g. static params + dynamic transforms)+ )+ => SomeStruct Vk.PipelineVertexInputStateCreateInfo+vertexInput = SomeStruct zero { Vk.vertexBindingDescriptions = binds , Vk.vertexAttributeDescriptions = attrs }@@ -401,6 +387,11 @@ attrs = attrBindings $ map snd bindings + bindings =+ filter (not . null . snd) $+ vertexInputBindings @vertices <>+ vertexInputBindings @instances+ -- * Utils attrBindings :: [[Vk.Format]] -> Vector Vk.VertexInputAttributeDescription@@ -426,22 +417,19 @@ , rest ) -formatSize :: Integral a => Vk.Format -> a-formatSize = \case- Vk.FORMAT_R32G32B32A32_SFLOAT -> 16- Vk.FORMAT_R32G32B32_SFLOAT -> 12- Vk.FORMAT_R32G32_SFLOAT -> 8- Vk.FORMAT_R32_SFLOAT -> 4+type VertexInputBinding = (Vk.VertexInputRate, [Vk.Format]) - Vk.FORMAT_R32G32B32A32_UINT -> 16- Vk.FORMAT_R32G32B32_UINT -> 12- Vk.FORMAT_R32G32_UINT -> 8- Vk.FORMAT_R32_UINT -> 4+vertexFormat :: forall a . HasVkFormat a => VertexInputBinding+vertexFormat = (Vk.VERTEX_INPUT_RATE_VERTEX, getVkFormat @a) - Vk.FORMAT_R32G32B32A32_SINT -> 16- Vk.FORMAT_R32G32B32_SINT -> 12- Vk.FORMAT_R32G32_SINT -> 8- Vk.FORMAT_R32_SINT -> 4+instanceFormat :: forall a . HasVkFormat a => VertexInputBinding+instanceFormat = (Vk.VERTEX_INPUT_RATE_INSTANCE, getVkFormat @a) - format ->- error $ "Format size unknown: " <> show format+class HasVertexInputBindings a where+ vertexInputBindings :: [VertexInputBinding]++instance HasVertexInputBindings () where+ vertexInputBindings = []++instance HasVertexInputBindings Transform where+ vertexInputBindings = [instanceFormat @Transform]
src/Engine/Vulkan/Shader.hs view
@@ -18,13 +18,15 @@ import Data.Vector qualified as Vector import Data.Vector.Storable qualified as Storable import Foreign qualified+import GHC.Stack (withFrozenCallStack) import Vulkan.Core10 qualified as Vk import Vulkan.CStruct.Extends (SomeStruct(..)) import Vulkan.Zero (Zero(..)) import Unsafe.Coerce (unsafeCoerce) import Engine.Vulkan.Pipeline.Stages (StageInfo(..))-import Engine.Vulkan.Types (HasVulkan(..))+import Engine.Vulkan.Types (MonadVulkan, getDevice)+import Resource.Vulkan.Named qualified as Named data Shader = Shader { sModules :: Vector Vk.ShaderModule@@ -32,23 +34,25 @@ } create- :: ( MonadIO io- , HasVulkan ctx+ :: ( MonadVulkan env io , StageInfo t+ , HasCallStack )- => ctx- -> t (Maybe ByteString)+ => t (Maybe ByteString) -> Maybe Vk.SpecializationInfo -> io Shader-create context stages spec = do+create stages spec = withFrozenCallStack do+ device <- asks getDevice staged <- Vector.forM collected \(stage, code) -> do module_ <- Vk.createShaderModule- (getDevice context)+ device zero { Vk.code = code } Nothing + Named.objectOrigin module_+ pure ( module_ , SomeStruct zero@@ -68,10 +72,11 @@ (stage, Just code) <- toList $ (,) <$> stageFlagBits <*> stages pure (stage, code) -destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Shader -> io ()-destroy context Shader{sModules} =+destroy :: MonadVulkan env io => Shader -> io ()+destroy Shader{sModules} = do+ device <- asks getDevice Vector.forM_ sModules \module_ ->- Vk.destroyShaderModule (getDevice context) module_ Nothing+ Vk.destroyShaderModule device module_ Nothing -- * Specialization constants
src/Engine/Vulkan/Swapchain.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedRecordDot #-} module Engine.Vulkan.Swapchain ( SwapchainResources(..)@@ -70,7 +71,9 @@ , HasVulkan env , HasLogFunc env )- => Khr.SwapchainKHR+ => Maybe Khr.PresentModeKHR+ -> Vk.SampleCountFlagBits+ -> Khr.SwapchainKHR -- ^ Previous swapchain, can be NULL_HANDLE -> Vk.Extent2D -- ^ If the swapchain size determines the surface size, use this size@@ -78,11 +81,11 @@ -> Worker.Var Vk.Extent2D -> RIO env SwapchainResources -- -> ResourceT (RIO env) SwapchainResources-allocSwapchainResources oldSwapchain windowSize surface screenVar = do+allocSwapchainResources present msaa oldSwapchain windowSize surface screenVar = do logDebug "Allocating swapchain resources" device <- asks getDevice- info@SwapchainInfo{..} <- createSwapchain oldSwapchain windowSize surface+ info@SwapchainInfo{..} <- createSwapchain present msaa oldSwapchain windowSize surface -- XXX: Get all the swapchain images, and create views for them (_, swapchainImages) <- Khr.getSwapchainImagesKHR device siSwapchain@@ -112,12 +115,16 @@ , HasVulkan env , HasLogFunc env )- => Vk.Extent2D+ => Maybe Khr.PresentModeKHR+ -> Vk.SampleCountFlagBits+ -> Vk.Extent2D -> SwapchainResources -- ^ The reference to these resources will be dropped -> RIO env SwapchainResources-recreateSwapchainResources windowSize oldResources = do+recreateSwapchainResources present msaa windowSize oldResources = do sr <- allocSwapchainResources+ present+ msaa (siSwapchain $ srInfo oldResources) windowSize (siSurface $ srInfo oldResources)@@ -131,13 +138,15 @@ , MonadVulkan env m , HasLogFunc env )- => Khr.SwapchainKHR+ => Maybe Khr.PresentModeKHR+ -> Vk.SampleCountFlagBits+ -> Khr.SwapchainKHR -- ^ Old swapchain, can be NULL_HANDLE -> Vk.Extent2D -- ^ If the swapchain size determines the surface size, use this size -> Khr.SurfaceKHR -> m SwapchainInfo-createSwapchain oldSwapchain explicitSize surf = do+createSwapchain present msaa oldSwapchain explicitSize surf = do physical <- asks getPhysicalDevice device <- asks getDevice props <- asks $ pdiProperties . getPhysicalDeviceInfo@@ -152,8 +161,15 @@ throwString $ "Surface images do not support " <> show flag -- Select a present mode+ desiredPresentModes <- case present of+ Nothing -> do+ logDebug $ "Using default present modes: " <> displayShow defaultPresentModes+ pure defaultPresentModes+ Just selected -> do+ logDebug $ "Forcing selected present mode: " <> displayShow selected+ pure [selected] (_, availablePresentModes) <- Khr.getPhysicalDeviceSurfacePresentModesKHR physical surf- presentMode <-+ presentMode <- case filter (`V.elem` availablePresentModes) desiredPresentModes of [] -> do logError "Unable to find a suitable present mode for swapchain"@@ -184,14 +200,14 @@ let minImageCount = let- limit = case Khr.maxImageCount (surfaceCaps :: Khr.SurfaceCapabilitiesKHR) of+ limit = case surfaceCaps.maxImageCount of 0 -> maxBound n -> n -- Request one additional image to prevent us having to wait for -- the driver to finish buffer = 1 desired =- buffer + Khr.minImageCount (surfaceCaps :: Khr.SurfaceCapabilitiesKHR)+ buffer + surfaceCaps.minImageCount in min limit desired @@ -232,7 +248,7 @@ , siSurfaceFormat = surfaceFormat , siSurfaceColorspace = surfaceColorspace , siDepthFormat = depthFormat- , siMultisample = msaaSamples props+ , siMultisample = msaaSamples msaa props , siAnisotropy = Vk.maxSamplerAnisotropy (Vk.limits props) , siImageExtent = imageExtent }@@ -295,10 +311,14 @@ , Vk.FORMAT_D32_SFLOAT ] -msaaSamples :: Vk.PhysicalDeviceProperties -> Vk.SampleCountFlagBits-msaaSamples Vk.PhysicalDeviceProperties{limits} =+msaaSamples+ :: Vk.SampleCountFlagBits+ -> Vk.PhysicalDeviceProperties+ -> Vk.SampleCountFlagBits+msaaSamples upperLimit Vk.PhysicalDeviceProperties{limits} = case samplesAvailable of [] ->+ -- XXX: Something went wrong... Vk.SAMPLE_COUNT_1_BIT best : _rest -> best@@ -309,27 +329,26 @@ samplesAvailable = do countBit <- msaaCandidates- guard $ (counts .&. countBit) /= zeroBits+ guard $ countBit <= upperLimit -- XXX: requested+ guard $ (counts .&. countBit) /= zeroBits -- XXX: capable pure countBit msaaCandidates :: [Vk.SampleCountFlagBits] msaaCandidates =- {-- XXX: Also possible, but not that impactful: 16x, 8x.-- Khronos MSAA best practice says:- Use 4x MSAA if possible; it's not expensive and provides good image quality improvements.- -}- [ Vk.SAMPLE_COUNT_4_BIT+ [ Vk.SAMPLE_COUNT_64_BIT -- XXX: extremely unrealistic?+ , Vk.SAMPLE_COUNT_32_BIT -- XXX: unrealistic?+ , Vk.SAMPLE_COUNT_16_BIT -- XXX: possible, but not that impactful+ , Vk.SAMPLE_COUNT_8_BIT+ , Vk.SAMPLE_COUNT_4_BIT -- XXX: Khronos-recommended for rasterizing , Vk.SAMPLE_COUNT_2_BIT+ , Vk.SAMPLE_COUNT_1_BIT -- XXX: i.e. "disable", always available ] -- | An ordered list of the present mode to be chosen for the swapchain.-desiredPresentModes :: [Khr.PresentModeKHR]-desiredPresentModes =+defaultPresentModes :: [Khr.PresentModeKHR]+defaultPresentModes = [ Khr.PRESENT_MODE_FIFO_RELAXED_KHR , Khr.PRESENT_MODE_FIFO_KHR -- ^ This will always be present- , Khr.PRESENT_MODE_IMMEDIATE_KHR -- ^ Keep this here for easy swapping for testing ] -- | The images in the swapchain must support these flags.@@ -402,8 +421,14 @@ Vk.Extent2D{width, height} = extent Vk.Rect2D{offset, extent} = viewrect -setDynamicFullscreen :: MonadIO io => Vk.CommandBuffer -> SwapchainResources -> io ()-setDynamicFullscreen cb sr = do+setDynamicFullscreen+ :: ( HasSwapchain swapchain+ , MonadIO io+ )+ => Vk.CommandBuffer+ -> swapchain+ -> io ()+setDynamicFullscreen cb swapchain = do Vk.cmdSetViewport cb 0 [ Vk.Viewport@@ -417,11 +442,10 @@ ] Vk.cmdSetScissor cb 0 [ Vk.Rect2D- { offset = Vk.Offset2D 0 0- , extent = siImageExtent+ { offset = zero+ , extent = extent } ] where- SwapchainResources{srInfo} = sr- SwapchainInfo{siImageExtent} = srInfo- Vk.Extent2D{width, height} = siImageExtent+ extent@Vk.Extent2D{width, height} =+ getSurfaceExtent swapchain
src/Engine/Vulkan/Types.hs view
@@ -11,7 +11,7 @@ , Queues(..) , DsLayouts- , DsBindings+ , DsLayoutBindings , Bound(..) ) where@@ -20,6 +20,7 @@ import Data.Kind (Type) import RIO.App (App(..))+import RIO.State (MonadState(..)) import UnliftIO.Resource (MonadResource) import Vulkan.Core10 qualified as Vk import Vulkan.Core12.Promoted_From_VK_EXT_descriptor_indexing qualified as Vk12@@ -115,8 +116,10 @@ => a -> RIO env () -type DsBindings = [(Vk.DescriptorSetLayoutBinding, Vk12.DescriptorBindingFlags)]+type DsLayoutBindings = [(Vk.DescriptorSetLayoutBinding, Vk12.DescriptorBindingFlags)] type DsLayouts = Vector Vk.DescriptorSetLayout newtype Bound (dsl :: [Type]) vertices instances m a = Bound (m a)- deriving (Foldable, Traversable, Functor, Applicative, Monad, MonadIO, MonadUnliftIO)+ deriving stock (Foldable, Traversable, Functor)+ deriving newtype (Applicative, Monad, MonadIO, MonadUnliftIO)+ deriving newtype (MonadReader r, MonadState s)
src/Engine/Window/CursorPos.hs view
@@ -16,11 +16,15 @@ import UnliftIO.Resource (ReleaseKey) import UnliftIO.Resource qualified as Resource -import Engine.Types (GlobalHandles(..), StageRIO)+import Engine.Types (ghWindow)+import Engine.Events.Sink (MonadSink) -type Callback st = Double -> Double -> StageRIO st ()+type Callback m = Double -> Double -> m () -callback :: Callback st -> StageRIO st ReleaseKey+callback+ :: MonadSink rs m+ => Callback m+ -> m ReleaseKey callback handler = do window <- asks $ ghWindow . appEnv withUnliftIO \ul ->@@ -28,7 +32,7 @@ Resource.register $ GLFW.setCursorPosCallback window Nothing -mkCallback :: UnliftIO (StageRIO st) -> Callback st -> GLFW.CursorPosCallback+mkCallback :: UnliftIO m -> Callback m -> GLFW.CursorPosCallback mkCallback (UnliftIO ul) action = \_window px py -> ul $ action px py
src/Engine/Window/Drop.hs view
@@ -12,11 +12,12 @@ import UnliftIO.Resource (ReleaseKey) import UnliftIO.Resource qualified as Resource -import Engine.Types (GlobalHandles(..), StageRIO)+import Engine.Events.Sink (MonadSink)+import Engine.Types (GlobalHandles(..)) -type Callback st = [FilePath] -> StageRIO st ()+type Callback m = [FilePath] -> m () -callback :: Callback st -> StageRIO st ReleaseKey+callback :: MonadSink rs m => Callback m -> m ReleaseKey callback handler = do window <- asks $ ghWindow . appEnv withUnliftIO \ul ->@@ -24,7 +25,7 @@ Resource.register $ GLFW.setDropCallback window Nothing -mkCallback :: UnliftIO (StageRIO st) -> Callback st -> GLFW.DropCallback+mkCallback :: UnliftIO m -> Callback m -> GLFW.DropCallback mkCallback (UnliftIO ul) action = \_window files -> ul $ action files
src/Engine/Window/Key.hs view
@@ -16,11 +16,12 @@ import UnliftIO.Resource (ReleaseKey) import UnliftIO.Resource qualified as Resource -import Engine.Types (GlobalHandles(..), StageRIO)+import Engine.Events.Sink (MonadSink)+import Engine.Types (ghWindow) -type Callback st = Int -> (GLFW.ModifierKeys, GLFW.KeyState, GLFW.Key) -> StageRIO st ()+type Callback m = Int -> (GLFW.ModifierKeys, GLFW.KeyState, GLFW.Key) -> m () -callback :: Callback st -> StageRIO st ReleaseKey+callback :: MonadSink rs m => Callback m -> m ReleaseKey callback handler = do window <- asks $ ghWindow . appEnv withUnliftIO \ul ->@@ -28,7 +29,7 @@ Resource.register $ GLFW.setKeyCallback window Nothing -mkCallback :: UnliftIO (StageRIO st) -> Callback st -> GLFW.KeyCallback+mkCallback :: UnliftIO m -> Callback m -> GLFW.KeyCallback mkCallback (UnliftIO ul) action = \_window key keyCode keyState mods -> ul $ action keyCode (mods, keyState, key)
src/Engine/Window/MouseButton.hs view
@@ -20,16 +20,20 @@ import RIO import Graphics.UI.GLFW qualified as GLFW+import Resource.Collection (Generic1, Generically1(..)) import RIO.App (appEnv) import UnliftIO.Resource (ReleaseKey) import UnliftIO.Resource qualified as Resource-import Resource.Collection (Generic1, Generically1(..)) -import Engine.Types (GlobalHandles(..), StageRIO)+import Engine.Events.Sink (MonadSink)+import Engine.Types (GlobalHandles(..)) -type Callback st = (GLFW.ModifierKeys, GLFW.MouseButtonState, GLFW.MouseButton) -> StageRIO st ()+type Callback m = (GLFW.ModifierKeys, GLFW.MouseButtonState, GLFW.MouseButton) -> m () -callback :: Callback st -> StageRIO st ReleaseKey+callback+ :: MonadSink rs m+ => Callback m+ -> m ReleaseKey callback handler = do window <- asks $ ghWindow . appEnv withUnliftIO \ul ->@@ -37,7 +41,7 @@ Resource.register $ GLFW.setMouseButtonCallback window Nothing -mkCallback :: UnliftIO (StageRIO st) -> Callback st -> GLFW.MouseButtonCallback+mkCallback :: UnliftIO m -> Callback m -> GLFW.MouseButtonCallback mkCallback (UnliftIO ul) action = \_window button buttonState mods -> ul $ action (mods, buttonState, button)
src/Engine/Window/Scroll.hs view
@@ -12,11 +12,15 @@ import UnliftIO.Resource (ReleaseKey) import UnliftIO.Resource qualified as Resource -import Engine.Types (GlobalHandles(..), StageRIO)+import Engine.Events.Sink (MonadSink)+import Engine.Types (GlobalHandles(..)) -type Callback st = Double -> Double -> StageRIO st ()+type Callback m = Double -> Double -> m () -callback :: Callback st -> StageRIO st ReleaseKey+callback+ :: MonadSink rs m+ => Callback m+ -> m ReleaseKey callback handler = do window <- asks $ ghWindow . appEnv withUnliftIO \ul ->@@ -24,7 +28,7 @@ Resource.register $ GLFW.setScrollCallback window Nothing -mkCallback :: UnliftIO (StageRIO st) -> Callback st -> GLFW.ScrollCallback+mkCallback :: UnliftIO m -> Callback m -> GLFW.ScrollCallback mkCallback (UnliftIO ul) action = \_window dx dy -> ul $ action dx dy
src/Engine/Worker.hs view
@@ -50,12 +50,6 @@ , newSource , pubSource , subSource-- , HasWorker(..)- , register- , registerCollection- , registered- , registeredCollection ) where import RIO@@ -286,7 +280,9 @@ type Cell input output = (Var input, Merge output) spawnCell- :: MonadUnliftIO m+ :: ( MonadUnliftIO m+ , MonadResource m+ ) => (input -> output) -> input -> m (Cell input output)@@ -298,6 +294,7 @@ -- | Timer-driven stateful producer. data Timed config output = Timed { tWorker :: ThreadId+ , tKey :: Resource.ReleaseKey , tActive :: TVar Bool , tConfig :: TVar config , tOutput :: Var output@@ -312,7 +309,9 @@ getOutput = tOutput spawnTimed_- :: MonadUnliftIO m+ :: ( MonadUnliftIO m+ , MonadResource m+ ) => Bool -> Int -> output@@ -330,7 +329,9 @@ () spawnTimed- :: MonadUnliftIO m+ :: ( MonadUnliftIO m+ , MonadResource m+ ) => Bool -> Either Int (config -> Int) -> (config -> m (output, state))@@ -344,6 +345,7 @@ tOutput <- newVar initialOutput tWorker <- forkIO $ step tActive tConfig tOutput initialState+ tKey <- Resource.register $ killThread tWorker pure Timed{..} where step activeVar configVar output curState = do@@ -365,8 +367,9 @@ -- | Supply-driven step cell. data Merge o = Merge- { mWorker :: ThreadId- , mOutput :: TVar (Versioned o)+ { mWorker :: ThreadId+ , mKey :: Resource.ReleaseKey+ , mOutput :: TVar (Versioned o) } instance HasOutput (Merge o) where@@ -375,6 +378,7 @@ spawnMerge1 :: ( MonadUnliftIO m+ , MonadResource m , HasOutput i ) => (GetOutput i -> o)@@ -402,13 +406,17 @@ else retrySTM + key <- Resource.register $ killThread worker+ pure Merge { mWorker = worker+ , mKey = key , mOutput = output } spawnMerge2 :: ( MonadUnliftIO m+ , MonadResource m , HasOutput i1 , HasOutput i2 )@@ -443,8 +451,11 @@ else retrySTM + key <- Resource.register $ killThread worker+ pure Merge { mWorker = worker+ , mKey = key , mOutput = output } where@@ -452,6 +463,7 @@ spawnMerge3 :: ( MonadUnliftIO m+ , MonadResource m , HasOutput i1 , HasOutput i2 , HasOutput i3@@ -490,8 +502,11 @@ else retrySTM + key <- Resource.register $ killThread worker+ pure Merge { mWorker = worker+ , mKey = key , mOutput = output } where@@ -499,6 +514,7 @@ spawnMerge4 :: ( MonadUnliftIO m+ , MonadResource m , HasOutput i1 , HasOutput i2 , HasOutput i3@@ -541,8 +557,11 @@ else retrySTM + key <- Resource.register $ killThread worker+ pure Merge { mWorker = worker+ , mKey = key , mOutput = output } where@@ -557,6 +576,7 @@ :: ( Traversable t , HasOutput input , MonadUnliftIO m+ , MonadResource m ) => (t (GetOutput input) -> output) -> t input@@ -585,8 +605,11 @@ else retrySTM + key <- Resource.register $ killThread worker+ pure Merge { mWorker = worker+ , mKey = key , mOutput = output } @@ -644,56 +667,3 @@ subSource :: MonadUnliftIO m => Source a -> m (UnagiPrim.OutChan a) subSource (Source ic) = liftIO $ UnagiPrim.dupChan ic---- * Utils--class HasWorker a where- getWorker :: a -> ThreadId--instance HasWorker (Cell i o) where- getWorker = mWorker . snd--instance HasWorker (Timed c o) where- getWorker = tWorker--instance HasWorker (Merge o) where- getWorker = mWorker--register- :: ( MonadResource m- , HasWorker process- )- => process- -> m Resource.ReleaseKey-register = Resource.register . killThread . getWorker--registerCollection- :: ( MonadResource m- , HasWorker process- , Foldable t- )- => t process- -> m Resource.ReleaseKey-registerCollection = Resource.register . traverse_ (killThread . getWorker)--registered :: (MonadResource m, HasWorker a) => m a -> m (Resource.ReleaseKey, a)-registered spawn = do- worker <- spawn- key <- Resource.register $ killThread (getWorker worker)- pure (key, worker)--registeredCollection- :: ( MonadResource m- , HasWorker process- , Traversable t- )- => (input -> m process)- -> t input- -> m (Resource.ReleaseKey, t process)-registeredCollection spawn inputs = do- workers <- traverse spawn inputs-- key <- Resource.register $- traverse_ (killThread . getWorker) workers-- pure (key, workers)
src/Render/Code.hs view
@@ -7,6 +7,7 @@ , compileVert , compileFrag , compileComp+ , targetEnv ) where import RIO@@ -24,10 +25,13 @@ show = Text.unpack . unCode compileVert :: Code -> Q Exp-compileVert = compileShaderQ Nothing "vert" Nothing . Text.unpack . unCode+compileVert = compileShaderQ (Just targetEnv) "vert" Nothing . Text.unpack . unCode compileFrag :: Code -> Q Exp-compileFrag = compileShaderQ Nothing "frag" Nothing . Text.unpack . unCode+compileFrag = compileShaderQ (Just targetEnv) "frag" Nothing . Text.unpack . unCode compileComp :: Code -> Q Exp-compileComp = compileShaderQ Nothing "comp" Nothing . Text.unpack . unCode+compileComp = compileShaderQ (Just targetEnv) "comp" Nothing . Text.unpack . unCode++targetEnv :: IsString a => a+targetEnv = "vulkan1.2"
src/Render/Draw.hs view
@@ -54,7 +54,7 @@ => Vk.CommandBuffer -> Model.Indexed storage pos attrs -> instances- -> Bound dsl attrs (Model.VertexBuffersOf instances) m ()+ -> Bound dsl (Model.Vertex pos attrs) (Model.VertexBuffersOf instances) m () indexed cmd model instances = indexedRanges cmd model instances [wholeIndexed] where wholeIndexed = Model.IndexRange@@ -73,10 +73,10 @@ -> Model.Indexed storage pos attrs -> instances -> [Model.IndexRange]- -> Bound dsl attrs (Model.VertexBuffersOf instances) m ()+ -> Bound dsl (Model.Vertex pos attrs) (Model.VertexBuffersOf instances) m () indexedRanges cmd model instances ranges = do- checkedTanges <- traverse check ranges- Bound $ unsafeIndexedRanges True cmd model instances checkedTanges+ checkedRanges <- traverse check ranges+ Bound $ unsafeIndexedRanges True cmd model instances checkedRanges where check ir@Model.IndexRange{..} | irFirstIndex > maxIndex =@@ -101,7 +101,7 @@ -> instances -> Int -> t Model.IndexRange- -> Bound dsl attrs (Model.VertexBuffersOf instances) m ()+ -> Bound dsl (Model.Vertex pos attrs) (Model.VertexBuffersOf instances) m () indexedParts drawAttrs cmd model instances startInstance parts = Bound $ unsafeIndexedParts drawAttrs cmd model instances startInstance parts @@ -113,7 +113,7 @@ => Vk.CommandBuffer -> Model.Indexed storage pos unusedAttrs -> instances- -> Bound dsl ignoreAttrs (Model.VertexBuffersOf instances) m ()+ -> Bound dsl (Model.Vertex pos ignoreAttrs) (Model.VertexBuffersOf instances) m () indexedPos cmd model instances = indexedPosRanges cmd model instances [wholeIndexed] where wholeIndexed = Model.IndexRange@@ -128,10 +128,10 @@ -> Model.Indexed storage pos unusedAttrs -> instances -> [Model.IndexRange]- -> Bound dsl ignoreAttrs (Model.VertexBuffersOf instances) m ()+ -> Bound dsl (Model.Vertex pos ignoreAttrs) (Model.VertexBuffersOf instances) m () indexedPosRanges cmd model instances ranges = do- checkedTanges <- traverse check ranges- Bound $ unsafeIndexedRanges False cmd model instances checkedTanges+ checkedRanges <- traverse check ranges+ Bound $ unsafeIndexedRanges False cmd model instances checkedRanges where check ir@Model.IndexRange{..} | irFirstIndex > maxIndex =
src/Render/Pass/Offscreen.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedRecordDot #-} module Render.Pass.Offscreen ( Settings(..)@@ -12,6 +13,7 @@ import RIO +import Control.Monad.Trans.Resource (ResourceT) import Control.Monad.Trans.Resource qualified as Resource import Data.Bits ((.|.)) import Data.Vector qualified as Vector@@ -21,11 +23,14 @@ import Vulkan.Utils.Debug qualified as Debug import Vulkan.Zero (zero) -import Engine.Types.RefCounted (RefCounted, newRefCounted, resourceTRefCount)+import Engine.Types.RefCounted (RefCounted)+import Engine.Types.RefCounted qualified as RefCounted import Engine.Vulkan.Types (HasVulkan(..), HasRenderPass(..), RenderPass(..), MonadVulkan) import Resource.Image (AllocatedImage) import Resource.Image qualified as Image+import Resource.Region qualified as Region import Resource.Texture (CubeMap, Flat, Texture(..))+import Resource.Vulkan.Named qualified as Named {- XXX: Consider spec wrt. parameters and intended use! @@ -35,7 +40,9 @@ { sLabel :: Text , sExtent :: Vk.Extent2D , sFormat :: Vk.Format+ , sColorLayout :: Maybe Vk.ImageLayout -- ^ Target color format when used for export. , sDepthFormat :: Vk.Format+ , sDepthLayout :: Maybe Vk.ImageLayout -- ^ Target depth format when used for export. , sLayers :: Word32 , sMultiView :: Bool -- ^ Makes sense only for multiple layers. , sSamples :: Vk.SampleCountFlagBits -- ^ Multisample prevents mipmapping and cubes.@@ -64,11 +71,11 @@ getRenderArea = oRenderArea instance RenderPass Offscreen where- refcountRenderpass = resourceTRefCount . oRelease+ refcountRenderpass = RefCounted.resourceTRefCount . oRelease colorTexture :: Offscreen -> Texture Flat colorTexture Offscreen{..} = Texture- { tFormat = Image.aiFormat oColor+ { tFormat = oColor.aiFormat , tMipLevels = oMipLevels , tLayers = oLayers , tAllocatedImage = oColor@@ -76,7 +83,7 @@ colorCube :: Offscreen -> Texture CubeMap colorCube Offscreen{..} = Texture- { tFormat = Image.aiFormat oColor+ { tFormat = oColor.aiFormat , tMipLevels = oMipLevels , tLayers = oLayers -- TODO: get from type param , tAllocatedImage = oColor@@ -84,7 +91,7 @@ depthTexture :: Offscreen -> Texture Flat depthTexture Offscreen{..} = Texture- { tFormat = Image.aiFormat oDepth+ { tFormat = oDepth.aiFormat , tMipLevels = oMipLevels , tLayers = oLayers , tAllocatedImage = oDepth@@ -92,7 +99,7 @@ depthCube :: Offscreen -> Texture CubeMap depthCube Offscreen{..} = Texture- { tFormat = Image.aiFormat oDepth+ { tFormat = oDepth.aiFormat , tMipLevels = oMipLevels , tLayers = oLayers -- TODO: get from type param , tAllocatedImage = oDepth@@ -106,11 +113,14 @@ => Settings -> m Offscreen allocate settings@Settings{..} = do- logDebug $ "Allocating Offscreen resources for " <> display sLabel-- (_rpKey, renderPass) <- allocateRenderPass settings+ (_passKey, renderPass) <- allocateRenderPass settings - (refcounted, color, depth, framebuffer) <- allocateFramebuffer settings renderPass+ (release, resources) <- RefCounted.wrapped $ Region.run do+ Region.logDebug+ ("Allocating Offscreen resources for " <> display sLabel)+ ("Releasing Offscreen resources for " <> display sLabel)+ allocateFramebuffer settings renderPass+ let (color, depth, framebuffer) = resources pure Offscreen { oRenderPass = renderPass@@ -122,7 +132,7 @@ , oColor = color , oDepth = depth , oFrameBuffer = framebuffer- , oRelease = refcounted+ , oRelease = release } where fullSurface = Vk.Rect2D@@ -174,7 +184,7 @@ { Vk.format = sFormat settings , Vk.samples = sSamples settings , Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED- , Vk.finalLayout = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL+ , Vk.finalLayout = fromMaybe Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL (sColorLayout settings) , Vk.loadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR , Vk.storeOp = Vk.ATTACHMENT_STORE_OP_STORE , Vk.stencilLoadOp = Vk.ATTACHMENT_LOAD_OP_DONT_CARE@@ -185,7 +195,7 @@ { Vk.format = sDepthFormat settings , Vk.samples = sSamples settings , Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED- , Vk.finalLayout = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL+ , Vk.finalLayout = fromMaybe Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL (sDepthLayout settings) , Vk.loadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR , Vk.storeOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE , Vk.stencilLoadOp = Vk.ATTACHMENT_LOAD_OP_DONT_CARE@@ -246,8 +256,7 @@ -- ** Framebuffer type FramebufferOffscreen =- ( RefCounted- , Image.AllocatedImage+ ( Image.AllocatedImage , Image.AllocatedImage , Vk.Framebuffer )@@ -259,44 +268,36 @@ ) => Settings -> Vk.RenderPass- -> m FramebufferOffscreen+ -> ResourceT m FramebufferOffscreen allocateFramebuffer Settings{..} renderPass = do- context <- ask- let device = getDevice context- let- Vk.Extent2D{width, height} = sExtent- mipLevels = extentMips sExtent+ Region.logDebug+ ("Allocating Offscreen resources for " <> display sLabel)+ ("Releasing Offscreen resources for " <> display sLabel) - (colorKey, color) <- Resource.allocate- ( Image.create- context- (Just $ sLabel <> ".color")- Vk.IMAGE_ASPECT_COLOR_BIT- sExtent- (if sMipMap then mipLevels else 1)- sLayers- sSamples- sFormat- ( Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT .|.- Vk.IMAGE_USAGE_SAMPLED_BIT .|.- Vk.IMAGE_USAGE_TRANSFER_SRC_BIT- )+ color <- Image.allocate+ (Just $ sLabel <> ".color")+ Vk.IMAGE_ASPECT_COLOR_BIT+ (Image.inflateExtent sExtent 1)+ (if sMipMap then mipLevels else 1)+ sLayers+ sSamples+ sFormat+ ( Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT .|.+ Vk.IMAGE_USAGE_SAMPLED_BIT .|.+ Vk.IMAGE_USAGE_TRANSFER_SRC_BIT )- (Image.destroy context) - (depthKey, depth) <- Resource.allocate- ( Image.create- context- (Just $ sLabel <> ".depth")- Vk.IMAGE_ASPECT_DEPTH_BIT- sExtent- (if sMipMap then mipLevels else 1)- sLayers- sSamples- sDepthFormat- (Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT .|. Vk.IMAGE_USAGE_SAMPLED_BIT)+ depth <- Image.allocate+ (Just $ sLabel <> ".depth")+ Vk.IMAGE_ASPECT_DEPTH_BIT+ (Image.inflateExtent sExtent 1)+ (if sMipMap then mipLevels else 1)+ sLayers+ sSamples+ sDepthFormat+ ( Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT .|.+ Vk.IMAGE_USAGE_SAMPLED_BIT )- (Image.destroy context) let attachments = Vector.fromList@@ -323,17 +324,15 @@ , Vk.layers = fbNumLayers } - (framebufferKey, framebuffer) <- Vk.withFramebuffer device fbCI Nothing Resource.allocate- Debug.nameObject device framebuffer $ encodeUtf8 sLabel <> ".FB"-- releaseDebug <- toIO $ logDebug "Releasing Offscreen resources"- release <- newRefCounted do- releaseDebug- Resource.release colorKey- Resource.release depthKey- Resource.release framebufferKey+ device <- asks getDevice+ framebuffer <- Vk.createFramebuffer device fbCI Nothing+ void $! Resource.register $ Vk.destroyFramebuffer device framebuffer Nothing+ Named.object framebuffer $ sLabel <> ".FB" - pure (release, color, depth, framebuffer)+ pure (color, depth, framebuffer)+ where+ Vk.Extent2D{width, height} = sExtent+ mipLevels = extentMips sExtent extentMips :: Num t => Vk.Extent2D -> t extentMips Vk.Extent2D{width, height} = go 1 (min width height)
src/Render/Samplers.hs view
@@ -1,18 +1,18 @@-{-# LANGUAGE DeriveAnyClass #-}- module Render.Samplers ( Collection(..) , allocate+ , allocateFrom , indices , Params , params- , create- , destroy++ , createInfo ) where import RIO +import Control.Monad.Trans.Resource (ResourceT) import Control.Monad.Trans.Resource qualified as Resource import GHC.Generics (Generic1) import Vulkan.Core10 qualified as Vk@@ -20,8 +20,9 @@ import Vulkan.Zero (zero) import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan)-import Resource.Collection qualified as Collection import Resource.Collection (Generically1(..))+import Resource.Collection qualified as Collection+import Vulkan.CStruct.Extends (PokeChain, Extendss) data Collection a = Collection { linearMipRepeat :: a -- 0@@ -54,44 +55,52 @@ indices = fmap fst $ Collection.enumerate params allocate- :: ( MonadVulkan env m- , Resource.MonadResource m- )+ :: MonadVulkan env io => "max anisotropy" ::: Float- -> m (Resource.ReleaseKey, Collection Vk.Sampler)-allocate maxAnisotropy = do- context <- ask- Resource.allocate- (for params $ create context maxAnisotropy)- (traverse_ $ destroy context)+ -> ResourceT io (Collection Vk.Sampler)+allocate maxAnisotropy =+ for params \p ->+ allocateFrom maxAnisotropy p id -create- :: (MonadIO io, HasVulkan context)- => context- -> "max anisotropy" ::: Float+allocateFrom+ :: ( PokeChain e+ , Extendss Vk.SamplerCreateInfo e+ , MonadVulkan env io+ )+ => "max anisotropy" ::: Float -> Params- -> io Vk.Sampler-create context maxAnisotropy (filt, mips, reps) =- Vk.createSampler (getDevice context) samplerCI Nothing- where- samplerCI = zero- { Vk.magFilter = filt- , Vk.minFilter = filt- , Vk.addressModeU = reps- , Vk.addressModeV = reps- , Vk.addressModeW = reps- , Vk.anisotropyEnable = maxAnisotropy > 1- , Vk.maxAnisotropy = maxAnisotropy- , Vk.borderColor = Vk.BORDER_COLOR_INT_OPAQUE_BLACK- , Vk.unnormalizedCoordinates = False- , Vk.compareEnable = False- , Vk.compareOp = Vk.COMPARE_OP_ALWAYS- , Vk.mipmapMode = Vk.SAMPLER_MIPMAP_MODE_LINEAR- , Vk.mipLodBias = 0- , Vk.minLod = 0- , Vk.maxLod = mips- }+ -> (Vk.SamplerCreateInfo '[] -> Vk.SamplerCreateInfo e)+ -> ResourceT io Vk.Sampler+allocateFrom maxAnisotropy (filt, mips, reps) f = do+ device <- asks getDevice+ sampler <- Vk.createSampler+ device+ (f $ createInfo maxAnisotropy filt mips reps)+ Nothing+ _key <- Resource.register $+ Vk.destroySampler device sampler Nothing+ pure sampler -destroy :: (MonadIO io, HasVulkan context) => context -> Vk.Sampler -> io ()-destroy context sampler =- Vk.destroySampler (getDevice context) sampler Nothing+createInfo+ :: "max anisotropy" ::: Float+ -> Vk.Filter+ -> "max LoD" ::: Float+ -> Vk.SamplerAddressMode+ -> Vk.SamplerCreateInfo '[]+createInfo maxAnisotropy filt mips reps = zero+ { Vk.magFilter = filt+ , Vk.minFilter = filt+ , Vk.addressModeU = reps+ , Vk.addressModeV = reps+ , Vk.addressModeW = reps+ , Vk.anisotropyEnable = maxAnisotropy > 1+ , Vk.maxAnisotropy = maxAnisotropy+ , Vk.borderColor = Vk.BORDER_COLOR_INT_OPAQUE_BLACK+ , Vk.unnormalizedCoordinates = False+ , Vk.compareEnable = False+ , Vk.compareOp = Vk.COMPARE_OP_ALWAYS+ , Vk.mipmapMode = Vk.SAMPLER_MIPMAP_MODE_LINEAR+ , Vk.mipLodBias = 0+ , Vk.minLod = 0+ , Vk.maxLod = mips+ }
src/Resource/Buffer.hs view
@@ -6,16 +6,17 @@ import Data.Vector.Storable qualified as VectorS import Foreign (Storable) import Foreign qualified+import UnliftIO.Resource (MonadResource) import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk import Vulkan.NamedType ((:::)) import Vulkan.Zero (zero) import VulkanMemoryAllocator qualified as VMA -import Engine.Types (StageRIO)-import Engine.Vulkan.Types (HasVulkan(getAllocator), Queues(qTransfer))+import Engine.Vulkan.Types (HasVulkan(getAllocator), Queues(qTransfer), MonadVulkan) import Engine.Worker qualified as Worker import Resource.CommandBuffer (oneshot_)+import Resource.Vulkan.Named qualified as Named data Store = Staged | Coherent @@ -26,31 +27,47 @@ , aCapacity :: Int , aUsed :: Word32 , aUsage :: Vk.BufferUsageFlagBits+ , aLabel :: Maybe Text } deriving (Show) +instance Vk.HasObjectType (Allocated s a) where+ objectTypeAndHandle =+ Vk.objectTypeAndHandle . aBuffer+ allocateCoherent- :: (Resource.MonadResource m, Storable a, HasVulkan context)- => context+ :: ( MonadVulkan env m+ , Resource.MonadResource m+ , Storable a+ )+ => Maybe Text -> Vk.BufferUsageFlagBits -> "initial size" ::: Int -> VectorS.Vector a -> m (Resource.ReleaseKey, Allocated 'Coherent a)-allocateCoherent context usage initialSize xs =- Resource.allocate- (createCoherent context usage initialSize xs)- (destroy context)+allocateCoherent label usage initialSize xs = do+ res <- createCoherent label usage initialSize xs+ destroyIO <- toIO do+ context <- ask+ destroy context res+ key <- Resource.register destroyIO+ pure (key, res) createCoherent- :: forall a context io . (Storable a, HasVulkan context, MonadUnliftIO io)- => context+ :: forall a env m+ . ( MonadVulkan env m+ , Storable a+ )+ => Maybe Text -> Vk.BufferUsageFlagBits -> "initial size" ::: Int -> VectorS.Vector a- -> io (Allocated 'Coherent a)-createCoherent context usage initialSize xs = do- (aBuffer, aAllocation, aAllocationInfo) <- VMA.createBuffer (getAllocator context) bci aci+ -> m (Allocated 'Coherent a)+createCoherent aLabel usage initialSize xs = do+ allocator <- asks getAllocator+ (aBuffer, aAllocation, aAllocationInfo) <- VMA.createBuffer allocator bci aci+ traverse_ (Named.object aBuffer) aLabel - when (len /= 0) $+ when (len > 0) $ if VMA.mappedData aAllocationInfo == Foreign.nullPtr then error "TODO: recover from unmapped data and flush manually" else@@ -64,7 +81,6 @@ , .. } where- len = VectorS.length xs lenBytes = Foreign.sizeOf (undefined :: a) * len @@ -88,34 +104,39 @@ } createStaged- :: forall a context io . (Storable a, HasVulkan context, MonadUnliftIO io)- => context+ :: forall a env m+ . ( Storable a+ , MonadVulkan env m+ )+ => Maybe Text -> Queues Vk.CommandPool -> Vk.BufferUsageFlagBits -> Int -> VectorS.Vector a- -> io (Allocated 'Staged a)-createStaged context commandQueues usage initialSize xs = do- (aBuffer, aAllocation, aAllocationInfo) <- VMA.createBuffer vma bci aci+ -> m (Allocated 'Staged a)+createStaged aLabel commandQueues usage initialSize xs = do+ allocator <- asks getAllocator+ (aBuffer, aAllocation, aAllocationInfo) <- VMA.createBuffer allocator bci aci+ traverse_ (Named.object aBuffer) aLabel - when (len /= 0) . liftIO $- VMA.withBuffer vma stageCI stageAI bracket \(staging, _stage, stageInfo) ->+ when (len > 0) $+ VMA.withBuffer allocator stageCI stageAI bracket \(staging, _stage, stageInfo) -> if VMA.mappedData stageInfo == Foreign.nullPtr then error "TODO: recover from unmapped data and flush manually" else do- VectorS.unsafeWith xs \src ->+ liftIO $ VectorS.unsafeWith xs \src -> Foreign.copyBytes (Foreign.castPtr $ VMA.mappedData stageInfo) src lenBytes+ context <- ask copyBuffer_ context commandQueues aBuffer staging (fromIntegral sizeBytes) pure Allocated { aCapacity = max initialSize len , aUsed = fromIntegral len , aUsage = usage+ , aLabel = Nothing , .. } where- vma = getAllocator context- len = VectorS.length xs lenBytes = Foreign.sizeOf (undefined :: a) * len @@ -144,6 +165,16 @@ , VMA.requiredFlags = Vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT } +register+ :: ( MonadVulkan env m+ , MonadResource m+ )+ => Allocated stage a+ -> m Resource.ReleaseKey+register Allocated{aBuffer, aAllocation} = do+ allocator <- asks getAllocator+ Resource.register $ VMA.destroyBuffer allocator aBuffer aAllocation+ destroy :: (MonadUnliftIO io, HasVulkan context) => context@@ -152,16 +183,13 @@ destroy context a = VMA.destroyBuffer (getAllocator context) (aBuffer a) (aAllocation a) -destroyAll- :: (MonadUnliftIO io, HasVulkan context, Foldable t)- => context- -> t (Allocated s a)- -> io ()-destroyAll context =- traverse_ \a ->- VMA.destroyBuffer (getAllocator context) (aBuffer a) (aAllocation a)--peekCoherent :: (MonadIO m, Storable a) => Word32 -> Allocated 'Coherent a -> m (Maybe a)+peekCoherent+ :: ( MonadIO m+ , Storable a+ )+ => Word32+ -> Allocated 'Coherent a+ -> m (Maybe a) peekCoherent ix Allocated{..} = case VMA.mappedData aAllocationInfo of _ | ix + 1 > aUsed ->@@ -172,8 +200,32 @@ liftIO . fmap Just $ Foreign.peekElemOff (Foreign.castPtr ptr) (fromIntegral ix) +{-# INLINE pokeCoherent #-}+pokeCoherent+ :: ( MonadVulkan env m+ , Storable a+ )+ => Allocated 'Coherent a+ -> Word32+ -> a+ -> m ()+pokeCoherent Allocated{..} ix new =+ case VMA.mappedData aAllocationInfo of+ _ | ix + 1 > aUsed ->+ pure ()+ ptr | ptr == Foreign.nullPtr ->+ pure ()+ ptr ->+ liftIO $+ Foreign.pokeElemOff+ (Foreign.castPtr ptr)+ (fromIntegral ix)+ new+ updateCoherent- :: (Foreign.Storable a, MonadUnliftIO io)+ :: ( MonadUnliftIO io+ , Foreign.Storable a+ ) => VectorS.Vector a -> Allocated 'Coherent a -> io (Allocated 'Coherent a)@@ -188,25 +240,30 @@ len = VectorS.length xs lenBytes = len * Foreign.sizeOf (VectorS.head xs) +{-# INLINE updateCoherentResize_ #-} updateCoherentResize_- :: (Storable a, HasVulkan context, MonadUnliftIO io)- => context- -> Allocated 'Coherent a+ :: ( MonadVulkan env m+ , Storable a+ )+ => Allocated 'Coherent a -> VectorS.Vector a- -> io (Allocated 'Coherent a)-updateCoherentResize_ ctx old xs =+ -> m (Allocated 'Coherent a)+updateCoherentResize_ old xs = if newSize <= oldSize then updateCoherent xs old else do- destroyAll ctx [old]- createCoherent ctx (aUsage old) newSize xs+ ctx <- ask+ destroy ctx old+ createCoherent (aLabel old) (aUsage old) newSize xs where oldSize = aCapacity old newSize = VectorS.length xs -- TODO: add a staged buffer to check out the transfer queue copyBuffer_- :: (MonadUnliftIO io, HasVulkan context)+ :: ( MonadUnliftIO io+ , HasVulkan context+ ) => context -> Queues Vk.CommandPool -> ("dstBuffer" ::: Vk.Buffer)@@ -221,16 +278,19 @@ type ObserverCoherent a = Worker.ObserverIO (Allocated 'Coherent a) newObserverCoherent- :: Storable a- => Vk.BufferUsageFlagBits+ :: ( MonadVulkan env m+ , Storable a+ )+ => "label" ::: Text+ -> Vk.BufferUsageFlagBits -> Int- -> Resource.ResourceT (StageRIO st) (ObserverCoherent a)-newObserverCoherent usage initialSize = do- context <- ask-- initialBuffer <- createCoherent context usage initialSize mempty+ -> VectorS.Vector a+ -> Resource.ResourceT m (ObserverCoherent a)+newObserverCoherent label usage initialCapacity initialData = do+ initialBuffer <- createCoherent (Just label) usage initialCapacity initialData observer <- Worker.newObserverIO initialBuffer + context <- ask void $! Resource.register do currentBuffer <- Worker.readObservedIO observer destroy context currentBuffer@@ -239,15 +299,27 @@ {-# INLINE observeCoherentResize_ #-} observeCoherentResize_- :: ( HasVulkan env+ :: ( MonadVulkan env m , Worker.HasOutput source , Worker.GetOutput source ~ VectorS.Vector output , Storable output ) => source -> ObserverCoherent output- -> RIO env ()+ -> m () observeCoherentResize_ source observer = do- context <- ask- Worker.observeIO_ source observer $- updateCoherentResize_ context+ Worker.observeIO_ source observer updateCoherentResize_++{-# INLINE observeCoherentSingle #-}+observeCoherentSingle+ :: ( MonadVulkan env m+ , Worker.HasOutput source+ , Worker.GetOutput source ~ output+ , Storable output+ )+ => source+ -> ObserverCoherent output+ -> m ()+observeCoherentSingle source observer =+ Worker.observeIO_ source observer \a b ->+ pokeCoherent a 0 b $> a
src/Resource/CommandBuffer.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedRecordDot #-}+ module Resource.CommandBuffer ( allocatePools , withPools@@ -10,6 +12,7 @@ import UnliftIO.Resource (MonadResource) import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk+import Vulkan.Core10.CommandBuffer qualified as CommandBuffer import Vulkan.CStruct.Extends (SomeStruct(..)) import Vulkan.Utils.QueueAssignment (QueueFamilyIndex(..)) import Vulkan.Zero (zero)@@ -55,9 +58,8 @@ Vk.withCommandBuffers device commandBufferAllocateInfo bracket \case (Vector.toList -> [buf]) -> do let- oneTime :: Vk.CommandBufferBeginInfo '[] oneTime = zero- { Vk.flags = Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT+ { CommandBuffer.flags = Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT } Vk.useCommandBuffer buf oneTime $ action buf
− src/Resource/DescriptorSet.hs
@@ -1,37 +0,0 @@-module Resource.DescriptorSet- ( allocatePool- , TypeMap- , mkPoolCI- ) where--import RIO--import UnliftIO.Resource qualified as Resource-import RIO.Vector qualified as Vector-import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)--import Engine.Vulkan.Types (HasVulkan(getDevice))--allocatePool- :: ( Resource.MonadResource m- , MonadReader env m- , HasVulkan env- )- => Word32- -> TypeMap Word32- -> m (Resource.ReleaseKey, Vk.DescriptorPool)-allocatePool maxSets sizes = do- device <- asks getDevice- Vk.withDescriptorPool device (mkPoolCI maxSets sizes) Nothing Resource.allocate--type TypeMap a = [(Vk.DescriptorType, a)]--mkPoolCI- :: Word32- -> TypeMap Word32- -> Vk.DescriptorPoolCreateInfo '[]-mkPoolCI maxSets sizes = zero- { Vk.maxSets = maxSets- , Vk.poolSizes = Vector.fromList $ map (uncurry Vk.DescriptorPoolSize) sizes- }
src/Resource/Image.hs view
@@ -1,29 +1,43 @@+{-# LANGUAGE OverloadedRecordDot #-}+ module Resource.Image ( AllocatedImage(..)- , create- , destroy- , subresource+ , allocate+ , allocateView++ , DstImage+ , allocateDst+ , copyBufferToDst+ , updateFromStorable+ , transitionLayout , copyBufferToImage++ , subresource+ , inflateExtent ) where import RIO import Data.Bits (shiftR, (.|.)) import RIO.Vector qualified as Vector+import RIO.Vector.Storable qualified as Storable+import UnliftIO.Resource (MonadResource)+import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk import Vulkan.CStruct.Extends (SomeStruct(..)) import Vulkan.NamedType ((:::))-import Vulkan.Utils.Debug qualified as Debug import Vulkan.Zero (zero) import VulkanMemoryAllocator qualified as VMA -import Engine.Vulkan.Types (HasVulkan(..), Queues(..))+import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, Queues(..))+import Resource.Buffer qualified as Buffer import Resource.CommandBuffer (oneshot_)+import Resource.Vulkan.Named qualified as Named data AllocatedImage = AllocatedImage { aiAllocation :: VMA.Allocation- , aiExtent :: Vk.Extent2D+ , aiExtent :: Vk.Extent3D , aiFormat :: Vk.Format , aiImage :: Vk.Image , aiImageView :: Vk.ImageView@@ -31,38 +45,32 @@ } deriving (Show) -create- :: ( MonadIO io- , HasVulkan ctx+allocate+ :: ( MonadVulkan env io+ , MonadResource io )- => ctx- -> Maybe Text+ => Maybe Text -> Vk.ImageAspectFlags- -> "image dimensions" ::: Vk.Extent2D+ -> "image dimensions" ::: Vk.Extent3D -> "mip levels" ::: Word32 -> "stored layers" ::: Word32 -> Vk.SampleCountFlagBits -> Vk.Format -> Vk.ImageUsageFlags -> io AllocatedImage-create context mlabel aspect extent mipLevels numLayers samples format usage = do- let- device = getDevice context- allocator = getAllocator context+allocate mlabel aspect extent mipLevels numLayers samples format usage = do+ allocator <- asks getAllocator (image, allocation, _info) <- VMA.createImage allocator imageCI imageAllocationCI- for_ mlabel \label ->- Debug.nameObject device image $ encodeUtf8 label <> ".label"+ void $! Resource.register $+ VMA.destroyImage allocator image allocation+ traverse_ (Named.object image) mlabel - imageView <- Vk.createImageView- device- (imageViewCI image)- Nothing- for_ mlabel \label ->- Debug.nameObject device image $ encodeUtf8 label <> ".view"+ imageView <- allocateView image format subr+ traverse_ (Named.object imageView) $ fmap (<> ":view") mlabel pure AllocatedImage { aiAllocation = allocation@@ -73,13 +81,18 @@ , aiImageRange = subr } where- Vk.Extent2D{width, height} = extent+ imageType =+ case extent of+ Vk.Extent3D{depth=1} ->+ Vk.IMAGE_TYPE_2D+ _ ->+ Vk.IMAGE_TYPE_3D imageCI = zero- { Vk.imageType = Vk.IMAGE_TYPE_2D+ { Vk.imageType = imageType , Vk.flags = createFlags , Vk.format = format- , Vk.extent = Vk.Extent3D width height 1+ , Vk.extent = extent , Vk.mipLevels = mipLevels , Vk.arrayLayers = numLayers , Vk.tiling = Vk.IMAGE_TILING_OPTIMAL@@ -94,45 +107,164 @@ , VMA.requiredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT } - imageViewCI image = zero+ createFlags =+ case numLayers of+ 6 ->+ Vk.IMAGE_CREATE_CUBE_COMPATIBLE_BIT+ _ ->+ zero++ subr = subresource aspect mipLevels numLayers++allocateView+ :: ( MonadVulkan env m+ , MonadResource m+ )+ => Vk.Image+ -> Vk.Format+ -> Vk.ImageSubresourceRange+ -> m Vk.ImageView+allocateView image format subr = do+ device <- asks getDevice+ imageView <- Vk.createImageView+ device+ imageViewCI+ Nothing+ void $! Resource.register $ Vk.destroyImageView device imageView Nothing+ pure imageView++ where+ imageViewCI = zero { Vk.image = image- , Vk.viewType = viewType+ , Vk.viewType = guessViewType subr , Vk.format = format , Vk.components = zero , Vk.subresourceRange = subr } - (createFlags, viewType) =- case numLayers of- 1 ->- (zero, Vk.IMAGE_VIEW_TYPE_2D)- 6 ->- (Vk.IMAGE_CREATE_CUBE_COMPATIBLE_BIT, Vk.IMAGE_VIEW_TYPE_CUBE)- _ ->- (zero, Vk.IMAGE_VIEW_TYPE_2D_ARRAY)+-------------------------------------------- - subr = subresource aspect mipLevels numLayers+newtype DstImage = DstImage AllocatedImage -destroy- :: ( MonadIO io- , HasVulkan context+-- | Allocate an image and transition it into TRANSFER_DST_OPTIOMAL+allocateDst+ :: ( MonadVulkan env m+ , MonadResource m )- => context+ => Queues Vk.CommandPool+ -> Maybe Text+ -> ("image dimensions" ::: Vk.Extent3D)+ -> ("mip levels" ::: Word32)+ -> ("stored layers" ::: Word32)+ -> Vk.Format+ -> m DstImage+allocateDst pool name extent3d mipLevels numLayers format = do+ ai <- allocate+ name+ Vk.IMAGE_ASPECT_COLOR_BIT+ extent3d+ mipLevels+ numLayers+ Vk.SAMPLE_COUNT_1_BIT+ format+ (Vk.IMAGE_USAGE_SAMPLED_BIT .|. Vk.IMAGE_USAGE_TRANSFER_DST_BIT)++ transitionLayout+ pool+ (aiImage ai)+ mipLevels+ numLayers -- XXX: arrayLayers is always 0 for now+ format+ Vk.IMAGE_LAYOUT_UNDEFINED+ Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL++ pure $ DstImage ai++copyBufferToDst+ :: ( MonadVulkan env m+ , Integral deviceSize+ , Foldable t+ )+ => Queues Vk.CommandPool+ -> Vk.Buffer+ -> DstImage+ -> "mip offsets" ::: t deviceSize+ -> m AllocatedImage+copyBufferToDst pool source (DstImage ai) offsets = do+ copyBufferToImage+ pool+ source+ (aiImage ai)+ (aiExtent ai)+ offsets+ layerCount++ transitionLayout+ pool+ (aiImage ai)+ levelCount+ layerCount -- XXX: arrayLayers is always 0 for now+ (aiFormat ai)+ Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL+ Vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL++ pure ai+ where+ Vk.ImageSubresourceRange{layerCount, levelCount} = aiImageRange ai++{-# INLINE updateFromStorable #-}+updateFromStorable+ :: ( Storable a+ , MonadVulkan env m+ , MonadResource m+ )+ => Queues Vk.CommandPool -> AllocatedImage- -> io ()-destroy context AllocatedImage{..} = do- -- traceM "destroyAllocatedImage"- Vk.destroyImageView (getDevice context) aiImageView Nothing- VMA.destroyImage (getAllocator context) aiImage aiAllocation+ -> Storable.Vector a+ -> m AllocatedImage+updateFromStorable pools ai update = do+ (_transient, staging) <-+ Buffer.allocateCoherent+ (Just "updateFromStorable:staging")+ Vk.BUFFER_USAGE_TRANSFER_SRC_BIT+ 1+ update ---------------------------------------------+ transitionLayout+ pools+ ai.aiImage+ 1+ 1+ ai.aiFormat+ Vk.IMAGE_LAYOUT_UNDEFINED+ Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + copyBufferToImage+ pools+ staging.aBuffer+ ai.aiImage+ (Vk.Extent3D ai.aiExtent.width ai.aiExtent.height 1)+ (Vector.singleton 0 :: Vector Word32) -- offsets+ 1++ transitionLayout+ pools+ ai.aiImage+ 1+ 1+ ai.aiFormat+ Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL+ Vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL++ -- logDebug "Updating map texture... done"+ pure ai++{-# INLINE transitionLayout #-} transitionLayout- :: ( HasVulkan context- , MonadUnliftIO m+ :: ( MonadVulkan env m+ -- , MonadUnliftIO m )- => context- -> Queues Vk.CommandPool+ => Queues Vk.CommandPool -> Vk.Image -> "mip levels" ::: Word32 -> "layer count" ::: Word32@@ -140,7 +272,8 @@ -> "old" ::: Vk.ImageLayout -> "new" ::: Vk.ImageLayout -> m ()-transitionLayout ctx pool image mipLevels layerCount format old new =+transitionLayout pool image mipLevels layerCount format old new = do+ ctx <- ask case (old, new) of (Vk.IMAGE_LAYOUT_UNDEFINED, Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) -> oneshot_ ctx pool qTransfer \buf ->@@ -207,56 +340,22 @@ , Vk.subresourceRange = subresource aspectMask mipLevels layerCount } --- imageBarrier cb oldLayout newLayout aspectMask srcStage srcMask dstStage dstMask image mipLevels layerCount =--- Vk.cmdPipelineBarrier--- cb--- srcStage--- dstStage--- zero--- mempty--- mempty--- (Vector.singleton barrier)--- where--- barrier = SomeStruct zero--- { Vk.srcAccessMask = srcMask--- , Vk.dstAccessMask = dstMask--- , Vk.oldLayout = oldLayout--- , Vk.newLayout = newLayout--- , Vk.srcQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED--- , Vk.dstQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED--- , Vk.image = image--- , Vk.subresourceRange = subresource aspectMask mipLevels layerCount--- }--subresource- :: Vk.ImageAspectFlags- -> "mip levels" ::: Word32- -> "layer count" ::: Word32- -> Vk.ImageSubresourceRange-subresource aspectMask mipLevels layerCount = Vk.ImageSubresourceRange- { aspectMask = aspectMask- , baseMipLevel = 0- , levelCount = mipLevels -- XXX: including base- , baseArrayLayer = 0- , layerCount = layerCount- }-+{-# INLINE copyBufferToImage #-} copyBufferToImage- :: ( HasVulkan context- , Foldable t+ :: ( Foldable t , Integral deviceSize- , MonadUnliftIO m+ , MonadVulkan env m )- => context- -> Queues Vk.CommandPool+ => Queues Vk.CommandPool -> Vk.Buffer -> Vk.Image -> "base extent" ::: Vk.Extent3D -> "mip offsets" ::: t deviceSize -> "layer count" ::: Word32 -> m ()-copyBufferToImage ctx pool src dst Vk.Extent3D{..} mipOffsets layerCount =- oneshot_ ctx pool qTransfer \cmd ->+copyBufferToImage pools src dst Vk.Extent3D{..} mipOffsets layerCount = do+ context <- ask+ oneshot_ context pools qTransfer \cmd -> Vk.cmdCopyBufferToImage cmd src dst Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL $ Vector.fromList copyRegions where@@ -276,6 +375,35 @@ , Vk.imageExtent = Vk.Extent3D { width = max 1 $ width `shiftR` mipLevel , height = max 1 $ height `shiftR` mipLevel- , depth = depth+ , depth = max 1 $ depth `shiftR` mipLevel } }++-- * Helpers++{-# INLINEABLE inflateExtent #-}+inflateExtent :: Vk.Extent2D -> Word32 -> Vk.Extent3D+inflateExtent Vk.Extent2D{..} depth = Vk.Extent3D{..}++subresource+ :: Vk.ImageAspectFlags+ -> "mip levels" ::: Word32+ -> "layer count" ::: Word32+ -> Vk.ImageSubresourceRange+subresource aspectMask mipLevels layerCount = Vk.ImageSubresourceRange+ { aspectMask = aspectMask+ , baseMipLevel = 0+ , levelCount = mipLevels -- XXX: including base+ , baseArrayLayer = 0+ , layerCount = layerCount+ }++guessViewType :: Vk.ImageSubresourceRange -> Vk.ImageViewType+guessViewType Vk.ImageSubresourceRange{layerCount} =+ case layerCount of+ 1 ->+ Vk.IMAGE_VIEW_TYPE_2D+ 6 ->+ Vk.IMAGE_VIEW_TYPE_CUBE+ _ ->+ Vk.IMAGE_VIEW_TYPE_2D_ARRAY
src/Resource/Mesh/Codec.hs view
@@ -27,6 +27,7 @@ import Engine.Vulkan.Types (MonadVulkan, Queues) import Resource.Buffer qualified as Buffer import Resource.Model qualified as Model+import Resource.Region qualified as Region -- * Format meta @@ -181,18 +182,21 @@ -> FilePath -> m ( Resource.ReleaseKey- , (meta, Storable.Vector nodes, Model.Indexed 'Buffer.Staged Vec3.Packed attrs)+ , ( meta+ , Storable.Vector nodes+ , Model.Indexed 'Buffer.Staged Vec3.Packed attrs+ ) ) loadIndexed pools fp = do logInfo $ "Loading " <> fromString fp (meta, nodes, (positions, indices, attrs)) <- loadBlobs fp logDebug $ displayShow meta - logDebug $ "Staging " <> fromString fp- context <- ask- indexed <- Model.createStaged context pools positions attrs indices- key <- Resource.register $ Model.destroyIndexed context indexed- pure (key, (meta, nodes, indexed))+ Region.run do+ logDebug $ "Staging " <> fromString fp+ indexed <- Model.createStaged (Just $ fromString fp) pools positions attrs indices+ Model.registerIndexed_ indexed+ pure (meta, nodes, indexed) loadBlobs :: forall attrs env nodes meta m
src/Resource/Model.hs view
@@ -1,22 +1,36 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+ module Resource.Model where import RIO+import GHC.Generics import Codec.Serialise qualified as CBOR+import Data.Kind (Constraint, Type) import Data.List qualified as List import Data.Vector.Storable qualified as Storable import Foreign (Storable(..))+import Geomancy (Vec2)+import Geomancy.Vec3 qualified as Vec3+import UnliftIO.Resource (MonadResource) import Vulkan.Core10 qualified as Vk -import Engine.Vulkan.Types (HasVulkan(..), Queues(..))+import Engine.Vulkan.Pipeline.Graphics (HasVertexInputBindings(..))+import Engine.Vulkan.Format (HasVkFormat(..))+import Engine.Vulkan.Pipeline.Graphics qualified as Graphics+import Engine.Vulkan.Types (MonadVulkan, Queues(..)) import Resource.Buffer qualified as Buffer data Indexed storage pos attrs = Indexed- { iPositions :: Buffer.Allocated storage pos+ { iLabel :: Maybe Text+ , iPositions :: Buffer.Allocated storage pos , iAttrs :: Buffer.Allocated storage attrs , iIndices :: Buffer.Allocated storage Word32 } deriving (Show) +type Vertex2d attrs = Vertex Vec2 attrs+type Vertex3d attrs = Vertex Vec3.Packed attrs+ data Vertex pos attrs = Vertex { vPosition :: pos , vAttrs :: attrs@@ -39,11 +53,37 @@ , vAttrs = inject pos } +instance+ ( HasVkFormat pos+ , HasVkFormat attrs+ ) =>+ Graphics.HasVertexInputBindings (Vertex pos attrs) where+ vertexInputBindings =+ [ Graphics.vertexFormat @pos+ , Graphics.vertexFormat @attrs+ ]+ class HasVertexBuffers a where type VertexBuffersOf a getVertexBuffers :: a -> [Vk.Buffer] getInstanceCount :: a -> Word32 + default getVertexBuffers+ :: ( Generic a+ , GHasVertexBuffers (Rep a)+ )+ => a+ -> [Vk.Buffer]+ getVertexBuffers = genericGetVertexBuffers++ default getInstanceCount+ :: ( Generic a+ , GHasVertexBuffers (Rep a)+ )+ => a+ -> Word32+ getInstanceCount = genericGetInstanceCount+ instance HasVertexBuffers () where type VertexBuffersOf () = () @@ -62,6 +102,39 @@ {-# INLINE getInstanceCount #-} getInstanceCount = Buffer.aUsed +genericGetVertexBuffers+ :: ( Generic a+ , GHasVertexBuffers (Rep a)+ )+ => a+ -> [Vk.Buffer]+genericGetVertexBuffers = gVertexBuffers . GHC.Generics.from++genericGetInstanceCount+ :: ( Generic a+ , GHasVertexBuffers (Rep a)+ )+ => a+ -> Word32+genericGetInstanceCount = gInstanceCount . GHC.Generics.from++type GHasVertexBuffers :: (Type -> Type) -> Constraint+class GHasVertexBuffers f where+ gVertexBuffers :: forall a . f a -> [Vk.Buffer]+ gInstanceCount :: forall a . f a -> Word32++instance GHasVertexBuffers f => GHasVertexBuffers (M1 c cb f) where+ gVertexBuffers (M1 f) = gVertexBuffers f+ gInstanceCount (M1 f) = gInstanceCount f++instance (GHasVertexBuffers l, GHasVertexBuffers r) => GHasVertexBuffers (l :*: r) where+ gVertexBuffers (l :*: r) = gVertexBuffers l <> gVertexBuffers r+ gInstanceCount (l :*: r) = min (gInstanceCount l) (gInstanceCount r)++instance HasVertexBuffers a => GHasVertexBuffers (K1 r a) where+ gVertexBuffers (K1 a) = getVertexBuffers a+ gInstanceCount (K1 a) = getInstanceCount a+ data IndexRange = IndexRange { irFirstIndex :: Word32 , irIndexCount :: Word32@@ -85,13 +158,16 @@ pokeByteOff ptr 4 irIndexCount createStagedL- :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io)- => context+ :: ( MonadVulkan env m+ , Storable pos+ , Storable attrs+ )+ => Maybe Text -> Queues Vk.CommandPool -> [Vertex pos attrs] -> Maybe [Word32]- -> io (Indexed 'Buffer.Staged pos attrs)-createStagedL context pool vertices mindices = createStaged context pool pv av iv+ -> m (Indexed 'Buffer.Staged pos attrs)+createStagedL label pool vertices mindices = createStaged label pool pv av iv where pv = Storable.fromList ps av = Storable.fromList as@@ -108,52 +184,72 @@ pure (vPosition, vAttrs) createStaged- :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io)- => context+ :: ( MonadVulkan env m+ , Storable pos+ , Storable attrs+ )+ => Maybe Text -> Queues Vk.CommandPool -> Storable.Vector pos -> Storable.Vector attrs -> Storable.Vector Word32- -> io (Indexed 'Buffer.Staged pos attrs)-createStaged context pool pv av iv = do- positions <- Buffer.createStaged context pool Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT 0 pv- attrs <- Buffer.createStaged context pool Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT 0 av- indices <- Buffer.createStaged context pool Vk.BUFFER_USAGE_INDEX_BUFFER_BIT 0 iv+ -> m (Indexed 'Buffer.Staged pos attrs)+createStaged label pool pv av iv = do+ positions <- Buffer.createStaged (label <&> \m -> mappend m ".positions") pool Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT 0 pv+ attrs <- Buffer.createStaged (label <&> \m -> mappend m ".attrs") pool Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT 0 av+ indices <- Buffer.createStaged (label <&> \m -> mappend m ".indices") pool Vk.BUFFER_USAGE_INDEX_BUFFER_BIT 0 iv pure Indexed- { iPositions = positions+ { iLabel = label+ , iPositions = positions , iAttrs = attrs , iIndices = indices } createCoherentEmpty- :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io)- => context+ :: ( MonadVulkan env m+ , Storable pos+ , Storable attrs+ )+ => Maybe Text -> Int- -> io (Indexed 'Buffer.Coherent pos attrs)-createCoherentEmpty ctx initialSize = Indexed- <$> Buffer.createCoherent ctx Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT initialSize mempty- <*> Buffer.createCoherent ctx Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT initialSize mempty- <*> Buffer.createCoherent ctx Vk.BUFFER_USAGE_INDEX_BUFFER_BIT initialSize mempty+ -> m (Indexed 'Buffer.Coherent pos attrs)+createCoherentEmpty label initialSize = Indexed label+ <$> Buffer.createCoherent (label <&> \m -> mappend m ".positions") Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT initialSize mempty+ <*> Buffer.createCoherent (label <&> \m -> mappend m ".attrs") Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT initialSize mempty+ <*> Buffer.createCoherent (label <&> \m -> mappend m ".indices") Vk.BUFFER_USAGE_INDEX_BUFFER_BIT initialSize mempty +registerIndexed_+ :: ( MonadVulkan env m+ , MonadResource m+ )+ => Indexed storage pos attrs+ -> m ()+registerIndexed_ Indexed{..} = do+ void $! Buffer.register iPositions+ void $! Buffer.register iAttrs+ void $! Buffer.register iIndices+ destroyIndexed- :: (HasVulkan context, MonadUnliftIO io)- => context- -> Indexed storage pos attrs- -> io ()-destroyIndexed ctx Indexed{..} = do- Buffer.destroyAll ctx [iPositions]- Buffer.destroyAll ctx [iAttrs]- Buffer.destroyAll ctx [iIndices]+ :: MonadVulkan env m+ => Indexed storage pos attrs+ -> m ()+destroyIndexed Indexed{..} = do+ ctx <- ask+ Buffer.destroy ctx iPositions+ Buffer.destroy ctx iAttrs+ Buffer.destroy ctx iIndices updateCoherent- :: (HasVulkan context, Storable pos, Storable attrs, MonadUnliftIO io)- => context- -> [Vertex pos attrs]+ :: ( MonadVulkan env m+ , Storable pos+ , Storable attrs+ )+ => [Vertex pos attrs] -> Indexed 'Buffer.Coherent pos attrs- -> io (Indexed 'Buffer.Coherent pos attrs)-updateCoherent ctx vertices old = do+ -> m (Indexed 'Buffer.Coherent pos attrs)+updateCoherent vertices old = do Indexed{..} <- pick- Indexed+ Indexed iLabel <$> Buffer.updateCoherent pv iPositions <*> Buffer.updateCoherent av iAttrs <*> Buffer.updateCoherent iv iIndices@@ -162,8 +258,8 @@ if oldSize > newSize then pure old else do- destroyIndexed ctx old- createCoherentEmpty ctx (max newSize $ oldSize * 2)+ destroyIndexed old+ createCoherentEmpty (iLabel old) (max newSize $ oldSize * 2) oldSize = Buffer.aCapacity $ iIndices old newSize = Storable.length pv
+ src/Resource/Model/Observer.hs view
@@ -0,0 +1,211 @@+{- | Experimental helpers for managing models with multiple instance buffers.++Only works with fully-coherent models and atomic stores.+Not particularly efficient: when any element is changed, everything gets fully updated.+-}++module Resource.Model.Observer+ ( newCoherent+ , observeCoherent++ , VertexBuffers(..)+ , genericCreateInitial+ , genericDestroyCurrent++ , UpdateCoherent(..)+ , genericUpdateCoherent+ ) where++import RIO+import GHC.Generics++import Control.Monad.Trans.Resource (ResourceT)+import Control.Monad.Trans.Resource qualified as Resource+import Data.Kind (Type)+import Data.Text qualified as Text+import Data.Vector.Storable qualified as Storable+import Vulkan.Core10 qualified as Vk++import Engine.Types ()+import Engine.Vulkan.Types (HasVulkan, MonadVulkan)+import Engine.Worker qualified as Worker+import Resource.Buffer qualified as Buffer++newCoherent+ :: ( VertexBuffers res+ , MonadVulkan env m+ )+ => Int+ -> Text+ -> ResourceT m (Worker.ObserverIO res)+newCoherent initialSize label = do+ initial <- createInitial initialSize label+ observer <- Worker.newObserverIO initial++ context <- ask+ void $! Resource.register do+ current <- Worker.readObservedIO observer+ destroyCurrent context current++ pure observer++observeCoherent+ :: ( MonadVulkan env m+ , Worker.HasOutput output+ , UpdateCoherent bufs (Worker.GetOutput output)+ )+ => output+ -> Worker.ObserverIO bufs+ -> m ()+observeCoherent process observer =+ void $ Worker.observeIO process observer updateCoherent++class VertexBuffers a where+ createInitial :: forall env m . MonadVulkan env m => Int -> Text -> ResourceT m a++ default createInitial+ :: forall env m+ . ( Generic a+ , GVertexBuffers (Rep a)+ , MonadVulkan env m+ )+ => Int+ -> Text+ -> ResourceT m a+ createInitial = genericCreateInitial++ destroyCurrent :: forall env . HasVulkan env => env -> a -> IO ()+ default destroyCurrent+ :: forall env+ . ( Generic a+ , GVertexBuffers (Rep a)+ , HasVulkan env+ )+ => env+ -> a+ -> IO ()+ destroyCurrent = genericDestroyCurrent++{- XXX: Terminal instance for generic observable requiring only MonadVulkan.++Initial values can be written post-hoc using `updateCoherent` on observed data.+The more problematic part is using hardcoded usage argument.+This is fine for the main use case of model instance buffers, but makes it unsuitable for other uses.+-}+instance Storable a => VertexBuffers (Buffer.Allocated 'Buffer.Coherent a) where+ createInitial initialCapacity label =+ Buffer.createCoherent+ (Just label)+ Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT+ initialCapacity+ mempty++ destroyCurrent ctx buf = Buffer.destroy ctx buf++genericCreateInitial+ :: ( Generic a+ , GVertexBuffers (Rep a)+ , MonadVulkan env m+ )+ => Int+ -> Text+ -> ResourceT m a+genericCreateInitial size label =+ fmap GHC.Generics.to (gcreate size label)++genericDestroyCurrent+ :: ( Generic a+ , GVertexBuffers (Rep a)+ , HasVulkan env+ )+ => env+ -> a+ -> IO ()+genericDestroyCurrent env = gdestroy env . GHC.Generics.from++class GVertexBuffers (f :: Type -> Type) where+ gcreate :: forall env m a . MonadVulkan env m => Int -> Text -> ResourceT m (f a)+ gdestroy :: forall env a . HasVulkan env => env -> f a -> IO ()++instance GVertexBuffers f => GVertexBuffers (D1 c f) where+ gcreate size label = fmap M1 $ gcreate size label+ gdestroy env (M1 a) = gdestroy env a++instance GVertexBuffers f => GVertexBuffers (C1 c f) where+ gcreate size label = fmap M1 $ gcreate size label+ gdestroy env (M1 a) = gdestroy env a++instance (GVertexBuffers l, GVertexBuffers r) => GVertexBuffers (l :*: r) where+ gcreate size label = (:*:)+ <$> gcreate size label+ <*> gcreate size label+ gdestroy env (l :*: r) =+ gdestroy env r <>+ gdestroy env l++instance (GVertexBuffers f, Selector c) => GVertexBuffers (M1 S c f) where+ gcreate size label =+ fmap M1 $+ gcreate size $+ Text.intercalate "."+ [ label+ , fromString $ selName (undefined :: t c f a)+ ]+ gdestroy env (M1 a) = gdestroy env a++instance VertexBuffers a => GVertexBuffers (K1 r a) where+ gcreate size label = fmap K1 $ createInitial size label+ gdestroy env (K1 a) = destroyCurrent env a++-- TODO: class UpdateStaged bufs stores++class UpdateCoherent bufs stores where+ updateCoherent :: forall env m . MonadVulkan env m => bufs -> stores -> m bufs++ default updateCoherent+ :: forall env m+ . ( Generic bufs+ , Generic stores+ , GUpdateCoherent (Rep bufs) (Rep stores)+ , MonadVulkan env m+ )+ => bufs+ -> stores+ -> m bufs+ updateCoherent = genericUpdateCoherent++genericUpdateCoherent+ :: ( Generic bufs+ , Generic stores+ , GUpdateCoherent (Rep bufs) (Rep stores)+ , MonadVulkan env m+ )+ => bufs+ -> stores+ -> m bufs+genericUpdateCoherent bufs stores =+ fmap GHC.Generics.to $+ gUpdateCoherent+ (GHC.Generics.from bufs)+ (GHC.Generics.from stores)++instance Storable a => UpdateCoherent (Buffer.Allocated 'Buffer.Coherent a) (Storable.Vector a) where+ updateCoherent = Buffer.updateCoherentResize_++-- TODO: barbies-style update+-- instance (ApplicativeB collection) => UpdateCoherent (collection (Buffer.Allocated 'Buffer.Coherent)) (collection Storable.Vector) where+-- update = bzipWith Buffer.updateCoherentResize_++class GUpdateCoherent (fb :: Type -> Type) (fs :: Type -> Type) where+ gUpdateCoherent :: forall env m b s . MonadVulkan env m => fb b -> fs s -> m (fb b)++instance GUpdateCoherent fb fs => GUpdateCoherent (M1 c cb fb) (M1 c cs fs) where+ gUpdateCoherent (M1 gb) (M1 gs) = fmap M1 $ gUpdateCoherent gb gs++instance (GUpdateCoherent fbl fsl, GUpdateCoherent fbr fsr) => GUpdateCoherent (fbl :*: fbr) (fsl :*: fsr) where+ gUpdateCoherent (bl :*: br) (sl :*: sr) = (:*:)+ <$> gUpdateCoherent bl sl+ <*> gUpdateCoherent br sr++instance UpdateCoherent ba sa => GUpdateCoherent (K1 br ba) (K1 sr sa) where+ gUpdateCoherent (K1 gb) (K1 gs) = fmap K1 $ updateCoherent gb gs
+ src/Resource/Model/Observer/Example.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE DeriveAnyClass #-}++module Resource.Model.Observer.Example where++import RIO++import Control.Monad.Trans.Resource (ResourceT)+import Data.Vector.Storable qualified as Storable++import Engine.Types (HKD, StageRIO)+import Engine.Vulkan.Types (MonadVulkan)+import Engine.Worker qualified as Worker+import Resource.Buffer qualified as Buffer+import Resource.Model.Observer qualified as Observer++data ExampleF f = Example+ { x :: HKD f Float+ , y :: HKD f Float+ }+ deriving (Generic)++type Example = ExampleF Identity+deriving instance Show Example+{- DON'T: instance GStorable Example.++Vertex attributes are packed while GStorable gives something like std140.+-}++type ExampleStores = ExampleF Storable.Vector+deriving instance Show ExampleStores++type ExampleBuffers = ExampleF (Buffer.Allocated 'Buffer.Coherent)+deriving instance Show ExampleBuffers+instance Observer.VertexBuffers ExampleBuffers++instance Observer.UpdateCoherent ExampleBuffers ExampleStores++{-# DEPRECATED new "Just use Observer.newCoherent" #-}+new :: ResourceT (StageRIO st) (Worker.ObserverIO ExampleBuffers)+new = Observer.newCoherent 1 "SomeExample"++{-# DEPRECATED update "Just use Observer.updateCoherent" #-}+update :: MonadVulkan env m => ExampleBuffers -> ExampleStores -> m ExampleBuffers+update = Observer.updateCoherent
src/Resource/Source.hs view
@@ -1,4 +1,3 @@- module Resource.Source ( Source(..) , load@@ -9,6 +8,7 @@ import Data.FileEmbed qualified import Data.Typeable+import GHC.Records (HasField(..)) import GHC.Stack (withFrozenCallStack) import Language.Haskell.TH.Syntax qualified as TH import Resource.Compressed.Zstd qualified as Zstd@@ -20,6 +20,13 @@ = Bytes (Maybe Text) ByteString | BytesZstd (Maybe Text) (Zstd.Compressed ByteString) | File (Maybe Text) FilePath++instance HasField "label" Source (Maybe Text) where+ {-# INLINE getField #-}+ getField = \case+ Bytes label _bytes -> label+ BytesZstd label _bytes -> label+ File label _path -> label instance Show Source where show = \case
src/Resource/Texture.hs view
@@ -2,7 +2,6 @@ module Resource.Texture ( Texture(..)- , destroy , TextureError(..) -- * Texture types@@ -12,8 +11,6 @@ , TextureLayers(..) -- * Utilities- , allocateCollectionWith- , allocateTextureWith , debugNameCollection , TextureLoader @@ -22,16 +19,20 @@ , imageAllocationCI , stageBufferCI , stageAllocationCI++ , withSize2d+ , withSize3d ) where import RIO import Data.Bits ((.|.)) import Data.List qualified as List+import Geomancy (Vec2, vec2) import GHC.Stack (withFrozenCallStack) import GHC.TypeLits (Nat, KnownNat, natVal)+import GHC.Records (HasField(..)) import RIO.FilePath (takeBaseName)-import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk import Vulkan.NamedType ((:::)) import Vulkan.Utils.Debug qualified as Debug@@ -52,7 +53,7 @@ instance Exception TextureError -data Texture a = Texture+data Texture tag = Texture { tFormat :: Vk.Format , tMipLevels :: Word32 , tLayers :: Word32 -- ^ Actual number of layers, up to @ArrayOf a@@@ -79,31 +80,8 @@ type TextureLoader m layers = Vk.Format -> Queues Vk.CommandPool -> FilePath -> m (Texture layers) -type TextureLoaderAction src m layers = src -> m (Texture layers)- -- * Allocation wrappers -allocateCollectionWith- :: (Resource.MonadResource m, MonadVulkan env m, Traversable t)- => TextureLoaderAction src m layers- -> t src- -> m (Resource.ReleaseKey, t (Texture layers))-allocateCollectionWith action collection = do- res <- traverse (allocateTextureWith action) collection- key <- Resource.register $- traverse_ (Resource.release . fst) res- pure (key, fmap snd res)--allocateTextureWith- :: (Resource.MonadResource m, MonadVulkan env m)- => TextureLoaderAction src m layers- -> src- -> m (Resource.ReleaseKey, Texture layers)-allocateTextureWith action path = do- context <- ask- createTexture <- toIO $ action path- Resource.allocate createTexture (destroy context)- debugNameCollection :: ( Traversable t , MonadVulkan env m@@ -127,10 +105,6 @@ -- * Implementation -destroy :: (MonadIO io, HasVulkan context) => context -> Texture a -> io ()-destroy context Texture{tAllocatedImage} =- Image.destroy context tAllocatedImage- createImageView :: (MonadIO io, HasVulkan context) => context@@ -206,3 +180,30 @@ , VMA.usage = VMA.MEMORY_USAGE_CPU_TO_GPU , VMA.requiredFlags = Vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT }++-- * Helpers++{-# INLINE withSize2d #-}+withSize2d :: Num i => (i -> i -> a) -> Texture tag -> a+withSize2d f t =+ f+ (fromIntegral width)+ (fromIntegral height)+ where+ Vk.Extent3D{width, height} =+ Image.aiExtent (tAllocatedImage t)++{-# INLINE withSize3d #-}+withSize3d :: Num i => (i -> i -> i -> a) -> Texture tag -> a+withSize3d f t =+ f+ (fromIntegral width)+ (fromIntegral height)+ (fromIntegral depth)+ where+ Vk.Extent3D{width, height, depth} =+ Image.aiExtent (tAllocatedImage t)++instance HasField "size" (Texture tag) Vec2 where+ {-# INLINE getField #-}+ getField = withSize2d vec2
src/Resource/Texture/Ktx1.hs view
@@ -12,12 +12,12 @@ import Data.Vector qualified as Vector import Foreign qualified import GHC.Stack (withFrozenCallStack)+import UnliftIO.Resource (MonadResource) import Vulkan.Core10 qualified as Vk import Vulkan.Utils.FromGL qualified as FromGL import VulkanMemoryAllocator qualified as VMA import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, Queues)-import Resource.Image (AllocatedImage(..)) import Resource.Image qualified as Image import Resource.Source (Source(..)) import Resource.Source qualified as Source@@ -27,6 +27,7 @@ load :: ( TextureLayers a , MonadVulkan env m+ , MonadResource m , MonadThrow m , HasLogFunc env , Typeable a@@ -42,6 +43,7 @@ loadBytes :: ( TextureLayers a , MonadVulkan env m+ , MonadResource m , MonadThrow m , HasLogFunc env )@@ -60,6 +62,7 @@ :: forall a m env . ( TextureLayers a , MonadVulkan env m+ , MonadResource m , MonadThrow m , HasLogFunc env )@@ -126,28 +129,17 @@ logDebug $ "mipSizes: " <> displayShow mipSizes logDebug $ "offsets: " <> displayShow offsets - {- XXX:- Image created before staging buffer due to `Image.copyBufferToImage`- issued inside `VMA.withBuffer` bracket.- -}- (image, allocation, _info) <- VMA.createImage- vma- (Texture.imageCI format extent mipLevels numLayers)- Texture.imageAllocationCI+ unless (numLayers == textureLayers @a) $+ throwM $ Texture.ArrayError (textureLayers @a) numLayers - Image.transitionLayout- context+ dst <- Image.allocateDst pool- image+ Nothing -- XXX: Name the whole collection later, tagging source with its index.+ extent mipLevels- numLayers -- XXX: arrayLayers is always 0 for now+ numLayers format- Vk.IMAGE_LAYOUT_UNDEFINED- Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL - unless (numLayers == textureLayers @a) $- throwM $ Texture.ArrayError (textureLayers @a) numLayers- VMA.withBuffer vma (Texture.stageBufferCI totalSize) Texture.stageAllocationCI bracket \(staging, stage, stageInfo) -> do let mipImages = Vector.zip offsets images Vector.iforM_ mipImages \mipIx (offset, Ktx1.MipLevel{imageSize, arrayElements}) -> do@@ -183,53 +175,15 @@ VMA.flushAllocation vma stage 0 Vk.WHOLE_SIZE - -- XXX: copying to image while the staging buffer is still alive- Image.copyBufferToImage- context+ final <- Image.copyBufferToDst pool staging- image- extent+ dst offsets- numLayers - -- XXX: staging buffer is gone-- Image.transitionLayout- context- pool- image- mipLevels- numLayers -- XXX: arrayLayers is always 0 for now- format- Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL- Vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL-- imageView <- Texture.createImageView- context- image- format- mipLevels- numLayers-- let- extent2d =- let- Vk.Extent3D{..} = extent- in- Vk.Extent2D{..}-- allocatedImage = AllocatedImage- { aiAllocation = allocation- , aiExtent = extent2d- , aiFormat = format- , aiImage = image- , aiImageView = imageView- , aiImageRange = Image.subresource Vk.IMAGE_ASPECT_COLOR_BIT mipLevels numLayers+ pure Texture+ { tFormat = format+ , tMipLevels = mipLevels+ , tLayers = numLayers+ , tAllocatedImage = final }- pure Texture- { tFormat = format- , tMipLevels = mipLevels- , tLayers = numLayers- , tAllocatedImage = allocatedImage- }
+ src/Resource/Texture/Ktx2.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoForeignFunctionInterface #-}++module Resource.Texture.Ktx2+ ( load+ , loadBytes+ , loadKtx2+ ) where++import RIO++import Codec.Compression.Zstd.FFI qualified as Zstd+import Codec.Ktx.KeyValue qualified as KeyValue+import Codec.Ktx2.Header qualified as Header+import Codec.Ktx2.Level qualified as Level+import Codec.Ktx2.Read qualified as Read+import Data.Vector qualified as Vector+import Foreign qualified+import GHC.Stack (withFrozenCallStack)+import UnliftIO.Resource (MonadResource)+import Vulkan.Core10 qualified as Vk+import VulkanMemoryAllocator qualified as VMA++import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, Queues)+import Resource.Image qualified as Image+import Resource.Source (Source(..))+import Resource.Source qualified as Source+import Resource.Texture (Texture(..), TextureLayers(..))+import Resource.Texture qualified as Texture++load+ :: ( TextureLayers a+ , MonadVulkan env m+ , MonadResource m+ , MonadThrow m+ , HasLogFunc env+ , Typeable a+ , HasCallStack+ )+ => Queues Vk.CommandPool+ -> Source+ -> m (Texture a)+load pool source =+ withFrozenCallStack $+ case source of+ Source.File label path ->+ -- XXX: the codec has a more efficient loader for files+ loadFile label pool path+ _bytes ->+ Source.load (loadBytes source.label pool) source++loadFile+ :: ( TextureLayers a+ , MonadVulkan env m+ , MonadResource m+ , MonadThrow m+ , HasLogFunc env+ )+ => Maybe Text+ -> Queues Vk.CommandPool+ -> FilePath+ -> m (Texture a)+loadFile label pool path =+ bracket (Read.open path) Read.close $+ loadKtx2 (label <|> Just (fromString path)) pool++loadBytes+ :: ( TextureLayers a+ , MonadVulkan env m+ , MonadResource m+ , MonadThrow m+ , HasLogFunc env+ )+ => Maybe Text+ -> Queues Vk.CommandPool+ -> ByteString+ -> m (Texture a)+loadBytes label pool bytes =+ Read.bytes bytes >>= loadKtx2 label pool++loadKtx2+ :: forall a m env src+ . ( TextureLayers a+ , MonadVulkan env m+ , MonadResource m+ , MonadThrow m+ , HasLogFunc env+ , Read.ReadChunk src+ , Read.ReadLevel src+ )+ => Maybe Text+ -> Queues Vk.CommandPool+ -> Read.Context src+ -> m (Texture a)+loadKtx2 label pool ktx2@(Read.Context _src header) = do+ logDebug $ displayShow (label, header)++ kvd <- Read.keyValueData ktx2+ logDebug $ displayShow (label, format, extent, numLayers, KeyValue.textual kvd)++ levels <- Read.levels ktx2++ unless (Vector.length levels > 0) $+ throwString "Ktx2 contains no image levels"++ unless (Vector.length levels == fromIntegral header.levelCount) $+ throwString $ "Ktx2 level count mismatch " <> show (Vector.length levels, header.levelCount)++ unless (numLayers == textureLayers @a) $+ throwM $ Texture.ArrayError (textureLayers @a) numLayers++ let+ levelSizes = fmap (fromIntegral . (.uncompressedByteLength)) levels+ totalSize = Vector.sum levelSizes+ offsets = Vector.init $ Vector.scanl' (+) 0 levelSizes++ dst <- Image.allocateDst+ pool+ label+ extent+ (fromIntegral $ Vector.length levels)+ numLayers+ format++ vma <- asks getAllocator+ VMA.withBuffer vma (Texture.stageBufferCI totalSize) Texture.stageAllocationCI bracket \(staging, stage, stageInfo) -> do+ liftIO case header.supercompressionScheme of+ Header.SC_NONE ->+ Vector.forM_ (Vector.zip offsets levels) \(offset, level) ->+ liftIO . Read.levelToPtr ktx2 level $+ Foreign.plusPtr (VMA.mappedData stageInfo) offset++ Header.SC_ZSTANDARD -> do+ let maxSize = Vector.maximum levelSizes+ Foreign.allocaBytesAligned maxSize 16 \src ->+ Vector.forM_ (Vector.zip offsets levels) \(offset, level) -> do+ let expected = fromIntegral level.uncompressedByteLength+ Read.levelToPtr ktx2 level src++ res <-+ Zstd.checkError $+ Zstd.decompress+ (Foreign.plusPtr (VMA.mappedData stageInfo) offset)+ expected+ src+ (fromIntegral level.byteLength)+ case res of+ Right size | size == expected ->+ pure ()+ Right unexpected ->+ throwString $+ "Zstd decompressed unexpected amount of bytes: " <> show (unexpected, expected)+ Left err ->+ throwString err++ huh ->+ error $ "Unexpected supercompression scheme: " ++ show huh++ VMA.flushAllocation vma stage 0 Vk.WHOLE_SIZE++ final <- Image.copyBufferToDst+ pool+ staging+ dst+ offsets++ pure Texture+ { tFormat = format+ , tMipLevels = header.levelCount+ , tLayers = numLayers+ , tAllocatedImage = final+ }+ where+ format = Vk.Format (fromIntegral header.vkFormat)++ extent = Vk.Extent3D+ { width = header.pixelWidth+ , height = header.pixelHeight+ , depth = max 1 header.pixelDepth+ }++ -- XXX: can be flat array or a cubemap+ numLayers = max header.faceCount header.layerCount
+ src/Resource/Vulkan/DescriptorLayout.hs view
@@ -0,0 +1,47 @@+module Resource.Vulkan.DescriptorLayout where++import RIO++import Data.Vector qualified as Vector+import Engine.Vulkan.Types (getDevice, MonadVulkan)+import RIO.List qualified as List+import Vulkan.Core10 qualified as Vk+import Vulkan.Core12 qualified as Vk12+import Vulkan.CStruct.Extends (pattern (:&), pattern (::&))+import Vulkan.Zero (zero)++create+ :: MonadVulkan env m+ => Vector [(Vk.DescriptorSetLayoutBinding, Vk12.DescriptorBindingFlags)]+ -> m (Vector Vk.DescriptorSetLayout)+create dsBindings = do+ device <- asks getDevice+ Vector.forM dsBindings \bindsFlags -> do+ let+ (binds, flags) = List.unzip bindsFlags++ setCI =+ zero+ { Vk.bindings = Vector.fromList binds+ }+ ::& zero+ { Vk12.bindingFlags = Vector.fromList flags+ }+ :& ()++ Vk.createDescriptorSetLayout device setCI Nothing++forPipeline+ :: MonadVulkan env m+ => Vector Vk.DescriptorSetLayout+ -> Vector Vk.PushConstantRange+ -> m Vk.PipelineLayout+forPipeline dsLayouts pushConstantRanges = do+ device <- asks getDevice+ Vk.createPipelineLayout device layoutCI Nothing+ where+ layoutCI = Vk.PipelineLayoutCreateInfo+ { flags = zero+ , setLayouts = dsLayouts+ , pushConstantRanges = pushConstantRanges+ }
+ src/Resource/Vulkan/DescriptorPool.hs view
@@ -0,0 +1,64 @@+module Resource.Vulkan.DescriptorPool+ ( allocate++ , allocateSetsFrom+ ) where++import RIO++import Data.Vector qualified as Vector+import UnliftIO.Resource (MonadResource, ReleaseKey)+import UnliftIO.Resource qualified as Resource+import Vulkan.Core10 qualified as Vk+import Vulkan.Zero (zero)++import Engine.Vulkan.Types (MonadVulkan, getDevice)+import Resource.Vulkan.Named qualified as Named++allocate+ :: ( MonadVulkan env m+ , MonadResource m+ )+ => Maybe Text+ -> Word32+ -> [(Vk.DescriptorType, Word32)]+ -> m (ReleaseKey, Vk.DescriptorPool)+allocate name maxSets sizes = do+ device <- asks getDevice+ res <- Vk.withDescriptorPool+ device+ poolCI+ Nothing+ Resource.allocate+ for_ name $+ Named.object (snd res)+ pure res+ where+ poolCI = zero+ { Vk.maxSets =+ maxSets+ , Vk.poolSizes =+ Vector.fromList $+ map (uncurry Vk.DescriptorPoolSize) sizes+ }++-- TODO: extract to Resource.Vulkan.DescriptorSets ?+allocateSetsFrom+ :: MonadVulkan env m+ => Vk.DescriptorPool+ -> Maybe Text+ -> Vector Vk.DescriptorSetLayout+ -> m (Vector Vk.DescriptorSet)+allocateSetsFrom pool name layouts = do+ device <- asks getDevice+ descSets <- Vk.allocateDescriptorSets device zero+ { Vk.descriptorPool = pool+ , Vk.setLayouts = layouts+ }+ for_ name \prefix ->+ Vector.iforM_ descSets \ix ds ->+ Named.object ds $ mconcat+ [ prefix+ , "(set=", fromString (show ix), ")"+ ]+ pure descSets
+ src/Resource/Vulkan/Named.hs view
@@ -0,0 +1,38 @@+module Resource.Vulkan.Named+ ( object+ , objectOrigin+ ) where++import RIO++import GHC.Stack (callStack, getCallStack, srcLocModule, withFrozenCallStack)+import RIO.List qualified as List+import Vulkan.Core10 qualified as Vk+import Vulkan.Utils.Debug qualified as Debug++import Engine.Vulkan.Types (MonadVulkan, getDevice)++object+ :: ( MonadVulkan env m+ , Vk.HasObjectType a+ )+ => a+ -> Text+ -> m ()+object o name = do+ device <- asks getDevice+ Debug.nameObject device o $+ encodeUtf8 name++objectOrigin+ :: ( MonadVulkan env m+ , Vk.HasObjectType a+ , HasCallStack+ )+ => a+ -> m ()+objectOrigin o =+ withFrozenCallStack $+ object o $+ fromString . List.intercalate "|" $+ map (srcLocModule . snd) (getCallStack callStack)