Copy-paste/checklist for getting things rolling.
Stack environment
# Check for a recent one at https://gitlab.com/keid/meta/-/tree/main/resolvers
resolver: https://gitlab.com/keid/meta/-/raw/main/resolvers/keid-engine-2024-10-02.yaml
packages:
- . # this one
extra-deps: []
# hacking time!
# - ../keid/engine/core
# - ../keid/engine/render-basic
# let the ghcup in
system-ghc: true
# your users will thank you later
ghc-options:
"$everything": -split-sections -j2
"$locals": -split-sections -j8
Entry point
app/Main.hs
module Main (main) where
-- Prelude used with Keid, from `rio` package
import RIO -- rio
-- From `rio-app` package
import Engine.App (engineMain)
-- Initial stage, from project library or executable
import Stage.StageName (stackStage)
main :: IO ()
main = engineMain stackStage
Global
Render
A bunch of keid-render-basic
types for now. The aliases are shared with downstream stage components.
module Global.Render where
import Engine.Stage.Component qualified as Stage
import Engine.Types qualified as Engine
import Render.Basic qualified as Basic
type Frame = Engine.Frame RenderPasses Pipelines
type Stage = Engine.Stage RenderPasses Pipelines
type StageFrameRIO fr rs = Engine.StageFrameRIO RenderPasses Pipelines fr rs
type StageResources fr rs = Stage.Resources RenderPasses Pipelines fr rs
type StageScene fr rs = Stage.Scene RenderPasses Pipelines fr rs
type RenderPasses = Basic.RenderPasses
type Pipelines = Basic.Pipelines
component :: Basic.Rendering st
component = Basic.rendering_
Assets
TBD
Stage
Setup
app/Stage/<StageName>.hs
:
module Stage.StageName where
-- the prelude
import RIO
import Engine.Stage.Component qualified as Stage
import Engine.Types (StackStage(..))
import Global.Render qualified as Render
import Stage.StageName.Resources qualified as Resources
import Stage.StageName.Scene qualified as Scene
import Stage.StageName.Types (Stage)
stackStage :: StackStage
stackStage = StackStage stage
stage :: StageName.Stage
stage =
Stage.assemble
"Main"
Render.component
Resources.component
(Just Scene.component)
Types
module Stage.StageName.Types where
import RIO
import Render.DescSets.Set0 qualified as Set0
import Vulkan.Core10 qualified as Vk
import Global.Render qualified as Render
import Global.Resource.Assets (Assets)
type Stage = Render.Stage FrameResources RunState
type Frame = Render.Frame FrameResources
type Scene = Render.StageScene RunState FrameResources
type RecordCommands a = Vk.CommandBuffer -> FrameResources -> Word32 -> Render.StageFrameRIO FrameResources RunState a
type Resources = Stage.Resources Render.RenderPasses Render.Pipelines RunState FrameResources
type UpdateBuffers a = RunState -> FrameResources -> Render.StageFrameRIO FrameResources RunState a
-- Double-buffered resources used in rendering
data FrameResources = FrameResources
{ frSceneUI :: Set0.FrameResource '[Set0.Scene]
, frSceneWorld :: Set0.FrameResource '[Set0.Scene]
}
-- Inter-frame persistent resources
data RunState = RunState
{ rsAssets :: Assets
, rsSceneUI :: Set0.Process
, rsSceneWorld :: Set0.Process
}
Resources
module Stage.StageName.Resources where
import RIO
import Control.Monad.Trans.Resource (ResourceT)
import Engine.Stage.Component qualified as Stage
import Engine.Types (StageRIO, StageSetupRIO)
import Engine.Vulkan.Types (Queues)
import Resource.Region (ReleaseKey)
import Resource.Region qualified as Region
import Vulkan.Core10 qualified as Vk
import Global.Render qualified
import Stage.StageName.Types qualified as StageName
component :: StageName.Resources
component = Stage.Resources
{ rInitialRS = initialRunState
, rInitialRR = initialRecycledResources
}
initialRunState :: StageSetupRIO (ReleaseKey, StageName.RunState)
initialRunState = Region.run do
-- Or, use a loader stage and request from arguments.
rsAssets <- Assets.load
ortho <- Camera.spawnOrthoPixelsCentered
rsSceneUI <- Worker.spawnMerge1 mkSceneUI ortho
pure StageName.RunState{..}
initialRecycledResources
:: Queues Vk.CommandPool
-> Global.Render.RenderPasses
-> Global.Render.Pipelines
-> ResourceT (StageRIO StageName.RunState) StageName.FrameResources
initialRecycledResources _pools _renderpasses _pipelines = do
assets <- gets Game.rsAssets
let combined = fmap snd assets.aCombined
frSceneUI <- Scene.allocate
pipelines.layout
combined
Nothing
Nothing
mempty
Nothing
frSceneWorld <- Scene.allocate
pipelines.layout
combined
Nothing
Nothing
mempty
Nothing
pure StageName.FrameResources{..}
Scene
module Stage.StageName.Scene where
import RIO
import Engine.Stage.Component qualified as Stage
import Engine.Types qualified as Engine
import Engine.Vulkan.Swapchain qualified as Swapchain
import Render.Basic qualified as Basic
import Render.Pass (usePass)
import Resource.Region qualified as Region
import Stage.StageName.Types qualified as StageName
component :: StageName.Scene
component = Stage.Scene
{ scBeforeLoop = pure () -- Set up event network
, scUpdateBuffers = updateBuffers
, scRecordCommands = recordCommands
}
updateBuffers :: StageName.UpdateBuffers ()
updateBuffers StageName.RunState{..} StageName.FrameResources{..} = do
Scene.observe rsSceneWorld frSceneWorld
Scene.observe rsSceneUi frSceneUi
recordCommands :: StageName.RecordCommands ()
recordCommands cb StageName.FrameResources{} imageIndex = do
Engine.Frame{fRenderpass, fSwapchainResources, fPipelines} <- asks snd
usePass (Basic.rpForwardMsaa fRenderpass) imageIndex cb do
Swapchain.setDynamicFullscreen cb fSwapchainResources