packages feed

keid-core 0.1.6.1 → 0.1.7.0

raw patch · 15 files changed

+1479/−492 lines, 15 filesdep +containersdep +spirv-reflect-typesdep +spirv-reflect-yamlPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: containers, spirv-reflect-types, spirv-reflect-yaml

API changes (from Hackage documentation)

- Engine.Stage.Bootstrap.Types: NoPipelines :: NoPipelines
- Engine.Stage.Bootstrap.Types: NoRendering :: NoRendering
- Engine.Stage.Bootstrap.Types: NoResources :: NoResources
- Engine.Stage.Bootstrap.Types: NoState :: NoState
- Engine.Stage.Bootstrap.Types: data NoPipelines
- Engine.Stage.Bootstrap.Types: data NoRendering
- Engine.Stage.Bootstrap.Types: data NoResources
- Engine.Stage.Bootstrap.Types: data NoState
- Engine.Stage.Bootstrap.Types: instance Engine.Vulkan.Types.RenderPass Engine.Stage.Bootstrap.Types.NoRendering
- Engine.Stage.Bootstrap.Types: type Stage = Stage NoRendering NoPipelines NoResources NoState
- Engine.Vulkan.Pipeline: Config :: Maybe ByteString -> Maybe ByteString -> 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: [$sel:cBlend:Config] :: Config (dsl :: [Type]) vertices instances spec -> Bool
- Engine.Vulkan.Pipeline: [$sel:cCull:Config] :: Config (dsl :: [Type]) vertices instances spec -> CullModeFlagBits
- Engine.Vulkan.Pipeline: [$sel:cDepthBias:Config] :: Config (dsl :: [Type]) vertices instances spec -> Maybe ("constant" ::: Float, "slope" ::: Float)
- Engine.Vulkan.Pipeline: [$sel:cDepthCompare:Config] :: Config (dsl :: [Type]) vertices instances spec -> CompareOp
- Engine.Vulkan.Pipeline: [$sel:cDepthTest:Config] :: Config (dsl :: [Type]) vertices instances spec -> Bool
- Engine.Vulkan.Pipeline: [$sel:cDepthWrite:Config] :: Config (dsl :: [Type]) vertices instances spec -> Bool
- Engine.Vulkan.Pipeline: [$sel:cDescLayouts:Config] :: Config (dsl :: [Type]) vertices instances spec -> Tagged dsl [DsBindings]
- Engine.Vulkan.Pipeline: [$sel:cFragmentCode:Config] :: Config (dsl :: [Type]) vertices instances spec -> Maybe ByteString
- Engine.Vulkan.Pipeline: [$sel:cPushConstantRanges:Config] :: Config (dsl :: [Type]) vertices instances spec -> Vector PushConstantRange
- Engine.Vulkan.Pipeline: [$sel:cSpecialization:Config] :: Config (dsl :: [Type]) vertices instances spec -> spec
- Engine.Vulkan.Pipeline: [$sel:cTopology:Config] :: Config (dsl :: [Type]) vertices instances spec -> PrimitiveTopology
- Engine.Vulkan.Pipeline: [$sel:cVertexCode:Config] :: Config (dsl :: [Type]) vertices instances spec -> Maybe ByteString
- Engine.Vulkan.Pipeline: [$sel:cVertexInput:Config] :: Config (dsl :: [Type]) vertices instances spec -> SomeStruct PipelineVertexInputStateCreateInfo
- Engine.Vulkan.Pipeline: allocate :: (MonadVulkan env m, MonadResource m, HasRenderPass renderpass, Specialization spec, HasCallStack) => Maybe Extent2D -> SampleCountFlagBits -> Config dsl vertices instances spec -> renderpass -> m (ReleaseKey, Pipeline dsl vertices instances)
- Engine.Vulkan.Pipeline: attrBindings :: [[Format]] -> Vector VertexInputAttributeDescription
- Engine.Vulkan.Pipeline: baseConfig :: Config '[] vertices instances ()
- Engine.Vulkan.Pipeline: bind :: (Compatible pipeLayout boundLayout, MonadIO m) => CommandBuffer -> Pipeline pipeLayout vertices instances -> Bound boundLayout vertices instances m () -> Bound boundLayout oldVertices oldInstances m ()
- Engine.Vulkan.Pipeline: 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: data Config (dsl :: [Type]) vertices instances spec
- Engine.Vulkan.Pipeline: formatSize :: Integral a => Format -> a
- Engine.Vulkan.Pipeline: pushPlaceholder :: PushConstantRange
- Engine.Vulkan.Pipeline: vertexInput :: [(VertexInputRate, [Format])] -> SomeStruct PipelineVertexInputStateCreateInfo
+ Engine.SpirV.Compile: glsl :: (HasLogFunc env, HasProcessContext env) => Maybe FilePath -> Text -> Text -> Code -> RIO env ()
+ Engine.SpirV.Compile: glslPipelines :: (StageInfo stages, HasLogFunc env, HasProcessContext env) => Maybe FilePath -> Map Text (stages (Maybe Code)) -> RIO env ()
+ Engine.SpirV.Compile: glslStages :: (StageInfo stages, HasLogFunc env, HasProcessContext env) => Maybe FilePath -> Text -> stages (Maybe Code) -> RIO env ()
+ Engine.SpirV.Reflect: BlockSignature :: Word32 -> Word32 -> TypeFlags -> Maybe Scalar -> BlockSignature
+ Engine.SpirV.Reflect: InterfaceSignature :: Format -> TypeFlags -> Maybe Matrix -> InterfaceSignature
+ Engine.SpirV.Reflect: Reflect :: BindMap BlockBinding -> StageInterface stages -> Text -> InterfaceBinds -> Reflect stages
+ Engine.SpirV.Reflect: [$sel:bindMap:Reflect] :: Reflect stages -> BindMap BlockBinding
+ Engine.SpirV.Reflect: [$sel:flags:BlockSignature] :: BlockSignature -> TypeFlags
+ Engine.SpirV.Reflect: [$sel:flags:InterfaceSignature] :: InterfaceSignature -> TypeFlags
+ Engine.SpirV.Reflect: [$sel:format:InterfaceSignature] :: InterfaceSignature -> Format
+ Engine.SpirV.Reflect: [$sel:inputStage:Reflect] :: Reflect stages -> Text
+ Engine.SpirV.Reflect: [$sel:inputs:Reflect] :: Reflect stages -> InterfaceBinds
+ Engine.SpirV.Reflect: [$sel:interfaces:Reflect] :: Reflect stages -> StageInterface stages
+ Engine.SpirV.Reflect: [$sel:matrix:InterfaceSignature] :: InterfaceSignature -> Maybe Matrix
+ Engine.SpirV.Reflect: [$sel:offset:BlockSignature] :: BlockSignature -> Word32
+ Engine.SpirV.Reflect: [$sel:scalar:BlockSignature] :: BlockSignature -> Maybe Scalar
+ Engine.SpirV.Reflect: [$sel:size:BlockSignature] :: BlockSignature -> Word32
+ Engine.SpirV.Reflect: bindMapUnionWith :: (a -> a -> Bool) -> BindMap a -> BindMap a -> Either (Int, Int, a, a) (BindMap a)
+ Engine.SpirV.Reflect: blockTree :: [Maybe Text] -> BlockVariable -> Tree ([Maybe Text], BlockSignature)
+ Engine.SpirV.Reflect: data BlockSignature
+ Engine.SpirV.Reflect: data InterfaceSignature
+ Engine.SpirV.Reflect: data Reflect stages
+ Engine.SpirV.Reflect: inputStageInterface :: (StageInfo stages, IsString label) => StageInterface stages -> Maybe (label, InterfaceBinds)
+ Engine.SpirV.Reflect: instance GHC.Classes.Eq (Engine.SpirV.Reflect.StageInterface stages) => GHC.Classes.Eq (Engine.SpirV.Reflect.Reflect stages)
+ Engine.SpirV.Reflect: instance GHC.Classes.Eq Engine.SpirV.Reflect.BlockSignature
+ Engine.SpirV.Reflect: instance GHC.Classes.Eq Engine.SpirV.Reflect.InterfaceSignature
+ Engine.SpirV.Reflect: instance GHC.Classes.Ord Engine.SpirV.Reflect.BlockSignature
+ Engine.SpirV.Reflect: instance GHC.Classes.Ord Engine.SpirV.Reflect.InterfaceSignature
+ Engine.SpirV.Reflect: instance GHC.Show.Show (Engine.SpirV.Reflect.StageInterface stages) => GHC.Show.Show (Engine.SpirV.Reflect.Reflect stages)
+ Engine.SpirV.Reflect: instance GHC.Show.Show Engine.SpirV.Reflect.BlockSignature
+ Engine.SpirV.Reflect: instance GHC.Show.Show Engine.SpirV.Reflect.InterfaceSignature
+ Engine.SpirV.Reflect: interfaceBinds :: StorageClass -> Vector InterfaceVariable -> InterfaceBinds
+ Engine.SpirV.Reflect: interfaceCompatible :: (StageInfo stages, IsString label) => StageInterface stages -> Either (IncompatibleInterfaces label) [CompatibleInterfaces label]
+ Engine.SpirV.Reflect: invoke :: (HasProcessContext env, HasLogFunc env, MonadReader env m, MonadIO m) => FilePath -> m Module
+ Engine.SpirV.Reflect: moduleBindMap :: Module -> BindMap BlockBinding
+ Engine.SpirV.Reflect: moduleInterfaceBinds :: Module -> (InterfaceBinds, InterfaceBinds)
+ Engine.SpirV.Reflect: stagesBindMap :: (MonadIO m, MonadReader env m, HasLogFunc env, StageInfo stages) => stages (Maybe Module) -> m (BindMap BlockBinding)
+ Engine.SpirV.Reflect: stagesInterfaceMap :: (MonadIO m, Traversable stages) => stages (Maybe Module) -> m (StageInterface stages)
+ Engine.SpirV.Reflect: type BindMap a = IntMap (IntMap a)
+ Engine.SpirV.Reflect: type BlockBinding = (Text, DescriptorType, Tree ([Maybe Text], BlockSignature))
+ Engine.SpirV.Reflect: type CompatibleInterfaces label = (label, label, IntMap ([Text], Matching (Maybe Text)))
+ Engine.SpirV.Reflect: type IncompatibleInterfaces label = (label, label, Int, Maybe (InterfaceSignature, InterfaceSignature))
+ Engine.SpirV.Reflect: type InterfaceBinding = (Maybe Text, [Text], InterfaceSignature)
+ Engine.SpirV.Reflect: type InterfaceBinds = IntMap InterfaceBinding
+ Engine.SpirV.Reflect: type Matching a = Either (a, a) a
+ Engine.SpirV.Reflect: type StageInterface stages = stages (Maybe (InterfaceBinds, InterfaceBinds))
+ Engine.Stage.Component: NoFrameResources :: NoFrameResources
+ Engine.Stage.Component: NoPipelines :: NoPipelines
+ Engine.Stage.Component: NoRenderPass :: NoRenderPass
+ Engine.Stage.Component: NoRunState :: NoRunState
+ Engine.Stage.Component: Rendering :: (SwapchainResources -> ResourceT (StageRIO st) rp) -> (SwapchainResources -> rp -> ResourceT (StageRIO st) p) -> Rendering rp p st
+ Engine.Stage.Component: Resources :: StageRIO (Maybe SwapchainResources) (ReleaseKey, st) -> (Queues CommandPool -> rp -> p -> ResourceT (StageRIO st) rr) -> Resources rp p st rr
+ Engine.Stage.Component: Scene :: ResourceT (StageRIO st) () -> (st -> rr -> StageFrameRIO rp p rr st ()) -> (CommandBuffer -> rr -> ("image index" ::: Word32) -> StageFrameRIO rp p rr st ()) -> Scene rp p st rr
+ Engine.Stage.Component: [$sel:rAllocateP:Rendering] :: Rendering rp p st -> SwapchainResources -> rp -> ResourceT (StageRIO st) p
+ Engine.Stage.Component: [$sel:rAllocateRP:Rendering] :: Rendering rp p st -> SwapchainResources -> ResourceT (StageRIO st) rp
+ Engine.Stage.Component: [$sel:rInitialRR:Resources] :: Resources rp p st rr -> Queues CommandPool -> rp -> p -> ResourceT (StageRIO st) rr
+ Engine.Stage.Component: [$sel:rInitialRS:Resources] :: Resources rp p st rr -> StageRIO (Maybe SwapchainResources) (ReleaseKey, st)
+ Engine.Stage.Component: [$sel:scBeforeLoop:Scene] :: Scene rp p st rr -> ResourceT (StageRIO st) ()
+ Engine.Stage.Component: [$sel:scRecordCommands:Scene] :: Scene rp p st rr -> CommandBuffer -> rr -> ("image index" ::: Word32) -> StageFrameRIO rp p rr st ()
+ Engine.Stage.Component: [$sel:scUpdateBuffers:Scene] :: Scene rp p st rr -> st -> rr -> StageFrameRIO rp p rr st ()
+ Engine.Stage.Component: assemble :: Foldable t => Text -> Rendering rp p st -> Resources rp p st rr -> t (Scene rp p st rr) -> Stage rp p rr st
+ Engine.Stage.Component: data NoFrameResources
+ Engine.Stage.Component: data NoPipelines
+ Engine.Stage.Component: data NoRenderPass
+ Engine.Stage.Component: data NoRunState
+ Engine.Stage.Component: data Rendering rp p st
+ Engine.Stage.Component: data Resources rp p st rr
+ Engine.Stage.Component: data Scene rp p st rr
+ Engine.Stage.Component: instance Engine.Vulkan.Types.RenderPass Engine.Stage.Component.NoRenderPass
+ Engine.Stage.Component: instance GHC.Base.Monoid (Engine.Stage.Component.Scene rp p st rr)
+ Engine.Stage.Component: instance GHC.Base.Semigroup (Engine.Stage.Component.Scene rp p st rr)
+ Engine.Stage.Component: noRendering :: NoRendering st
+ Engine.Stage.Component: noResources :: NoResources rp p
+ Engine.Stage.Component: type NoRendering = Rendering NoRenderPass NoPipelines
+ Engine.Stage.Component: type NoResources rp p = Resources rp p NoRunState NoFrameResources
+ Engine.Vulkan.Pipeline.Compute: Stages :: a -> Stages a
+ Engine.Vulkan.Pipeline.Compute: [$sel:comp:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Compute: instance Data.Foldable.Foldable Engine.Vulkan.Pipeline.Compute.Stages
+ Engine.Vulkan.Pipeline.Compute: instance Data.Traversable.Traversable Engine.Vulkan.Pipeline.Compute.Stages
+ Engine.Vulkan.Pipeline.Compute: instance Engine.Vulkan.Pipeline.Stages.StageInfo Engine.Vulkan.Pipeline.Compute.Stages
+ Engine.Vulkan.Pipeline.Compute: instance GHC.Base.Applicative Engine.Vulkan.Pipeline.Compute.Stages
+ Engine.Vulkan.Pipeline.Compute: instance GHC.Base.Functor Engine.Vulkan.Pipeline.Compute.Stages
+ Engine.Vulkan.Pipeline.Compute: instance GHC.Classes.Eq a => GHC.Classes.Eq (Engine.Vulkan.Pipeline.Compute.Stages a)
+ Engine.Vulkan.Pipeline.Compute: instance GHC.Classes.Ord a => GHC.Classes.Ord (Engine.Vulkan.Pipeline.Compute.Stages a)
+ Engine.Vulkan.Pipeline.Compute: instance GHC.Generics.Generic1 Engine.Vulkan.Pipeline.Compute.Stages
+ Engine.Vulkan.Pipeline.Compute: instance GHC.Show.Show a => GHC.Show.Show (Engine.Vulkan.Pipeline.Compute.Stages a)
+ Engine.Vulkan.Pipeline.Compute: newtype Stages a
+ Engine.Vulkan.Pipeline.Compute: stageFlagBits :: StageInfo t => t ShaderStageFlagBits
+ Engine.Vulkan.Pipeline.Compute: stageNames :: (StageInfo t, IsString label) => t label
+ Engine.Vulkan.Pipeline.Compute: type StageCode = Stages (Maybe Code)
+ Engine.Vulkan.Pipeline.Compute: type StageReflect = Reflect Stages
+ Engine.Vulkan.Pipeline.Compute: type StageSpirv = Stages (Maybe ByteString)
+ Engine.Vulkan.Pipeline.External: data ConfigureCompute p
+ Engine.Vulkan.Pipeline.External: data ConfigureGraphics p
+ Engine.Vulkan.Pipeline.External: data Observers p
+ Engine.Vulkan.Pipeline.External: dumpPipelines :: StageInfo t => MonadIO io => FilePath -> Map Text (t (Maybe Code)) -> io ()
+ Engine.Vulkan.Pipeline.External: loadConfig :: (Traversable stages, MonadIO io) => stages (Maybe FilePath) -> io (stages (Maybe ByteString))
+ Engine.Vulkan.Pipeline.External: loadConfigReflect :: (StageInfo stages, MonadIO io, MonadReader env io, HasLogFunc env, HasProcessContext env) => stages (Maybe FilePath) -> io (stages (Maybe ByteString), Reflect stages)
+ Engine.Vulkan.Pipeline.External: newObserverCompute :: (config ~ Configure pipeline (), pipeline ~ Pipeline dsl Compute Compute) => Process config -> ResourceT (StageRIO rs) (Observer pipeline)
+ Engine.Vulkan.Pipeline.External: newObserverGraphics :: (pipeline ~ Pipeline dsl vertices instances, HasOutput worker, Specialization (Specialization pipeline), HasRenderPass renderpass, GetOutput worker ~ Configure pipeline) => renderpass -> SampleCountFlagBits -> worker -> ResourceT (StageRIO rs) (Observer pipeline)
+ 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: 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: 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: 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: spawnReflect :: (MonadUnliftIO m, MonadReader env m, HasLogFunc env, HasProcessContext env, StageInfo stages) => Text -> stages (Maybe FilePath) -> ((stages (Maybe ByteString), Reflect stages) -> config) -> m (Process config)
+ Engine.Vulkan.Pipeline.External: type Observer pipeline = ObserverIO (ReleaseKey, pipeline)
+ Engine.Vulkan.Pipeline.External: type Process config = Timed () config
+ Engine.Vulkan.Pipeline.External: type family f ^ p
+ 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: Pipeline :: Pipeline -> Tagged dsl PipelineLayout -> Tagged dsl DsLayouts -> Pipeline (dsl :: [Type]) vertices instances
+ Engine.Vulkan.Pipeline.Graphics: Stages :: a -> a -> a -> a -> a -> Stages a
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cBlend:Config] :: Config (dsl :: [Type]) vertices instances spec -> Bool
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cCull:Config] :: Config (dsl :: [Type]) vertices instances spec -> CullModeFlagBits
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cDepthBias:Config] :: Config (dsl :: [Type]) vertices instances spec -> Maybe ("constant" ::: Float, "slope" ::: Float)
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cDepthCompare:Config] :: Config (dsl :: [Type]) vertices instances spec -> CompareOp
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cDepthTest:Config] :: Config (dsl :: [Type]) vertices instances spec -> Bool
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cDepthWrite:Config] :: Config (dsl :: [Type]) vertices instances spec -> Bool
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cDescLayouts:Config] :: Config (dsl :: [Type]) vertices instances spec -> Tagged dsl [DsBindings]
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cPushConstantRanges:Config] :: Config (dsl :: [Type]) vertices instances spec -> Vector PushConstantRange
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cReflect:Config] :: Config (dsl :: [Type]) vertices instances spec -> Maybe StageReflect
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cSpecialization:Config] :: Config (dsl :: [Type]) vertices instances spec -> spec
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cStages:Config] :: Config (dsl :: [Type]) vertices instances spec -> StageSpirv
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cTopology:Config] :: Config (dsl :: [Type]) vertices instances spec -> PrimitiveTopology
+ Engine.Vulkan.Pipeline.Graphics: [$sel:cVertexInput:Config] :: Config (dsl :: [Type]) vertices instances spec -> SomeStruct PipelineVertexInputStateCreateInfo
+ Engine.Vulkan.Pipeline.Graphics: [$sel:frag:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Graphics: [$sel:geom:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Graphics: [$sel:pDescLayouts:Pipeline] :: Pipeline (dsl :: [Type]) vertices instances -> Tagged dsl DsLayouts
+ Engine.Vulkan.Pipeline.Graphics: [$sel:pLayout:Pipeline] :: Pipeline (dsl :: [Type]) vertices instances -> Tagged dsl PipelineLayout
+ Engine.Vulkan.Pipeline.Graphics: [$sel:pipeline:Pipeline] :: Pipeline (dsl :: [Type]) vertices instances -> Pipeline
+ Engine.Vulkan.Pipeline.Graphics: [$sel:tesc:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Graphics: [$sel:tese:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Graphics: [$sel:vert:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Graphics: allocate :: (config ~ Configure pipeline, pipeline ~ Pipeline dsl vertices instances, spec ~ Specialization pipeline, Specialization spec, HasCallStack, MonadVulkan env m, MonadResource m, HasRenderPass renderpass) => Maybe Extent2D -> SampleCountFlagBits -> Config dsl vertices instances spec -> renderpass -> m (ReleaseKey, pipeline)
+ Engine.Vulkan.Pipeline.Graphics: baseConfig :: Config '[] vertices instances ()
+ Engine.Vulkan.Pipeline.Graphics: basicStages :: ("vert" ::: a) -> ("frag" ::: a) -> Stages (Maybe a)
+ Engine.Vulkan.Pipeline.Graphics: bind :: (Compatible pipeLayout boundLayout, MonadIO m) => CommandBuffer -> Pipeline pipeLayout vertices instances -> Bound boundLayout vertices instances m () -> Bound boundLayout oldVertices oldInstances m ()
+ 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: data Config (dsl :: [Type]) vertices instances spec
+ Engine.Vulkan.Pipeline.Graphics: data Pipeline (dsl :: [Type]) vertices instances
+ Engine.Vulkan.Pipeline.Graphics: data Stages a
+ Engine.Vulkan.Pipeline.Graphics: destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Pipeline dsl vertices instances -> io ()
+ Engine.Vulkan.Pipeline.Graphics: formatSize :: Integral a => Format -> a
+ Engine.Vulkan.Pipeline.Graphics: instance Data.Foldable.Foldable Engine.Vulkan.Pipeline.Graphics.Stages
+ Engine.Vulkan.Pipeline.Graphics: instance Data.Traversable.Traversable Engine.Vulkan.Pipeline.Graphics.Stages
+ Engine.Vulkan.Pipeline.Graphics: instance Engine.Vulkan.Pipeline.Stages.StageInfo Engine.Vulkan.Pipeline.Graphics.Stages
+ Engine.Vulkan.Pipeline.Graphics: instance GHC.Base.Applicative Engine.Vulkan.Pipeline.Graphics.Stages
+ Engine.Vulkan.Pipeline.Graphics: instance GHC.Base.Functor Engine.Vulkan.Pipeline.Graphics.Stages
+ Engine.Vulkan.Pipeline.Graphics: instance GHC.Classes.Eq a => GHC.Classes.Eq (Engine.Vulkan.Pipeline.Graphics.Stages a)
+ Engine.Vulkan.Pipeline.Graphics: instance GHC.Classes.Ord a => GHC.Classes.Ord (Engine.Vulkan.Pipeline.Graphics.Stages a)
+ Engine.Vulkan.Pipeline.Graphics: instance GHC.Generics.Generic1 Engine.Vulkan.Pipeline.Graphics.Stages
+ Engine.Vulkan.Pipeline.Graphics: instance GHC.Show.Show a => GHC.Show.Show (Engine.Vulkan.Pipeline.Graphics.Stages a)
+ Engine.Vulkan.Pipeline.Graphics: pushPlaceholder :: PushConstantRange
+ Engine.Vulkan.Pipeline.Graphics: stageFlagBits :: StageInfo t => t ShaderStageFlagBits
+ Engine.Vulkan.Pipeline.Graphics: stageNames :: (StageInfo t, IsString label) => t label
+ Engine.Vulkan.Pipeline.Graphics: type StageCode = Stages (Maybe Code)
+ Engine.Vulkan.Pipeline.Graphics: type StageReflect = Reflect Stages
+ Engine.Vulkan.Pipeline.Graphics: type StageSpirv = Stages (Maybe ByteString)
+ Engine.Vulkan.Pipeline.Graphics: type family Specialization pipeline
+ Engine.Vulkan.Pipeline.Graphics: vertexInput :: [(VertexInputRate, [Format])] -> SomeStruct PipelineVertexInputStateCreateInfo
+ Engine.Vulkan.Pipeline.Graphics: vertexOnly :: ("vert" ::: a) -> Stages (Maybe a)
+ Engine.Vulkan.Pipeline.Raytrace: Stages :: a -> a -> a -> a -> a -> a -> Stages a
+ Engine.Vulkan.Pipeline.Raytrace: [$sel:rahit:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Raytrace: [$sel:rcall:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Raytrace: [$sel:rchit:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Raytrace: [$sel:rgen:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Raytrace: [$sel:rint:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Raytrace: [$sel:rmiss:Stages] :: Stages a -> a
+ Engine.Vulkan.Pipeline.Raytrace: data Stages a
+ Engine.Vulkan.Pipeline.Raytrace: instance Data.Foldable.Foldable Engine.Vulkan.Pipeline.Raytrace.Stages
+ Engine.Vulkan.Pipeline.Raytrace: instance Data.Traversable.Traversable Engine.Vulkan.Pipeline.Raytrace.Stages
+ Engine.Vulkan.Pipeline.Raytrace: instance Engine.Vulkan.Pipeline.Stages.StageInfo Engine.Vulkan.Pipeline.Raytrace.Stages
+ Engine.Vulkan.Pipeline.Raytrace: instance GHC.Base.Applicative Engine.Vulkan.Pipeline.Raytrace.Stages
+ Engine.Vulkan.Pipeline.Raytrace: instance GHC.Base.Functor Engine.Vulkan.Pipeline.Raytrace.Stages
+ Engine.Vulkan.Pipeline.Raytrace: instance GHC.Classes.Eq a => GHC.Classes.Eq (Engine.Vulkan.Pipeline.Raytrace.Stages a)
+ Engine.Vulkan.Pipeline.Raytrace: instance GHC.Classes.Ord a => GHC.Classes.Ord (Engine.Vulkan.Pipeline.Raytrace.Stages a)
+ Engine.Vulkan.Pipeline.Raytrace: instance GHC.Generics.Generic1 Engine.Vulkan.Pipeline.Raytrace.Stages
+ Engine.Vulkan.Pipeline.Raytrace: instance GHC.Show.Show a => GHC.Show.Show (Engine.Vulkan.Pipeline.Raytrace.Stages a)
+ Engine.Vulkan.Pipeline.Stages: class (Applicative t, Traversable t) => StageInfo t
+ Engine.Vulkan.Pipeline.Stages: stageFlagBits :: StageInfo t => t ShaderStageFlagBits
+ Engine.Vulkan.Pipeline.Stages: stageNames :: (StageInfo t, IsString label) => t label
+ Engine.Vulkan.Pipeline.Stages: withLabels :: (StageInfo t, IsString label) => t a -> t (label, a)
+ Render.Code: instance Data.String.IsString Render.Code.Code
- Engine.Stage.Bootstrap.Setup: bootstrapStage :: (a -> StackStage) -> StageSetupRIO a -> Stage NoRendering NoPipelines NoResources NoState
+ Engine.Stage.Bootstrap.Setup: bootstrapStage :: (a -> StackStage) -> StageSetupRIO a -> Stage NoRenderPass NoPipelines NoFrameResources NoRunState
- Engine.Vulkan.Pipeline: type family Configure pipeline spec
+ Engine.Vulkan.Pipeline: type family Specialization pipeline
- Engine.Vulkan.Pipeline.Compute: bind :: (Compatible pipeLayout boundLayout, MonadIO m) => CommandBuffer -> Pipeline pipeLayout vertices instances -> Bound boundLayout vertices instances m () -> Bound boundLayout oldVertices oldInstances m ()
+ Engine.Vulkan.Pipeline.Compute: bind :: (Compatible pipeLayout boundLayout, MonadIO m) => CommandBuffer -> Pipeline pipeLayout Compute Compute -> Bound boundLayout Compute Compute m () -> Bound boundLayout noVertices noInstances m ()
- Engine.Vulkan.Shader: create :: (MonadIO io, HasVulkan ctx) => ctx -> Vector (ShaderStageFlagBits, ByteString) -> Maybe SpecializationInfo -> io Shader
+ Engine.Vulkan.Shader: create :: (MonadIO io, HasVulkan ctx, StageInfo t) => ctx -> t (Maybe ByteString) -> Maybe SpecializationInfo -> io Shader
- Render.Code: compileComp :: String -> Q Exp
+ Render.Code: compileComp :: Code -> Q Exp
- Render.Code: compileFrag :: String -> Q Exp
+ Render.Code: compileFrag :: Code -> Q Exp
- Render.Code: compileVert :: String -> Q Exp
+ Render.Code: compileVert :: Code -> Q Exp

Files

ChangeLog.md view
@@ -1,5 +1,15 @@ # Changelog for keid-core +## 0.1.7.0++- Graphics pipelines extracted to `Engine.Vulkan.Pipeline.Graphics`.+- Added pipeline stage collections and `StageInfo` class.+  * Shader creation can now pluck stage flags from the collection.+- Added `Engine.Vulkan.Pipeline.Raytrace` stages (no pipelines yet).+- Added `Engine.Vulkan.Pipeline.External` worker for reloading pipelines.+- Added `Engine.SpirV.Reflect` to facilitate safe reloads using `spirv-reflect` toolchain.+- Added `Engine.Engine.Stage.Component` with rendering/resources/scenes decoupled.+ ## 0.1.6.1  - Minimal `vulkan` version is now 3.17.
keid-core.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           keid-core-version:        0.1.6.1+version:        0.1.7.0 synopsis:       Core parts of Keid engine. category:       Game Engine homepage:       https://keid.haskell-game.dev@@ -41,8 +41,10 @@       Engine.Setup       Engine.Setup.Device       Engine.Setup.Window+      Engine.SpirV.Compile+      Engine.SpirV.Reflect       Engine.Stage.Bootstrap.Setup-      Engine.Stage.Bootstrap.Types+      Engine.Stage.Component       Engine.StageSwitch       Engine.Types       Engine.Types.Options@@ -52,6 +54,10 @@       Engine.Vulkan.DescSets       Engine.Vulkan.Pipeline       Engine.Vulkan.Pipeline.Compute+      Engine.Vulkan.Pipeline.External+      Engine.Vulkan.Pipeline.Graphics+      Engine.Vulkan.Pipeline.Raytrace+      Engine.Vulkan.Pipeline.Stages       Engine.Vulkan.Shader       Engine.Vulkan.Swapchain       Engine.Vulkan.Types@@ -154,6 +160,7 @@     , base >=4.7 && <5     , binary     , bytestring+    , containers     , cryptohash-md5     , derive-storable     , derive-storable-plugin@@ -168,6 +175,8 @@     , rio >=0.1.12.0     , rio-app     , serialise+    , spirv-reflect-types+    , spirv-reflect-yaml     , tagged     , template-haskell     , text
+ src/Engine/SpirV/Compile.hs view
@@ -0,0 +1,86 @@+module Engine.SpirV.Compile+  ( glsl+  , glslStages+  , glslPipelines+  ) where++import RIO++import RIO.ByteString qualified as ByteString+import RIO.Directory (createDirectoryIfMissing, doesFileExist)+import RIO.FilePath ((<.>), (</>))+import RIO.Map qualified as Map+import RIO.Process (HasProcessContext, proc, readProcess_)+import RIO.Text qualified as Text++import Render.Code (Code(..))+import Engine.Vulkan.Pipeline.Stages qualified as Stages++glsl+  :: ( HasLogFunc env+     , HasProcessContext env+     )+  => Maybe FilePath+  -> Text+  -> Text+  -> Code+  -> RIO env ()+glsl outdir basename stage (Code source) = do+  withDir \dir -> do+    let+      shaderFile = dir </> Text.unpack basename <.> Text.unpack stage+      outFile  = shaderFile <.> "spv"+      outBytes = encodeUtf8 source++    exists <- doesFileExist shaderFile+    same <-+      if exists then do+        oldBytes <- ByteString.readFile shaderFile+        pure $ oldBytes == outBytes+      else+        pure False++    unless same do+      ByteString.writeFile shaderFile outBytes+      (_out, _err) <- proc+        "glslangValidator"+        [ "--target-env", "vulkan1.2"+        , "-S", Text.unpack stage+        , "-V", shaderFile+        , "-o", outFile+        ]+        readProcess_+      logDebug $ displayShow (_out, _err)+  where+    withDir action =+      case outdir of+        Nothing ->+          withSystemTempDirectory "keid-shader" action+        Just dir -> do+          createDirectoryIfMissing True dir+          action dir++glslStages+  :: ( Stages.StageInfo stages+     , HasLogFunc env+     , HasProcessContext env+     )+  => Maybe FilePath+  -> Text+  -> stages (Maybe Code)+  -> RIO env ()+glslStages outdir basename stages =+  for_ (Stages.withLabels stages) \(label, mstage) ->+    traverse_ (glsl outdir basename label) mstage++glslPipelines+  :: ( Stages.StageInfo stages+     , HasLogFunc env+     , HasProcessContext env+     )+  => Maybe FilePath+  -> Map Text (stages (Maybe Code))+  -> RIO env ()+glslPipelines outdir = traverse_ compile . Map.toList+  where+    compile (label, stages) = glslStages outdir label stages
+ src/Engine/SpirV/Reflect.hs view
@@ -0,0 +1,338 @@+{-# LANGUAGE UndecidableInstances #-}++module Engine.SpirV.Reflect where++import RIO++import Data.IntMap qualified as IntMap+import Data.List qualified as List+import Data.SpirV.Reflect.BlockVariable qualified as BlockVariable+import Data.SpirV.Reflect.DescriptorBinding qualified as DescriptorBinding+import Data.SpirV.Reflect.DescriptorSet qualified as DescriptorSet+import Data.SpirV.Reflect.Enums qualified as Enums+import Data.SpirV.Reflect.InterfaceVariable (InterfaceVariable)+import Data.SpirV.Reflect.InterfaceVariable qualified as InterfaceVariable+import Data.SpirV.Reflect.Module (Module)+import Data.SpirV.Reflect.Module qualified as Module+import Data.SpirV.Reflect.Traits qualified as Traits+import Data.SpirV.Reflect.TypeDescription qualified as TypeDescription+import Data.SpirV.Reflect.Yaml qualified as ReflectYaml+import Data.Tree (Tree(..))+import Engine.Vulkan.Pipeline.Stages (StageInfo(..), withLabels)+import RIO.Process (HasProcessContext, proc, readProcess_)+import RIO.Text qualified as Text+import Vulkan.Core10.Enums.Format qualified as VkFormat++invoke+  :: ( HasProcessContext env+     , HasLogFunc env+     , MonadReader env m+     , MonadIO m+     )+  => FilePath+  -> m Module+invoke file = do+  (out, _err) <- proc+    "spirv-reflect"+    [ "-v"+    , "1"+    , "--yaml"+    , file+    ]+    readProcess_++  ReflectYaml.loadBytes ("spirv-reflect < " <> file) out++data Reflect stages = Reflect+  { bindMap    :: BindMap BlockBinding+  , interfaces :: StageInterface stages+  , inputStage :: Text+  , inputs     :: InterfaceBinds+  }++-- | @layout(set=X, binding=Y) ...@+type BindMap a = IntMap (IntMap a)++type StageInterface stages = stages (Maybe (InterfaceBinds, InterfaceBinds))++-- | @layout(location=N)+type InterfaceBinds = IntMap InterfaceBinding++deriving instance (Eq (StageInterface stages)) => Eq (Reflect stages)+deriving instance (Show (StageInterface stages)) => Show (Reflect stages)++-- * Block variables++-- | @uniform Foo { ... } foo;@+type BlockBinding =+  ( Text+  , Enums.DescriptorType+  , Tree ([Maybe Text], BlockSignature)+  )++data BlockSignature = BlockSignature+  { offset :: Word32+  , size   :: Word32+  , flags  :: Enums.TypeFlags+  , scalar :: Maybe Traits.Scalar+  }+  deriving (Eq, Ord, Show)++stagesBindMap+  :: ( MonadIO m+     , MonadReader env m+     , HasLogFunc env+     , StageInfo stages+     )+  => stages (Maybe Module)+  -> m (BindMap BlockBinding)+stagesBindMap = fmap snd . foldM collect ([] :: [Text], mempty) . annotate+  where+    annotate modules = (,)+      <$> stageNames+      <*> modules++    collect acc@(visited, old) (source, stageModule) =+      case stageModule of+        Nothing ->+          pure acc+        Just new ->+          case unionDS old (moduleBindMap new) of+            Left (six, bix, inAcc, inNew) -> do+              logError $ mconcat+                [ "incompatible data at "+                , "layout("+                , "set=", display six+                , ", "+                , "binding=", display bix+                , ")"+                ]+              logError $ "old: " <> displayDS inAcc+              logError $ "  from " <> displayShow visited+              logError $ "new: " <> displayDS inNew+              logError $ "  from " <> displayShow source+              throwString "catch this"+            Right matching ->+              pure (visited <> [source], matching)++    unionDS =+      bindMapUnionWith \(_, adt, asig) (_, bdt, bsig) ->+        adt == bdt &&+        fmap snd asig == fmap snd bsig++    displayDS (name, dt, sigs) = mconcat+      [ display name+      , " :: "+      , maybe (displayShow dt) display $+          Enums.descriptorTypeName @Text dt+      , " -- "+      , displayShow . toList $+          sigs <&> \(path, BlockSignature{..}) ->+            ( Text.intercalate "|" (catMaybes path)+            , (size, offset, Enums.typeFlagsNames @Text flags)+            , scalar+            )+      ]++moduleBindMap :: Module -> BindMap BlockBinding+moduleBindMap refl = IntMap.fromList do+  ds <- toList $ Module.descriptor_sets refl+  pure+    ( fromIntegral $ DescriptorSet.set ds+    , IntMap.fromList do+        db <- toList $ DescriptorSet.bindings ds+        let+          DescriptorBinding.DescriptorBinding+            {binding, name, descriptor_type, block} = db+        pure+          ( fromIntegral binding+          , ( name+            , descriptor_type+            , blockTree [] block+            )+          )+    )++blockTree+  :: [Maybe Text]+  -> BlockVariable.BlockVariable+  -> Tree ([Maybe Text], BlockSignature)+blockTree ancestors bv = Node (path, here) $ map (blockTree path) there+  where+    here = BlockSignature+      { offset      = BlockVariable.offset bv+      , size        = BlockVariable.size bv+      , ..+      }+      where+        (flags, scalar) =+          case BlockVariable.type_description bv of+            Nothing ->+              (Enums.TYPE_FLAG_UNDEFINED, Nothing)+            Just td ->+              ( TypeDescription.type_flags td+              , do+                  TypeDescription.Traits{numeric} <- TypeDescription.traits td+                  let st@Traits.Scalar{width} = Traits.scalar numeric+                  guard $ width > 0+                  pure st+              )++    path =+      ancestors ++ [BlockVariable.name bv]++    there =+      toList $ BlockVariable.members bv++{-# INLINE bindMapUnionWith #-}+bindMapUnionWith+  :: (a -> a -> Bool)+  -> BindMap a+  -> BindMap a+  -> Either (Int, Int, a, a) (BindMap a)+bindMapUnionWith compatible as bs = traverse sequence validated+  where+    validated =+      IntMap.unionWithKey+        (IntMap.unionWithKey . check)+        (wrap as)+        (wrap bs)++    wrap = fmap (fmap pure)++    check six bix a' b' = do+      a <- a'+      b <- b'+      if compatible a b then+        Right a+      else+        Left (six, bix, a, b)++-- * Interface variables++type InterfaceBinding =+  ( Maybe Text+  , [Text]+  , InterfaceSignature+  )++data InterfaceSignature = InterfaceSignature+  { format :: VkFormat.Format+  , flags  :: Enums.TypeFlags+  , matrix :: Maybe Traits.Matrix+  }+  deriving (Eq, Ord, Show)++stagesInterfaceMap+  :: ( MonadIO m+     , Traversable stages+     )+  => stages (Maybe Module)+  -> m (StageInterface stages)+stagesInterfaceMap = traverse $ traverse (pure . moduleInterfaceBinds)++moduleInterfaceBinds :: Module -> (InterfaceBinds, InterfaceBinds)+moduleInterfaceBinds refl =+  ( interfaceBinds Enums.StorageClassInput (Module.input_variables refl)+  , interfaceBinds Enums.StorageClassOutput (Module.output_variables refl)+  )++interfaceBinds :: Enums.StorageClass -> Vector InterfaceVariable -> InterfaceBinds+interfaceBinds cls vars = IntMap.fromList do+  var@InterfaceVariable.InterfaceVariable{location} <- toList vars+  guard $ InterfaceVariable.storage_class var == cls++  let+    td = InterfaceVariable.type_description var+    Enums.Format format = InterfaceVariable.format var+    flags = maybe Enums.TYPE_FLAG_UNDEFINED TypeDescription.type_flags td++    stuff = do+      TypeDescription.TypeDescription{traits} <- td+      TypeDescription.Traits{numeric} <- traits+      let+        mt@Traits.Matrix{column_count, row_count} = Traits.matrix numeric+      guard $ column_count > 0 && row_count > 0+      pure mt++    signature = InterfaceSignature+      { format = VkFormat.Format $ fromIntegral format+      , flags  = flags+      , matrix = stuff+      }++  pure+    ( fromIntegral location+    , ( InterfaceVariable.name var+      , Enums.typeFlagsNames @Text flags+      , signature+      )+    )++type IncompatibleInterfaces label = (label, label, Int, Maybe (InterfaceSignature, InterfaceSignature))+type CompatibleInterfaces label = (label, label, IntMap ([Text], Matching (Maybe Text)))+type Matching a = Either (a, a) a++interfaceCompatible+  :: ( StageInfo stages+     , IsString label+     )+  => StageInterface stages+  -> Either (IncompatibleInterfaces label) [CompatibleInterfaces label]+interfaceCompatible staged =+  for chained \((inputLabel, input), (outputLabel, output)) -> do+    checked <- for (IntMap.assocs input) \(location, requested) ->+      case IntMap.lookup location output of+        Just provided -> do+          let+            (rName, rFlags, rSignature) = requested+            (pName, _pFlags, pSignature) = provided+          if rSignature == pSignature then+            let+              names =+                if rName == pName then+                  Right rName+                else+                  Left (rName, pName)+            in+              Right (location, (rFlags, names))+          else+            Left+              ( inputLabel+              , outputLabel+              , location+              , Just (rSignature, pSignature)+              )+        Nothing ->+          Left+            ( inputLabel+            , outputLabel+            , location+            , Nothing+            )+    Right+      ( outputLabel+      , inputLabel+      , IntMap.fromList checked+      )++  where+    chained = zip (drop 1 ins) outs++    (ins, outs) =+      List.unzip do+        (label, Just binds) <- toList $ withLabels staged+        pure+          ( (label, fst binds)+          , (label, snd binds)+          )++inputStageInterface+  :: (StageInfo stages, IsString label)+  => StageInterface stages+  -> Maybe (label, InterfaceBinds)+inputStageInterface staged = listToMaybe active+  where+    active = do+      (label, Just binds) <- toList $ withLabels staged+      pure (label, fst binds)
src/Engine/Stage/Bootstrap/Setup.hs view
@@ -5,18 +5,13 @@  import RIO -import Control.Monad.Trans.Resource (ResourceT) import UnliftIO.Resource qualified as Resource-import Vulkan.Core10 qualified as Vk-import Vulkan.NamedType ((:::)) -import Engine.Types (StackStage(..), StageRIO, StageFrameRIO)+import Engine.Stage.Component qualified as Stage+import Engine.Types (StackStage(..)) import Engine.Types qualified as Engine-import Engine.Vulkan.Types (Queues) import Engine.StageSwitch (trySwitchStage) -import Engine.Stage.Bootstrap.Types (NoRendering(..), NoPipelines(..), NoResources(..), NoState(..))- stackStage   :: (a -> StackStage)   -> Engine.StageSetupRIO a@@ -26,48 +21,18 @@ bootstrapStage   :: (a -> StackStage)   -> Engine.StageSetupRIO a-  -> Engine.Stage NoRendering NoPipelines NoResources NoState-bootstrapStage handoff action = Engine.Stage-  { sTitle = "Bootstrap"--  , sAllocateRP = noRendering-  , sAllocateP  = noPipelines-  , sInitialRS  = transitState handoff action-  , sInitialRR  = noFrameResources--  , sBeforeLoop     = pure ()-  , sUpdateBuffers  = noUpdates-  , sRecordCommands = noCommands-  , sAfterLoop      = pure-  }--noRendering :: swapchain -> ResourceT (StageRIO st) NoRendering-noRendering _swapchain =-  pure NoRendering--noPipelines :: swapchain -> NoRendering -> ResourceT (StageRIO st) NoPipelines-noPipelines _swapchain NoRendering =-  pure NoPipelines--noCommands-  :: Vk.CommandBuffer-  -> rd-  -> "image index" ::: Word32-  -> StageFrameRIO rp p rd st ()-noCommands _cb _rd _index =-  pure ()--noUpdates-  :: st-  -> rd-  -> StageFrameRIO rp p rd st ()-noUpdates _st _rd =-  pure ()+  -> Engine.Stage Stage.NoRenderPass Stage.NoPipelines Stage.NoFrameResources Stage.NoRunState+bootstrapStage handoff action = Stage.assemble "Bootstrap" Stage.noRendering resources Nothing+  where+    resources = Stage.noResources+      { Stage.rInitialRS =+          transitState handoff action+      }  transitState   :: (a -> StackStage)   -> Engine.StageSetupRIO a-  -> Engine.StageSetupRIO (Resource.ReleaseKey, NoState)+  -> Engine.StageSetupRIO (Resource.ReleaseKey, Stage.NoRunState) transitState handoff action = do   res <- action @@ -78,15 +43,4 @@     logError "Bootstrap switch failed"    key <- Resource.register $ pure ()-  pure (key, NoState)--noFrameResources-  :: Queues Vk.CommandPool-  -> renderPasses-  -> pipelines-  -> ResourceT (StageRIO rs) NoResources-noFrameResources _queues _rp _p =-  fmap snd $!-    Resource.allocate-      (pure NoResources)-      (\NoResources -> pure ())+  pure (key, Stage.NoRunState)
− src/Engine/Stage/Bootstrap/Types.hs
@@ -1,27 +0,0 @@-module Engine.Stage.Bootstrap.Types-  ( Stage--  , NoRendering(..)-  , NoPipelines(..)-  , NoResources(..)-  , NoState(..)-  ) where--import RIO--import Engine.Vulkan.Types (RenderPass(..))-import Engine.Types qualified as Engine--data NoRendering = NoRendering--instance RenderPass NoRendering where-  updateRenderpass _context = pure-  refcountRenderpass _rp = pure ()--data NoPipelines = NoPipelines--type Stage = Engine.Stage NoRendering NoPipelines NoResources NoState--data NoResources = NoResources--data NoState = NoState
+ src/Engine/Stage/Component.hs view
@@ -0,0 +1,115 @@+module Engine.Stage.Component where++import RIO++import Control.Monad.Trans.Resource (ResourceT)+import Data.Semigroup (Semigroup(..))+import UnliftIO.Resource (ReleaseKey, allocate)+import Vulkan.Core10 qualified as Vk+import Vulkan.NamedType ((:::))++import Engine.Types (Stage(..), StageFrameRIO, StageRIO)+import Engine.Vulkan.Swapchain (SwapchainResources(..))+import Engine.Vulkan.Types qualified as Vulkan+import Resource.Region qualified as Region++assemble+  :: Foldable t+  => Text+  -> Rendering rp p st+  -> Resources rp p st rr+  -> t (Scene rp p st rr)+  -> Stage rp p rr st+assemble title Rendering{..} Resources{..} (fold -> Scene{..}) = Stage+  { sTitle = title++  , sAllocateRP = rAllocateRP+  , sAllocateP  = rAllocateP++  , sInitialRS  = rInitialRS+  , sInitialRR  = rInitialRR++  , sBeforeLoop       = Region.exec scBeforeLoop+  , sUpdateBuffers    = scUpdateBuffers+  , sRecordCommands   = scRecordCommands+  , sAfterLoop        = Region.release+  }++data Rendering rp p st = Rendering+  { rAllocateRP :: SwapchainResources -> ResourceT (StageRIO st) rp+  , rAllocateP  :: SwapchainResources -> rp -> ResourceT (StageRIO st) p+  }++data  NoRenderPass = NoRenderPass++instance Vulkan.RenderPass NoRenderPass where+  updateRenderpass _context = pure+  refcountRenderpass _rp = pure ()++data NoPipelines = NoPipelines+type  NoRendering = Rendering NoRenderPass NoPipelines++noRendering :: NoRendering st+noRendering = Rendering+  { rAllocateRP = \_swapchain ->+      pure NoRenderPass+  , rAllocateP = \_swapchain NoRenderPass ->+      pure NoPipelines+  }++data Resources rp p st rr = Resources+  { rInitialRS :: StageRIO (Maybe SwapchainResources) (ReleaseKey, st)+  , rInitialRR :: Vulkan.Queues Vk.CommandPool -> rp -> p -> ResourceT (StageRIO st) rr+  }++type NoResources rp p = Resources rp p NoRunState NoFrameResources++data NoRunState = NoRunState+data NoFrameResources = NoFrameResources++noResources :: NoResources rp p+noResources = Resources+  { rInitialRS =+      allocate+        (pure NoRunState)+        (\NoRunState -> pure ())+  , rInitialRR = \_pool _rp _p ->+      pure NoFrameResources+  }++data Scene rp p st rr = Scene+  { scBeforeLoop       :: ResourceT (StageRIO st) ()+  , scUpdateBuffers    :: st -> rr -> StageFrameRIO rp p rr st ()+  , scRecordCommands   :: Vk.CommandBuffer -> rr -> "image index" ::: Word32 -> StageFrameRIO rp p rr st ()+  }++instance Semigroup (Scene rp p st rr) where+  a <> b = Scene+    { scBeforeLoop = do+        scBeforeLoop a+        scBeforeLoop b+    , scUpdateBuffers = \st rr -> do+        scUpdateBuffers a st rr+        scUpdateBuffers b st rr+    , scRecordCommands = \cb rr ii -> do+        scRecordCommands a cb rr ii+        scRecordCommands b cb rr ii+    }++  sconcat scenes = Scene+    { scBeforeLoop = do+        traverse_ scBeforeLoop scenes+    , scUpdateBuffers = \st rr ->+        for_ scenes \Scene{scUpdateBuffers} ->+          scUpdateBuffers st rr+    , scRecordCommands = \cb rr ii ->+        for_ scenes \Scene{scRecordCommands} ->+          scRecordCommands cb rr ii+    }++instance Monoid (Scene rp p st rr) where+  mempty = Scene+    { scBeforeLoop = pure ()+    , scUpdateBuffers = mempty+    , scRecordCommands = mempty+    }
src/Engine/Vulkan/Pipeline.hs view
@@ -1,48 +1,17 @@--- XXX: TypeError in Compatible generates unused constraint argument-{-# OPTIONS_GHC -Wno-redundant-constraints #-}--{-# LANGUAGE OverloadedLists #-}- module Engine.Vulkan.Pipeline-  ( Config(..)-  , baseConfig--  , Configure--  , Pipeline(..)-  , allocate-  , create+  ( Pipeline(..)   , destroy--  , bind--  , pushPlaceholder--  , vertexInput-  , attrBindings-  , formatSize+  , Specialization   ) where  import RIO -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.Stack (callStack, getCallStack, srcLocModule, 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.NamedType ((:::))-import Vulkan.Utils.Debug qualified as Debug-import Vulkan.Zero (Zero(..)) -import Engine.Vulkan.DescSets (Bound(..), Compatible)-import Engine.Vulkan.Types (HasVulkan(..), HasRenderPass(..), MonadVulkan, DsBindings, DsLayouts, getPipelineCache)-import Engine.Vulkan.Shader qualified as Shader+import Engine.Vulkan.Types (HasVulkan(..), DsLayouts)  data Pipeline (dsl :: [Type]) vertices instances = Pipeline   { pipeline     :: Vk.Pipeline@@ -50,284 +19,13 @@   , pDescLayouts :: Tagged dsl DsLayouts   } --- * Pipeline--type family Configure pipeline spec where-  Configure (Pipeline dsl vertices instances) spec = Config dsl vertices instances spec--data Config (dsl :: [Type]) vertices instances spec = Config-  { cVertexCode         :: Maybe ByteString-  , cFragmentCode       :: Maybe ByteString-  , cVertexInput        :: SomeStruct Vk.PipelineVertexInputStateCreateInfo-  , cDescLayouts        :: Tagged dsl [DsBindings]-  , cPushConstantRanges :: Vector Vk.PushConstantRange-  , cBlend              :: Bool-  , cDepthWrite         :: Bool-  , cDepthTest          :: Bool-  , cDepthCompare       :: Vk.CompareOp-  , cTopology           :: Vk.PrimitiveTopology-  , cCull               :: Vk.CullModeFlagBits-  , cDepthBias          :: Maybe ("constant" ::: Float, "slope" ::: Float)-  , cSpecialization     :: spec-  }---- | Settings for generic triangle-rendering pipeline.-baseConfig :: Config '[] vertices instances ()-baseConfig = Config-  { cVertexCode         = Nothing-  , cFragmentCode       = Nothing-  , cVertexInput        = zero-  , cDescLayouts        = Tagged []-  , cPushConstantRanges = mempty-  , cBlend              = False-  , cDepthWrite         = True-  , cDepthTest          = True-  , cDepthCompare       = Vk.COMPARE_OP_LESS-  , cTopology           = Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST-  , cCull               = Vk.CULL_MODE_BACK_BIT-  , cDepthBias          = Nothing-  , cSpecialization     = ()-  }---- XXX: consider using instance attrs or uniforms-pushPlaceholder :: Vk.PushConstantRange-pushPlaceholder = Vk.PushConstantRange-  { Vk.stageFlags = Vk.SHADER_STAGE_VERTEX_BIT .|. Vk.SHADER_STAGE_FRAGMENT_BIT-  , Vk.offset     = 0-  , Vk.size       = 4 * dwords-  }-  where-    -- XXX: each 4 word32s eat up one register (on AMD)-    dwords = 4--allocate-  :: ( MonadVulkan env m-     , MonadResource m-     , HasRenderPass renderpass-     , Shader.Specialization spec-     , HasCallStack-     )-  => Maybe Vk.Extent2D-  -> Vk.SampleCountFlagBits-  -> Config dsl vertices instances spec-  -> renderpass-  -> m (ReleaseKey, Pipeline dsl vertices instances)-allocate extent msaa config renderpass = withFrozenCallStack do-  ctx <- ask-  Resource.allocate-    (create ctx extent msaa renderpass config)-    (destroy ctx)--create-  :: ( MonadUnliftIO io+destroy+  :: ( MonadIO io      , HasVulkan ctx-     , HasRenderPass renderpass-     , Shader.Specialization spec-     , HasCallStack      )   => ctx-  -> 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--  -- TODO: get from outside-  layout <- Vk.createPipelineLayout device (layoutCI dsLayouts) Nothing-  Debug.nameObject device layout originModule--  shader <- Shader.withSpecialization cSpecialization $-    Shader.create context $-      case (cVertexCode, cFragmentCode) of-        (Just vertCode, Just fragCode) ->-          [ (Vk.SHADER_STAGE_VERTEX_BIT, vertCode)-          , (Vk.SHADER_STAGE_FRAGMENT_BIT, fragCode)-          ]--        (Just vertCode, Nothing) ->-          [ (Vk.SHADER_STAGE_VERTEX_BIT, vertCode)-          ]--        (Nothing, Just fragCode) ->-          -- XXX: good luck-          [ (Vk.SHADER_STAGE_FRAGMENT_BIT, fragCode)-          ]--        (Nothing, Nothing) ->-          []--  let-    cis = Vector.singleton . SomeStruct $-      pipelineCI (Shader.sPipelineStages shader) layout--  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-          pure Pipeline-            { pipeline     = one-            , pLayout      = Tagged layout-            , pDescLayouts = Tagged dsLayouts-            }-        _ ->-          error "assert: exactly one pipeline requested"-    (err, _) ->-      error $ "createGraphicsPipelines: " <> show err-  where-    device = getDevice context-    cache = getPipelineCache context--    layoutCI dsLayouts = Vk.PipelineLayoutCreateInfo-      { flags              = zero-      , setLayouts         = dsLayouts-      , pushConstantRanges = cPushConstantRanges-      }--    pipelineCI stages layout = zero-      { Vk.stages             = stages-      , Vk.vertexInputState   = Just cVertexInput-      , Vk.inputAssemblyState = Just inputAsembly-      , Vk.viewportState      = Just $ SomeStruct viewportState-      , Vk.rasterizationState = Just $ SomeStruct rasterizationState-      , Vk.multisampleState   = Just $ SomeStruct multisampleState-      , Vk.depthStencilState  = Just depthStencilState-      , Vk.colorBlendState    = Just $ SomeStruct colorBlendState-      , Vk.dynamicState       = dynamicState-      , Vk.layout             = layout-      , Vk.renderPass         = getRenderPass renderpass-      , Vk.subpass            = 0-      , Vk.basePipelineHandle = zero-      }-      where-        inputAsembly = zero-          { Vk.topology               = cTopology-          , Vk.primitiveRestartEnable = restartable-          }--        restartable = elem @Set cTopology-          [ Vk.PRIMITIVE_TOPOLOGY_LINE_STRIP-          , Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP-          , Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_FAN-          ]--        (viewportState, dynamicState) = case mextent of-          Nothing ->-            ( zero-                { Vk.viewportCount = 1-                , Vk.scissorCount = 1-                }-            , Just zero-                { Vk.dynamicStates = Vector.fromList-                    [ Vk.DYNAMIC_STATE_VIEWPORT-                    , Vk.DYNAMIC_STATE_SCISSOR-                    ]-                }-            )-          Just extent@Vk.Extent2D{width, height} ->-            ( zero-                { Vk.viewports = Vector.fromList-                    [ Vk.Viewport-                        { Vk.x        = 0-                        , Vk.y        = 0-                        , Vk.width    = realToFrac width-                        , Vk.height   = realToFrac height-                        , Vk.minDepth = 0-                        , Vk.maxDepth = 1-                        }-                    ]-                , Vk.scissors = Vector.singleton Vk.Rect2D-                    { Vk.offset = Vk.Offset2D 0 0-                    , extent    = extent-                    }-                }-            , Nothing-            )--        rasterizationState = case cDepthBias of-          Nothing ->-            rasterizationBase-          Just (constantFactor, slopeFactor) ->-            rasterizationBase-              { Vk.depthBiasEnable         = True-              , Vk.depthBiasConstantFactor = constantFactor-              , Vk.depthBiasSlopeFactor    = slopeFactor-              }--        rasterizationBase = zero-          { Vk.depthClampEnable        = False-          , Vk.rasterizerDiscardEnable = False-          , Vk.lineWidth               = 1-          , Vk.polygonMode             = Vk.POLYGON_MODE_FILL-          , Vk.cullMode                = cCull-          , Vk.frontFace               = Vk.FRONT_FACE_CLOCKWISE-          , Vk.depthBiasEnable         = False-          }--        multisampleState = zero-          { Vk.rasterizationSamples = msaa-          , Vk.sampleShadingEnable  = enable-          , Vk.minSampleShading     = if enable then 0.2 else 1.0-          , Vk.sampleMask           = Vector.singleton maxBound-          }-          where-            enable = True -- TODO: check and enable sample rate shading feature--        depthStencilState = zero-          { Vk.depthTestEnable       = cDepthTest-          , Vk.depthWriteEnable      = cDepthWrite-          , Vk.depthCompareOp        = cDepthCompare-          , Vk.depthBoundsTestEnable = False-          , Vk.minDepthBounds        = 0.0 -- Optional-          , Vk.maxDepthBounds        = 1.0 -- Optional-          , Vk.stencilTestEnable     = False-          , Vk.front                 = zero -- Optional-          , Vk.back                  = zero -- Optional-          }--        colorBlendState = zero-          { Vk.logicOpEnable =-              False-          , Vk.attachments = Vector.singleton zero-              { Vk.blendEnable         = cBlend-              , Vk.srcColorBlendFactor = Vk.BLEND_FACTOR_ONE-              , Vk.dstColorBlendFactor = Vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA-              , Vk.colorBlendOp        = Vk.BLEND_OP_ADD-              , Vk.srcAlphaBlendFactor = Vk.BLEND_FACTOR_ONE-              , Vk.dstAlphaBlendFactor = Vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA-              , Vk.alphaBlendOp        = Vk.BLEND_OP_ADD-              , Vk.colorWriteMask      = colorRgba-              }-          }--        colorRgba =-          Vk.COLOR_COMPONENT_R_BIT .|.-          Vk.COLOR_COMPONENT_G_BIT .|.-          Vk.COLOR_COMPONENT_B_BIT .|.-          Vk.COLOR_COMPONENT_A_BIT--destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Pipeline dsl vertices instances -> io ()+  -> Pipeline dsl vertices instances+  -> io () destroy context Pipeline{..} = do   Vector.forM_ (unTagged pDescLayouts) \dsLayout ->     Vk.destroyDescriptorSetLayout device dsLayout Nothing@@ -336,75 +34,4 @@   where     device = getDevice context -bind-  :: ( Compatible pipeLayout boundLayout-     , MonadIO m-     )-  => Vk.CommandBuffer-  -> Pipeline pipeLayout vertices instances-  -> Bound boundLayout vertices instances m ()-  -> Bound boundLayout oldVertices oldInstances m ()-bind cb Pipeline{pipeline} (Bound attrAction) = do-  Bound $ Vk.cmdBindPipeline cb Vk.PIPELINE_BIND_POINT_GRAPHICS pipeline-  Bound attrAction--vertexInput :: [(Vk.VertexInputRate, [Vk.Format])] -> SomeStruct Vk.PipelineVertexInputStateCreateInfo-vertexInput bindings = SomeStruct zero-  { Vk.vertexBindingDescriptions   = binds-  , Vk.vertexAttributeDescriptions = attrs-  }-  where-    binds = Vector.fromList do-      (ix, (rate, formats)) <- zip [0..] bindings-      pure Vk.VertexInputBindingDescription-        { binding   = ix-        , stride    = sum $ map formatSize formats-        , inputRate = rate-        }--    attrs = attrBindings $ map snd bindings---- * Utils--attrBindings :: [[Vk.Format]] -> Vector Vk.VertexInputAttributeDescription-attrBindings bindings = mconcat $ List.unfoldr shiftLocations (0, 0, bindings)-  where-    shiftLocations = \case-      (_binding, _lastLoc, [])           -> Nothing-      (binding, lastLoc, formats : rest) -> Just (bound, next)-        where-          bound = Vector.fromList do-            (ix, format) <- zip [0..] formats-            let offset = sum . map formatSize $ take ix formats-            pure zero-              { Vk.binding  = binding-              , Vk.location = fromIntegral $ lastLoc + ix-              , Vk.format   = format-              , Vk.offset   = offset-              }--          next =-            ( binding + 1-            , lastLoc + Vector.length bound-            , 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--  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+type family Specialization pipeline
src/Engine/Vulkan/Pipeline/Compute.hs view
@@ -7,6 +7,13 @@   ( Config(..)   , Configure +  , Stages(..)+  , stageNames+  , stageFlagBits+  , StageCode+  , StageSpirv+  , StageReflect+   , Pipeline(..)   , allocate   , create@@ -22,6 +29,7 @@ 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 UnliftIO.Resource (MonadResource, ReleaseKey) import UnliftIO.Resource qualified as Resource@@ -31,11 +39,14 @@ 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.Types (HasVulkan(..), MonadVulkan, DsBindings, getPipelineCache)- import Engine.Vulkan.Pipeline (Pipeline(..), destroy)+import Engine.Vulkan.Pipeline.Stages (StageInfo(..)) import Engine.Vulkan.Shader qualified as Shader+import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, DsBindings, getPipelineCache)+import Render.Code (Code)+import Resource.Collection (Generically1(..))  data Config (dsl :: [Type]) spec = Config   { cComputeCode        :: ByteString@@ -49,6 +60,25 @@ type family Configure pipeline spec where   Configure (Pipeline dsl Compute Compute) spec = Config dsl spec +newtype Stages a = Stages+  { comp :: a -- ^ compute+  }+  deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic1)+  deriving Applicative via (Generically1 Stages)++instance StageInfo Stages where+  stageNames = Stages+    { comp = "comp"+    }++  stageFlagBits = Stages+    { comp = Vk.SHADER_STAGE_COMPUTE_BIT+    }++type StageCode = Stages (Maybe Code)+type StageSpirv = Stages (Maybe ByteString)+type StageReflect = Reflect Stages+ allocate   :: ( MonadVulkan env m      , MonadResource m@@ -100,9 +130,9 @@   -- Compute stuff begins...    shader <- Shader.withSpecialization cSpecialization $-    Shader.create-      context-      [(Vk.SHADER_STAGE_COMPUTE_BIT, cComputeCode)]+    Shader.create context Stages+      { comp = Just cComputeCode+      }    let     cis = Vector.singleton . SomeStruct $@@ -149,9 +179,9 @@      , MonadIO m      )   => Vk.CommandBuffer-  -> Pipeline pipeLayout vertices instances-  -> Bound boundLayout vertices instances m ()-  -> Bound boundLayout oldVertices oldInstances m ()+  -> Pipeline pipeLayout Compute Compute+  -> Bound boundLayout Compute Compute m ()+  -> Bound boundLayout noVertices noInstances m () bind cb Pipeline{pipeline} (Bound attrAction) = do   Bound $ Vk.cmdBindPipeline cb Vk.PIPELINE_BIND_POINT_COMPUTE pipeline   Bound attrAction
+ src/Engine/Vulkan/Pipeline/External.hs view
@@ -0,0 +1,337 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE UndecidableInstances #-}++module Engine.Vulkan.Pipeline.External+  ( Process+  , spawn+  , spawnReflect++  , loadConfig+  , loadConfigReflect++  , Observer++  , newObserverGraphics+  , observeGraphics++  , newObserverCompute+  , observeCompute++  , type (^)+  , ConfigureGraphics+  , ConfigureCompute+  , Observers+  , observeField++  , dumpPipelines+  ) where++import RIO++import Control.Monad.Trans.Resource (ReleaseKey, ResourceT, release)+import Data.List (maximum)+import Data.Tagged (Tagged(..))+import RIO.ByteString qualified as ByteString+import RIO.Directory (createDirectoryIfMissing, getModificationTime, doesFileExist)+import RIO.Map qualified as Map+import RIO.Process (HasProcessContext)+import RIO.Text qualified as Text+import RIO.Time (UTCTime, getCurrentTime)+import RIO.FilePath ((</>), (<.>))+import Vulkan.Core10 qualified as Vk++import Render.Code (Code(..))+import Engine.SpirV.Reflect qualified as Reflect+import Engine.Types (StageFrameRIO, StageRIO)+import Engine.Vulkan.Pipeline.Compute (Compute)+import Engine.Vulkan.Pipeline.Compute qualified as Compute+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.Worker qualified as Worker++type Process config = Worker.Timed () config++spawn+  :: ( Foldable stages+     , MonadUnliftIO m+     , MonadReader env m+     , HasLogFunc env+     )+  => (stages (Maybe FilePath) -> m stuff)+  -> Text+  -> stages (Maybe FilePath)+  -> (stuff -> config)+  -> m (Process config)+spawn loader label stageFiles makeConfig =+  Worker.spawnTimed startActive dtF initF stepF ()+  where+    startActive = True++    dtF = Left 1e6++    initF () = do+      logDebug $ "Starting pipeline watch on " <> display label++      result <- loader stageFiles+      initialTime <- getCurrentTime+      pure (makeConfig result, initialTime)++    stepF oldTime () = do+      checkTime oldTime (catMaybes $ toList stageFiles) >>= \case+        Nothing ->+          -- logDebug $ "Skipping pipeline update from " <> display label+          pure (Nothing, oldTime)+        Just newTime -> do+          logInfo $ "Updating pipeline from " <> display label+          try (loader stageFiles) >>= \case+            Left (SomeException err) -> do+              logError $ displayShow err+              pure (Nothing, newTime)+            Right result ->+              pure+                ( Just $ makeConfig result+                , newTime+                )++spawnReflect+  :: ( MonadUnliftIO m+     , MonadReader env m+     , HasLogFunc env+     , HasProcessContext env+     , StageInfo stages+     )+  => Text+  -> stages (Maybe FilePath)+  -> ((stages (Maybe ByteString), Reflect.Reflect stages) -> config)+  -> m (Process config)+spawnReflect = spawn loadConfigReflect++checkTime+  :: MonadIO io+  => UTCTime+  -> [FilePath]+  -> io (Maybe UTCTime)+checkTime oldTime = fmap collect . traverse getModificationTime+  where+    collect = \case+      [] ->+        Nothing+      (maximum -> maxTime) ->+        if maxTime <= oldTime then+          Nothing+        else+          Just maxTime++loadConfig+  :: ( Traversable stages+     , MonadIO io+     )+  => stages (Maybe FilePath)+  -> io (stages (Maybe ByteString))+loadConfig stageFiles =+  for stageFiles $+    traverse ByteString.readFile++loadConfigReflect+  :: ( StageInfo stages+     , MonadIO io+     , MonadReader env io+     , HasLogFunc env+     , HasProcessContext env+     )+  => stages (Maybe FilePath)+  -> io (stages (Maybe ByteString), Reflect.Reflect stages)+loadConfigReflect stageFiles = do+  stageCode <- loadConfig stageFiles++  -- TODO: use stageCode?+  stageRefl <- for stageFiles $+    traverse Reflect.invoke++  reflDS <- Reflect.stagesBindMap stageRefl++  reflIS <- Reflect.stagesInterfaceMap stageRefl+  case Reflect.interfaceCompatible reflIS of+    Right ok ->+      logDebug $ displayShow ok+    Left (inputStage, outputStage, location, err) -> do+      let+        between = "Between " <> inputStage <> " and " <> outputStage+        locInfo = "(location=" <> show location <> ")"+      case err of+        Nothing ->+          throwString $+            unwords [ between, locInfo <> ":", "missing output" ]+        Just (sigRequested, sigProvided) ->+          throwString $ unlines+            [ unwords [ between, locInfo <> ":", "incompatible signatures" ]+            , "  requested: " <> show sigRequested+            , "  provided: " <> show sigProvided+            ]++  (inputStage, inputs) <-+    case Reflect.inputStageInterface reflIS of+      Nothing ->+        throwString "No active stage"+      Just found ->+        pure found++  let+    reflect = Reflect.Reflect+      { bindMap    = reflDS+      , interfaces = reflIS+      , inputStage = inputStage+      , inputs     = inputs+      }+  pure+    ( stageCode+    , reflect+    )++type Observer pipeline = Worker.ObserverIO (ReleaseKey, pipeline)++newObserverGraphics+  :: ( pipeline ~ Graphics.Pipeline dsl vertices instances+    , Worker.HasOutput worker+    , Shader.Specialization (Graphics.Specialization pipeline)+    , HasRenderPass renderpass+    , Worker.GetOutput worker ~ Graphics.Configure pipeline+    )+  => renderpass+  -> Vk.SampleCountFlagBits+  -> worker+  -> ResourceT (StageRIO rs) (Observer pipeline)+newObserverGraphics rp msaa process = do+  initialConfig <- Worker.getOutputData process++  initial <- Graphics.allocate+    Nothing+    msaa+    initialConfig+    rp++  Worker.newObserverIO initial++observeGraphics+  :: ( HasRenderPass renderpass+     , Worker.HasOutput output+     , Worker.GetOutput output ~ Graphics.Configure pipeline+     , pipeline ~ Graphics.Pipeline dsl vertices instances+     , spec ~ Graphics.Specialization pipeline+     , Shader.Specialization spec+     )+  => renderpass+  -> Vk.SampleCountFlagBits+  -> Tagged dsl [DsBindings]+  -> 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+    mapRIO fst $ Graphics.allocate+      Nothing+      msaa+      ( config+          { Graphics.cDescLayouts = sceneBinds+          }+      )+      rp++newObserverCompute+  :: ( config ~ Compute.Configure pipeline ()+     , pipeline ~ Compute.Pipeline dsl Compute Compute+     )+  => Process config+  -> ResourceT (StageRIO rs) (Observer pipeline)+newObserverCompute process = do+  initialConfig <- Worker.getOutputData process++  initial <- Compute.allocate initialConfig++  Worker.newObserverIO initial++observeCompute+  :: ( Worker.HasOutput output+     , Worker.GetOutput output ~ config+     , Shader.Specialization spec+     , config ~ Compute.Configure pipeline spec+     , pipeline ~ Compute.Pipeline dsl Compute Compute+     )+  => Tagged dsl [DsBindings]+  -> 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+    mapRIO fst $ Compute.allocate config+      { Compute.cDescLayouts = binds+      }++-- * HKD wrappers++data ConfigureGraphics p+data ConfigureCompute p+data Observers p++type family f ^ p where+  Identity ^ p = p+  ConfigureGraphics ^ p = Process (Graphics.Configure p)+  ConfigureCompute ^ p = Process (Compute.Configure p ())+  Observers ^ p = Observer p+  f ^ p = f p++observeField+  :: forall+      pf+      p+      renderpass+      dsl+      s vs is+      rps ps fr rs+  .   ( p ~ Graphics.Pipeline s vs is+      , Shader.Specialization (Graphics.Specialization p)+      , HasRenderPass renderpass+      )+  => renderpass+  -> Vk.SampleCountFlagBits+  -> Tagged dsl DsBindings+  -> pf ConfigureGraphics+  -> pf Observers+  -> (forall a . pf a -> a ^ p)+  -> StageFrameRIO rps ps fr rs ()+observeField rp msaa binds workers observers field =+  observeGraphics+    rp+    msaa+    (Tagged [unTagged binds])+    (field @ConfigureGraphics workers)+    (field @Observers observers)++dumpPipelines+  :: StageInfo t+  => MonadIO io+  => FilePath+  -> Map Text (t (Maybe Code))+  -> io ()+dumpPipelines prefix pipelines = do+  createDirectoryIfMissing True prefix+  for_ (Map.toList pipelines) \(pipeline, stageCode) -> do+    let stages = (,) <$> stageNames <*> stageCode+    for_ stages \(stage, mcode) ->+      for_ mcode \(Code code) -> do+        let+          file = prefix </> Text.unpack pipeline <.> stage+          bytes = encodeUtf8 code+        exists <- doesFileExist file+        if exists then do+          old <- ByteString.readFile file+          unless (bytes == old) $+            ByteString.writeFile file bytes+        else+          ByteString.writeFile file bytes
+ src/Engine/Vulkan/Pipeline/Graphics.hs view
@@ -0,0 +1,447 @@+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE UndecidableInstances #-}++-- XXX: TypeError in Compatible generates unused constraint argument+{-# OPTIONS_GHC -Wno-redundant-constraints #-}++module Engine.Vulkan.Pipeline.Graphics+  ( Config(..)+  , baseConfig++  , Configure+  , Pipeline.Specialization++  , vertexInput+  , formatSize+  , pushPlaceholder++  , Stages(..)+  , stageNames+  , stageFlagBits+  , basicStages+  , vertexOnly+  , StageCode+  , StageSpirv+  , StageReflect++  , Pipeline(..)+  , allocate+  , create+  , Pipeline.destroy++  , bind+  ) where++import RIO++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 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.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.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 Render.Code (Code)+import Resource.Collection (Generically1(..))++data Stages a = Stages+  { vert :: a -- ^ vertex+  , tesc :: a -- ^ tessellation control+  , tese :: a -- ^ tessellation evaluation+  , geom :: a -- ^ geometry+  , frag :: a -- ^ fragment+  }+  deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic1)+  deriving Applicative via (Generically1 Stages)++instance StageInfo Stages where+  stageNames = Stages+    { vert = "vert"+    , tesc = "tesc"+    , tese = "tese"+    , geom = "geom"+    , frag = "frag"+    }++  stageFlagBits = Stages+    { vert = Vk.SHADER_STAGE_VERTEX_BIT+    , tesc = Vk.SHADER_STAGE_TESSELLATION_CONTROL_BIT+    , tese = Vk.SHADER_STAGE_TESSELLATION_EVALUATION_BIT+    , geom = Vk.SHADER_STAGE_GEOMETRY_BIT+    , frag = Vk.SHADER_STAGE_FRAGMENT_BIT+    }++basicStages+  :: "vert" ::: a+  -> "frag" ::: a+  -> Stages (Maybe a)+basicStages v f = (pure Nothing)+  { vert = Just v+  , frag = Just f+  }++vertexOnly+  :: "vert" ::: a+  -> Stages (Maybe a)+vertexOnly v = (pure Nothing)+  { vert = Just v+  }++type StageCode = Stages (Maybe Code)+type StageSpirv = Stages (Maybe ByteString)+type StageReflect = Reflect Stages++type family Configure pipeline where+  Configure (Pipeline dsl vertices instances) =+    Config+      dsl+      vertices+      instances+      (Pipeline.Specialization (Pipeline dsl vertices instances))++data Config (dsl :: [Type]) vertices instances spec = Config+  { cStages             :: StageSpirv+  , cReflect            :: Maybe StageReflect+  , cVertexInput        :: SomeStruct Vk.PipelineVertexInputStateCreateInfo+  , cDescLayouts        :: Tagged dsl [DsBindings]+  , cPushConstantRanges :: Vector Vk.PushConstantRange+  , cBlend              :: Bool+  , cDepthWrite         :: Bool+  , cDepthTest          :: Bool+  , cDepthCompare       :: Vk.CompareOp+  , cTopology           :: Vk.PrimitiveTopology+  , cCull               :: Vk.CullModeFlagBits+  , cDepthBias          :: Maybe ("constant" ::: Float, "slope" ::: Float)+  , cSpecialization     :: spec+  }++-- | Settings for generic triangle-rendering pipeline.+baseConfig :: Config '[] vertices instances ()+baseConfig = Config+  { cStages             = pure Nothing+  , cVertexInput        = zero+  , cReflect            = Nothing+  , cDescLayouts        = Tagged []+  , cPushConstantRanges = mempty+  , cBlend              = False+  , cDepthWrite         = True+  , cDepthTest          = True+  , cDepthCompare       = Vk.COMPARE_OP_LESS+  , cTopology           = Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST+  , cCull               = Vk.CULL_MODE_BACK_BIT+  , cDepthBias          = Nothing+  , cSpecialization     = ()+  }++-- XXX: consider using instance attrs or uniforms+pushPlaceholder :: Vk.PushConstantRange+pushPlaceholder = Vk.PushConstantRange+  { Vk.stageFlags = Vk.SHADER_STAGE_VERTEX_BIT .|. Vk.SHADER_STAGE_FRAGMENT_BIT+  , Vk.offset     = 0+  , Vk.size       = 4 * dwords+  }+  where+    -- XXX: each 4 word32s eat up one register (on AMD)+    dwords = 4++allocate+  :: ( config ~ Configure pipeline+     , pipeline ~ Pipeline dsl vertices instances+     , spec ~ Pipeline.Specialization pipeline+     , Shader.Specialization spec+     , HasCallStack+     , MonadVulkan env m+     , MonadResource m+     , HasRenderPass renderpass+     )+  => Maybe Vk.Extent2D+  -> Vk.SampleCountFlagBits+  -> 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)++create+  :: ( MonadUnliftIO io+     , HasVulkan ctx+     , HasRenderPass renderpass+     , Shader.Specialization spec+     , HasCallStack+     )+  => ctx+  -> 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++  -- TODO: get from outside+  layout <- Vk.createPipelineLayout device (layoutCI dsLayouts) Nothing+  Debug.nameObject device layout originModule++  shader <- Shader.withSpecialization cSpecialization $+    Shader.create context cStages++  let+    cis = Vector.singleton . SomeStruct $+      pipelineCI (Shader.sPipelineStages shader) layout++  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+          pure Pipeline+            { pipeline     = one+            , pLayout      = Tagged layout+            , pDescLayouts = Tagged dsLayouts+            }+        _ ->+          error "assert: exactly one pipeline requested"+    (err, _) ->+      error $ "createGraphicsPipelines: " <> show err+  where+    device = getDevice context+    cache = getPipelineCache context++    layoutCI dsLayouts = Vk.PipelineLayoutCreateInfo+      { flags              = zero+      , setLayouts         = dsLayouts+      , pushConstantRanges = cPushConstantRanges+      }++    pipelineCI stages layout = zero+      { Vk.stages             = stages+      , Vk.vertexInputState   = Just cVertexInput+      , Vk.inputAssemblyState = Just inputAsembly+      , Vk.viewportState      = Just $ SomeStruct viewportState+      , Vk.rasterizationState = Just $ SomeStruct rasterizationState+      , Vk.multisampleState   = Just $ SomeStruct multisampleState+      , Vk.depthStencilState  = Just depthStencilState+      , Vk.colorBlendState    = Just $ SomeStruct colorBlendState+      , Vk.dynamicState       = dynamicState+      , Vk.layout             = layout+      , Vk.renderPass         = getRenderPass renderpass+      , Vk.subpass            = 0+      , Vk.basePipelineHandle = zero+      }+      where+        inputAsembly = zero+          { Vk.topology               = cTopology+          , Vk.primitiveRestartEnable = restartable+          }++        restartable = elem @Set cTopology+          [ Vk.PRIMITIVE_TOPOLOGY_LINE_STRIP+          , Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP+          , Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_FAN+          ]++        (viewportState, dynamicState) = case mextent of+          Nothing ->+            ( zero+                { Vk.viewportCount = 1+                , Vk.scissorCount = 1+                }+            , Just zero+                { Vk.dynamicStates = Vector.fromList+                    [ Vk.DYNAMIC_STATE_VIEWPORT+                    , Vk.DYNAMIC_STATE_SCISSOR+                    ]+                }+            )+          Just extent@Vk.Extent2D{width, height} ->+            ( zero+                { Vk.viewports = Vector.fromList+                    [ Vk.Viewport+                        { Vk.x        = 0+                        , Vk.y        = 0+                        , Vk.width    = realToFrac width+                        , Vk.height   = realToFrac height+                        , Vk.minDepth = 0+                        , Vk.maxDepth = 1+                        }+                    ]+                , Vk.scissors = Vector.singleton Vk.Rect2D+                    { Vk.offset = Vk.Offset2D 0 0+                    , extent    = extent+                    }+                }+            , Nothing+            )++        rasterizationState = case cDepthBias of+          Nothing ->+            rasterizationBase+          Just (constantFactor, slopeFactor) ->+            rasterizationBase+              { Vk.depthBiasEnable         = True+              , Vk.depthBiasConstantFactor = constantFactor+              , Vk.depthBiasSlopeFactor    = slopeFactor+              }++        rasterizationBase = zero+          { Vk.depthClampEnable        = False+          , Vk.rasterizerDiscardEnable = False+          , Vk.lineWidth               = 1+          , Vk.polygonMode             = Vk.POLYGON_MODE_FILL+          , Vk.cullMode                = cCull+          , Vk.frontFace               = Vk.FRONT_FACE_CLOCKWISE+          , Vk.depthBiasEnable         = False+          }++        multisampleState = zero+          { Vk.rasterizationSamples = msaa+          , Vk.sampleShadingEnable  = enable+          , Vk.minSampleShading     = if enable then 0.2 else 1.0+          , Vk.sampleMask           = Vector.singleton maxBound+          }+          where+            enable = True -- TODO: check and enable sample rate shading feature++        depthStencilState = zero+          { Vk.depthTestEnable       = cDepthTest+          , Vk.depthWriteEnable      = cDepthWrite+          , Vk.depthCompareOp        = cDepthCompare+          , Vk.depthBoundsTestEnable = False+          , Vk.minDepthBounds        = 0.0 -- Optional+          , Vk.maxDepthBounds        = 1.0 -- Optional+          , Vk.stencilTestEnable     = False+          , Vk.front                 = zero -- Optional+          , Vk.back                  = zero -- Optional+          }++        colorBlendState = zero+          { Vk.logicOpEnable =+              False+          , Vk.attachments = Vector.singleton zero+              { Vk.blendEnable         = cBlend+              , Vk.srcColorBlendFactor = Vk.BLEND_FACTOR_ONE+              , Vk.dstColorBlendFactor = Vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA+              , Vk.colorBlendOp        = Vk.BLEND_OP_ADD+              , Vk.srcAlphaBlendFactor = Vk.BLEND_FACTOR_ONE+              , Vk.dstAlphaBlendFactor = Vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA+              , Vk.alphaBlendOp        = Vk.BLEND_OP_ADD+              , Vk.colorWriteMask      = colorRgba+              }+          }++        colorRgba =+          Vk.COLOR_COMPONENT_R_BIT .|.+          Vk.COLOR_COMPONENT_G_BIT .|.+          Vk.COLOR_COMPONENT_B_BIT .|.+          Vk.COLOR_COMPONENT_A_BIT++bind+  :: ( Compatible pipeLayout boundLayout+     , MonadIO m+     )+  => Vk.CommandBuffer+  -> Pipeline pipeLayout vertices instances+  -> Bound boundLayout vertices instances m ()+  -> Bound boundLayout oldVertices oldInstances m ()+bind cb Pipeline{pipeline} (Bound attrAction) = do+  Bound $ Vk.cmdBindPipeline cb Vk.PIPELINE_BIND_POINT_GRAPHICS pipeline+  Bound attrAction++vertexInput :: [(Vk.VertexInputRate, [Vk.Format])] -> SomeStruct Vk.PipelineVertexInputStateCreateInfo+vertexInput bindings = SomeStruct zero+  { Vk.vertexBindingDescriptions   = binds+  , Vk.vertexAttributeDescriptions = attrs+  }+  where+    binds = Vector.fromList do+      (ix, (rate, formats)) <- zip [0..] bindings+      pure Vk.VertexInputBindingDescription+        { binding   = ix+        , stride    = sum $ map formatSize formats+        , inputRate = rate+        }++    attrs = attrBindings $ map snd bindings++-- * Utils++attrBindings :: [[Vk.Format]] -> Vector Vk.VertexInputAttributeDescription+attrBindings bindings = mconcat $ List.unfoldr shiftLocations (0, 0, bindings)+  where+    shiftLocations = \case+      (_binding, _lastLoc, [])           -> Nothing+      (binding, lastLoc, formats : rest) -> Just (bound, next)+        where+          bound = Vector.fromList do+            (ix, format) <- zip [0..] formats+            let offset = sum . map formatSize $ take ix formats+            pure zero+              { Vk.binding  = binding+              , Vk.location = fromIntegral $ lastLoc + ix+              , Vk.format   = format+              , Vk.offset   = offset+              }++          next =+            ( binding + 1+            , lastLoc + Vector.length bound+            , 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++  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/Raytrace.hs view
@@ -0,0 +1,38 @@+module Engine.Vulkan.Pipeline.Raytrace where++import RIO++import Vulkan.Core10 qualified as Vk++import Engine.Vulkan.Pipeline.Stages (StageInfo(..))+import Resource.Collection (Generic1, Generically1(..))++data Stages a = Stages+  { rgen  :: a -- ^ ray generation+  , rint  :: a -- ^ ray intersection+  , rahit :: a -- ^ ray any hit+  , rchit :: a -- ^ ray closest hit+  , rmiss :: a -- ^ ray miss+  , rcall :: a -- ^ ray callable+  }+  deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic1)+  deriving Applicative via (Generically1 Stages)++instance StageInfo Stages where+  stageNames = Stages+    { rgen  = "rgen"+    , rint  = "rint"+    , rahit = "rahit"+    , rchit = "rchit"+    , rmiss = "rmiss"+    , rcall = "rcall"+    }++  stageFlagBits = Stages+    { rgen  = Vk.SHADER_STAGE_CALLABLE_BIT_KHR+    , rint  = Vk.SHADER_STAGE_INTERSECTION_BIT_KHR+    , rahit = Vk.SHADER_STAGE_MISS_BIT_KHR+    , rchit = Vk.SHADER_STAGE_CLOSEST_HIT_BIT_KHR+    , rmiss = Vk.SHADER_STAGE_ANY_HIT_BIT_KHR+    , rcall = Vk.SHADER_STAGE_RAYGEN_BIT_KHR+    }
+ src/Engine/Vulkan/Pipeline/Stages.hs view
@@ -0,0 +1,15 @@+module Engine.Vulkan.Pipeline.Stages+  ( StageInfo(..)+  , withLabels+  ) where++import RIO++import Vulkan.Core10 qualified as Vk++class (Applicative t, Traversable t) => StageInfo t where+  stageNames :: IsString label => t label+  stageFlagBits :: t Vk.ShaderStageFlagBits++withLabels :: (StageInfo t, IsString label) => t a -> t (label, a)+withLabels staged = (,) <$> stageNames <*> staged
src/Engine/Vulkan/Shader.hs view
@@ -23,6 +23,7 @@ import Vulkan.Zero (Zero(..)) import Unsafe.Coerce (unsafeCoerce) +import Engine.Vulkan.Pipeline.Stages (StageInfo(..)) import Engine.Vulkan.Types (HasVulkan(..))  data Shader = Shader@@ -31,13 +32,16 @@   }  create-  :: (MonadIO io, HasVulkan ctx)+  :: ( MonadIO io+     , HasVulkan ctx+     , StageInfo t+     )   => ctx-  -> Vector (Vk.ShaderStageFlagBits, ByteString)+  -> t (Maybe ByteString)   -> Maybe Vk.SpecializationInfo   -> io Shader create context stages spec = do-  staged <- Vector.forM stages \(stage, code) -> do+  staged <- Vector.forM collected \(stage, code) -> do     module_ <- Vk.createShaderModule       (getDevice context)       zero@@ -59,6 +63,10 @@     { sModules        = modules     , sPipelineStages = pStages     }+  where+    collected = Vector.fromList do+      (stage, Just code) <- toList $ (,) <$> stageFlagBits <*> stages+      pure (stage, code)  destroy :: (MonadIO io, HasVulkan ctx) => ctx -> Shader -> io () destroy context Shader{sModules} =
src/Render/Code.hs view
@@ -1,12 +1,12 @@ {-# OPTIONS_GHC -fno-prof-auto #-}  module Render.Code-  ( compileVert-  , compileFrag-  , compileComp+  ( Code(..)   , glsl   , trimming-  , Code(..)+  , compileVert+  , compileFrag+  , compileComp   ) where  import RIO@@ -16,18 +16,18 @@ import RIO.Text qualified as Text import Vulkan.Utils.ShaderQQ.GLSL.Glslang (compileShaderQ, glsl) -compileVert :: String -> Q Exp-compileVert = compileShaderQ Nothing "vert" Nothing--compileFrag :: String -> Q Exp-compileFrag = compileShaderQ Nothing "frag" Nothing--compileComp :: String -> Q Exp-compileComp = compileShaderQ Nothing "comp" Nothing- -- | A wrapper to `show` code into `compileShaderQ` vars. newtype Code = Code { unCode :: Text }-  deriving (Eq, Ord)+  deriving (Eq, Ord, IsString)  instance Show Code where   show = Text.unpack . unCode++compileVert :: Code -> Q Exp+compileVert = compileShaderQ Nothing "vert" Nothing . Text.unpack . unCode++compileFrag :: Code -> Q Exp+compileFrag = compileShaderQ Nothing "frag" Nothing . Text.unpack . unCode++compileComp :: Code -> Q Exp+compileComp = compileShaderQ Nothing "comp" Nothing . Text.unpack . unCode