packages feed

keid-render-basic 0.1.4.0 → 0.1.5.0

raw patch · 20 files changed

+669/−98 lines, 20 filesdep ~keid-corePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: keid-core

API changes (from Hackage documentation)

- Render.Basic: [$sel:pDebug:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: Settings :: Word32 -> Word32 -> Settings
+ Render.Basic: [$sel:pDebugShadow:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: [$sel:pDebugTexture:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: [$sel:pDebugUV:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: [$sel:pDepthOnly:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: [$sel:pTileMap:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: [$sel:pTileMapBlend:Pipelines] :: Pipelines -> Pipeline
+ Render.Basic: [$sel:sShadowLayers:Settings] :: Settings -> Word32
+ Render.Basic: [$sel:sShadowSize:Settings] :: Settings -> Word32
+ Render.Basic: allocate :: (HasSwapchain swapchain, HasLogFunc env, HasVulkan env) => Settings -> swapchain -> ResourceT (RIO env) RenderPasses
+ Render.Basic: allocate_ :: (HasSwapchain swapchain, HasLogFunc env, HasVulkan env) => swapchain -> ResourceT (RIO env) RenderPasses
+ Render.Basic: data Settings
+ Render.Basic: instance GHC.Classes.Eq Render.Basic.Settings
+ Render.Basic: instance GHC.Show.Show Render.Basic.Settings
+ Render.Basic: instance Vulkan.Zero.Zero Render.Basic.Settings
+ Render.Debug.Pipeline: Shadow :: Word32 -> Mode
+ Render.Debug.Pipeline: Texture :: Mode
+ Render.Debug.Pipeline: UV :: Mode
+ Render.Debug.Pipeline: data Mode
+ Render.Debug.Pipeline: instance Engine.Vulkan.Shader.Specialization Render.Debug.Pipeline.Mode
+ Render.Debug.Pipeline: instance GHC.Classes.Eq Render.Debug.Pipeline.Mode
+ Render.Debug.Pipeline: instance GHC.Classes.Ord Render.Debug.Pipeline.Mode
+ Render.Debug.Pipeline: instance GHC.Show.Show Render.Debug.Pipeline.Mode
+ Render.DepthOnly.Pipeline: allocate :: (HasVulkan env, HasRenderPass renderpass) => SampleCountFlagBits -> Tagged Scene DsBindings -> renderpass -> ResourceT (RIO env) Pipeline
+ Render.DepthOnly.Pipeline: config :: Tagged Scene DsBindings -> Config
+ Render.DepthOnly.Pipeline: type Config = Configure Pipeline ()
+ Render.DepthOnly.Pipeline: type Pipeline = Pipeline '[Scene] () Transform
+ Render.Unlit.TileMap.Model: InstanceAttrs :: TileMapParams -> Transform -> InstanceAttrs
+ Render.Unlit.TileMap.Model: InstanceBuffers :: InstanceTileMap tilemapStage -> InstanceTransform transformStage -> InstanceBuffers tilemapStage transformStage
+ Render.Unlit.TileMap.Model: TileMapParams :: IVec4 -> Vec2 -> Vec2 -> Vec2 -> Vec2 -> Vec2 -> TileMapParams
+ Render.Unlit.TileMap.Model: [$sel:ibTileMap:InstanceBuffers] :: InstanceBuffers tilemapStage transformStage -> InstanceTileMap tilemapStage
+ Render.Unlit.TileMap.Model: [$sel:ibTransform:InstanceBuffers] :: InstanceBuffers tilemapStage transformStage -> InstanceTransform transformStage
+ Render.Unlit.TileMap.Model: [$sel:tilemapParams:InstanceAttrs] :: InstanceAttrs -> TileMapParams
+ Render.Unlit.TileMap.Model: [$sel:tmpMapTextureSize:TileMapParams] :: TileMapParams -> Vec2
+ Render.Unlit.TileMap.Model: [$sel:tmpTextureIds:TileMapParams] :: TileMapParams -> IVec4
+ Render.Unlit.TileMap.Model: [$sel:tmpTileSize:TileMapParams] :: TileMapParams -> Vec2
+ Render.Unlit.TileMap.Model: [$sel:tmpTilesetTextureSize:TileMapParams] :: TileMapParams -> Vec2
+ Render.Unlit.TileMap.Model: [$sel:tmpViewOffset:TileMapParams] :: TileMapParams -> Vec2
+ Render.Unlit.TileMap.Model: [$sel:tmpViewportSize:TileMapParams] :: TileMapParams -> Vec2
+ Render.Unlit.TileMap.Model: [$sel:transformMat4:InstanceAttrs] :: InstanceAttrs -> Transform
+ Render.Unlit.TileMap.Model: allocateInstancesCoherent :: (MonadReader env m, HasVulkan env, MonadResource m, MonadUnliftIO m) => [InstanceAttrs] -> m (ReleaseKey, InstanceBuffers 'Coherent 'Coherent)
+ Render.Unlit.TileMap.Model: allocateInstancesCoherent_ :: HasVulkan env => Int -> ResourceT (RIO env) (InstanceBuffers 'Coherent 'Coherent)
+ Render.Unlit.TileMap.Model: allocateInstancesWith :: (MonadResource m, MonadUnliftIO m) => (BufferUsageFlagBits -> Int -> Vector TileMapParams -> m (InstanceTileMap texture)) -> (BufferUsageFlagBits -> Int -> Vector Transform -> m (InstanceTransform transform)) -> (forall stage a. Allocated stage a -> m ()) -> [InstanceAttrs] -> m (ReleaseKey, InstanceBuffers texture transform)
+ Render.Unlit.TileMap.Model: data InstanceAttrs
+ Render.Unlit.TileMap.Model: data InstanceBuffers tilemapStage transformStage
+ Render.Unlit.TileMap.Model: data TileMapParams
+ Render.Unlit.TileMap.Model: data Transform
+ Render.Unlit.TileMap.Model: instance Foreign.Storable.Storable Render.Unlit.TileMap.Model.TileMapParams
+ Render.Unlit.TileMap.Model: instance GHC.Show.Show Render.Unlit.TileMap.Model.TileMapParams
+ Render.Unlit.TileMap.Model: instance Resource.Model.HasVertexBuffers (Render.Unlit.TileMap.Model.InstanceBuffers tilemapStage transformStage)
+ Render.Unlit.TileMap.Model: instance Vulkan.Zero.Zero Render.Unlit.TileMap.Model.InstanceAttrs
+ Render.Unlit.TileMap.Model: instance Vulkan.Zero.Zero Render.Unlit.TileMap.Model.TileMapParams
+ Render.Unlit.TileMap.Model: type Model buf = Indexed buf Packed VertexAttrs
+ Render.Unlit.TileMap.Model: type StorableAttrs = (Vector TileMapParams, Vector Transform)
+ Render.Unlit.TileMap.Model: type VertexAttrs = "uv" ::: Vec2
+ Render.Unlit.TileMap.Model: updateCoherentResize_ :: (HasVulkan context, MonadUnliftIO m) => context -> InstanceBuffers 'Coherent 'Coherent -> (Vector TileMapParams, Vector Transform) -> m (InstanceBuffers 'Coherent 'Coherent)
+ Render.Unlit.TileMap.Model: vkInstanceTileMap :: [Format]
+ Render.Unlit.TileMap.Model: vkVertexAttrs :: [Format]
+ Render.Unlit.TileMap.Pipeline: allocate :: (HasVulkan env, HasRenderPass renderpass) => SampleCountFlagBits -> Tagged Scene DsBindings -> renderpass -> ResourceT (RIO env) Pipeline
+ Render.Unlit.TileMap.Pipeline: allocateBlend :: (HasVulkan env, HasRenderPass renderpass) => SampleCountFlagBits -> Tagged Scene DsBindings -> renderpass -> ResourceT (RIO env) Pipeline
+ Render.Unlit.TileMap.Pipeline: config :: Tagged Scene DsBindings -> Config
+ Render.Unlit.TileMap.Pipeline: configBlend :: Tagged Scene DsBindings -> Config
+ Render.Unlit.TileMap.Pipeline: type Config = Configure Pipeline ()
+ Render.Unlit.TileMap.Pipeline: type Pipeline = Pipeline '[Scene] VertexAttrs InstanceAttrs
- Render.Basic: Pipelines :: SampleCountFlagBits -> Tagged Scene DsBindings -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipelines
+ Render.Basic: Pipelines :: SampleCountFlagBits -> Tagged Scene DsBindings -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipeline -> Pipelines
- Render.Debug.Pipeline: allocate :: (HasVulkan env, HasRenderPass renderpass) => SampleCountFlagBits -> Tagged Scene DsBindings -> renderpass -> ResourceT (RIO env) Pipeline
+ Render.Debug.Pipeline: allocate :: (HasVulkan env, HasRenderPass renderpass) => Mode -> SampleCountFlagBits -> Tagged Scene DsBindings -> renderpass -> ResourceT (RIO env) Pipeline
- Render.Lit.Colored.Pipeline: type Config = Config '[Scene] VertexAttrs InstanceAttrs
+ Render.Lit.Colored.Pipeline: type Config = Configure Pipeline ()
- Render.Lit.Material.Pipeline: type Config = Config '[Scene] VertexAttrs InstanceAttrs
+ Render.Lit.Material.Pipeline: type Config = Configure Pipeline ()
- Render.Lit.Textured.Pipeline: type Config = Config '[Scene] VertexAttrs InstanceAttrs
+ Render.Lit.Textured.Pipeline: type Config = Configure Pipeline ()
- Render.ShadowMap.Pipeline: type Config = Config '[Sun] () Transform
+ Render.ShadowMap.Pipeline: type Config = Configure Pipeline ()
- Render.Unlit.Colored.Pipeline: type Config = Config '[Scene] VertexAttrs InstanceAttrs
+ Render.Unlit.Colored.Pipeline: type Config = Configure Pipeline ()
- Render.Unlit.Textured.Pipeline: type Config = Config '[Scene] VertexAttrs InstanceAttrs
+ Render.Unlit.Textured.Pipeline: type Config = Configure Pipeline ()

Files

ChangeLog.md view
@@ -1,5 +1,16 @@ # Changelog for keid-render-basic +## 0.1.5.0++- Extracted deprecated `allocateRenderpass_` into `Render.Basic.allocate`.+- Extracted hardcoded shadowmap params into `Render.Basic.Settings`.+- Added `Render.Unlit.TileMap` pipeline.+- Added `Render.DepthOnly` pipeline.+- Updated lit pipelines to match vertex position calculation.+- Fixed shadowmap index guard.+- Pipelines updated for new configs with specialization.+  * `pDebug` is split into multiple pipelines, specialized by mode.+ ## 0.1.4.0  Update to match `keid-core-0.1.4.0`.
README.md view
@@ -5,12 +5,17 @@   * Unlit pipelines     * Colored     * Textured+    * Tile map+    * Wires   * Lit pipelines     * Colored     * Textured     * Material   * Skybox pipeline-  * Debug pipeline+  * Debug pipelines+    * UV+    * Shadowmap - Shadowmap pass   * Shadow-casting pipeline+- Depth-only - Asset loader stage
keid-render-basic.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           keid-render-basic-version:        0.1.4.0+version:        0.1.5.0 synopsis:       Basic rendering programs for Keid engine. category:       Game Engine author:         IC Rainbow@@ -38,6 +38,7 @@       Render.Code.Lit       Render.Debug.Model       Render.Debug.Pipeline+      Render.DepthOnly.Pipeline       Render.DescSets.Set0       Render.DescSets.Set0.Code       Render.DescSets.Sun@@ -59,6 +60,8 @@       Render.Unlit.Colored.Pipeline       Render.Unlit.Textured.Model       Render.Unlit.Textured.Pipeline+      Render.Unlit.TileMap.Model+      Render.Unlit.TileMap.Pipeline       Resource.Font       Resource.Font.EvanW       Resource.Mesh.Lit@@ -139,7 +142,7 @@     , distributive     , file-embed >=0.0.10     , geomancy-    , keid-core >=0.1.4.0+    , keid-core >=0.1.5.0     , keid-geometry     , neat-interpolation     , resourcet
src/Render/Basic.hs view
@@ -10,6 +10,7 @@ import Data.Tagged (Tagged(..)) import RIO.Vector.Partial as Vector (headM) import Vulkan.Core10 qualified as Vk+import Vulkan.Zero (Zero(..))  -- keid-core @@ -17,11 +18,12 @@ import Engine.Types qualified as Engine import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Swapchain qualified as Swapchain-import Engine.Vulkan.Types (DsBindings, HasSwapchain, RenderPass(..))+import Engine.Vulkan.Types (DsBindings, HasSwapchain, HasVulkan, RenderPass(..))  -- 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)@@ -39,6 +41,7 @@ import Render.Skybox.Pipeline qualified as Skybox import Render.Unlit.Colored.Pipeline qualified as UnlitColored 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@@ -50,33 +53,67 @@   }  instance RenderPass RenderPasses where-  allocateRenderpass_ context = do-    rpForwardMsaa <- ForwardMsaa.allocateMsaa context--    -- TODO: get from config-    let-      size      = 4096 -- XXX: 2048 is kinda minimum, even with PCF enabled-      numLayers = 2    -- XXX: no-cascade sun + moon-    rpShadowPass <- ShadowPass.allocate context size numLayers--    pure RenderPasses{..}--  updateRenderpass context RenderPasses{..} = RenderPasses-    <$> ForwardMsaa.updateMsaa context rpForwardMsaa+  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+  }+  deriving (Eq, Show)++instance Zero Settings where+  zero = Settings+    -- XXX: 1x1 placeholder image+    { sShadowSize   = 1+    , sShadowLayers = 1+    }++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_ = allocate zero+ data Pipelines = Pipelines   { pMSAA       :: Vk.SampleCountFlagBits   , pSceneBinds :: Tagged Scene DsBindings    , pEvanwSdf :: EvanwSdf.Pipeline   , pSkybox   :: Skybox.Pipeline-  , pDebug    :: Debug.Pipeline +  , pDebugUV      :: Debug.Pipeline+  , pDebugTexture :: Debug.Pipeline+  , pDebugShadow  :: Debug.Pipeline++  , pDepthOnly :: DepthOnly.Pipeline+   , pLitColored       :: LitColored.Pipeline   , pLitColoredBlend  :: LitColored.Pipeline   , pLitMaterial      :: LitMaterial.Pipeline@@ -88,6 +125,8 @@   , pUnlitColoredNoDepth :: UnlitColored.Pipeline   , pUnlitTextured       :: UnlitTextured.Pipeline   , pUnlitTexturedBlend  :: UnlitTextured.Pipeline+  , pTileMap             :: UnlitTileMap.Pipeline+  , pTileMapBlend        :: UnlitTileMap.Pipeline   , pWireframe           :: UnlitColored.Pipeline   , pWireframeNoDepth    :: UnlitColored.Pipeline @@ -114,10 +153,15 @@   -> RenderPasses   -> ResourceT (StageRIO st) Pipelines allocatePipelines pSceneBinds pMSAA RenderPasses{..} = do-  pDebug    <- Debug.allocate pMSAA pSceneBinds rpForwardMsaa+  pDebugUV      <- Debug.allocate Debug.UV         pMSAA pSceneBinds rpForwardMsaa+  pDebugTexture <- Debug.allocate Debug.Texture    pMSAA pSceneBinds rpForwardMsaa+  pDebugShadow  <- Debug.allocate (Debug.Shadow 1) pMSAA pSceneBinds rpForwardMsaa+   pEvanwSdf <- EvanwSdf.allocate pMSAA pSceneBinds rpForwardMsaa   pSkybox   <- Skybox.allocate pMSAA pSceneBinds rpForwardMsaa +  pDepthOnly <- DepthOnly.allocate pMSAA pSceneBinds rpForwardMsaa+   pLitColored       <- LitColored.allocate pMSAA pSceneBinds rpForwardMsaa   pLitColoredBlend  <- LitColored.allocateBlend pMSAA pSceneBinds rpForwardMsaa   pLitMaterial      <- LitMaterial.allocate pMSAA pSceneBinds rpForwardMsaa@@ -129,6 +173,8 @@   pUnlitColoredNoDepth <- UnlitColored.allocate False pMSAA pSceneBinds rpForwardMsaa   pUnlitTextured       <- UnlitTextured.allocate pMSAA pSceneBinds rpForwardMsaa   pUnlitTexturedBlend  <- UnlitTextured.allocateBlend pMSAA pSceneBinds rpForwardMsaa+  pTileMap             <- UnlitTileMap.allocate pMSAA pSceneBinds rpForwardMsaa+  pTileMapBlend        <- UnlitTileMap.allocateBlend pMSAA pSceneBinds rpForwardMsaa   pWireframe           <- UnlitColored.allocateWireframe True pMSAA pSceneBinds rpForwardMsaa   pWireframeNoDepth    <- UnlitColored.allocateWireframe False pMSAA pSceneBinds rpForwardMsaa 
src/Render/Code/Lit.hs view
@@ -141,7 +141,7 @@       vec3 lightDir = normalize(lights[l].direction.xyz); // L        float shade = 1.0; // XXX: 0 - occluded, 1 - lit-      if (lights[l].shadow.z > 0) {+      if (lights[l].shadow.z > -1) {         vec4 light_space_pos = lights[l].viewProjection * fPosition;         vec4 shadowCoord = light_space_pos /= light_space_pos.w; 
src/Render/Debug/Pipeline.hs view
@@ -1,6 +1,7 @@ module Render.Debug.Pipeline   ( Pipeline   , allocate+  , Mode(..)   ) where  import RIO@@ -8,9 +9,9 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline+import Engine.Vulkan.Shader qualified as Shader import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings) import Render.Code (compileVert, compileFrag, glsl) import Render.Debug.Model qualified as Model@@ -19,27 +20,41 @@  type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs +data Mode+  = UV+  | Texture+  | Shadow Word32+  deriving (Eq, Ord, Show)++instance Shader.Specialization Mode where+  specializationData = \case+    UV ->+      [0]+    Texture ->+      [1]+    Shadow bits ->+      [2, bits]+ allocate   :: ( HasVulkan env      , HasRenderPass renderpass      )-  => Vk.SampleCountFlagBits+  => Mode+  -> Vk.SampleCountFlagBits   -> Tagged Scene DsBindings   -> renderpass   -> ResourceT (RIO env) Pipeline-allocate multisample (Tagged set0) rp = do-  (_, p) <- Pipeline.allocate-    Nothing-    multisample-    (zero-      { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]-      , Pipeline.cVertexCode   = Just vertCode-      , Pipeline.cVertexInput  = vertexInput-      , Pipeline.cFragmentCode = Just fragCode-      })-    rp-  pure p+allocate mode multisample (Tagged set0) = do+  fmap snd . Pipeline.allocate Nothing multisample config   where+    config = Pipeline.baseConfig+      { Pipeline.cDescLayouts    = Tagged @'[Scene] [set0]+      , Pipeline.cVertexCode     = Just vertCode+      , Pipeline.cVertexInput    = vertexInput+      , Pipeline.cFragmentCode   = Just fragCode+      , Pipeline.cSpecialization = mode+      }+     vertexInput = Pipeline.vertexInput       [ vertexPos -- vPosition       , (Vk.VERTEX_INPUT_RATE_VERTEX,   Model.vkVertexAttrs)@@ -72,11 +87,12 @@     layout(location = 2) flat out ivec2 fTextureIds;      void main() {+      vec4 fPosition = iModel * vec4(vPosition, 1.0);+       gl_Position         = scene.projection         * scene.view-        * iModel-        * vec4(vPosition, 1.0);+        * fPosition;        fTexCoord     = vTexCoord * iTextureScaleOffset.st + iTextureScaleOffset.pq;       fTextureGamma = iTextureGamma;@@ -102,7 +118,8 @@      layout(location = 0) out vec4 oColor; -    const int mode = 1;+    layout (constant_id=0) const uint mode = 0;+    layout (constant_id=1) const uint shadow_mask = 0;      void main() {       vec4 texel = vec4(0);@@ -125,17 +142,28 @@           break;          case 2:-          float d0 = texture(-            shadowmaps,-            vec3(fTexCoord, 0.0)-          ).x;+          float d0 = 1.0;+          if ((shadow_mask & 1) == 1)+            d0 = texture(+              shadowmaps,+              vec3(fTexCoord, 0.0)+            ).x; -          float d1 = texture(-            shadowmaps,-            vec3(fTexCoord, 1.0)-          ).x;+          float d1 = 1.0;+          if ((shadow_mask & 2) == 2)+            d1 = texture(+              shadowmaps,+              vec3(fTexCoord, 1.0)+            ).x; -          texel = vec4(1-d0, 0, 1-d1, 1.0);+          float d2 = 1.0;+          if ((shadow_mask & 4) == 4)+            d2 = texture(+              shadowmaps,+              vec3(fTexCoord, 2.0)+            ).x;++          texel = vec4(1-d0, 1-d1, 1-d2, 1.0);           break;          default:
+ src/Render/DepthOnly/Pipeline.hs view
@@ -0,0 +1,81 @@+module Render.DepthOnly.Pipeline+  ( Pipeline+  , allocate++  , Config+  , config+  ) where++import RIO++import Control.Monad.Trans.Resource (ResourceT)+import Data.Tagged (Tagged(..))+import Vulkan.Core10 qualified as Vk++import Geomancy (Transform)+import Engine.Vulkan.Pipeline qualified as Pipeline+import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)+import Render.Code (compileVert, {- compileFrag, -} glsl)+import Render.DescSets.Set0 (Scene, vertexPos, instanceTransform)+import Render.DescSets.Set0.Code (set0binding0)++type Config = Pipeline.Configure Pipeline ()+type Pipeline = Pipeline.Pipeline '[Scene] () Transform++allocate+  :: ( HasVulkan env+     , HasRenderPass renderpass+     )+  => Vk.SampleCountFlagBits+  -> Tagged Scene DsBindings+  -> renderpass+  -> ResourceT (RIO env) Pipeline+allocate multisample tset0 rp = do+  (_, p) <- Pipeline.allocate+    Nothing+    multisample+    (config tset0)+    rp+  pure p++config :: Tagged Scene DsBindings -> Config+config (Tagged set0) = Pipeline.baseConfig+  { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]+  , Pipeline.cVertexCode   = Just vertCode+  , Pipeline.cVertexInput  = vertexInput+  , Pipeline.cFragmentCode = Nothing -- XXX: needed for alpha cut-outs+  }+  where+    vertexInput = Pipeline.vertexInput+      [ vertexPos+      , instanceTransform+      ]++vertCode :: ByteString+vertCode =+  $(compileVert [glsl|+    #version 450+    #extension GL_ARB_separate_shader_objects : enable++    invariant gl_Position;++    ${set0binding0}++    layout(location = 0) in vec3 vPosition;+    layout(location = 1) in mat4 iModel;++    // layout(location = 0) out vec4 fColor;++    void main() {+      vec4 fPosition = iModel * vec4(vPosition, 1.0);++      gl_Position+        = scene.projection+        * scene.view+        * fPosition;++      // XXX: needed for alpha cut-outs+      // fColor = vColor;+      // fColor.rgb * fColor.a;+    }+  |])
src/Render/Font/EvanwSdf/Pipeline.hs view
@@ -10,9 +10,7 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (Zero(..)) -import Engine.Vulkan.Pipeline (Config(..)) import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings) import Render.Code (compileVert, compileFrag, glsl)@@ -34,15 +32,15 @@   fmap snd . Pipeline.allocate     Nothing     multisample-    zero-      { cVertexCode         = Just vertCode-      , cFragmentCode       = Just fragCode-      , cDescLayouts        = Tagged @'[Scene] [set0]-      , cVertexInput        = vertexInput-      , cDepthTest          = False-      , cDepthWrite         = False-      , cBlend              = True-      , cCull               = Vk.CULL_MODE_NONE+    Pipeline.baseConfig+      { Pipeline.cVertexCode     = Just vertCode+      , Pipeline.cFragmentCode   = Just fragCode+      , Pipeline.cDescLayouts    = Tagged @'[Scene] [set0]+      , Pipeline.cVertexInput    = vertexInput+      , Pipeline.cDepthTest      = False+      , Pipeline.cDepthWrite     = False+      , Pipeline.cBlend          = True+      , Pipeline.cCull           = Vk.CULL_MODE_NONE       }   where     vertexInput = Pipeline.vertexInput
src/Render/ForwardMsaa.hs view
@@ -33,16 +33,13 @@   }  instance HasRenderPass ForwardMsaa where-  getRenderPass          = fmRenderPass-  getFramebuffers        = fmFrameBuffers-  getClearValues         = fmClear-  getRenderArea          = fmRenderArea+  getRenderPass   = fmRenderPass+  getFramebuffers = fmFrameBuffers+  getClearValues  = fmClear+  getRenderArea   = fmRenderArea  instance RenderPass ForwardMsaa where-  allocateRenderpass_ = allocateMsaa -- error "Thou shalt allocateMsaa instead"-   updateRenderpass = updateMsaa-   refcountRenderpass = resourceTRefCount . fmRelease  allocateMsaa
src/Render/Lit/Colored/Pipeline.hs view
@@ -13,7 +13,6 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)@@ -23,7 +22,7 @@ import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2, set0binding3, set0binding4, set0binding5) import Render.Lit.Colored.Model qualified as Model -type Config = Pipeline.Config '[Scene] Model.VertexAttrs Model.InstanceAttrs+type Config = Pipeline.Configure Pipeline () type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs  allocate@@ -59,7 +58,7 @@   pure p  config :: Tagged Scene DsBindings -> Config-config (Tagged set0) = zero+config (Tagged set0) = Pipeline.baseConfig   { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]   , Pipeline.cVertexCode   = Just vertCode   , Pipeline.cVertexInput  = vertexInput@@ -83,6 +82,8 @@     #version 450     #extension GL_ARB_separate_shader_objects : enable +    invariant gl_Position;+     ${set0binding0}      layout(location = 0) in vec3 vPosition;@@ -123,6 +124,8 @@     #version 450     #extension GL_ARB_separate_shader_objects : enable     #extension GL_EXT_nonuniform_qualifier : enable++    layout(early_fragment_tests) in;      // TODO: move to spec constant     const uint MAX_LIGHTS = 255;
src/Render/Lit/Material/Pipeline.hs view
@@ -13,7 +13,6 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)@@ -23,7 +22,7 @@ import Render.Code.Lit (litMain, shadowFuns, structLight, structMaterial, brdfSpecular) import Render.Lit.Material.Model qualified as Model -type Config = Pipeline.Config '[Scene] Model.VertexAttrs Model.InstanceAttrs+type Config = Pipeline.Configure Pipeline () type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs  allocate@@ -59,11 +58,13 @@   pure p  config :: Tagged Scene DsBindings -> Config-config (Tagged set0) = zero+config (Tagged set0) = Pipeline.baseConfig   { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]   , Pipeline.cVertexCode   = Just vertCode   , Pipeline.cVertexInput  = vertexInput   , Pipeline.cFragmentCode = Just fragCode+  , Pipeline.cDepthWrite   = False -- XXX: Lit pipelines require depth pre-pass+  , Pipeline.cDepthCompare = Vk.COMPARE_OP_EQUAL   }   where     vertexInput = Pipeline.vertexInput@@ -74,8 +75,11 @@  configBlend :: Tagged Scene DsBindings -> Config configBlend tset0 = (config tset0)-  { Pipeline.cBlend      = True-  , Pipeline.cDepthWrite = False+  { Pipeline.cBlend =+      True+  , Pipeline.cDepthCompare =+      -- XXX: blended objects aren't a part of depth prepass+      Vk.COMPARE_OP_LESS_OR_EQUAL   }  vertCode :: ByteString@@ -84,6 +88,8 @@     #version 450     #extension GL_ARB_separate_shader_objects : enable +    invariant gl_Position;+     ${set0binding0}      // vertexPos@@ -130,6 +136,8 @@     #version 450     #extension GL_ARB_separate_shader_objects : enable     #extension GL_EXT_nonuniform_qualifier : enable++    layout(early_fragment_tests) in;      // XXX: copypasta from Lit.Colored     // TODO: move to spec constant
src/Render/Lit/Textured/Pipeline.hs view
@@ -13,7 +13,6 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)@@ -23,7 +22,7 @@ import Render.Code.Lit (litMain, shadowFuns, structLight, brdfSpecular) import Render.Lit.Textured.Model qualified as Model -type Config = Pipeline.Config '[Scene] Model.VertexAttrs Model.InstanceAttrs+type Config = Pipeline.Configure Pipeline () type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs  allocate@@ -59,11 +58,13 @@   pure p  config :: Tagged Scene DsBindings -> Config-config (Tagged set0) = zero+config (Tagged set0) = Pipeline.baseConfig   { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]   , Pipeline.cVertexCode   = Just vertCode   , Pipeline.cVertexInput  = vertexInput   , Pipeline.cFragmentCode = Just fragCode+  , Pipeline.cDepthWrite   = False -- XXX: Lit pipelines require depth pre-pass+  , Pipeline.cDepthCompare = Vk.COMPARE_OP_EQUAL   }   where     vertexInput = Pipeline.vertexInput@@ -76,7 +77,7 @@ configBlend :: Tagged Scene DsBindings -> Config configBlend tset0 = (config tset0)   { Pipeline.cBlend      = True-  , Pipeline.cDepthWrite = False+  , Pipeline.cDepthCompare = Vk.COMPARE_OP_LESS_OR_EQUAL   }  vertCode :: ByteString@@ -85,6 +86,8 @@     #version 450     #extension GL_ARB_separate_shader_objects : enable +    invariant gl_Position;+     ${set0binding0}      // vertexPos@@ -128,6 +131,8 @@     #version 450     #extension GL_ARB_separate_shader_objects : enable     #extension GL_EXT_nonuniform_qualifier : enable++    layout(early_fragment_tests) in;      // XXX: copypasta from Lit.Colored     // TODO: move to spec constant
src/Render/ShadowMap/Pipeline.hs view
@@ -15,7 +15,6 @@ import Data.Tagged (Tagged(..)) import Geomancy (Transform) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)@@ -24,7 +23,7 @@ import Render.DescSets.Set0 (vertexPos, instanceTransform) import Render.DescSets.Sun (Sun, pattern MAX_VIEWS) -type Config = Pipeline.Config '[Sun] () Transform+type Config = Pipeline.Configure Pipeline () type Pipeline = Pipeline.Pipeline '[Sun] () Transform  data Settings = Settings@@ -55,7 +54,7 @@   pure p  config :: Tagged Sun DsBindings -> Settings -> Config-config (Tagged set0) Settings{..} = zero+config (Tagged set0) Settings{..} = Pipeline.baseConfig   { Pipeline.cDescLayouts  = Tagged @'[Sun] [set0]   , Pipeline.cVertexCode   = Just vertCode   , Pipeline.cVertexInput  = vertexInput
src/Render/ShadowMap/RenderPass.hs view
@@ -44,8 +44,6 @@   getRenderArea   = smRenderArea  instance RenderPass ShadowMap where-  allocateRenderpass_ = error "Don't allocate ShadowMap via class"-  updateRenderpass    = error "Don't recreate ShadowMap on resize"   refcountRenderpass  = resourceTRefCount . smRelease  allocate
src/Render/Skybox/Pipeline.hs view
@@ -10,9 +10,7 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (Zero(..)) -import Engine.Vulkan.Pipeline (Config(..)) import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings) import Render.Code (compileVert, compileFrag, glsl)@@ -33,11 +31,11 @@   fmap snd . Pipeline.allocate     Nothing     multisample-    zero-      { cVertexCode         = Just vertCode-      , cFragmentCode       = Just fragCode-      , cDescLayouts        = Tagged @'[Scene] [set0]-      , cCull               = Vk.CULL_MODE_NONE+    Pipeline.baseConfig+      { Pipeline.cVertexCode   = Just vertCode+      , Pipeline.cFragmentCode = Just fragCode+      , Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]+      , Pipeline.cCull         = Vk.CULL_MODE_NONE       }  vertCode :: ByteString
src/Render/Unlit/Colored/Pipeline.hs view
@@ -13,7 +13,6 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)@@ -22,7 +21,7 @@ import Render.DescSets.Set0.Code (set0binding0) import Render.Unlit.Colored.Model qualified as Model -type Config = Pipeline.Config '[Scene] Model.VertexAttrs Model.InstanceAttrs+type Config = Pipeline.Configure Pipeline () type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs  allocate@@ -60,7 +59,7 @@   pure p  config :: Bool -> Tagged Scene DsBindings -> Config-config useDepth (Tagged set0) = zero+config useDepth (Tagged set0) = Pipeline.baseConfig   { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]   , Pipeline.cVertexCode   = Just vertCode   , Pipeline.cVertexInput  = vertexInput@@ -97,11 +96,12 @@     layout(location = 0) out vec4 fColor;      void main() {+      vec4 fPosition = iModel * vec4(vPosition, 1.0);+       gl_Position         = scene.projection         * scene.view-        * iModel-        * vec4(vPosition, 1.0);+        * fPosition;        fColor = vColor;       fColor.rgb * fColor.a;
src/Render/Unlit/Textured/Pipeline.hs view
@@ -14,7 +14,6 @@ import Control.Monad.Trans.Resource (ResourceT) import Data.Tagged (Tagged(..)) import Vulkan.Core10 qualified as Vk-import Vulkan.Zero (zero)  import Engine.Vulkan.Pipeline qualified as Pipeline import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)@@ -22,7 +21,7 @@ import Render.DescSets.Set0 (Scene, vertexPos, instanceTransform) import Render.Unlit.Textured.Model qualified as Model -type Config = Pipeline.Config '[Scene] Model.VertexAttrs Model.InstanceAttrs+type Config = Pipeline.Configure Pipeline () type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs  allocate@@ -58,7 +57,7 @@   pure p  config :: Tagged Scene DsBindings -> Config-config (Tagged set0) = zero+config (Tagged set0) = Pipeline.baseConfig   { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]   , Pipeline.cVertexCode   = Just vertCode   , Pipeline.cVertexInput  = vertexInput@@ -103,11 +102,12 @@     layout(location = 2) flat out ivec2 fTextureIds;      void main() {+      vec4 fPosition = iModel * vec4(vPosition, 1.0);+       gl_Position         = scene.projection         * scene.view-        * iModel-        * vec4(vPosition, 1.0);+        * fPosition;        fTexCoord     = vTexCoord * iTextureScaleOffset.st + iTextureScaleOffset.pq;       fTextureGamma = iTextureGamma;
+ src/Render/Unlit/TileMap/Model.hs view
@@ -0,0 +1,203 @@+module Render.Unlit.TileMap.Model+  ( Model+  , VertexAttrs+  , vkVertexAttrs++  , InstanceAttrs(..)+  -- , instanceAttrs++  , StorableAttrs+  -- , storableAttrs1++  , InstanceBuffers(..)++  , TileMapParams(..)+  , vkInstanceTileMap++  , allocateInstancesWith+  , allocateInstancesCoherent+  , allocateInstancesCoherent_+  , updateCoherentResize_+  , Transform+  ) where++import RIO++import Foreign (Storable(..))+import Geomancy (IVec4, Transform, Vec2)+import Geomancy.Vec3 qualified as Vec3+import RIO.Vector.Storable qualified as Storable+import UnliftIO.Resource (MonadResource, ReleaseKey, ResourceT, allocate)+import Vulkan.Core10 qualified as Vk+import Vulkan.NamedType ((:::))+import Vulkan.Zero (Zero(..))++import Engine.Vulkan.Types (HasVulkan)+import Resource.Buffer qualified as Buffer+import Resource.Model qualified as Model++type Model buf = Model.Indexed buf Vec3.Packed VertexAttrs++type VertexAttrs = "uv" ::: Vec2++vkVertexAttrs :: [Vk.Format]+vkVertexAttrs =+  [ Vk.FORMAT_R32G32_SFLOAT -- vTexCoord :: vec2+  ]++-- | Data for a single element.+data InstanceAttrs = InstanceAttrs+  { tilemapParams :: TileMapParams+  , transformMat4 :: Transform+  }++instance Zero InstanceAttrs where+  zero = InstanceAttrs+    { tilemapParams = zero+    , transformMat4 = mempty+    }++-- | Intermediate data to be shipped.+type StorableAttrs =+  ( Storable.Vector TileMapParams+  , Storable.Vector Transform+  )++-- | GPU-bound data.+data InstanceBuffers tilemapStage transformStage = InstanceBuffers+  { ibTileMap   :: InstanceTileMap tilemapStage+  , ibTransform :: InstanceTransform transformStage+  }++type InstanceTileMap stage = Buffer.Allocated stage TileMapParams++type InstanceTransform stage = Buffer.Allocated stage Transform++instance Model.HasVertexBuffers (InstanceBuffers tilemapStage transformStage) where+  type VertexBuffersOf (InstanceBuffers tilemapStage transformStage) = InstanceAttrs++  {-# INLINE getVertexBuffers #-}+  getVertexBuffers InstanceBuffers{..} =+    [ Buffer.aBuffer ibTileMap+    , Buffer.aBuffer ibTransform+    ]++  {-# INLINE getInstanceCount #-}+  getInstanceCount InstanceBuffers{..} =+    min+      (Buffer.aUsed ibTileMap)+      (Buffer.aUsed ibTransform)++data TileMapParams = TileMapParams+  { tmpTextureIds         :: IVec4+  , tmpViewOffset         :: Vec2+  , tmpViewportSize       :: Vec2+  , tmpMapTextureSize     :: Vec2+  , tmpTilesetTextureSize :: Vec2+  , tmpTileSize           :: Vec2+  }+  deriving (Show)++instance Zero TileMapParams where+  zero = TileMapParams+    { tmpTextureIds         = 0+    , tmpViewOffset         = 0+    , tmpViewportSize       = 1+    , tmpMapTextureSize     = 1+    , tmpTilesetTextureSize = 1+    , tmpTileSize           = 1+    }++instance Storable TileMapParams where+  alignment ~_ = 4++  sizeOf ~_ = 16 + 8 + 8 + 8 + 8 + 8++  poke ptr TileMapParams{..} = do+    pokeByteOff ptr  0 tmpTextureIds+    pokeByteOff ptr 16 tmpViewOffset+    pokeByteOff ptr 24 tmpViewportSize+    pokeByteOff ptr 32 tmpMapTextureSize+    pokeByteOff ptr 40 tmpTilesetTextureSize+    pokeByteOff ptr 48 tmpTileSize++  peek ptr = do+    tmpTextureIds         <- peekByteOff ptr  0+    tmpViewOffset         <- peekByteOff ptr 16+    tmpViewportSize       <- peekByteOff ptr 24+    tmpMapTextureSize     <- peekByteOff ptr 32+    tmpTilesetTextureSize <- peekByteOff ptr 48+    tmpTileSize           <- peekByteOff ptr 56+    pure TileMapParams{..}++vkInstanceTileMap :: [Vk.Format]+vkInstanceTileMap =+  [ Vk.FORMAT_R32G32B32A32_SINT -- tmpTextureIds         :: IVec4+  , Vk.FORMAT_R32G32_SFLOAT     -- tmpViewOffset         :: Vec2+  , Vk.FORMAT_R32G32_SFLOAT     -- tmpViewportSize       :: Vec2+  , Vk.FORMAT_R32G32_SFLOAT     -- tmpMapTextureSize     :: Vec2+  , Vk.FORMAT_R32G32_SFLOAT     -- tmpTilesetTextureSize :: Vec2+  , Vk.FORMAT_R32G32_SFLOAT     -- tmpTileSize           :: Vec2+  ]++allocateInstancesWith+  :: ( MonadResource m+     , MonadUnliftIO m+     )+  => (Vk.BufferUsageFlagBits -> Int -> Storable.Vector TileMapParams -> m (InstanceTileMap texture))+  -> (Vk.BufferUsageFlagBits -> Int -> Storable.Vector Transform -> m (InstanceTransform transform))+  -> (forall stage a . Buffer.Allocated stage a -> m ())+  -> [InstanceAttrs]+  -> m (ReleaseKey, InstanceBuffers texture transform)+allocateInstancesWith createTextures createTransforms bufferDestroy instances = do+  ul <- askUnliftIO+  allocate (create ul) (destroy ul)+  where+    textures   = Storable.fromList $ map tilemapParams instances+    transforms = Storable.fromList $ map transformMat4 instances+    numInstances = Storable.length textures++    create (UnliftIO ul) = ul do+      ibTileMap   <- createTextures   Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT numInstances textures+      ibTransform <- createTransforms Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT numInstances transforms+      pure InstanceBuffers{..}++    destroy (UnliftIO ul) InstanceBuffers{..} = ul do+      bufferDestroy ibTileMap+      bufferDestroy ibTransform++allocateInstancesCoherent+  :: ( MonadReader env m+     , HasVulkan env+     , MonadResource m+     , MonadUnliftIO m+     )+  => [InstanceAttrs]+  -> m (ReleaseKey, InstanceBuffers 'Buffer.Coherent 'Buffer.Coherent)+allocateInstancesCoherent instances = do+  context <- ask+  allocateInstancesWith+    (Buffer.createCoherent context)+    (Buffer.createCoherent context)+    (Buffer.destroy context)+    instances++allocateInstancesCoherent_+  :: (HasVulkan env)+  => Int+  -> ResourceT (RIO env) (InstanceBuffers 'Buffer.Coherent 'Buffer.Coherent)+allocateInstancesCoherent_ n =+  fmap snd $ allocateInstancesCoherent (replicate n zero)++updateCoherentResize_+  :: ( HasVulkan context+     , MonadUnliftIO m+     )+  => context+  -> InstanceBuffers 'Buffer.Coherent 'Buffer.Coherent+  -> (Storable.Vector TileMapParams, Storable.Vector Transform)+  -> m (InstanceBuffers 'Buffer.Coherent 'Buffer.Coherent)+updateCoherentResize_ context InstanceBuffers{..} (textures, transforms) =+  InstanceBuffers+    <$> Buffer.updateCoherentResize_ context ibTileMap textures+    <*> Buffer.updateCoherentResize_ context ibTransform transforms
+ src/Render/Unlit/TileMap/Pipeline.hs view
@@ -0,0 +1,179 @@+module Render.Unlit.TileMap.Pipeline+  ( Pipeline+  , allocate+  , allocateBlend++  , Config+  , config+  , configBlend+  ) where++import RIO+import Render.DescSets.Set0.Code++import Control.Monad.Trans.Resource (ResourceT)+import Data.Tagged (Tagged(..))+import Vulkan.Core10 qualified as Vk++import Engine.Vulkan.Pipeline qualified as Pipeline+import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)+import Render.Code (compileVert, compileFrag, glsl)+import Render.DescSets.Set0 (Scene, vertexPos, instanceTransform)+import Render.Unlit.TileMap.Model qualified as Model++type Config = Pipeline.Configure Pipeline ()+type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs++allocate+  :: ( HasVulkan env+     , HasRenderPass renderpass+     )+  => Vk.SampleCountFlagBits+  -> Tagged Scene DsBindings+  -> renderpass+  -> ResourceT (RIO env) Pipeline+allocate multisample tset0 rp = do+  (_, p) <- Pipeline.allocate+    Nothing+    multisample+    (config tset0)+    rp+  pure p++allocateBlend+  :: ( HasVulkan env+     , HasRenderPass renderpass+     )+  => Vk.SampleCountFlagBits+  -> Tagged Scene DsBindings+  -> renderpass+  -> ResourceT (RIO env) Pipeline+allocateBlend multisample tset0 rp = do+  (_, p) <- Pipeline.allocate+    Nothing+    multisample+    (configBlend tset0)+    rp+  pure p++config :: Tagged Scene DsBindings -> Config+config (Tagged set0) = Pipeline.baseConfig+  { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]+  , Pipeline.cVertexCode   = Just vertCode+  , Pipeline.cVertexInput  = vertexInput+  , Pipeline.cFragmentCode = Just fragCode+  }+  where+    vertexInput = Pipeline.vertexInput+      [ vertexPos+      , (Vk.VERTEX_INPUT_RATE_VERTEX,   Model.vkVertexAttrs)+      , (Vk.VERTEX_INPUT_RATE_INSTANCE, Model.vkInstanceTileMap)+      , instanceTransform+      ]++configBlend :: Tagged Scene DsBindings -> Config+configBlend tset0 = (config tset0)+  { Pipeline.cBlend      = True+  , Pipeline.cDepthWrite = False+  }++vertCode :: ByteString+vertCode =+  $(compileVert [glsl|+    #version 450+    #extension GL_ARB_separate_shader_objects : enable++    ${set0binding0}++    // vertexPos+    layout(location = 0) in vec3 vPosition;+    // vertexAttrs+    layout(location = 1) in vec2 vTexCoord;+    // tilemapParams+    layout(location = 2) in ivec4 iTextureIds; // combined: tileset, tileset sampler, map, repeat+    layout(location = 3) in vec2  iViewOffset;+    layout(location = 4) in vec2  iViewportSize;+    layout(location = 5) in vec2  iMapTextureSize;+    layout(location = 6) in vec2  iTilesetTextureSize;+    layout(location = 7) in vec2  iTileSize;++    // transformMat+    layout(location = 8) in mat4 iModel;++    layout(location = 0)      out  vec2 fTexCoord;+    layout(location = 1)      out  vec2 fPixCoord;+    layout(location = 2) flat out ivec4 fTextureIds;+    layout(location = 3) flat out  vec2 fTilesetTextureSize;+    layout(location = 4) flat out  vec2 fTileSize;++    void main() {+      vec4 fPosition = iModel * vec4(vPosition, 1.0);++      gl_Position+        = scene.projection+        * scene.view+        * fPosition;++      fPixCoord = (vTexCoord * iViewportSize) + iViewOffset;+      fTexCoord = fPixCoord / iMapTextureSize / iTileSize;++      fTextureIds = iTextureIds;+      fTilesetTextureSize = iTilesetTextureSize;+      fTileSize = iTileSize;+    }+  |])++fragCode :: ByteString+fragCode =+  $(compileFrag [glsl|+    #version 450+    #extension GL_ARB_separate_shader_objects : enable+    #extension GL_EXT_nonuniform_qualifier : enable++    ${set0binding1}+    ${set0binding2}++    layout(location = 0) in vec2 fTexCoord;+    layout(location = 1) in vec2 fPixCoord;++    // combined: tileset, tileset sampler, map, repeat+    layout(location = 2) flat in ivec4 fTextureIds;+    layout(location = 3) flat in vec2 fTilesetTextureSize;+    layout(location = 4) flat in vec2 fTileSize;++    layout(location = 0) out vec4 oColor;++    int tilesetTextureIx = fTextureIds[0];+    int tilesetSamplerIx = fTextureIds[1];+    int mapTextureIx     = fTextureIds[2];+    int repeatTiles      = fTextureIds[3];++    // TODO+    // const vec4 fTextureGamma = vec4(1.0);++    void main() {+      if (repeatTiles == 0 && (fTexCoord.x < 0.0 || fTexCoord.y < 0.0 || fTexCoord.x > 1.0 || fTexCoord.y > 1.0)) {+        discard;+      }++      vec4 map = texture(+        sampler2D(+          textures[nonuniformEXT(mapTextureIx)],+          samplers[7] // nearest+        ),+        fTexCoord+      );++      vec2 spriteOffset = floor(map.xy * 256.0) * fTileSize;+      vec2 spriteCoord = mod(fPixCoord, fTileSize);+      vec2 spriteUV = (spriteOffset + spriteCoord) / fTilesetTextureSize;++      oColor = texture(+        sampler2D(+          textures[nonuniformEXT(tilesetTextureIx)],+          samplers[nonuniformEXT(tilesetSamplerIx)]+        ),+        spriteUV+      );+    }+  |])
src/Stage/Loader/Setup.hs view
@@ -122,6 +122,7 @@ loaderStage loadAction nextStage uiSettings = Stage   { sTitle = "Loader" +  , sAllocateRP = allocateRenderPass   , sAllocateP  = allocatePipelines   , sInitialRS  = initialRunState loadAction nextStage uiSettings   , sInitialRR  = initialFrameResources uiSettings@@ -133,6 +134,14 @@   , sAfterLoop    = pure   }   where+    allocateRenderPass swapchain = do+      Basic.allocate+        Basic.Settings+          { sShadowLayers = 1+          , sShadowSize   = 1+          }+        swapchain+     allocatePipelines swapchain rps = do       logDebug "Allocating loader pipelines"       (_, samplers) <- Samplers.allocate