packages feed

keid-render-basic-0.1.12.0: src/Render/Basic.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedRecordDot #-}

{- |
  All the provided render passes and pipelines packaged and delivered.
-}

module Render.Basic where

import RIO

import Control.Monad.Trans.Resource (ResourceT)
import Data.Kind (Type)
import Data.Tagged (Tagged(..))
import RIO.FilePath ((</>), (<.>))
import RIO.Vector.Partial as Vector (headM)
import Vulkan.Core10 qualified as Vk

-- keid-core

import Engine.Stage.Component qualified as Stage
import Engine.Types (StageRIO)
import Engine.Types qualified as Engine
import Engine.Vulkan.Pipeline qualified as Pipeline
import Engine.Vulkan.Pipeline.External (type (^))
import Engine.Vulkan.Pipeline.External qualified as External
import Engine.Vulkan.Pipeline.Graphics qualified as Graphics
import Engine.Vulkan.Shader qualified as Shader
import Engine.Vulkan.Swapchain qualified as Swapchain
import Engine.Vulkan.Types (DsLayoutBindings, HasSwapchain, HasVulkan, RenderPass(..))
import Engine.Worker qualified as Worker

-- keid-render-basic

import Render.Debug.Pipeline qualified as Debug
import Render.DepthOnly.Pipeline qualified as DepthOnly
import Render.DescSets.Set0 (Scene)
import Render.DescSets.Set0 qualified as Scene
import Render.DescSets.Sun (Sun)
import Render.DescSets.Sun qualified as Sun
import Render.Font.Msdf.Pipeline qualified as Msdf
import Render.ForwardMsaa (ForwardMsaa)
import Render.ForwardMsaa qualified as ForwardMsaa
import Render.Lit.Colored.Pipeline qualified as LitColored
import Render.Lit.Material.Pipeline qualified as LitMaterial
import Render.Lit.Specialization qualified as Lit
import Render.Lit.Textured.Pipeline qualified as LitTextured
import Render.Samplers qualified as Samplers
import Render.ShadowMap.Pipeline qualified as ShadowPipe
import Render.ShadowMap.RenderPass (ShadowMap)
import Render.ShadowMap.RenderPass qualified as ShadowPass
import Render.Skybox.Pipeline qualified as Skybox
import Render.Unlit.Colored.Pipeline qualified as UnlitColored
import Render.Unlit.Line2d.Pipeline qualified as UnlitLine2d
import Render.Unlit.Sprite.Pipeline qualified as UnlitSprite
import Render.Unlit.Textured.Pipeline qualified as UnlitTextured
import Render.Unlit.TileMap.Pipeline qualified as UnlitTileMap

type Stage = Engine.Stage RenderPasses Pipelines
type Frame = Engine.Frame RenderPasses Pipelines
type StageFrameRIO r s a = Engine.StageFrameRIO RenderPasses Pipelines r s a

-- |  Basic rendering component without any extensions.
type Rendering = Stage.Rendering RenderPasses Pipelines

-- |  Basic rendering component without any extensions and resources.
rendering_ :: Rendering st
rendering_ = Stage.Rendering
  { rAllocateRP = allocate_
  , rAllocateP = allocatePipelines_
  }

data RenderPasses = RenderPasses
  { rpForwardMsaa :: ForwardMsaa
  , rpShadowPass  :: ShadowMap
  }

instance RenderPass RenderPasses where
  updateRenderpass swapchain RenderPasses{..} = RenderPasses
    <$> ForwardMsaa.updateMsaa swapchain rpForwardMsaa
    <*> pure rpShadowPass -- XXX: not a screen pass

  refcountRenderpass RenderPasses{..} = do
    refcountRenderpass rpForwardMsaa
    refcountRenderpass rpShadowPass

data Settings = Settings
  { sShadowSize   :: Word32
  , sShadowLayers :: Word32
  , sMSAA         :: Vk.SampleCountFlagBits
  , sAnisotropy   :: Float
  , sLitSpec      :: Lit.Specialization
  }
  deriving (Eq, Show)

swapchainSettings :: HasSwapchain swapchain => swapchain -> Settings
swapchainSettings swapchain = Settings
  -- XXX: 1x1 placeholder image
  { sShadowSize   = 1
  , sShadowLayers = 1
  , sMSAA         = Swapchain.getMultisample swapchain
  , sAnisotropy   = Swapchain.getAnisotropy swapchain
  , sLitSpec      = Lit.defaultSpec
  }

allocate
  :: ( HasSwapchain swapchain
     , HasLogFunc env
     , HasVulkan env
     )
  => Settings
  -> swapchain
  -> ResourceT (RIO env) RenderPasses
allocate Settings{..} swapchain = do
  rpForwardMsaa <- ForwardMsaa.allocateMsaa swapchain

  rpShadowPass <- ShadowPass.allocate
    swapchain
    sShadowSize
    sShadowLayers

  pure RenderPasses{..}

allocate_
  :: ( HasSwapchain swapchain
     , HasLogFunc env
     , HasVulkan env
     )
  => swapchain
  -> ResourceT (RIO env) RenderPasses
allocate_ swapchain = allocate (swapchainSettings swapchain) swapchain

type Pipelines = PipelinesF Identity
type PipelineObservers = PipelinesF External.Observers
type PipelineWorkers = PipelinesF External.ConfigureGraphics

data PipelinesF (f :: Type -> Type) = Pipelines
  { pSettings :: f ^ External.Params Settings

  , pSceneBinds :: Tagged Scene DsLayoutBindings
  , pSceneLayout :: Tagged '[Scene] Vk.DescriptorSetLayout

  , pShadowBinds :: Tagged Sun DsLayoutBindings
  , pShadowLayout :: Tagged '[Sun] Vk.DescriptorSetLayout

  , pMsdf :: f ^ Msdf.Pipeline
  , pSoftmask :: f ^ Msdf.Pipeline
  , pSkybox   :: f ^ Skybox.Pipeline

  , pDebugUV      :: f ^ Debug.Pipeline
  , pDebugTexture :: f ^ Debug.Pipeline
  , pDebugShadow  :: f ^ Debug.Pipeline

  , pDepthOnly :: f ^ DepthOnly.Pipeline

  , pLitColored       :: f ^ LitColored.Pipeline
  , pLitColoredBlend  :: f ^ LitColored.Pipeline
  , pLitMaterial      :: f ^ LitMaterial.Pipeline
  , pLitMaterialBlend :: f ^ LitMaterial.Pipeline
  , pLitTextured      :: f ^ LitTextured.Pipeline
  , pLitTexturedBlend :: f ^ LitTextured.Pipeline

  , pUnlitColored        :: f ^ UnlitColored.Pipeline
  , pUnlitColoredNoDepth :: f ^ UnlitColored.Pipeline
  , pUnlitTextured       :: f ^ UnlitTextured.Pipeline
  , pUnlitTexturedBlend  :: f ^ UnlitTextured.Pipeline

  , pLine2d        :: f ^ UnlitLine2d.Pipeline
  , pLine2dNoDepth :: f ^ UnlitLine2d.Pipeline

  , pSprite              :: f ^ UnlitSprite.Pipeline
  , pSpriteOutline       :: f ^ UnlitSprite.Pipeline
  , pTileMap             :: f ^ UnlitTileMap.Pipeline
  , pTileMapBlend        :: f ^ UnlitTileMap.Pipeline
  , pWireframe           :: f ^ UnlitColored.Pipeline
  , pWireframeNoDepth    :: f ^ UnlitColored.Pipeline

  , pShadowCast :: f ^ ShadowPipe.Pipeline
  }

allocatePipelines_
  :: HasSwapchain swapchain
  => swapchain
  -> RenderPasses
  -> ResourceT (StageRIO st) Pipelines
allocatePipelines_ swapchain = allocatePipelinesFor settings Nothing Nothing
  where
    settings = swapchainSettings swapchain

allocatePipelinesFor
  :: ( Foldable textures
     , Foldable cubes
     )
  => Settings
  -> textures t
  -> cubes c
  -> RenderPasses
  -> ResourceT (StageRIO st) Pipelines
allocatePipelinesFor settings textures cubes renderpasses = do
  samplers <- Samplers.allocate settings.sAnisotropy

  allocatePipelines
    settings
    (Scene.mkBindings samplers textures cubes shadows)
    renderpasses
  where
    shadows = renderpasses.rpShadowPass.smLayerCount

allocatePipelines
  :: Settings
  -> Tagged Scene DsLayoutBindings
  -> RenderPasses
  -> ResourceT (StageRIO st) Pipelines
allocatePipelines pSettings@Settings{..} pSceneBinds RenderPasses{..} = do
  pMsdf <- Msdf.allocate Msdf.defaultSpecialization sMSAA pSceneBinds rpForwardMsaa
  pSoftmask <- Msdf.allocate Msdf.defaultSpecialization{Msdf.mode=Msdf.SoftMask} sMSAA pSceneBinds rpForwardMsaa
  pSkybox   <- Skybox.allocate sMSAA pSceneBinds rpForwardMsaa

  pDebugUV      <- Debug.allocate Debug.UV         sMSAA pSceneBinds rpForwardMsaa
  pDebugTexture <- Debug.allocate Debug.Texture    sMSAA pSceneBinds rpForwardMsaa
  pDebugShadow  <- Debug.allocate (Debug.Shadow 1) sMSAA pSceneBinds rpForwardMsaa

  pDepthOnly <- DepthOnly.allocate sMSAA pSceneBinds rpForwardMsaa

  pLitColored       <- LitColored.allocate sLitSpec sMSAA pSceneBinds rpForwardMsaa
  pLitColoredBlend  <- LitColored.allocateBlend sLitSpec sMSAA pSceneBinds rpForwardMsaa
  pLitMaterial      <- LitMaterial.allocate sLitSpec sMSAA pSceneBinds rpForwardMsaa
  pLitMaterialBlend <- LitMaterial.allocateBlend sLitSpec sMSAA pSceneBinds rpForwardMsaa
  pLitTextured      <- LitTextured.allocate sLitSpec sMSAA pSceneBinds rpForwardMsaa
  pLitTexturedBlend <- LitTextured.allocateBlend sLitSpec sMSAA pSceneBinds rpForwardMsaa

  pLine2d              <- UnlitLine2d.allocate True sMSAA pSceneBinds rpForwardMsaa
  pLine2dNoDepth       <- UnlitLine2d.allocate False sMSAA pSceneBinds rpForwardMsaa
  pSprite              <- UnlitSprite.allocate sMSAA Nothing False pSceneBinds rpForwardMsaa
  pSpriteOutline       <- UnlitSprite.allocate sMSAA Nothing True pSceneBinds rpForwardMsaa
  pTileMap             <- UnlitTileMap.allocate sMSAA pSceneBinds rpForwardMsaa
  pTileMapBlend        <- UnlitTileMap.allocateBlend sMSAA pSceneBinds rpForwardMsaa
  pUnlitColored        <- UnlitColored.allocate True sMSAA pSceneBinds rpForwardMsaa
  pUnlitColoredNoDepth <- UnlitColored.allocate False sMSAA pSceneBinds rpForwardMsaa
  pUnlitTextured       <- UnlitTextured.allocate sMSAA pSceneBinds rpForwardMsaa
  pUnlitTexturedBlend  <- UnlitTextured.allocateBlend sMSAA pSceneBinds rpForwardMsaa
  pWireframe           <- UnlitColored.allocateWireframe True sMSAA pSceneBinds rpForwardMsaa
  pWireframeNoDepth    <- UnlitColored.allocateWireframe False sMSAA pSceneBinds rpForwardMsaa

  let pShadowBinds = Sun.set0
  pShadowCast <- ShadowPipe.allocate pShadowBinds rpShadowPass ShadowPipe.defaults

  let
    pSceneLayout =
      case Vector.headM (unTagged $ Pipeline.pDescLayouts pLitColored) of
        Nothing ->
          error "pLitColored has at least set0 in layout"
        Just set0layout ->
          Tagged set0layout

    pShadowLayout =
      case Vector.headM (unTagged $ Pipeline.pDescLayouts pShadowCast) of
        Nothing ->
          error "pShadowCast has at least set0 in layout"
        Just set0layout ->
          Tagged set0layout

  pure Pipelines{..}

allocateWorkers
  :: Settings
  -> Tagged Scene DsLayoutBindings
  -> RenderPasses
  -> ResourceT (StageRIO st) PipelineWorkers
allocateWorkers settings sceneBinds renderPasses = do
  builtin <- allocatePipelines settings sceneBinds renderPasses

  settingsVar <- Worker.newVar settings
  litSpecUpdates <- Worker.mergeNeq1 settingsVar (.sLitSpec)

  msdfLoader <-
    External.spawnReflect_ "msdf" $
      Graphics.basicStages
        (shaderDir </> "msdf" <.> "vert" <.> "spv")
        (shaderDir </> "msdf" <.> "frag" <.> "spv")
  msdfWorker <-
    Worker.merge1 msdfLoader \(stageCode, reflect) ->
      (Msdf.config Msdf.defaultSpecialization sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  softmaskWorker <-
    Worker.merge1 msdfLoader \(stageCode, reflect) ->
      (Msdf.config Msdf.defaultSpecialization sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  let
    skyboxFiles =
      Graphics.basicStages
        (shaderDir </> "skybox" <.> "vert" <.> "spv")
        (shaderDir </> "skybox" <.> "frag" <.> "spv")

  skyboxWorker <-
    External.spawnReflect "skybox" skyboxFiles \(stageCode, reflect) ->
      (Skybox.config sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  let
    debugFiles =
      Graphics.basicStages
        (shaderDir </> "debug" <.> "vert" <.> "spv")
        (shaderDir </> "debug" <.> "frag" <.> "spv")

  debugUVWorker <-
    External.spawnReflect "debug-uv" debugFiles \(stageCode, reflect) ->
      (Debug.config Debug.UV sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  debugTextureWorker <-
    External.spawnReflect "debug-texture" debugFiles \(stageCode, reflect) ->
      (Debug.config Debug.Texture sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  debugShadowWorker <-
    External.spawnReflect "debug-shadow" debugFiles \(stageCode, reflect) ->
      (Debug.config (Debug.Shadow 1) sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  let
    depthOnlyFiles = Graphics.vertexOnly
      (shaderDir </> "depth-only" <.> "vert" <.> "spv")

  depthOnlyWorker <-
    External.spawnReflect "debug" depthOnlyFiles \(stageCode, reflect) ->
      (DepthOnly.config sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  litColoredLoader <- External.spawnReflect_ "lit-colored" $
    Graphics.basicStages
      (shaderDir </> "lit-colored" <.> "vert" <.> "spv")
      (shaderDir </> "lit-colored" <.> "frag" <.> "spv")
  litColoredWorker <-
    Worker.merge2 litSpecUpdates litColoredLoader \litSpec (stageCode, reflect) ->
      (LitColored.config litSpec sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  litColoredBlendWorker <-
    Worker.merge2 litSpecUpdates litColoredLoader \litSpec (stageCode, reflect) ->
      (LitColored.configBlend litSpec sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  litMaterialLoader <-
    External.spawnReflect_ "lit-material" $
      Graphics.basicStages
        (shaderDir </> "lit-material" <.> "vert" <.> "spv")
        (shaderDir </> "lit-material" <.> "frag" <.> "spv")
  litMaterialWorker <-
    Worker.merge2 litSpecUpdates litMaterialLoader \litSpec (stageCode, reflect) ->
      (LitMaterial.config litSpec sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  litMaterialBlendWorker <-
    Worker.merge2 litSpecUpdates litMaterialLoader \litSpec (stageCode, reflect) ->
      (LitMaterial.configBlend litSpec sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  litTexturedLoader <-
    External.spawnReflect_ "lit-textured" $
      Graphics.basicStages
        (shaderDir </> "lit-textured" <.> "vert" <.> "spv")
        (shaderDir </> "lit-textured" <.> "frag" <.> "spv")
  litTexturedWorker <-
    Worker.merge2 litSpecUpdates litTexturedLoader \litSpec (stageCode, reflect) ->
      (LitTextured.config litSpec sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  litTexturedBlendWorker <-
    Worker.merge2 litSpecUpdates litTexturedLoader \litSpec (stageCode, reflect) ->
      (LitTextured.configBlend litSpec sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  line2dLoader <-
    External.spawnReflect_ "line2d" $
      Graphics.basicStages
        (shaderDir </> "line-2d" <.> "vert" <.> "spv")
        (shaderDir </> "line-2d" <.> "frag" <.> "spv")
  line2dWorker <-
    Worker.merge1 line2dLoader \(stageCode, reflect) ->
      (UnlitLine2d.config True sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  line2dNoDepthWorker <-
    Worker.merge1 line2dLoader \(stageCode, reflect) ->
      (UnlitLine2d.config False sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  unlitColoredLoader <-
    External.spawnReflect_ "unlit-colored" $
      Graphics.basicStages
        (shaderDir </> "unlit-colored" <.> "vert" <.> "spv")
        (shaderDir </> "unlit-colored" <.> "frag" <.> "spv")
  unlitColoredWorker <-
     Worker.merge1 unlitColoredLoader \(stageCode, reflect) ->
      (UnlitColored.config True sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  unlitColoredNoDepthWorker <-
    Worker.merge1 unlitColoredLoader \(stageCode, reflect) ->
      (UnlitColored.config False sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  wireframeWorker <-
    Worker.merge1 unlitColoredLoader \(stageCode, reflect) ->
      (UnlitColored.configWireframe True sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  wireframeNoDepthWorker <-
    Worker.merge1 unlitColoredLoader \(stageCode, reflect) ->
      (UnlitColored.configWireframe False sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  unlitTexturedLoader <-
    External.spawnReflect_ "unlit-textured" $
      Graphics.basicStages
        (shaderDir </> "unlit-textured" <.> "vert" <.> "spv")
        (shaderDir </> "unlit-textured" <.> "frag" <.> "spv")
  unlitTexturedWorker <-
    Worker.merge1 unlitTexturedLoader \(stageCode, reflect) ->
      (UnlitTextured.config sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  unlitTexturedBlendWorker <-
    Worker.merge1 unlitTexturedLoader \(stageCode, reflect) ->
      (UnlitTextured.configBlend sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  spriteLoader <-
    External.spawnReflect_ "sprite" $
      Graphics.basicStages
        (shaderDir </> "sprite" <.> "vert" <.> "spv")
        (shaderDir </> "sprite" <.> "frag" <.> "spv")
  spriteWorker <-
    Worker.merge1 spriteLoader \(stageCode, reflect) ->
      (UnlitSprite.config Nothing False sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  spriteOutlineWorker <-
    Worker.merge1 spriteLoader \(stageCode, reflect) ->
      (UnlitSprite.config Nothing True sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  tileMapLoader <-
    External.spawnReflect_ "tilemap" $
      Graphics.basicStages
        (shaderDir </> "tilemap" <.> "vert" <.> "spv")
        (shaderDir </> "tilemap" <.> "frag" <.> "spv")
  tileMapWorker <-
    Worker.merge1 tileMapLoader \(stageCode, reflect) ->
      (UnlitTileMap.config sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }
  tileMapBlendWorker <-
    Worker.merge1 tileMapLoader \(stageCode, reflect) ->
      (UnlitTileMap.configBlend sceneBinds)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  let
    shadowCastFiles =
      Graphics.vertexOnly
        (shaderDir </> "shadow-cast" <.> "vert" <.> "spv")
  shadowCastWorker <-
    External.spawnReflect "shadow-cast" shadowCastFiles \(stageCode, reflect) ->
      (ShadowPipe.config (pShadowBinds builtin) ShadowPipe.defaults)
        { Graphics.cStages = stageCode
        , Graphics.cReflect = Just reflect
        }

  pure builtin
    { pSettings = Worker.getOutput settingsVar

    , pSoftmask = Worker.getOutput softmaskWorker
    , pMsdf = Worker.getOutput msdfWorker
    , pSkybox   = Worker.getOutput skyboxWorker

    , pDebugUV      = Worker.getOutput debugUVWorker
    , pDebugTexture = Worker.getOutput debugTextureWorker
    , pDebugShadow  = Worker.getOutput debugShadowWorker
    , pDepthOnly    = Worker.getOutput depthOnlyWorker

    , pLitColored       = Worker.getOutput litColoredWorker
    , pLitColoredBlend  = Worker.getOutput litColoredBlendWorker
    , pLitMaterial      = Worker.getOutput litMaterialWorker
    , pLitMaterialBlend = Worker.getOutput litMaterialBlendWorker
    , pLitTextured      = Worker.getOutput litTexturedWorker
    , pLitTexturedBlend = Worker.getOutput litTexturedBlendWorker

    , pLine2d              = Worker.getOutput line2dWorker
    , pLine2dNoDepth       = Worker.getOutput line2dNoDepthWorker
    , pUnlitColored        = Worker.getOutput unlitColoredWorker
    , pUnlitColoredNoDepth = Worker.getOutput unlitColoredNoDepthWorker
    , pUnlitTextured       = Worker.getOutput unlitTexturedWorker
    , pUnlitTexturedBlend  = Worker.getOutput unlitTexturedBlendWorker
    , pSprite              = Worker.getOutput spriteWorker
    , pSpriteOutline       = Worker.getOutput spriteOutlineWorker
    , pTileMap             = Worker.getOutput tileMapWorker
    , pTileMapBlend        = Worker.getOutput tileMapBlendWorker
    , pWireframe           = Worker.getOutput wireframeWorker
    , pWireframeNoDepth    = Worker.getOutput wireframeNoDepthWorker
    , pShadowCast          = Worker.getOutput shadowCastWorker
    }

allocateObservers
  :: RenderPasses
  -> PipelineWorkers
  -> ResourceT (Engine.StageRIO rs) PipelineObservers
allocateObservers renderPasses Pipelines{..} = do
  -- let Settings{sMSAA} = pSettings
  initialSettings@Settings{sMSAA} <- Worker.getOutputData pSettings
  settingsExt <- Worker.newObserverIO initialSettings

  msdfExt <- External.newObserverGraphics
    forward
    sMSAA
    pMsdf

  softmaskExt <- External.newObserverGraphics
    forward
    sMSAA
    pSoftmask

  skyboxExt <- External.newObserverGraphics
    forward
    sMSAA
    pSkybox

  debugUVExt <- External.newObserverGraphics
    forward
    sMSAA
    pDebugUV

  debugTextureExt <- External.newObserverGraphics
    forward
    sMSAA
    pDebugTexture

  debugShadowExt <- External.newObserverGraphics
    forward
    sMSAA
    pDebugShadow

  depthOnlyExt <- External.newObserverGraphics
    forward
    sMSAA
    pDepthOnly

  litColoredExt <- External.newObserverGraphics
    forward
    sMSAA
    pLitColored

  litColoredBlendExt <- External.newObserverGraphics
    forward
    sMSAA
    pLitColoredBlend

  litMaterialExt <- External.newObserverGraphics
    forward
    sMSAA
    pLitMaterial

  litMaterialBlendExt <- External.newObserverGraphics
    forward
    sMSAA
    pLitMaterialBlend

  litTexturedExt <- External.newObserverGraphics
    forward
    sMSAA
    pLitTextured

  litTexturedBlendExt <- External.newObserverGraphics
    forward
    sMSAA
    pLitTexturedBlend

  line2dExt <- External.newObserverGraphics
    forward
    sMSAA
    pLine2d

  line2dNoDepthExt <- External.newObserverGraphics
    forward
    sMSAA
    pLine2dNoDepth

  unlitColoredExt <- External.newObserverGraphics
    forward
    sMSAA
    pUnlitColored

  unlitColoredNoDepthExt <- External.newObserverGraphics
    forward
    sMSAA
    pUnlitColoredNoDepth

  unlitTexturedExt <- External.newObserverGraphics
    forward
    sMSAA
    pUnlitTextured

  unlitTexturedBlendExt <- External.newObserverGraphics
    forward
    sMSAA
    pUnlitTexturedBlend

  spriteExt <- External.newObserverGraphics
    forward
    sMSAA
    pSprite

  spriteOutlineExt <- External.newObserverGraphics
    forward
    sMSAA
    pSpriteOutline

  tileMapExt <- External.newObserverGraphics
    forward
    sMSAA
    pTileMap

  tileMapBlendExt <- External.newObserverGraphics
    forward
    sMSAA
    pTileMapBlend

  wireframeExt <- External.newObserverGraphics
    forward
    sMSAA
    pWireframe

  wireframeNoDepthExt <- External.newObserverGraphics
    forward
    sMSAA
    pWireframeNoDepth

  shadowCastExt <- External.newObserverGraphics
    forward
    sMSAA
    pShadowCast

  pure Pipelines
    { pSettings = settingsExt
    , pMsdf = msdfExt
    , pSoftmask = softmaskExt
    , pSkybox = skyboxExt
    , pDebugUV = debugUVExt
    , pDebugTexture = debugTextureExt
    , pDebugShadow = debugShadowExt
    , pDepthOnly = depthOnlyExt
    , pLitColored = litColoredExt
    , pLitColoredBlend = litColoredBlendExt
    , pLitMaterial = litMaterialExt
    , pLitMaterialBlend = litMaterialBlendExt
    , pLitTextured = litTexturedExt
    , pLitTexturedBlend = litTexturedBlendExt
    , pLine2d = line2dExt
    , pLine2dNoDepth = line2dNoDepthExt
    , pUnlitColored = unlitColoredExt
    , pUnlitColoredNoDepth = unlitColoredNoDepthExt
    , pUnlitTextured = unlitTexturedExt
    , pUnlitTexturedBlend = unlitTexturedBlendExt
    , pSprite = spriteExt
    , pSpriteOutline = spriteOutlineExt
    , pTileMap = tileMapExt
    , pTileMapBlend = tileMapBlendExt
    , pWireframe = wireframeExt
    , pWireframeNoDepth = wireframeNoDepthExt
    , pShadowCast = shadowCastExt
    , ..
    }
  where
    forward = rpForwardMsaa renderPasses

observePipelines
  :: RenderPasses
  -> PipelineWorkers
  -> PipelineObservers
  -> Engine.StageFrameRIO rp p fr rs ()
observePipelines fRenderpass workers pipelines = do
  observe @Msdf.Pipeline pMsdf
  observe @Msdf.Pipeline pSoftmask

  observe @Skybox.Pipeline pSkybox

  observe @Debug.Pipeline pDebugUV
  observe @Debug.Pipeline pDebugTexture
  observe @Debug.Pipeline pDebugShadow

  observe @DepthOnly.Pipeline pDepthOnly

  observe @LitColored.Pipeline pLitColored
  observe @LitColored.Pipeline pLitColoredBlend

  observe @LitTextured.Pipeline pLitTextured
  observe @LitTextured.Pipeline pLitTexturedBlend

  observe @LitMaterial.Pipeline pLitMaterial
  observe @LitMaterial.Pipeline pLitMaterialBlend

  observe @UnlitColored.Pipeline pUnlitColored
  observe @UnlitColored.Pipeline pUnlitColoredNoDepth

  observe @UnlitTextured.Pipeline pUnlitTextured
  observe @UnlitTextured.Pipeline pUnlitTexturedBlend

  observe @UnlitSprite.Pipeline pSprite
  observe @UnlitSprite.Pipeline pSpriteOutline

  observe @UnlitTileMap.Pipeline pTileMap
  observe @UnlitTileMap.Pipeline pTileMapBlend

  observe @UnlitColored.Pipeline pWireframe
  observe @UnlitColored.Pipeline pWireframeNoDepth

  settings <- Worker.readVar workers.pSettings
  External.observeGraphics
    (rpShadowPass fRenderpass) -- XXX: different RP here
    settings.sMSAA
    (Tagged [unTagged $ pSceneBinds pipelines])
    workers.pShadowCast
    pipelines.pShadowCast
  where
    observe
      :: forall
         p
         s vs is
         rps ps fr rs
      .  ( p ~ Graphics.Pipeline s vs is
         , Shader.Specialization (Graphics.Specialization p)
         )
      => (forall a . PipelinesF a -> a ^ p)
      -> Engine.StageFrameRIO rps ps fr rs ()
    observe f = do
      settings <- Worker.readVar workers.pSettings
      External.observeField
        @PipelinesF
        @p
        (rpForwardMsaa fRenderpass)
        settings.sMSAA
        (pSceneBinds pipelines)
        workers
        pipelines
        f

getSceneLayout :: PipelinesF f -> Tagged '[Scene] Vk.DescriptorSetLayout
getSceneLayout = pSceneLayout

getSunLayout :: Pipelines -> Tagged '[Sun] Vk.DescriptorSetLayout
getSunLayout = pShadowLayout

shaderDir :: FilePath
shaderDir = "data" </> "shaders" </> "basic"

stageSources :: Map Text Graphics.StageCode
stageSources =
  [ ("msdf", Msdf.stageCode)
  , ("skybox", Skybox.stageCode)
  , ("debug", Debug.stageCode)
  , ("depth-only", DepthOnly.stageCode)
  , ("lit-colored", LitColored.stageCode)
  , ("lit-material", LitMaterial.stageCode)
  , ("lit-textured", LitTextured.stageCode)
  , ("unlit-colored", UnlitColored.stageCode)
  , ("unlit-textured", UnlitTextured.stageCode)
  , ("line-2d", UnlitLine2d.stageCode)
  , ("sprite", UnlitSprite.stageCode)
  , ("tilemap", UnlitTileMap.stageCode)
  , ("shadow-cast", ShadowPipe.stageCode)
  ]